From fe289cb8414a1a43867c72c6a5bda3a80719b086 Mon Sep 17 00:00:00 2001 From: FiniteSkills Date: Fri, 3 Jul 2026 13:06:04 +0530 Subject: [PATCH] chore: add reusable local checks --- .github/workflows/ci.yml | 98 ++-------------------------------------- README.md | 31 +++++-------- package.json | 7 ++- scripts/check-json.mjs | 19 ++++++++ scripts/check-safety.mjs | 70 ++++++++++++++++++++++++++++ 5 files changed, 111 insertions(+), 114 deletions(-) create mode 100644 scripts/check-json.mjs create mode 100644 scripts/check-safety.mjs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 10a3588..05b31f3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,103 +34,13 @@ jobs: run: npm run test:evidence - name: JS syntax checks - run: | - node --check background.js - node --check content-script.js - node --check popup/popup.js - node --check scripts/generate-rules.mjs - node --check scripts/evaluate-test-set.mjs - node --check shared/config.js + run: npm run check:syntax - name: JSON parse checks - run: | - node - <<'NODE' - const fs = require("fs"); - const files = [ - "manifest.json", - "rules/rules.json", - "shared/tracker-catalog.json", - "shared/tracking-params.json", - "test/tracker-test-set.json", - "package.json" - ]; - for (const file of files) { - JSON.parse(fs.readFileSync(file, "utf8")); - } - console.log("JSON OK"); - NODE + run: npm run check:json - name: Generated files are in sync - run: | - git diff --exit-code -- rules/rules.json shared/config.js + run: npm run check:generated - name: Safety checks - run: | - node - <<'NODE' - const fs = require("fs"); - const path = require("path"); - - const sourceFiles = [ - "manifest.json", - "background.js", - "content-script.js", - "popup/popup.html", - "popup/popup.css", - "popup/popup.js", - "rules/rules.json", - "shared/config.js", - "shared/tracker-catalog.json", - "shared/tracking-params.json", - "scripts/generate-rules.mjs", - "scripts/evaluate-test-set.mjs", - "test/tracker-test-set.json" - ]; - const forbidden = [ - "declarativeNetRequestFeedback", - "webRequestBlocking", - "onRuleMatchedDebug", - "getMatchedRules", - "privacyScore", - "privacy-score" - ]; - const remoteCallPatterns = [ - /\bfetch\s*\(/, - /\bXMLHttpRequest\b/, - /\bsendBeacon\b/ - ]; - let failed = false; - - for (const file of sourceFiles) { - const text = fs.readFileSync(file, "utf8"); - for (const term of forbidden) { - if (text.includes(term)) { - console.error(`${file}: forbidden term "${term}"`); - failed = true; - } - } - for (const pattern of remoteCallPatterns) { - if (pattern.test(text)) { - console.error(`${file}: possible external runtime call ${pattern}`); - failed = true; - } - } - } - - const manifest = JSON.parse(fs.readFileSync("manifest.json", "utf8")); - const allowedPermissions = new Set([ - "declarativeNetRequest", - "storage", - "webNavigation" - ]); - for (const permission of manifest.permissions || []) { - if (!allowedPermissions.has(permission)) { - console.error(`manifest.json: unexpected permission "${permission}"`); - failed = true; - } - } - - if (failed) { - process.exit(1); - } - console.log("Safety checks OK"); - NODE + run: npm run check:safety diff --git a/README.md b/README.md index d00332b..45de436 100644 --- a/README.md +++ b/README.md @@ -119,32 +119,25 @@ Do not edit generated rules/config by hand unless you also update the generator ## Run Tests -Regenerate rules: +Run the same local validation layers that CI uses: ```bash -npm run generate:rules +npm run check ``` -Run the local ruleset evidence test: - -```bash -npm run test:evidence -``` +This regenerates rules, runs the evidence fixture, checks JavaScript syntax, +parses JSON data files, verifies generated files are in sync, and runs the +privacy/safety guardrails. -Useful syntax checks: +Useful individual checks: ```bash -node --check background.js -node --check content-script.js -node --check popup/popup.js -node --check scripts/generate-rules.mjs -node --check scripts/evaluate-test-set.mjs -``` - -Useful JSON checks: - -```bash -node -e "for (const f of ['manifest.json','rules/rules.json','shared/tracker-catalog.json','shared/tracking-params.json','test/tracker-test-set.json','package.json']) JSON.parse(require('fs').readFileSync(f,'utf8')); console.log('JSON OK')" +npm run generate:rules +npm run test:evidence +npm run check:syntax +npm run check:json +npm run check:generated +npm run check:safety ``` ## Manual Browser Check diff --git a/package.json b/package.json index 0db93c9..6ea70cb 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,11 @@ "description": "GetBlocked! is a free browser extension that reduces common third-party website trackers.", "scripts": { "generate:rules": "node scripts/generate-rules.mjs", - "test:evidence": "node scripts/evaluate-test-set.mjs" + "test:evidence": "node scripts/evaluate-test-set.mjs", + "check": "npm run generate:rules && npm run test:evidence && npm run check:syntax && npm run check:json && npm run check:generated && npm run check:safety", + "check:syntax": "node --check background.js && node --check content-script.js && node --check popup/popup.js && node --check scripts/generate-rules.mjs && node --check scripts/evaluate-test-set.mjs && node --check scripts/check-json.mjs && node --check scripts/check-safety.mjs && node --check shared/config.js", + "check:json": "node scripts/check-json.mjs", + "check:generated": "git diff --exit-code -- rules/rules.json shared/config.js", + "check:safety": "node scripts/check-safety.mjs" } } diff --git a/scripts/check-json.mjs b/scripts/check-json.mjs new file mode 100644 index 0000000..714c9fa --- /dev/null +++ b/scripts/check-json.mjs @@ -0,0 +1,19 @@ +import fs from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); +const files = [ + "manifest.json", + "rules/rules.json", + "shared/tracker-catalog.json", + "shared/tracking-params.json", + "test/tracker-test-set.json", + "package.json" +]; + +for (const file of files) { + JSON.parse(fs.readFileSync(path.join(rootDir, file), "utf8")); +} + +console.log(`JSON OK (${files.length} files)`); diff --git a/scripts/check-safety.mjs b/scripts/check-safety.mjs new file mode 100644 index 0000000..304c3f1 --- /dev/null +++ b/scripts/check-safety.mjs @@ -0,0 +1,70 @@ +import fs from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); +const sourceFiles = [ + "manifest.json", + "background.js", + "content-script.js", + "popup/popup.html", + "popup/popup.css", + "popup/popup.js", + "rules/rules.json", + "shared/config.js", + "shared/tracker-catalog.json", + "shared/tracking-params.json", + "scripts/generate-rules.mjs", + "scripts/evaluate-test-set.mjs", + "test/tracker-test-set.json" +]; +const forbidden = [ + "declarativeNetRequestFeedback", + "webRequestBlocking", + "onRuleMatchedDebug", + "getMatchedRules", + "privacyScore", + "privacy-score" +]; +const remoteCallPatterns = [ + /\bfetch\s*\(/, + /\bXMLHttpRequest\b/, + /\bsendBeacon\b/ +]; +const allowedPermissions = new Set([ + "declarativeNetRequest", + "storage", + "webNavigation" +]); + +let failed = false; + +for (const file of sourceFiles) { + const text = fs.readFileSync(path.join(rootDir, file), "utf8"); + for (const term of forbidden) { + if (text.includes(term)) { + console.error(`${file}: forbidden term "${term}"`); + failed = true; + } + } + for (const pattern of remoteCallPatterns) { + if (pattern.test(text)) { + console.error(`${file}: possible external runtime call ${pattern}`); + failed = true; + } + } +} + +const manifest = JSON.parse(fs.readFileSync(path.join(rootDir, "manifest.json"), "utf8")); +for (const permission of manifest.permissions || []) { + if (!allowedPermissions.has(permission)) { + console.error(`manifest.json: unexpected permission "${permission}"`); + failed = true; + } +} + +if (failed) { + process.exit(1); +} + +console.log("Safety checks OK");