Skip to content

Commit 30256dd

Browse files
committed
feat: add local config and policy files
1 parent a4534e8 commit 30256dd

16 files changed

Lines changed: 628 additions & 296 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
node_modules/
22
.wrangler/
33
.dev.vars
4+
custom/v8s-local-config.json
45
*.old
56
*.swp
67
.DS_Store

README.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ Features:
1111
* URL redirection (301, 302, 303, 307 and 308)
1212
* Advanced redirection with splats (e.g., /news/* → /blog/:splat )
1313
* Continuous integration managed by Cloudflare Page Engine
14-
* Configurable destination blocklists with `blocked_keywords` in `v8s-blocklist.json`
14+
* Configurable destination policies with `blocked_keywords` in `v8s-policies.json`
1515
* Exact-match destination previews through `/expand/` and the `v8s --print <slug>` CLI helper
1616
* Link expiration dates with the `expires_at` field in `v8s-links.txt`
1717

@@ -40,13 +40,14 @@ As long as you secure your Github and Cloudflare accounts with robust authentica
4040
The 2.x runtime includes a few requested capabilities that are easy to
4141
miss because they are implemented through configuration or helper scripts:
4242

43-
* Short URL blocklists are handled through `blocked_keywords` in
44-
`defaults/v8s-blocklist.json` or an instance-specific
45-
`custom/v8s-blocklist.json`.
43+
* Short URL policies are handled through `blocked_keywords` in
44+
`defaults/v8s-policies.json` or an instance-specific
45+
`custom/v8s-policies.json`.
4646
* Looking up a destination from a short name is available in two places:
4747
`/expand/` previews exact-match short links in the deployed Worker, and
4848
`v8s --print <slug>` prints a destination from the generated local
49-
registry after `npm run build`.
49+
registry after `npm run build`. Run `npm run local-install` once to
50+
install the Bash/Zsh helper and opt in to local registry syncing.
5051
* Expiration dates are stored with the `expires_at` field in
5152
`v8s-links.txt`; `./scripts/lnk --expires-at DATE ...` writes that
5253
value, and the Worker treats expired links as expired at runtime.
Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,22 @@
55
"block_private_networks": true,
66
"block_localhost": true,
77
"block_auth_in_url": true,
8-
"allowed_protocols": ["http:", "https:"],
9-
"blocked_file_extensions": [".exe", ".scr", ".bat", ".cmd", ".com", ".msi", ".ps1", ".vbs", ".js", ".jar"]
10-
},
11-
"generated_sources": {
12-
"urlhaus_malware": {
13-
"enabled": true,
14-
"category": "malware",
15-
"severity": "high",
16-
"url": "https://urlhaus.abuse.ch/downloads/hostfile/"
17-
},
18-
"url_shorteners": {
19-
"enabled": true,
20-
"category": "shortener-loop",
21-
"severity": "medium",
22-
"url": "https://raw.githubusercontent.com/PeterDaveHello/url-shorteners/master/list"
23-
}
8+
"allowed_protocols": [
9+
"http:",
10+
"https:"
11+
],
12+
"blocked_file_extensions": [
13+
".exe",
14+
".scr",
15+
".bat",
16+
".cmd",
17+
".com",
18+
".msi",
19+
".ps1",
20+
".vbs",
21+
".js",
22+
".jar"
23+
]
2424
},
2525
"allow_domains": [
2626
{

defaults/public/llms-full.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ The runtime registry is private to the Worker runtime. Instance owners should no
4848

4949
The `/_stats/` dashboard exposes routing inventory and should be protected with Cloudflare Access or equivalent controls when deployed publicly.
5050

51-
The default blocklist rejects risky destinations such as credentialed URLs, non-HTTP(S) protocols, private IP ranges, known shortener chains, phishing lures, and high-risk executable downloads. Instance owners should add local policy in `custom/v8s-blocklist.json` and must not use the redirect engine for phishing, malware, undisclosed tracking, or other abuse.
51+
The default policy rejects risky destinations such as credentialed URLs, non-HTTP(S) protocols, private IP ranges, known shortener chains, phishing lures, and high-risk executable downloads. Instance owners should add local policy in `custom/v8s-policies.json` and must not use the redirect engine for phishing, malware, undisclosed tracking, or other abuse.
5252

5353
## Useful Files
5454

defaults/v8s-blocklist-categories.json

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,16 @@
55
"description": "Fake login pages, credential theft, and brand impersonation"
66
},
77
"malware": {
8-
"description": "Malware distribution, payload hosting, exploit delivery, and command-and-control infrastructure"
8+
"description": "Malware distribution, payload hosting, exploit delivery, and command-and-control infrastructure",
9+
"default_sources": [
10+
"urlhaus_malware"
11+
]
912
},
1013
"shortener-loop": {
11-
"description": "Public URL shorteners used to hide redirect chains or obscure the final destination"
14+
"description": "Public URL shorteners used to hide redirect chains or obscure the final destination",
15+
"default_sources": [
16+
"url_shorteners"
17+
]
1218
},
1319
"scanner-probe": {
1420
"description": "Automated vulnerability scanner paths that should not resolve as short links"
@@ -48,5 +54,19 @@
4854
"info": {
4955
"description": "Documentation, classification, or owner-defined informational category"
5056
}
57+
},
58+
"sources": {
59+
"urlhaus_malware": {
60+
"enabled": true,
61+
"category": "malware",
62+
"severity": "high",
63+
"url": "https://urlhaus.abuse.ch/downloads/hostfile/"
64+
},
65+
"url_shorteners": {
66+
"enabled": true,
67+
"category": "shortener-loop",
68+
"severity": "medium",
69+
"url": "https://raw.githubusercontent.com/PeterDaveHello/url-shorteners/master/list"
70+
}
5171
}
5272
}

defaults/v8s-local-config.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"schema_version": "1.0",
3+
"shell_helper": {
4+
"enabled": false,
5+
"install_path": "$XDG_CONFIG_HOME/zsh/v8s.sh",
6+
"rc_file": "$XDG_CONFIG_HOME/zsh/.zshrc"
7+
},
8+
"registry": {
9+
"local_path": "~/.v8s.json"
10+
},
11+
"repository": {
12+
"path": ""
13+
}
14+
}
Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,22 @@
55
"block_private_networks": true,
66
"block_localhost": true,
77
"block_auth_in_url": true,
8-
"allowed_protocols": ["http:", "https:"],
9-
"blocked_file_extensions": [".exe", ".scr", ".bat", ".cmd", ".com", ".msi", ".ps1", ".vbs", ".js", ".jar"]
10-
},
11-
"generated_sources": {
12-
"urlhaus_malware": {
13-
"enabled": true,
14-
"category": "malware",
15-
"severity": "high",
16-
"url": "https://urlhaus.abuse.ch/downloads/hostfile/"
17-
},
18-
"url_shorteners": {
19-
"enabled": true,
20-
"category": "shortener-loop",
21-
"severity": "medium",
22-
"url": "https://raw.githubusercontent.com/PeterDaveHello/url-shorteners/master/list"
23-
}
8+
"allowed_protocols": [
9+
"http:",
10+
"https:"
11+
],
12+
"blocked_file_extensions": [
13+
".exe",
14+
".scr",
15+
".bat",
16+
".cmd",
17+
".com",
18+
".msi",
19+
".ps1",
20+
".vbs",
21+
".js",
22+
".jar"
23+
]
2424
},
2525
"allow_domains": [
2626
{

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@
1212
"dev": "wrangler dev",
1313
"generate:blocklist": "node scripts/generate-blocklist.mjs build/blocklist.generated.json",
1414
"lint": "node scripts/lint.mjs",
15+
"local-install": "node scripts/local-install.mjs",
1516
"setup": "node scripts/install.mjs",
1617
"smoke:analytics": "npm run build && node scripts/smoke-analytics.mjs",
1718
"test": "node scripts/src/worker.test.mjs",
1819
"upgrade": "node scripts/upgrade.mjs",
20+
"update": "npm run upgrade",
1921
"validate": "node scripts/validate-registry.mjs build/v8s.json",
2022
"check": "node scripts/build.mjs && npm run lint && npm test",
2123
"check:targets": "node scripts/check-targets.mjs build/v8s.json",

scripts/blocklist-cli.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import fs from "node:fs";
44
import path from "node:path";
55

6-
const POLICY_PATH = process.env.BLOCKLIST_FILE || "custom/v8s-blocklist.json";
6+
const POLICY_PATH = process.env.V8S_POLICY_FILE || process.env.BLOCKLIST_FILE || "custom/v8s-policies.json";
77
const CATEGORIES_PATH = "defaults/v8s-blocklist-categories.json";
88

99
function usage() {

scripts/blocklist-policy.mjs

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,34 @@
11
import fs from "node:fs";
22
import net from "node:net";
33

4-
const DEFAULT_POLICY_PATH = "defaults/v8s-blocklist.json";
5-
const DEFAULT_CUSTOM_POLICY_PATH = "custom/v8s-blocklist.json";
4+
const DEFAULT_POLICY_PATH = "defaults/v8s-policies.json";
5+
const LEGACY_POLICY_PATH = "defaults/v8s-blocklist.json";
6+
const DEFAULT_CUSTOM_POLICY_PATH = "custom/v8s-policies.json";
7+
const LEGACY_CUSTOM_POLICY_PATH = "custom/v8s-blocklist.json";
68
const DEFAULT_GENERATED_POLICY_PATH = "build/blocklist.generated.json";
79

810
export function loadBlocklistPolicy(path = DEFAULT_POLICY_PATH) {
9-
const raw = fs.existsSync(path) ? JSON.parse(fs.readFileSync(path, "utf8")) : {};
10-
const custom = path === DEFAULT_POLICY_PATH && fs.existsSync(DEFAULT_CUSTOM_POLICY_PATH)
11-
? JSON.parse(fs.readFileSync(DEFAULT_CUSTOM_POLICY_PATH, "utf8"))
11+
const resolvedPath = resolvePolicyPath(path, LEGACY_POLICY_PATH);
12+
const raw = fs.existsSync(resolvedPath) ? JSON.parse(fs.readFileSync(resolvedPath, "utf8")) : {};
13+
const isDefaultPolicy = path === DEFAULT_POLICY_PATH || path === LEGACY_POLICY_PATH;
14+
const customPath = resolvePolicyPath(DEFAULT_CUSTOM_POLICY_PATH, LEGACY_CUSTOM_POLICY_PATH);
15+
const custom = isDefaultPolicy && fs.existsSync(customPath)
16+
? JSON.parse(fs.readFileSync(customPath, "utf8"))
1217
: {};
13-
const generated = path === DEFAULT_POLICY_PATH && fs.existsSync(DEFAULT_GENERATED_POLICY_PATH)
18+
const generated = isDefaultPolicy && fs.existsSync(DEFAULT_GENERATED_POLICY_PATH)
1419
? JSON.parse(fs.readFileSync(DEFAULT_GENERATED_POLICY_PATH, "utf8"))
1520
: {};
16-
const ownerPolicy = path === DEFAULT_POLICY_PATH ? mergePolicy(custom, raw) : raw;
21+
const ownerPolicy = isDefaultPolicy ? mergePolicy(custom, raw) : raw;
1722

1823
return normalizePolicy(mergePolicy(ownerPolicy, generated));
1924
}
2025

26+
function resolvePolicyPath(primary, legacy) {
27+
if (fs.existsSync(primary)) return primary;
28+
if (legacy && fs.existsSync(legacy)) return legacy;
29+
return primary;
30+
}
31+
2132
export function checkTargetUrl(target, policy = loadBlocklistPolicy()) {
2233
const violations = [];
2334
let url;

0 commit comments

Comments
 (0)