Skip to content

Commit 34b539f

Browse files
committed
refactor: use zod to parse config
1 parent 42299f4 commit 34b539f

12 files changed

Lines changed: 564 additions & 392 deletions

File tree

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
exports['test/config/config.default.test.ts should config default values keep stable 1'] = {
2+
"security": {
3+
"domainWhiteList": [],
4+
"protocolWhiteList": [],
5+
"defaultMiddleware": [
6+
"csrf",
7+
"hsts",
8+
"methodnoallow",
9+
"noopen",
10+
"nosniff",
11+
"csp",
12+
"xssProtection",
13+
"xframe",
14+
"dta"
15+
],
16+
"csrf": {
17+
"enable": true,
18+
"type": "ctoken",
19+
"ignoreJSON": false,
20+
"cookieName": "csrfToken",
21+
"sessionName": "csrfToken",
22+
"headerName": "x-csrf-token",
23+
"bodyName": "_csrf",
24+
"queryName": "_csrf",
25+
"rotateWhenInvalid": false,
26+
"useSession": false,
27+
"supportedRequests": [
28+
{
29+
"path": {},
30+
"methods": [
31+
"POST",
32+
"PATCH",
33+
"DELETE",
34+
"PUT",
35+
"CONNECT"
36+
]
37+
}
38+
],
39+
"refererWhiteList": [],
40+
"cookieOptions": {
41+
"signed": false,
42+
"httpOnly": false,
43+
"overwrite": true
44+
}
45+
},
46+
"xframe": {
47+
"enable": true,
48+
"value": "SAMEORIGIN"
49+
},
50+
"hsts": {
51+
"enable": false,
52+
"maxAge": 31536000,
53+
"includeSubdomains": false
54+
},
55+
"methodnoallow": {
56+
"enable": true
57+
},
58+
"noopen": {
59+
"enable": true
60+
},
61+
"nosniff": {
62+
"enable": true
63+
},
64+
"xssProtection": {
65+
"enable": true,
66+
"value": "1; mode=block"
67+
},
68+
"csp": {
69+
"enable": false,
70+
"policy": {}
71+
},
72+
"referrerPolicy": {
73+
"enable": false,
74+
"value": "no-referrer-when-downgrade"
75+
},
76+
"dta": {
77+
"enable": true
78+
},
79+
"ssrf": {}
80+
},
81+
"helper": {
82+
"shtml": {}
83+
}
84+
}

