@hatem427/code-guard-ci 3.0.0 → 3.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/config/fastify.config.ts +326 -0
- package/config/hono.config.ts +331 -0
- package/config/nestjs.config.ts +500 -0
- package/config/node.config.ts +425 -0
- package/config/python.config.ts +512 -0
- package/dist/config/fastify.config.d.ts +17 -0
- package/dist/config/fastify.config.d.ts.map +1 -0
- package/dist/config/fastify.config.js +279 -0
- package/dist/config/fastify.config.js.map +1 -0
- package/dist/config/hono.config.d.ts +17 -0
- package/dist/config/hono.config.d.ts.map +1 -0
- package/dist/config/hono.config.js +287 -0
- package/dist/config/hono.config.js.map +1 -0
- package/dist/config/nestjs.config.d.ts +17 -0
- package/dist/config/nestjs.config.d.ts.map +1 -0
- package/dist/config/nestjs.config.js +440 -0
- package/dist/config/nestjs.config.js.map +1 -0
- package/dist/config/node.config.d.ts +17 -0
- package/dist/config/node.config.d.ts.map +1 -0
- package/dist/config/node.config.js +363 -0
- package/dist/config/node.config.js.map +1 -0
- package/dist/config/python.config.d.ts +15 -0
- package/dist/config/python.config.d.ts.map +1 -0
- package/dist/config/python.config.js +475 -0
- package/dist/config/python.config.js.map +1 -0
- package/dist/scripts/auto-fix.d.ts +5 -0
- package/dist/scripts/auto-fix.d.ts.map +1 -1
- package/dist/scripts/auto-fix.js +5 -0
- package/dist/scripts/auto-fix.js.map +1 -1
- package/dist/scripts/cli.js +2 -2
- package/dist/scripts/cli.js.map +1 -1
- package/dist/scripts/config-generators/ai-config-generator.d.ts.map +1 -1
- package/dist/scripts/config-generators/ai-config-generator.js +6 -0
- package/dist/scripts/config-generators/ai-config-generator.js.map +1 -1
- package/dist/scripts/config-generators/eslint-generator.d.ts.map +1 -1
- package/dist/scripts/config-generators/eslint-generator.js +108 -0
- package/dist/scripts/config-generators/eslint-generator.js.map +1 -1
- package/dist/scripts/config-generators/frameworks/fastify.d.ts +6 -0
- package/dist/scripts/config-generators/frameworks/fastify.d.ts.map +1 -0
- package/dist/scripts/config-generators/frameworks/fastify.js +68 -0
- package/dist/scripts/config-generators/frameworks/fastify.js.map +1 -0
- package/dist/scripts/config-generators/frameworks/hono.d.ts +6 -0
- package/dist/scripts/config-generators/frameworks/hono.d.ts.map +1 -0
- package/dist/scripts/config-generators/frameworks/hono.js +63 -0
- package/dist/scripts/config-generators/frameworks/hono.js.map +1 -0
- package/dist/scripts/config-generators/frameworks/index.d.ts +3 -0
- package/dist/scripts/config-generators/frameworks/index.d.ts.map +1 -1
- package/dist/scripts/config-generators/frameworks/index.js +7 -1
- package/dist/scripts/config-generators/frameworks/index.js.map +1 -1
- package/dist/scripts/config-generators/frameworks/nestjs.d.ts +6 -0
- package/dist/scripts/config-generators/frameworks/nestjs.d.ts.map +1 -0
- package/dist/scripts/config-generators/frameworks/nestjs.js +83 -0
- package/dist/scripts/config-generators/frameworks/nestjs.js.map +1 -0
- package/dist/scripts/config-generators/frameworks/node.d.ts +2 -2
- package/dist/scripts/config-generators/frameworks/node.d.ts.map +1 -1
- package/dist/scripts/config-generators/frameworks/node.js +56 -11
- package/dist/scripts/config-generators/frameworks/node.js.map +1 -1
- package/dist/scripts/config-generators/typescript-generator.d.ts.map +1 -1
- package/dist/scripts/config-generators/typescript-generator.js +33 -0
- package/dist/scripts/config-generators/typescript-generator.js.map +1 -1
- package/dist/scripts/config-generators/vscode-generator.d.ts.map +1 -1
- package/dist/scripts/config-generators/vscode-generator.js +73 -0
- package/dist/scripts/config-generators/vscode-generator.js.map +1 -1
- package/dist/scripts/generate-pr-checklist.d.ts +5 -0
- package/dist/scripts/generate-pr-checklist.d.ts.map +1 -1
- package/dist/scripts/generate-pr-checklist.js +6 -1
- package/dist/scripts/generate-pr-checklist.js.map +1 -1
- package/dist/scripts/postinstall.js +38 -0
- package/dist/scripts/postinstall.js.map +1 -1
- package/dist/scripts/precommit-check.d.ts +13 -0
- package/dist/scripts/precommit-check.d.ts.map +1 -1
- package/dist/scripts/precommit-check.js +288 -5
- package/dist/scripts/precommit-check.js.map +1 -1
- package/dist/scripts/utils/naming-validator.d.ts.map +1 -1
- package/dist/scripts/utils/naming-validator.js +96 -2
- package/dist/scripts/utils/naming-validator.js.map +1 -1
- package/dist/scripts/utils/project-detector.d.ts +12 -9
- package/dist/scripts/utils/project-detector.d.ts.map +1 -1
- package/dist/scripts/utils/project-detector.js +63 -11
- package/dist/scripts/utils/project-detector.js.map +1 -1
- package/dist/scripts/utils/structure-validator.d.ts.map +1 -1
- package/dist/scripts/utils/structure-validator.js +50 -0
- package/dist/scripts/utils/structure-validator.js.map +1 -1
- package/package.json +10 -3
- package/scripts/auto-fix.ts +5 -0
- package/scripts/cli.ts +2 -2
- package/scripts/config-generators/ai-config-generator.ts +9 -0
- package/scripts/config-generators/eslint-generator.ts +110 -0
- package/scripts/config-generators/frameworks/fastify.ts +65 -0
- package/scripts/config-generators/frameworks/hono.ts +60 -0
- package/scripts/config-generators/frameworks/index.ts +3 -0
- package/scripts/config-generators/frameworks/nestjs.ts +80 -0
- package/scripts/config-generators/frameworks/node.ts +57 -11
- package/scripts/config-generators/typescript-generator.ts +36 -0
- package/scripts/config-generators/vscode-generator.ts +84 -0
- package/scripts/generate-pr-checklist.ts +6 -1
- package/scripts/postinstall.ts +38 -0
- package/scripts/precommit-check.ts +334 -6
- package/scripts/utils/naming-validator.ts +104 -2
- package/scripts/utils/project-detector.ts +78 -11
- package/scripts/utils/structure-validator.ts +54 -0
- package/templates/feature-doc-backend.md +114 -0
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* ============================================================================
|
|
4
|
+
* node.config.ts — Express / Node.js-specific coding rules
|
|
5
|
+
* ============================================================================
|
|
6
|
+
*
|
|
7
|
+
* Registers rules that apply to Express and generic Node.js backend projects:
|
|
8
|
+
* - Route handler / service separation
|
|
9
|
+
* - Async error propagation
|
|
10
|
+
* - Input validation enforcement
|
|
11
|
+
* - Security: helmet, CORS, parameterized queries
|
|
12
|
+
* - Environment configuration patterns
|
|
13
|
+
* - Graceful shutdown
|
|
14
|
+
*/
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.nodeRules = void 0;
|
|
17
|
+
const guidelines_config_1 = require("./guidelines.config");
|
|
18
|
+
const nodeRules = [
|
|
19
|
+
// ── Architecture ──────────────────────────────────────────────────────────
|
|
20
|
+
// ─────────────────────────────────────────
|
|
21
|
+
// RULE: node-thin-route-handler
|
|
22
|
+
// ROLE: Enforce separation of HTTP concerns from business logic
|
|
23
|
+
// PURPOSE: Route handlers that contain business logic cannot be unit-tested
|
|
24
|
+
// without spinning up an Express server. Logic should live in
|
|
25
|
+
// service functions that are independently testable.
|
|
26
|
+
// EXAMPLE:
|
|
27
|
+
// WRONG:
|
|
28
|
+
// router.post('/users', async (req, res) => {
|
|
29
|
+
// const salt = await bcrypt.genSalt();
|
|
30
|
+
// const hash = await bcrypt.hash(req.body.password, salt);
|
|
31
|
+
// const user = await db.users.create({ ...req.body, password: hash });
|
|
32
|
+
// res.status(201).json(user);
|
|
33
|
+
// });
|
|
34
|
+
// RIGHT:
|
|
35
|
+
// router.post('/users', async (req, res, next) => {
|
|
36
|
+
// try {
|
|
37
|
+
// const user = await userService.createUser(req.body);
|
|
38
|
+
// res.status(201).json(user);
|
|
39
|
+
// } catch (err) { next(err); }
|
|
40
|
+
// });
|
|
41
|
+
// ─────────────────────────────────────────
|
|
42
|
+
{
|
|
43
|
+
id: 'node-thin-route-handler',
|
|
44
|
+
label: 'Keep route handlers thin — delegate to services',
|
|
45
|
+
description: 'Route handlers should only parse the request, call a service, and send the response. Business logic belongs in service modules.',
|
|
46
|
+
severity: 'warning',
|
|
47
|
+
fileExtensions: ['ts', 'js'],
|
|
48
|
+
pattern: null,
|
|
49
|
+
customCheck: (file) => {
|
|
50
|
+
const violations = [];
|
|
51
|
+
if (!file.relativePath.includes('/routes/') &&
|
|
52
|
+
!file.relativePath.endsWith('.router.ts') &&
|
|
53
|
+
!file.relativePath.endsWith('.routes.ts')) {
|
|
54
|
+
return [];
|
|
55
|
+
}
|
|
56
|
+
const businessPatterns = [
|
|
57
|
+
{ regex: /bcrypt\.(hash|compare|genSalt)/g, label: 'bcrypt hashing' },
|
|
58
|
+
{ regex: /\.save\s*\(|\.create\s*\(/g, label: 'direct ORM calls' },
|
|
59
|
+
{ regex: /jwt\.sign|jwt\.verify/g, label: 'JWT operations' },
|
|
60
|
+
];
|
|
61
|
+
for (let i = 0; i < file.lines.length; i++) {
|
|
62
|
+
for (const { regex, label } of businessPatterns) {
|
|
63
|
+
regex.lastIndex = 0;
|
|
64
|
+
if (regex.test(file.lines[i])) {
|
|
65
|
+
violations.push({
|
|
66
|
+
line: i + 1,
|
|
67
|
+
message: `Business logic (${label}) found in route handler. Move to a service module.`,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return violations;
|
|
73
|
+
},
|
|
74
|
+
applicableTo: ['node'],
|
|
75
|
+
category: 'Architecture',
|
|
76
|
+
},
|
|
77
|
+
// ── Async & Error Handling ─────────────────────────────────────────────────
|
|
78
|
+
// ─────────────────────────────────────────
|
|
79
|
+
// RULE: node-async-next-error
|
|
80
|
+
// ROLE: Enforce error propagation in async route handlers
|
|
81
|
+
// PURPOSE: Without next(err), an error thrown in an async handler
|
|
82
|
+
// produces an unhandled promise rejection, not a proper HTTP 500.
|
|
83
|
+
// Wrapping with try/catch + next(err) — or using express-async-errors
|
|
84
|
+
// — ensures errors reach the global error-handling middleware.
|
|
85
|
+
// EXAMPLE:
|
|
86
|
+
// WRONG:
|
|
87
|
+
// router.get('/users', async (req, res) => {
|
|
88
|
+
// const users = await userService.findAll(); // throws → unhandled rejection
|
|
89
|
+
// res.json(users);
|
|
90
|
+
// });
|
|
91
|
+
// RIGHT:
|
|
92
|
+
// router.get('/users', async (req, res, next) => {
|
|
93
|
+
// try {
|
|
94
|
+
// const users = await userService.findAll();
|
|
95
|
+
// res.json(users);
|
|
96
|
+
// } catch (err) { next(err); }
|
|
97
|
+
// });
|
|
98
|
+
// ─────────────────────────────────────────
|
|
99
|
+
{
|
|
100
|
+
id: 'node-async-next-error',
|
|
101
|
+
label: 'Async route handlers must forward errors to next()',
|
|
102
|
+
description: 'Async Express route handlers must use try/catch and call next(err) on failure, or use express-async-errors. Unhandled rejections bypass error-handling middleware.',
|
|
103
|
+
severity: 'error',
|
|
104
|
+
fileExtensions: ['ts', 'js'],
|
|
105
|
+
pattern: null,
|
|
106
|
+
customCheck: (file) => {
|
|
107
|
+
const violations = [];
|
|
108
|
+
if (!file.relativePath.includes('/routes/') &&
|
|
109
|
+
!file.relativePath.endsWith('.router.ts') &&
|
|
110
|
+
!file.relativePath.endsWith('.routes.ts') &&
|
|
111
|
+
!file.relativePath.endsWith('.router.js')) {
|
|
112
|
+
return [];
|
|
113
|
+
}
|
|
114
|
+
// Detect async route handlers without try/catch or next(err)
|
|
115
|
+
const asyncHandlerRegex = /router\.\w+\(['"`][^'"]+['"`]\s*,\s*async\s*\(req/g;
|
|
116
|
+
let match;
|
|
117
|
+
while ((match = asyncHandlerRegex.exec(file.content)) !== null) {
|
|
118
|
+
const afterMatch = file.content.slice(match.index, match.index + 500);
|
|
119
|
+
if (!afterMatch.includes('try {') && !afterMatch.includes('next(err')) {
|
|
120
|
+
// Approximate line number
|
|
121
|
+
const lineNum = file.content.slice(0, match.index).split('\n').length;
|
|
122
|
+
violations.push({
|
|
123
|
+
line: lineNum,
|
|
124
|
+
message: 'Async route handler without error handling. Wrap with try/catch and call next(err), or install express-async-errors.',
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return violations;
|
|
129
|
+
},
|
|
130
|
+
applicableTo: ['node'],
|
|
131
|
+
category: 'Error Handling',
|
|
132
|
+
},
|
|
133
|
+
// ─────────────────────────────────────────
|
|
134
|
+
// RULE: node-error-middleware-signature
|
|
135
|
+
// ROLE: Enforce 4-argument error middleware signature
|
|
136
|
+
// PURPOSE: Express only treats a middleware as an error handler when it
|
|
137
|
+
// has exactly 4 parameters (err, req, res, next). A 3-param
|
|
138
|
+
// function that happens to receive an error object will not work.
|
|
139
|
+
// EXAMPLE:
|
|
140
|
+
// WRONG:
|
|
141
|
+
// app.use((err, res) => { res.status(500).json({ error: err.message }); });
|
|
142
|
+
// RIGHT:
|
|
143
|
+
// app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
|
|
144
|
+
// res.status(err.statusCode ?? 500).json({ message: err.message });
|
|
145
|
+
// });
|
|
146
|
+
// ─────────────────────────────────────────
|
|
147
|
+
{
|
|
148
|
+
id: 'node-error-middleware-signature',
|
|
149
|
+
label: 'Global error handler must have 4 parameters (err, req, res, next)',
|
|
150
|
+
description: 'Express error-handling middleware must declare exactly 4 parameters: (err, req, res, next). With fewer params, Express will not recognise it as an error handler.',
|
|
151
|
+
severity: 'error',
|
|
152
|
+
fileExtensions: ['ts', 'js'],
|
|
153
|
+
pattern: null,
|
|
154
|
+
customCheck: (file) => {
|
|
155
|
+
if (!file.relativePath.includes('error') &&
|
|
156
|
+
!file.relativePath.includes('middleware') &&
|
|
157
|
+
!file.relativePath.endsWith('app.ts') &&
|
|
158
|
+
!file.relativePath.endsWith('server.ts')) {
|
|
159
|
+
return [];
|
|
160
|
+
}
|
|
161
|
+
const violations = [];
|
|
162
|
+
for (let i = 0; i < file.lines.length; i++) {
|
|
163
|
+
const line = file.lines[i];
|
|
164
|
+
// Detect app.use with callback that looks like error handler but has <4 params
|
|
165
|
+
if (/app\.use\s*\(\s*(?:['"`][^']+['"`]\s*,\s*)?\s*(?:async\s*)?\([^)]*\)\s*=>/.test(line)) {
|
|
166
|
+
const params = (line.match(/\(([^)]*)\)/) || [])[1] || '';
|
|
167
|
+
const count = params.split(',').filter((p) => p.trim()).length;
|
|
168
|
+
if (count === 3 && /err|error/i.test(params)) {
|
|
169
|
+
violations.push({
|
|
170
|
+
line: i + 1,
|
|
171
|
+
message: 'Error-handling middleware must declare 4 parameters: (err, req, res, next). Express uses arity to detect error handlers.',
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return violations;
|
|
177
|
+
},
|
|
178
|
+
applicableTo: ['node'],
|
|
179
|
+
category: 'Error Handling',
|
|
180
|
+
},
|
|
181
|
+
// ── Input Validation ──────────────────────────────────────────────────────
|
|
182
|
+
// ─────────────────────────────────────────
|
|
183
|
+
// RULE: node-validate-request-body
|
|
184
|
+
// ROLE: Enforce input sanitisation before use
|
|
185
|
+
// PURPOSE: Using req.body without validation exposes the service to
|
|
186
|
+
// malformed data, SQL injection, and XSS. Every route that reads
|
|
187
|
+
// req.body must apply a schema validation middleware first.
|
|
188
|
+
// EXAMPLE:
|
|
189
|
+
// WRONG:
|
|
190
|
+
// router.post('/users', async (req, res) => {
|
|
191
|
+
// const { email, name } = req.body; // unvalidated
|
|
192
|
+
// });
|
|
193
|
+
// RIGHT:
|
|
194
|
+
// import { createUserSchema } from '../schemas/user.schema';
|
|
195
|
+
// router.post('/users', validate(createUserSchema), async (req, res) => { ... });
|
|
196
|
+
// ─────────────────────────────────────────
|
|
197
|
+
{
|
|
198
|
+
id: 'node-validate-request-body',
|
|
199
|
+
label: 'Validate req.body before use',
|
|
200
|
+
description: 'Always define and apply a validation schema (Zod, Joi, express-validator) for routes that read req.body. Unvalidated input is the root cause of most injection vulnerabilities.',
|
|
201
|
+
severity: 'warning',
|
|
202
|
+
fileExtensions: ['ts', 'js'],
|
|
203
|
+
pattern: null,
|
|
204
|
+
customCheck: (file) => {
|
|
205
|
+
const violations = [];
|
|
206
|
+
if (!file.relativePath.includes('/routes/') &&
|
|
207
|
+
!file.relativePath.endsWith('.router.ts') &&
|
|
208
|
+
!file.relativePath.endsWith('.routes.ts')) {
|
|
209
|
+
return [];
|
|
210
|
+
}
|
|
211
|
+
const hasValidation = /validate\s*\(|zValidator|z\.object|Joi\.object|checkSchema|body\s*\(/.test(file.content);
|
|
212
|
+
if (!hasValidation && /req\.body/.test(file.content)) {
|
|
213
|
+
violations.push({
|
|
214
|
+
line: null,
|
|
215
|
+
message: 'req.body is used without any visible validation middleware (Zod, Joi, express-validator). Add a schema validation step before the route handler.',
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
return violations;
|
|
219
|
+
},
|
|
220
|
+
applicableTo: ['node'],
|
|
221
|
+
category: 'Security',
|
|
222
|
+
},
|
|
223
|
+
// ── Security ──────────────────────────────────────────────────────────────
|
|
224
|
+
// ─────────────────────────────────────────
|
|
225
|
+
// RULE: node-use-helmet
|
|
226
|
+
// ROLE: Enforce security headers
|
|
227
|
+
// PURPOSE: Without Helmet, Express servers are vulnerable to well-known
|
|
228
|
+
// HTTP header-based attacks (clickjacking, MIME-sniffing, XSS via
|
|
229
|
+
// missing CSP). Helmet sets a secure baseline in one call.
|
|
230
|
+
// EXAMPLE:
|
|
231
|
+
// WRONG:
|
|
232
|
+
// const app = express();
|
|
233
|
+
// app.use(cors());
|
|
234
|
+
// RIGHT:
|
|
235
|
+
// const app = express();
|
|
236
|
+
// app.use(helmet());
|
|
237
|
+
// app.use(cors({ origin: allowedOrigins }));
|
|
238
|
+
// ─────────────────────────────────────────
|
|
239
|
+
{
|
|
240
|
+
id: 'node-use-helmet',
|
|
241
|
+
label: 'Use helmet() for security headers',
|
|
242
|
+
description: 'Apply helmet() middleware in the Express app setup to set secure HTTP headers (Content-Security-Policy, X-Frame-Options, HSTS, etc.).',
|
|
243
|
+
severity: 'error',
|
|
244
|
+
fileExtensions: ['ts', 'js'],
|
|
245
|
+
pattern: null,
|
|
246
|
+
customCheck: (file) => {
|
|
247
|
+
if (!file.relativePath.endsWith('app.ts') &&
|
|
248
|
+
!file.relativePath.endsWith('server.ts') &&
|
|
249
|
+
!file.relativePath.endsWith('app.js') &&
|
|
250
|
+
!file.relativePath.endsWith('server.js')) {
|
|
251
|
+
return [];
|
|
252
|
+
}
|
|
253
|
+
const hasExpress = /express\s*\(\s*\)/.test(file.content);
|
|
254
|
+
const hasHelmet = /helmet\s*\(\s*\)/.test(file.content);
|
|
255
|
+
if (hasExpress && !hasHelmet) {
|
|
256
|
+
return [
|
|
257
|
+
{
|
|
258
|
+
line: null,
|
|
259
|
+
message: 'Express app is missing helmet(). Install helmet and add app.use(helmet()) before other middleware.',
|
|
260
|
+
},
|
|
261
|
+
];
|
|
262
|
+
}
|
|
263
|
+
return [];
|
|
264
|
+
},
|
|
265
|
+
applicableTo: ['node'],
|
|
266
|
+
category: 'Security',
|
|
267
|
+
},
|
|
268
|
+
// ─────────────────────────────────────────
|
|
269
|
+
// RULE: node-no-process-env-scatter
|
|
270
|
+
// ROLE: Centralise configuration access
|
|
271
|
+
// PURPOSE: Using process.env in route handlers and services scatters
|
|
272
|
+
// configuration reads, prevents startup validation, and makes
|
|
273
|
+
// tests hard to write (no clean injection point).
|
|
274
|
+
// Validate env vars once at startup in a config module.
|
|
275
|
+
// EXAMPLE:
|
|
276
|
+
// WRONG:
|
|
277
|
+
// const secret = process.env['JWT_SECRET'];
|
|
278
|
+
// RIGHT:
|
|
279
|
+
// import { config } from '../config'; // validated at startup
|
|
280
|
+
// const secret = config.jwtSecret;
|
|
281
|
+
// ─────────────────────────────────────────
|
|
282
|
+
{
|
|
283
|
+
id: 'node-no-process-env-scatter',
|
|
284
|
+
label: 'Centralise process.env access in a config module',
|
|
285
|
+
description: 'Do not read process.env in route handlers, services, or models. Validate env vars at startup in a dedicated config module and import from there.',
|
|
286
|
+
severity: 'warning',
|
|
287
|
+
fileExtensions: ['ts', 'js'],
|
|
288
|
+
pattern: null,
|
|
289
|
+
customCheck: (file) => {
|
|
290
|
+
const violations = [];
|
|
291
|
+
// Skip config/env files
|
|
292
|
+
if (file.relativePath.includes('/config/') ||
|
|
293
|
+
file.relativePath.includes('.config.') ||
|
|
294
|
+
file.relativePath.endsWith('env.ts') ||
|
|
295
|
+
file.relativePath.endsWith('env.js') ||
|
|
296
|
+
file.relativePath.endsWith('main.ts') ||
|
|
297
|
+
file.relativePath.endsWith('server.ts')) {
|
|
298
|
+
return [];
|
|
299
|
+
}
|
|
300
|
+
for (let i = 0; i < file.lines.length; i++) {
|
|
301
|
+
if (/process\.env\b/.test(file.lines[i])) {
|
|
302
|
+
violations.push({
|
|
303
|
+
line: i + 1,
|
|
304
|
+
message: 'process.env read inside business code. Move to a config module (validated at startup) and import from there.',
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
return violations;
|
|
309
|
+
},
|
|
310
|
+
applicableTo: ['node'],
|
|
311
|
+
category: 'Security',
|
|
312
|
+
},
|
|
313
|
+
// ── Graceful Shutdown ─────────────────────────────────────────────────────
|
|
314
|
+
// ─────────────────────────────────────────
|
|
315
|
+
// RULE: node-graceful-shutdown
|
|
316
|
+
// ROLE: Enforce graceful process shutdown
|
|
317
|
+
// PURPOSE: Without SIGTERM handling, a Node.js server terminates
|
|
318
|
+
// immediately when the container or OS signals shutdown.
|
|
319
|
+
// In-flight requests are cut short, and DB connections leak.
|
|
320
|
+
// Graceful shutdown waits for open connections to close first.
|
|
321
|
+
// EXAMPLE:
|
|
322
|
+
// WRONG:
|
|
323
|
+
// server.listen(port);
|
|
324
|
+
// RIGHT:
|
|
325
|
+
// server.listen(port);
|
|
326
|
+
// process.on('SIGTERM', () => {
|
|
327
|
+
// server.close(() => { db.end(); process.exit(0); });
|
|
328
|
+
// });
|
|
329
|
+
// ─────────────────────────────────────────
|
|
330
|
+
{
|
|
331
|
+
id: 'node-graceful-shutdown',
|
|
332
|
+
label: 'Handle SIGTERM for graceful shutdown',
|
|
333
|
+
description: 'Listen for SIGTERM and SIGINT signals to close the HTTP server and DB connections gracefully before exiting. Required in containerised environments.',
|
|
334
|
+
severity: 'warning',
|
|
335
|
+
fileExtensions: ['ts', 'js'],
|
|
336
|
+
pattern: null,
|
|
337
|
+
customCheck: (file) => {
|
|
338
|
+
if (!file.relativePath.endsWith('main.ts') &&
|
|
339
|
+
!file.relativePath.endsWith('server.ts') &&
|
|
340
|
+
!file.relativePath.endsWith('index.ts') &&
|
|
341
|
+
!file.relativePath.endsWith('server.js')) {
|
|
342
|
+
return [];
|
|
343
|
+
}
|
|
344
|
+
const hasSigterm = /SIGTERM|SIGINT/.test(file.content);
|
|
345
|
+
const hasServerListen = /\.listen\s*\(/.test(file.content);
|
|
346
|
+
if (hasServerListen && !hasSigterm) {
|
|
347
|
+
return [
|
|
348
|
+
{
|
|
349
|
+
line: null,
|
|
350
|
+
message: 'Server starts but no SIGTERM/SIGINT handler found. Add graceful shutdown: process.on("SIGTERM", () => server.close(() => process.exit(0)))',
|
|
351
|
+
},
|
|
352
|
+
];
|
|
353
|
+
}
|
|
354
|
+
return [];
|
|
355
|
+
},
|
|
356
|
+
applicableTo: ['node'],
|
|
357
|
+
category: 'Reliability',
|
|
358
|
+
},
|
|
359
|
+
];
|
|
360
|
+
exports.nodeRules = nodeRules;
|
|
361
|
+
// Register all Node.js (Express) specific rules
|
|
362
|
+
(0, guidelines_config_1.registerRules)('node', nodeRules);
|
|
363
|
+
//# sourceMappingURL=node.config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node.config.js","sourceRoot":"","sources":["../../config/node.config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ============================================================================
|
|
3
|
+
* python.config.ts — Python-specific coding rules
|
|
4
|
+
* ============================================================================
|
|
5
|
+
*
|
|
6
|
+
* Registers rules that apply to Python projects (Django, FastAPI, Flask,
|
|
7
|
+
* and generic Python). Rules are applied based on the `applicableTo` field:
|
|
8
|
+
* - ['python'] → all Python projects
|
|
9
|
+
* - ['django'] → Django only
|
|
10
|
+
* - ['fastapi'] → FastAPI only
|
|
11
|
+
* - ['flask'] → Flask only
|
|
12
|
+
* - ['django', 'fastapi'] → multi-framework
|
|
13
|
+
*/
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=python.config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"python.config.d.ts","sourceRoot":"","sources":["../../config/python.config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG"}
|