@gfargo/doorman 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +506 -0
- package/bin/run +3 -0
- package/bin/run-deprecated +11 -0
- package/bin/run.ts +23 -0
- package/dist/bin/run.d.mts +2 -0
- package/dist/bin/run.d.ts +2 -0
- package/dist/bin/run.js +167 -0
- package/dist/bin/run.mjs +167 -0
- package/dist/src/index.d.mts +23 -0
- package/dist/src/index.d.ts +23 -0
- package/dist/src/index.js +1 -0
- package/dist/src/index.mjs +1 -0
- package/package.json +157 -0
- package/skills/doorman/SKILL.md +132 -0
- package/skills/doorman/rules-reference.md +478 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
declare const WORDPRESS_PATHS: string[];
|
|
2
|
+
declare const PHP_CONTROL_PANEL_PATHS: string[];
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Creates a doorman function that checks if a request's pathname matches any of the provided paths.
|
|
6
|
+
*
|
|
7
|
+
* @param paths - An array of string paths to block. If a request's pathname starts with any of these paths, the doorman will block the request.
|
|
8
|
+
* @returns A function that takes a request object containing a nextUrl.pathname and returns a boolean indicating whether the request should be blocked.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* const doorman = createDoorman(['/admin', '/private']);
|
|
13
|
+
* const isBlocked = doorman({ nextUrl: { pathname: '/admin/dashboard' } });
|
|
14
|
+
* // isBlocked will be true
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
declare function createDoorman(paths: string[]): (request: {
|
|
18
|
+
nextUrl: {
|
|
19
|
+
pathname: string;
|
|
20
|
+
};
|
|
21
|
+
}) => boolean;
|
|
22
|
+
|
|
23
|
+
export { PHP_CONTROL_PANEL_PATHS, WORDPRESS_PATHS, createDoorman };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
declare const WORDPRESS_PATHS: string[];
|
|
2
|
+
declare const PHP_CONTROL_PANEL_PATHS: string[];
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Creates a doorman function that checks if a request's pathname matches any of the provided paths.
|
|
6
|
+
*
|
|
7
|
+
* @param paths - An array of string paths to block. If a request's pathname starts with any of these paths, the doorman will block the request.
|
|
8
|
+
* @returns A function that takes a request object containing a nextUrl.pathname and returns a boolean indicating whether the request should be blocked.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* const doorman = createDoorman(['/admin', '/private']);
|
|
13
|
+
* const isBlocked = doorman({ nextUrl: { pathname: '/admin/dashboard' } });
|
|
14
|
+
* // isBlocked will be true
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
declare function createDoorman(paths: string[]): (request: {
|
|
18
|
+
nextUrl: {
|
|
19
|
+
pathname: string;
|
|
20
|
+
};
|
|
21
|
+
}) => boolean;
|
|
22
|
+
|
|
23
|
+
export { PHP_CONTROL_PANEL_PATHS, WORDPRESS_PATHS, createDoorman };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var o=Object.defineProperty;var c=Object.getOwnPropertyDescriptor;var m=Object.getOwnPropertyNames;var h=Object.prototype.hasOwnProperty;var l=(r,p)=>{for(var t in p)o(r,t,{get:p[t],enumerable:!0})},s=(r,p,t,n)=>{if(p&&typeof p=="object"||typeof p=="function")for(let e of m(p))!h.call(r,e)&&e!==t&&o(r,e,{get:()=>p[e],enumerable:!(n=c(p,e))||n.enumerable});return r};var x=r=>s(o({},"__esModule",{value:!0}),r);var w={};l(w,{PHP_CONTROL_PANEL_PATHS:()=>f,WORDPRESS_PATHS:()=>i,createDoorman:()=>a});module.exports=x(w);var i=["/wp-content/","/wp-admin/","/wp-includes/","/wp-json/","/wp-login.php","/xmlrpc.php"],f=["/ws.php","/xmrlpc.php","/makeasmtp.php","/text.php","/images/xmrlpc.php","/img/xmrlpc.php","/cgi-bin/xmrlpc.php","/css/xmrlpc.php","/alfa-rex2.php","/alfanew2.php"];function a(r){return p=>{let t=!1;for(let n of r)if(p.nextUrl.pathname.startsWith(n)){t=!0;break}return t}}0&&(module.exports={PHP_CONTROL_PANEL_PATHS,WORDPRESS_PATHS,createDoorman});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var o=["/wp-content/","/wp-admin/","/wp-includes/","/wp-json/","/wp-login.php","/xmlrpc.php"],a=["/ws.php","/xmrlpc.php","/makeasmtp.php","/text.php","/images/xmrlpc.php","/img/xmrlpc.php","/cgi-bin/xmrlpc.php","/css/xmrlpc.php","/alfa-rex2.php","/alfanew2.php"];function n(r){return t=>{let p=!1;for(let e of r)if(t.nextUrl.pathname.startsWith(e)){p=!0;break}return p}}export{a as PHP_CONTROL_PANEL_PATHS,o as WORDPRESS_PATHS,n as createDoorman};
|
package/package.json
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@gfargo/doorman",
|
|
3
|
+
"version": "2.1.0",
|
|
4
|
+
"description": "Manage firewall rules as code across multiple providers",
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"access": "public"
|
|
7
|
+
},
|
|
8
|
+
"bin": {
|
|
9
|
+
"doorman": "./bin/run",
|
|
10
|
+
"vercel-doorman": "./bin/run-deprecated"
|
|
11
|
+
},
|
|
12
|
+
"directories": {
|
|
13
|
+
"lib": "src",
|
|
14
|
+
"bin": "bin"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"bin",
|
|
19
|
+
"skills"
|
|
20
|
+
],
|
|
21
|
+
"agents": {
|
|
22
|
+
"skills": [
|
|
23
|
+
{
|
|
24
|
+
"name": "doorman",
|
|
25
|
+
"path": "./skills/doorman"
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
},
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "git+ssh://git@github.com/gfargo/doorman.git"
|
|
32
|
+
},
|
|
33
|
+
"scripts": {
|
|
34
|
+
"prebuild": "pnpm clean",
|
|
35
|
+
"build": "tsup-node",
|
|
36
|
+
"build:schema": "ts-node ./schema/generate-schema.ts; pnpm lint:fix; pnpm format:fix",
|
|
37
|
+
"build:watch": "tsup-node --watch",
|
|
38
|
+
"clean": "rimraf dist",
|
|
39
|
+
"commit": "cz",
|
|
40
|
+
"commitlint": "commitlint --edit",
|
|
41
|
+
"compile": "tsc",
|
|
42
|
+
"format": "prettier . --check",
|
|
43
|
+
"format:fix": "prettier . --write",
|
|
44
|
+
"lint": "eslint .",
|
|
45
|
+
"lint:fix": "eslint . --fix",
|
|
46
|
+
"start": "ts-node ./bin/run.ts",
|
|
47
|
+
"start:node": "node ./bin/run",
|
|
48
|
+
"test": "jest",
|
|
49
|
+
"test:watch": "jest --watchAll",
|
|
50
|
+
"test:coverage": "jest --coverage",
|
|
51
|
+
"test:ci": "jest --ci --coverage --watchAll=false",
|
|
52
|
+
"dev": "pnpm start",
|
|
53
|
+
"doorman": "pnpm start",
|
|
54
|
+
"prepare": "husky",
|
|
55
|
+
"release": "semantic-release",
|
|
56
|
+
"docs:dev": "echo 'Documentation server would start here'",
|
|
57
|
+
"validate:examples": "find examples -name '*.json' -exec pnpm start validate --config {} \\;",
|
|
58
|
+
"benchmark": "echo 'Performance benchmarks would run here'"
|
|
59
|
+
},
|
|
60
|
+
"keywords": [
|
|
61
|
+
"firewall",
|
|
62
|
+
"waf",
|
|
63
|
+
"IaC",
|
|
64
|
+
"security",
|
|
65
|
+
"vercel",
|
|
66
|
+
"cloudflare",
|
|
67
|
+
"cli"
|
|
68
|
+
],
|
|
69
|
+
"author": "Griffen Fargo <ghfargo@gmail.com>",
|
|
70
|
+
"license": "MIT",
|
|
71
|
+
"devDependencies": {
|
|
72
|
+
"@commitlint/cli": "^20.5.3",
|
|
73
|
+
"@commitlint/config-conventional": "^20.5.3",
|
|
74
|
+
"@jest/globals": "^30.3.0",
|
|
75
|
+
"@semantic-release/changelog": "^6.0.3",
|
|
76
|
+
"@semantic-release/git": "^10.0.1",
|
|
77
|
+
"@semantic-release/github": "^12.0.6",
|
|
78
|
+
"@semantic-release/npm": "^12.0.1",
|
|
79
|
+
"@tsconfig/node20": "^20.1.4",
|
|
80
|
+
"@types/jest": "^30.0.0",
|
|
81
|
+
"@types/node": "^20.12.12",
|
|
82
|
+
"@types/prompts": "^2.4.9",
|
|
83
|
+
"@types/signale": "^1.4.7",
|
|
84
|
+
"@types/yargs": "^17.0.32",
|
|
85
|
+
"@typescript-eslint/eslint-plugin": "^8.59.1",
|
|
86
|
+
"@typescript-eslint/parser": "^8.59.1",
|
|
87
|
+
"commitizen": "^4.3.0",
|
|
88
|
+
"cz-conventional-changelog": "^3.3.0",
|
|
89
|
+
"eslint": "^9.39.4",
|
|
90
|
+
"eslint-config-prettier": "^9.1.0",
|
|
91
|
+
"eslint-plugin-jest": "^29.15.2",
|
|
92
|
+
"eslint-plugin-prettier": "^5.1.3",
|
|
93
|
+
"eslint-plugin-unused-imports": "^4.4.1",
|
|
94
|
+
"globals": "^17.6.0",
|
|
95
|
+
"husky": "^9.0.11",
|
|
96
|
+
"jest": "^30.3.0",
|
|
97
|
+
"prettier": "^3.2.5",
|
|
98
|
+
"rimraf": "^6.1.3",
|
|
99
|
+
"semantic-release": "^25.0.3",
|
|
100
|
+
"ts-jest": "^29.4.9",
|
|
101
|
+
"ts-json-schema-generator": "^2.3.0",
|
|
102
|
+
"ts-node": "^10.9.2",
|
|
103
|
+
"tsup": "^8.5.1",
|
|
104
|
+
"typescript": "^5.4.5",
|
|
105
|
+
"typescript-eslint": "^8.59.1"
|
|
106
|
+
},
|
|
107
|
+
"dependencies": {
|
|
108
|
+
"ajv": "^8.18.0",
|
|
109
|
+
"chalk": "^5.3.0",
|
|
110
|
+
"cli-table3": "^0.6.5",
|
|
111
|
+
"consola": "^3.4.2",
|
|
112
|
+
"dotenv": "^16.4.5",
|
|
113
|
+
"find-up": "^6.3.0",
|
|
114
|
+
"yargs": "^17.7.2",
|
|
115
|
+
"zod": "^3.23.8"
|
|
116
|
+
},
|
|
117
|
+
"config": {
|
|
118
|
+
"commitizen": {
|
|
119
|
+
"path": "cz-conventional-changelog"
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
"pnpm": {
|
|
123
|
+
"overrides": {
|
|
124
|
+
"micromatch@<4.0.8": ">=4.0.8",
|
|
125
|
+
"cross-spawn": "^7.0.6",
|
|
126
|
+
"nanoid": ">=3.3.8",
|
|
127
|
+
"@octokit/request-error": ">=6.1.7",
|
|
128
|
+
"@octokit/endpoint": ">=10.1.3",
|
|
129
|
+
"@octokit/request": ">=9.2.1",
|
|
130
|
+
"@octokit/plugin-paginate-rest": ">=11.4.1",
|
|
131
|
+
"braces": ">=3.0.3",
|
|
132
|
+
"tmp": ">=0.2.6",
|
|
133
|
+
"esbuild": ">=0.25.0",
|
|
134
|
+
"@babel/helpers": ">=7.26.10",
|
|
135
|
+
"@babel/core": ">=7.29.6",
|
|
136
|
+
"handlebars": ">=4.7.9",
|
|
137
|
+
"minimatch@<3.1.4": ">=3.1.4",
|
|
138
|
+
"minimatch@>=9.0.0 <9.0.7": ">=9.0.7",
|
|
139
|
+
"glob@>=10.2.0 <10.5.0": ">=10.5.0",
|
|
140
|
+
"brace-expansion@<1.1.13": ">=1.1.13",
|
|
141
|
+
"brace-expansion@>=2.0.0 <2.0.3": ">=2.0.3",
|
|
142
|
+
"brace-expansion": ">=5.0.9",
|
|
143
|
+
"flatted@<3.4.2": ">=3.4.2",
|
|
144
|
+
"picomatch@<2.3.2": ">=2.3.2",
|
|
145
|
+
"js-yaml": ">=4.3.1",
|
|
146
|
+
"ajv@<6.14.0": "6.14.0",
|
|
147
|
+
"lodash@<=4.17.23": ">=4.18.0",
|
|
148
|
+
"lodash-es@<=4.17.23": ">=4.18.0",
|
|
149
|
+
"rollup@>=4.0.0 <4.59.0": ">=4.59.0",
|
|
150
|
+
"postcss@<8.5.10": ">=8.5.10",
|
|
151
|
+
"yaml@>=2.0.0 <2.8.3": ">=2.8.3",
|
|
152
|
+
"diff@>=4.0.0 <4.0.4": ">=4.0.4",
|
|
153
|
+
"undici": ">=7.29.0",
|
|
154
|
+
"fast-uri": ">=3.1.5"
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: doorman
|
|
3
|
+
description: 'Use when managing Vercel or Cloudflare WAF rules as code, configuring firewall rules, IP blocking, rate limiting, bot protection, or automating multi-provider security configuration deployment with Doorman CLI.'
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Doorman
|
|
7
|
+
|
|
8
|
+
Multi-provider WAF automation as code. Manage Vercel Firewall and Cloudflare WAF rules in version-controlled `.doorman.json` files with automated deployment via CLI.
|
|
9
|
+
|
|
10
|
+
## Key Concepts
|
|
11
|
+
|
|
12
|
+
- **Config file**: `.doorman.json` (also supports legacy `vercel-firewall.config.json`)
|
|
13
|
+
- **Providers**: Vercel (default) and Cloudflare (`--provider cloudflare`)
|
|
14
|
+
- **Workflow**: Edit config → validate → diff → sync
|
|
15
|
+
- **Schema**: `https://doorman.griffen.codes/schema.json`
|
|
16
|
+
|
|
17
|
+
## Commands
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
doorman init --interactive # Create new config
|
|
21
|
+
doorman validate # Check config syntax
|
|
22
|
+
doorman status # Sync status + health score
|
|
23
|
+
doorman list # Show deployed rules
|
|
24
|
+
doorman diff # Local vs remote differences
|
|
25
|
+
doorman sync # Deploy local config to provider
|
|
26
|
+
doorman download # Pull remote rules to local config
|
|
27
|
+
doorman template <name> # Add pre-built rule template
|
|
28
|
+
doorman watch # Auto-sync on file changes
|
|
29
|
+
doorman backup # Create config backup
|
|
30
|
+
doorman export --format <fmt> # Export as markdown|json|yaml|terraform
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
All commands accept `--provider vercel|cloudflare` and `--config <path>`.
|
|
34
|
+
|
|
35
|
+
## Environment Variables
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# Vercel
|
|
39
|
+
VERCEL_TOKEN=your_token
|
|
40
|
+
VERCEL_PROJECT_ID=prj_xxx
|
|
41
|
+
VERCEL_TEAM_ID=team_xxx
|
|
42
|
+
|
|
43
|
+
# Cloudflare
|
|
44
|
+
CLOUDFLARE_API_TOKEN=your_token
|
|
45
|
+
CLOUDFLARE_ZONE_ID=zone_xxx
|
|
46
|
+
CLOUDFLARE_ACCOUNT_ID=acc_xxx # optional, enables Lists API
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Config Structure
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"$schema": "https://doorman.griffen.codes/schema.json",
|
|
54
|
+
"projectId": "prj_xxx",
|
|
55
|
+
"teamId": "team_xxx",
|
|
56
|
+
"rules": [],
|
|
57
|
+
"ips": []
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
For Cloudflare, use `provider` and `providers` fields instead of `projectId`/`teamId`.
|
|
62
|
+
|
|
63
|
+
## Rule Quick Reference
|
|
64
|
+
|
|
65
|
+
```json
|
|
66
|
+
{
|
|
67
|
+
"id": "rule_block_admin",
|
|
68
|
+
"name": "Block Admin Access",
|
|
69
|
+
"description": "Block unauthorized admin access",
|
|
70
|
+
"active": true,
|
|
71
|
+
"conditionGroup": [
|
|
72
|
+
{
|
|
73
|
+
"conditions": [
|
|
74
|
+
{ "type": "path", "op": "pre", "value": "/admin" },
|
|
75
|
+
{ "type": "method", "op": "eq", "value": "POST" }
|
|
76
|
+
]
|
|
77
|
+
}
|
|
78
|
+
],
|
|
79
|
+
"action": {
|
|
80
|
+
"mitigate": {
|
|
81
|
+
"action": "deny"
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**Logic**: Conditions within a group are AND'd. Multiple groups are OR'd.
|
|
88
|
+
|
|
89
|
+
**Condition types**: `path`, `method`, `host`, `user_agent`, `ip_address`, `header`, `query`, `cookie`, `geo_country`, `geo_city`, `geo_continent`, `scheme`
|
|
90
|
+
|
|
91
|
+
**Operators**: `eq`, `pre` (starts_with), `suf` (ends_with), `sub` (contains), `inc` (in array), `re` (regex), `ex` (exists), `nex` (not exists)
|
|
92
|
+
|
|
93
|
+
**Actions**: `deny`, `challenge`, `rate_limit`, `redirect`, `log`, `bypass`
|
|
94
|
+
|
|
95
|
+
**See `rules-reference.md` for complete field documentation, advanced patterns, and examples.**
|
|
96
|
+
|
|
97
|
+
## IP Blocking
|
|
98
|
+
|
|
99
|
+
```json
|
|
100
|
+
{
|
|
101
|
+
"ips": [{ "ip": "192.168.1.0/24", "action": "deny", "hostname": "bad-subnet", "notes": "Blocked for abuse" }]
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Templates
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
doorman template bad-bots # Block malicious bots
|
|
109
|
+
doorman template ai-bots # Block AI crawlers
|
|
110
|
+
doorman template wordpress # Block WordPress attack paths
|
|
111
|
+
doorman template block-ofac-sanctioned-countries # OFAC compliance
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Workflow
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
# Add a rule: edit .doorman.json, then:
|
|
118
|
+
doorman validate && doorman sync
|
|
119
|
+
|
|
120
|
+
# Pull existing rules from provider:
|
|
121
|
+
doorman download
|
|
122
|
+
|
|
123
|
+
# Check what would change before deploying:
|
|
124
|
+
doorman diff
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Resources
|
|
128
|
+
|
|
129
|
+
- [Docs](https://doorman.griffen.codes/docs)
|
|
130
|
+
- [GitHub](https://github.com/gfargo/doorman)
|
|
131
|
+
- [Wiki](https://github.com/gfargo/doorman/wiki)
|
|
132
|
+
- [Examples](https://github.com/gfargo/doorman/tree/main/examples)
|