__snapshots__/csp.test.ts.js

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
exports['test/csp.test.ts should ignore path 1'] = {
2+
"domainWhiteList": [],
3+
"protocolWhiteList": [],
4+
"defaultMiddleware": "csp",
5+
"csrf": {
6+
"enable": true,
7+
"type": "ctoken",
8+
"ignoreJSON": false,
9+
"cookieName": "csrfToken",
10+
"sessionName": "csrfToken",
11+
"headerName": "x-csrf-token",
12+
"bodyName": "_csrf",
13+
"queryName": "_csrf",
14+
"rotateWhenInvalid": false,
15+
"useSession": false,
16+
"supportedRequests": [
17+
{
18+
"path": {},
19+
"methods": [
20+
"POST",
21+
"PATCH",
22+
"DELETE",
23+
"PUT",
24+
"CONNECT"
25+
]
26+
}
27+
],
28+
"refererWhiteList": [],
29+
"cookieOptions": {
30+
"signed": false,
31+
"httpOnly": false,
32+
"overwrite": true
33+
}
34+
},
35+
"xframe": {
36+
"enable": true,
37+
"value": "SAMEORIGIN"
38+
},
39+
"hsts": {
40+
"enable": false,
41+
"maxAge": 31536000,
42+
"includeSubdomains": false
43+
},
44+
"methodnoallow": {
45+
"enable": true
46+
},
47+
"noopen": {
48+
"enable": true
49+
},
50+
"nosniff": {
51+
"enable": true
52+
},
53+
"xssProtection": {
54+
"enable": true,
55+
"value": "1; mode=block"
56+
},
57+
"csp": {
58+
"ignore": "/api/",
59+
"enable": true,
60+
"policy": {
61+
"script-src": [
62+
"'self'",
63+
"'unsafe-inline'",
64+
"'unsafe-eval'",
65+
"www.google-analytics.com"
66+
],
67+
"style-src": [
68+
"'unsafe-inline'",
69+
"www.google-analytics.com"
70+
],
71+
"img-src": [
72+
"'self'",
73+
"data:",
74+
"www.google-analytics.com"
75+
],
76+
"frame-ancestors": [
77+
"'self'"
78+
],
79+
"report-uri": "http://pointman.domain.com/csp?app=csp"
80+
}
81+
},
82+
"referrerPolicy": {
83+
"enable": false,
84+
"value": "no-referrer-when-downgrade"
85+
},
86+
"dta": {
87+
"enable": true
88+
},
89+
"ssrf": {}
90+
}

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@
4646
"matcher": "^4.0.0",
4747
"nanoid": "^3.3.8",
4848
"type-is": "^1.6.18",
49-
"xss": "^1.0.3"
49+
"xss": "^1.0.3",
50+
"zod": "^3.24.1"
5051
},
5152
"devDependencies": {
5253
"@arethetypeswrong/cli": "^0.17.1",
@@ -66,6 +67,7 @@
6667
"eslint": "8",
6768
"eslint-config-egg": "14",
6869
"rimraf": "6",
70+
"snap-shot-it": "^7.9.10",
6971
"spy": "^1.0.0",
7072
"supertest": "^6.3.3",
7173
"tshy": "3",

src/app.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import assert from 'node:assert';
22
import type { ILifecycleBoot, EggCore } from '@eggjs/core';
33
import { preprocessConfig } from './lib/utils.js';
4+
import { SecurityConfig } from './config/config.default.js';
45

56
export default class AgentBoot implements ILifecycleBoot {
67
private readonly app;
@@ -24,6 +25,8 @@ export default class AgentBoot implements ILifecycleBoot {
2425
'[@eggjs/security/ap] `config.security.csrf.type` must be one of ' + legalTypes.join(', '));
2526
}
2627

28+
// parse config and check if config is legal
29+
app.config.security = SecurityConfig.parse(app.config.security);
2730
preprocessConfig(app.config.security);
2831
}
2932
}

src/app/middleware/securities.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,14 @@ import compose from 'koa-compose';
33
import { pathMatching } from 'egg-path-matching';
44
import { EggCore, MiddlewareFunc } from '@eggjs/core';
55
import securityMiddlewares from '../../lib/middlewares/index.js';
6+
import type { SecurityMiddlewareName } from '../../config/config.default.js';
67

78
export default (_: unknown, app: EggCore) => {
89
const options = app.config.security;
910
const middlewares: MiddlewareFunc[] = [];
10-
const defaultMiddleware = options.defaultMiddleware.split(',').map(m => m.trim()).filter(m => !!m);
11+
const defaultMiddlewares = typeof options.defaultMiddleware === 'string'
12+
? options.defaultMiddleware.split(',').map(m => m.trim()).filter(m => !!m) as SecurityMiddlewareName[]
13+
: options.defaultMiddleware;
1114

1215
if (options.match || options.ignore) {
1316
app.coreLogger.warn('[@eggjs/security/middleware/securities] Please set `match` or `ignore` on sub config');
@@ -19,8 +22,8 @@ export default (_: unknown, app: EggCore) => {
1922
options.csrf.cookieDomain = () => originalCookieDomain;
2023
}
2124

22-
defaultMiddleware.forEach(middlewareName => {
23-
const opt = Reflect.get(options, middlewareName);
25+
defaultMiddlewares.forEach(middlewareName => {
26+
const opt = Reflect.get(options, middlewareName) as any;
2427
if (opt === false) {
2528
app.coreLogger.warn('[egg-security] Please use `config.security.%s = { enable: false }` instead of `config.security.%s = false`', middlewareName, middlewareName);
2629
}
@@ -49,10 +52,7 @@ export default (_: unknown, app: EggCore) => {
4952
opt.matching = pathMatching(opt);
5053

5154
const createMiddleware = securityMiddlewares[middlewareName];
52-
if (!createMiddleware) {
53-
throw new TypeError(`[@eggjs/security/middleware/securities] Can't find middleware ${middlewareName}`);
54-
}
55-
const fn = createMiddleware(opt, app);
55+
const fn = createMiddleware(opt);
5656
middlewares.push(fn);
5757
app.coreLogger.info('[@eggjs/security/middleware/securities] use %s middleware', middlewareName);
5858
});

0 commit comments

Comments
 (0)