Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 4 additions & 94 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
31 changes: 12 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
19 changes: 19 additions & 0 deletions scripts/check-json.mjs
Original file line number Diff line number Diff line change
@@ -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)`);
70 changes: 70 additions & 0 deletions scripts/check-safety.mjs
Original file line number Diff line number Diff line change
@@ -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");
Loading