@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.
Files changed (102) hide show
  1. package/config/fastify.config.ts +326 -0
  2. package/config/hono.config.ts +331 -0
  3. package/config/nestjs.config.ts +500 -0
  4. package/config/node.config.ts +425 -0
  5. package/config/python.config.ts +512 -0
  6. package/dist/config/fastify.config.d.ts +17 -0
  7. package/dist/config/fastify.config.d.ts.map +1 -0
  8. package/dist/config/fastify.config.js +279 -0
  9. package/dist/config/fastify.config.js.map +1 -0
  10. package/dist/config/hono.config.d.ts +17 -0
  11. package/dist/config/hono.config.d.ts.map +1 -0
  12. package/dist/config/hono.config.js +287 -0
  13. package/dist/config/hono.config.js.map +1 -0
  14. package/dist/config/nestjs.config.d.ts +17 -0
  15. package/dist/config/nestjs.config.d.ts.map +1 -0
  16. package/dist/config/nestjs.config.js +440 -0
  17. package/dist/config/nestjs.config.js.map +1 -0
  18. package/dist/config/node.config.d.ts +17 -0
  19. package/dist/config/node.config.d.ts.map +1 -0
  20. package/dist/config/node.config.js +363 -0
  21. package/dist/config/node.config.js.map +1 -0
  22. package/dist/config/python.config.d.ts +15 -0
  23. package/dist/config/python.config.d.ts.map +1 -0
  24. package/dist/config/python.config.js +475 -0
  25. package/dist/config/python.config.js.map +1 -0
  26. package/dist/scripts/auto-fix.d.ts +5 -0
  27. package/dist/scripts/auto-fix.d.ts.map +1 -1
  28. package/dist/scripts/auto-fix.js +5 -0
  29. package/dist/scripts/auto-fix.js.map +1 -1
  30. package/dist/scripts/cli.js +2 -2
  31. package/dist/scripts/cli.js.map +1 -1
  32. package/dist/scripts/config-generators/ai-config-generator.d.ts.map +1 -1
  33. package/dist/scripts/config-generators/ai-config-generator.js +6 -0
  34. package/dist/scripts/config-generators/ai-config-generator.js.map +1 -1
  35. package/dist/scripts/config-generators/eslint-generator.d.ts.map +1 -1
  36. package/dist/scripts/config-generators/eslint-generator.js +108 -0
  37. package/dist/scripts/config-generators/eslint-generator.js.map +1 -1
  38. package/dist/scripts/config-generators/frameworks/fastify.d.ts +6 -0
  39. package/dist/scripts/config-generators/frameworks/fastify.d.ts.map +1 -0
  40. package/dist/scripts/config-generators/frameworks/fastify.js +68 -0
  41. package/dist/scripts/config-generators/frameworks/fastify.js.map +1 -0
  42. package/dist/scripts/config-generators/frameworks/hono.d.ts +6 -0
  43. package/dist/scripts/config-generators/frameworks/hono.d.ts.map +1 -0
  44. package/dist/scripts/config-generators/frameworks/hono.js +63 -0
  45. package/dist/scripts/config-generators/frameworks/hono.js.map +1 -0
  46. package/dist/scripts/config-generators/frameworks/index.d.ts +3 -0
  47. package/dist/scripts/config-generators/frameworks/index.d.ts.map +1 -1
  48. package/dist/scripts/config-generators/frameworks/index.js +7 -1
  49. package/dist/scripts/config-generators/frameworks/index.js.map +1 -1
  50. package/dist/scripts/config-generators/frameworks/nestjs.d.ts +6 -0
  51. package/dist/scripts/config-generators/frameworks/nestjs.d.ts.map +1 -0
  52. package/dist/scripts/config-generators/frameworks/nestjs.js +83 -0
  53. package/dist/scripts/config-generators/frameworks/nestjs.js.map +1 -0
  54. package/dist/scripts/config-generators/frameworks/node.d.ts +2 -2
  55. package/dist/scripts/config-generators/frameworks/node.d.ts.map +1 -1
  56. package/dist/scripts/config-generators/frameworks/node.js +56 -11
  57. package/dist/scripts/config-generators/frameworks/node.js.map +1 -1
  58. package/dist/scripts/config-generators/typescript-generator.d.ts.map +1 -1
  59. package/dist/scripts/config-generators/typescript-generator.js +33 -0
  60. package/dist/scripts/config-generators/typescript-generator.js.map +1 -1
  61. package/dist/scripts/config-generators/vscode-generator.d.ts.map +1 -1
  62. package/dist/scripts/config-generators/vscode-generator.js +73 -0
  63. package/dist/scripts/config-generators/vscode-generator.js.map +1 -1
  64. package/dist/scripts/generate-pr-checklist.d.ts +5 -0
  65. package/dist/scripts/generate-pr-checklist.d.ts.map +1 -1
  66. package/dist/scripts/generate-pr-checklist.js +6 -1
  67. package/dist/scripts/generate-pr-checklist.js.map +1 -1
  68. package/dist/scripts/postinstall.js +38 -0
  69. package/dist/scripts/postinstall.js.map +1 -1
  70. package/dist/scripts/precommit-check.d.ts +13 -0
  71. package/dist/scripts/precommit-check.d.ts.map +1 -1
  72. package/dist/scripts/precommit-check.js +288 -5
  73. package/dist/scripts/precommit-check.js.map +1 -1
  74. package/dist/scripts/utils/naming-validator.d.ts.map +1 -1
  75. package/dist/scripts/utils/naming-validator.js +96 -2
  76. package/dist/scripts/utils/naming-validator.js.map +1 -1
  77. package/dist/scripts/utils/project-detector.d.ts +12 -9
  78. package/dist/scripts/utils/project-detector.d.ts.map +1 -1
  79. package/dist/scripts/utils/project-detector.js +63 -11
  80. package/dist/scripts/utils/project-detector.js.map +1 -1
  81. package/dist/scripts/utils/structure-validator.d.ts.map +1 -1
  82. package/dist/scripts/utils/structure-validator.js +50 -0
  83. package/dist/scripts/utils/structure-validator.js.map +1 -1
  84. package/package.json +10 -3
  85. package/scripts/auto-fix.ts +5 -0
  86. package/scripts/cli.ts +2 -2
  87. package/scripts/config-generators/ai-config-generator.ts +9 -0
  88. package/scripts/config-generators/eslint-generator.ts +110 -0
  89. package/scripts/config-generators/frameworks/fastify.ts +65 -0
  90. package/scripts/config-generators/frameworks/hono.ts +60 -0
  91. package/scripts/config-generators/frameworks/index.ts +3 -0
  92. package/scripts/config-generators/frameworks/nestjs.ts +80 -0
  93. package/scripts/config-generators/frameworks/node.ts +57 -11
  94. package/scripts/config-generators/typescript-generator.ts +36 -0
  95. package/scripts/config-generators/vscode-generator.ts +84 -0
  96. package/scripts/generate-pr-checklist.ts +6 -1
  97. package/scripts/postinstall.ts +38 -0
  98. package/scripts/precommit-check.ts +334 -6
  99. package/scripts/utils/naming-validator.ts +104 -2
  100. package/scripts/utils/project-detector.ts +78 -11
  101. package/scripts/utils/structure-validator.ts +54 -0
  102. package/templates/feature-doc-backend.md +114 -0
@@ -0,0 +1,512 @@
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
+
15
+ import { registerRules, Rule } from './guidelines.config';
16
+
17
+ const pythonRules: Rule[] = [
18
+
19
+ // ── Generic Python ────────────────────────────────────────────────────────
20
+
21
+ // ─────────────────────────────────────────
22
+ // RULE: python-no-bare-except
23
+ // ROLE: Enforce specific exception handling
24
+ // PURPOSE: `except:` (bare) silently swallows every exception including
25
+ // KeyboardInterrupt and SystemExit, making debugging impossible
26
+ // and preventing graceful shutdown.
27
+ // EXAMPLE:
28
+ // WRONG:
29
+ // try:
30
+ // result = db.query(...)
31
+ // except:
32
+ // pass
33
+ // RIGHT:
34
+ // try:
35
+ // result = db.query(...)
36
+ // except Exception as e:
37
+ // logger.error("Query failed: %s", e)
38
+ // raise
39
+ // ─────────────────────────────────────────
40
+ {
41
+ id: 'python-no-bare-except',
42
+ label: 'No bare except clauses',
43
+ description:
44
+ 'Always catch a specific exception type (e.g. `except ValueError`) or at minimum `except Exception`. Bare `except:` swallows every error including KeyboardInterrupt.',
45
+ severity: 'error',
46
+ fileExtensions: ['py'],
47
+ pattern: /^\s*except\s*:/,
48
+ applicableTo: ['python', 'django', 'fastapi', 'flask'],
49
+ category: 'Error Handling',
50
+ },
51
+
52
+ // ─────────────────────────────────────────
53
+ // RULE: python-no-print-statements
54
+ // ROLE: Enforce structured logging
55
+ // PURPOSE: print() in production code bypasses log aggregation systems
56
+ // (CloudWatch, Datadog, etc.) and cannot be toggled by log level.
57
+ // Use the standard `logging` module or a structured logger instead.
58
+ // EXAMPLE:
59
+ // WRONG: print(f"User {user_id} created")
60
+ // RIGHT: logger.info("User %s created", user_id)
61
+ // ─────────────────────────────────────────
62
+ {
63
+ id: 'python-no-print-statements',
64
+ label: 'No print() in production code — use logging',
65
+ description:
66
+ 'Replace print() calls with proper logging (logging.info / logger.info). print() bypasses log levels, log aggregation, and structured logging pipelines.',
67
+ severity: 'warning',
68
+ fileExtensions: ['py'],
69
+ pattern: /(?<![#\w])print\s*\(/,
70
+ applicableTo: ['python', 'django', 'fastapi', 'flask'],
71
+ category: 'Observability',
72
+ },
73
+
74
+ // ─────────────────────────────────────────
75
+ // RULE: python-no-hardcoded-secrets
76
+ // ROLE: Prevent credential leaks in source code
77
+ // PURPOSE: Hardcoded passwords, API keys, and secret tokens get committed
78
+ // to version control and are trivially discoverable.
79
+ // EXAMPLE:
80
+ // WRONG: SECRET_KEY = "mysupersecret123"
81
+ // RIGHT: SECRET_KEY = os.environ["SECRET_KEY"]
82
+ // ─────────────────────────────────────────
83
+ {
84
+ id: 'python-no-hardcoded-secrets',
85
+ label: 'No hardcoded secrets or API keys',
86
+ description:
87
+ 'Do not hardcode passwords, API keys, or secret tokens. Load sensitive values from environment variables or a secrets manager.',
88
+ severity: 'error',
89
+ fileExtensions: ['py'],
90
+ pattern: /(?:password|secret|api_key|apikey|token|auth_key)\s*=\s*['"][^'"]{6,}['"]/i,
91
+ applicableTo: ['python', 'django', 'fastapi', 'flask'],
92
+ category: 'Security',
93
+ },
94
+
95
+ // ─────────────────────────────────────────
96
+ // RULE: python-no-eval
97
+ // ROLE: Prevent arbitrary code execution
98
+ // PURPOSE: eval() and exec() execute arbitrary strings as code, opening
99
+ // a direct remote-code-execution vector if user input reaches them.
100
+ // EXAMPLE:
101
+ // WRONG: result = eval(user_input)
102
+ // RIGHT: use ast.literal_eval() for safe literal parsing
103
+ // ─────────────────────────────────────────
104
+ {
105
+ id: 'python-no-eval',
106
+ label: 'No eval() or exec() with dynamic input',
107
+ description:
108
+ 'eval() and exec() are remote code execution vulnerabilities when fed user-controlled data. Use ast.literal_eval() for data parsing or redesign the logic.',
109
+ severity: 'error',
110
+ fileExtensions: ['py'],
111
+ pattern: /\beval\s*\(|\bexec\s*\(/,
112
+ applicableTo: ['python', 'django', 'fastapi', 'flask'],
113
+ category: 'Security',
114
+ },
115
+
116
+ // ─────────────────────────────────────────
117
+ // RULE: python-type-hints-required
118
+ // ROLE: Enforce type annotations on function signatures
119
+ // PURPOSE: Type hints enable static analysis (mypy, pyright), improve IDE
120
+ // autocompletion, and make APIs self-documenting.
121
+ // EXAMPLE:
122
+ // WRONG:
123
+ // def create_user(name, email):
124
+ // ...
125
+ // RIGHT:
126
+ // def create_user(name: str, email: str) -> User:
127
+ // ...
128
+ // ─────────────────────────────────────────
129
+ {
130
+ id: 'python-type-hints-required',
131
+ label: 'Add type hints to function parameters and return types',
132
+ description:
133
+ 'Public functions and methods should have type annotations on all parameters and the return type. Enables mypy/pyright static analysis.',
134
+ severity: 'warning',
135
+ fileExtensions: ['py'],
136
+ pattern: /^def\s+[a-z_]\w*\s*\([^)]*\)\s*:/,
137
+ customCheck: (file) => {
138
+ const violations: Array<{ line: number | null; message: string }> = [];
139
+ for (let i = 0; i < file.lines.length; i++) {
140
+ const line = file.lines[i];
141
+ // Match function definitions without return type annotation (no ->)
142
+ if (/^\s*def\s+[a-z_]\w*\s*\([^)]*\)\s*:/.test(line) && !line.includes('->')) {
143
+ // Skip dunder methods like __init__, __str__
144
+ if (!/__\w+__/.test(line)) {
145
+ violations.push({
146
+ line: i + 1,
147
+ message: 'Function is missing a return type annotation. Add `-> ReturnType:` after the parameter list.',
148
+ });
149
+ }
150
+ }
151
+ }
152
+ return violations;
153
+ },
154
+ applicableTo: ['python', 'django', 'fastapi', 'flask'],
155
+ category: 'Code Quality',
156
+ },
157
+
158
+ // ── Django ────────────────────────────────────────────────────────────────
159
+
160
+ // ─────────────────────────────────────────
161
+ // RULE: django-no-raw-sql
162
+ // ROLE: Prevent SQL injection via raw queries
163
+ // PURPOSE: cursor.execute() and raw() with string formatting bypass the ORM
164
+ // parameterization, enabling SQL injection if user input is present.
165
+ // EXAMPLE:
166
+ // WRONG:
167
+ // cursor.execute(f"SELECT * FROM users WHERE id = {user_id}")
168
+ // RIGHT:
169
+ // cursor.execute("SELECT * FROM users WHERE id = %s", [user_id])
170
+ // OR: User.objects.filter(id=user_id)
171
+ // ─────────────────────────────────────────
172
+ {
173
+ id: 'django-no-raw-sql',
174
+ label: 'No string-formatted raw SQL queries',
175
+ description:
176
+ 'cursor.execute() and .raw() with f-strings or % formatting are SQL injection risks. Use parameterized queries or the Django ORM.',
177
+ severity: 'error',
178
+ fileExtensions: ['py'],
179
+ pattern: /cursor\.execute\s*\(\s*f['"]|cursor\.execute\s*\(\s*['"][^'"]*%\s*(?!s\b|\()/,
180
+ applicableTo: ['django'],
181
+ category: 'Security',
182
+ },
183
+
184
+ // ─────────────────────────────────────────
185
+ // RULE: django-no-logic-in-view
186
+ // ROLE: Enforce thin views (controller pattern)
187
+ // PURPOSE: Business logic in Django views cannot be unit-tested without
188
+ // the full HTTP request/response cycle. Logic belongs in service
189
+ // modules or model methods.
190
+ // EXAMPLE:
191
+ // WRONG:
192
+ // class UserCreateView(APIView):
193
+ // def post(self, request):
194
+ // salt = bcrypt.gensalt()
195
+ // hashed = bcrypt.hashpw(request.data['password'].encode(), salt)
196
+ // User.objects.create(password=hashed, ...)
197
+ // RIGHT:
198
+ // class UserCreateView(APIView):
199
+ // def post(self, request):
200
+ // serializer = UserSerializer(data=request.data)
201
+ // serializer.is_valid(raise_exception=True)
202
+ // return UserService.create(serializer.validated_data)
203
+ // ─────────────────────────────────────────
204
+ {
205
+ id: 'django-no-logic-in-view',
206
+ label: 'No business logic in Django views',
207
+ description:
208
+ 'Django views should only validate input (via serializers/forms) and delegate to service modules. Move hashing, computation, and ORM writes to a service layer.',
209
+ severity: 'warning',
210
+ fileExtensions: ['py'],
211
+ pattern: null,
212
+ customCheck: (file) => {
213
+ const violations: Array<{ line: number | null; message: string }> = [];
214
+ if (
215
+ !file.relativePath.includes('/views/') &&
216
+ !file.relativePath.endsWith('views.py') &&
217
+ !file.relativePath.endsWith('viewsets.py')
218
+ ) {
219
+ return [];
220
+ }
221
+ const businessPatterns = [
222
+ { regex: /bcrypt\.(hashpw|checkpw|gensalt)/g, label: 'bcrypt hashing' },
223
+ { regex: /\.objects\.create\s*\(/g, label: 'direct ORM create()' },
224
+ { regex: /send_mail\s*\(|send_message\s*\(/g, label: 'direct email/message send' },
225
+ ];
226
+ for (let i = 0; i < file.lines.length; i++) {
227
+ for (const { regex, label } of businessPatterns) {
228
+ regex.lastIndex = 0;
229
+ if (regex.test(file.lines[i])) {
230
+ violations.push({
231
+ line: i + 1,
232
+ message: `Business logic (${label}) in view. Move to a service module.`,
233
+ });
234
+ }
235
+ }
236
+ }
237
+ return violations;
238
+ },
239
+ applicableTo: ['django'],
240
+ category: 'Architecture',
241
+ },
242
+
243
+ // ─────────────────────────────────────────
244
+ // RULE: django-use-serializer-validation
245
+ // ROLE: Enforce validation via DRF serializers
246
+ // PURPOSE: Accessing request.data directly without calling is_valid() skips
247
+ // all field-level validation and can allow malformed or malicious
248
+ // data into the system.
249
+ // EXAMPLE:
250
+ // WRONG:
251
+ // def post(self, request):
252
+ // User.objects.create(**request.data)
253
+ // RIGHT:
254
+ // def post(self, request):
255
+ // serializer = UserSerializer(data=request.data)
256
+ // serializer.is_valid(raise_exception=True)
257
+ // serializer.save()
258
+ // ─────────────────────────────────────────
259
+ {
260
+ id: 'django-use-serializer-validation',
261
+ label: 'Validate request data through a serializer before use',
262
+ description:
263
+ 'Do not pass request.data directly to model constructors or ORM calls. Always use a DRF Serializer with is_valid(raise_exception=True) first.',
264
+ severity: 'error',
265
+ fileExtensions: ['py'],
266
+ pattern: /\.objects\.create\s*\(\s*\*\*request\.data/,
267
+ applicableTo: ['django'],
268
+ category: 'Validation',
269
+ },
270
+
271
+ // ── FastAPI ───────────────────────────────────────────────────────────────
272
+
273
+ // ─────────────────────────────────────────
274
+ // RULE: fastapi-use-pydantic-models
275
+ // ROLE: Enforce schema-based request validation
276
+ // PURPOSE: FastAPI route functions that accept `request: Request` and then
277
+ // call await request.json() bypass Pydantic validation entirely.
278
+ // Pydantic models provide automatic type coercion, validation, and
279
+ // OpenAPI schema generation.
280
+ // EXAMPLE:
281
+ // WRONG:
282
+ // @router.post("/users")
283
+ // async def create_user(request: Request):
284
+ // body = await request.json()
285
+ // RIGHT:
286
+ // @router.post("/users")
287
+ // async def create_user(body: CreateUserRequest):
288
+ // ...
289
+ // ─────────────────────────────────────────
290
+ {
291
+ id: 'fastapi-use-pydantic-models',
292
+ label: 'Use Pydantic models for request bodies — not raw Request',
293
+ description:
294
+ 'Route handlers should declare their request body as a typed Pydantic model. Using `await request.json()` bypasses validation and OpenAPI schema generation.',
295
+ severity: 'error',
296
+ fileExtensions: ['py'],
297
+ pattern: /await\s+request\.json\s*\(\)/,
298
+ applicableTo: ['fastapi'],
299
+ category: 'Validation',
300
+ },
301
+
302
+ // ─────────────────────────────────────────
303
+ // RULE: fastapi-async-route-handler
304
+ // ROLE: Enforce async route handlers
305
+ // PURPOSE: Synchronous route handlers block the FastAPI event loop, which
306
+ // defeats the purpose of the async framework and causes latency
307
+ // spikes under concurrent load.
308
+ // EXAMPLE:
309
+ // WRONG:
310
+ // @router.get("/users/{id}")
311
+ // def get_user(id: int, db: Session = Depends(get_db)):
312
+ // return db.query(User).filter(User.id == id).first()
313
+ // RIGHT:
314
+ // @router.get("/users/{id}")
315
+ // async def get_user(id: int, db: AsyncSession = Depends(get_db)):
316
+ // return await db.get(User, id)
317
+ // ─────────────────────────────────────────
318
+ {
319
+ id: 'fastapi-async-route-handler',
320
+ label: 'Route handlers should be async',
321
+ description:
322
+ 'Synchronous route handlers block the FastAPI event loop. Use `async def` with async DB drivers (e.g. SQLAlchemy async, databases, asyncpg).',
323
+ severity: 'warning',
324
+ fileExtensions: ['py'],
325
+ pattern: null,
326
+ customCheck: (file) => {
327
+ const violations: Array<{ line: number | null; message: string }> = [];
328
+ for (let i = 0; i < file.lines.length; i++) {
329
+ const line = file.lines[i];
330
+ // Detect @router.get/post/put/patch/delete followed by a sync def on the next line
331
+ if (/@(?:router|app)\s*\.\s*(?:get|post|put|patch|delete)\s*\(/.test(line)) {
332
+ const nextLine = file.lines[i + 1] || '';
333
+ if (/^\s*def\s+/.test(nextLine) && !/^\s*async\s+def\s+/.test(nextLine)) {
334
+ violations.push({
335
+ line: i + 2,
336
+ message:
337
+ 'Route handler is synchronous. Use `async def` to avoid blocking the FastAPI event loop.',
338
+ });
339
+ }
340
+ }
341
+ }
342
+ return violations;
343
+ },
344
+ applicableTo: ['fastapi'],
345
+ category: 'Performance',
346
+ },
347
+
348
+ // ─────────────────────────────────────────
349
+ // RULE: fastapi-response-model-required
350
+ // ROLE: Enforce response type declarations
351
+ // PURPOSE: Without a response_model, FastAPI will serialize the entire
352
+ // returned object, potentially leaking internal fields (passwords,
353
+ // tokens, internal IDs). response_model also generates the OpenAPI
354
+ // response schema.
355
+ // EXAMPLE:
356
+ // WRONG:
357
+ // @router.post("/users")
358
+ // async def create_user(body: CreateUserRequest, db: AsyncSession = Depends(get_db)):
359
+ // return await user_service.create(db, body)
360
+ // RIGHT:
361
+ // @router.post("/users", response_model=UserResponse, status_code=201)
362
+ // async def create_user(body: CreateUserRequest, db: AsyncSession = Depends(get_db)):
363
+ // return await user_service.create(db, body)
364
+ // ─────────────────────────────────────────
365
+ {
366
+ id: 'fastapi-response-model-required',
367
+ label: 'Declare response_model on all mutating routes',
368
+ description:
369
+ 'POST/PUT/PATCH/DELETE routes should declare a response_model to prevent data leaks and generate OpenAPI response schemas.',
370
+ severity: 'warning',
371
+ fileExtensions: ['py'],
372
+ pattern: null,
373
+ customCheck: (file) => {
374
+ const violations: Array<{ line: number | null; message: string }> = [];
375
+ for (let i = 0; i < file.lines.length; i++) {
376
+ const line = file.lines[i];
377
+ if (/@(?:router|app)\s*\.\s*(?:post|put|patch|delete)\s*\(/.test(line)) {
378
+ // Collect the decorator (may span multiple lines)
379
+ const decoratorBlock = file.lines.slice(i, i + 5).join(' ');
380
+ if (!decoratorBlock.includes('response_model')) {
381
+ violations.push({
382
+ line: i + 1,
383
+ message:
384
+ 'Mutating route is missing response_model. Add response_model=YourSchema to prevent data leaks.',
385
+ });
386
+ }
387
+ }
388
+ }
389
+ return violations;
390
+ },
391
+ applicableTo: ['fastapi'],
392
+ category: 'Security',
393
+ },
394
+
395
+ // ── Flask ─────────────────────────────────────────────────────────────────
396
+
397
+ // ─────────────────────────────────────────
398
+ // RULE: flask-use-blueprint-structure
399
+ // ROLE: Enforce Flask Blueprint organisation
400
+ // PURPOSE: Defining all routes directly on the `app` object in a single
401
+ // file does not scale. Blueprints allow grouping related routes
402
+ // into separate modules with their own URL prefixes.
403
+ // EXAMPLE:
404
+ // WRONG:
405
+ // @app.route('/users', methods=['POST'])
406
+ // def create_user(): ...
407
+ // RIGHT:
408
+ // users_bp = Blueprint('users', __name__, url_prefix='/users')
409
+ // @users_bp.route('/', methods=['POST'])
410
+ // def create_user(): ...
411
+ // ─────────────────────────────────────────
412
+ {
413
+ id: 'flask-use-blueprint-structure',
414
+ label: 'Define routes on Blueprints, not directly on app',
415
+ description:
416
+ 'Register routes on Blueprint instances instead of the global `app` object. Blueprints enable modular structure and independent URL prefixes.',
417
+ severity: 'warning',
418
+ fileExtensions: ['py'],
419
+ pattern: /@app\.route\s*\(/,
420
+ customCheck: (file) => {
421
+ const violations: Array<{ line: number | null; message: string }> = [];
422
+ // Only flag if the file does not define a Blueprint itself
423
+ const definesBp = file.content.includes('Blueprint(');
424
+ if (definesBp) return [];
425
+ for (let i = 0; i < file.lines.length; i++) {
426
+ if (/@app\.route\s*\(/.test(file.lines[i])) {
427
+ violations.push({
428
+ line: i + 1,
429
+ message:
430
+ 'Route registered directly on `app`. Move to a Blueprint for modular structure.',
431
+ });
432
+ }
433
+ }
434
+ return violations;
435
+ },
436
+ applicableTo: ['flask'],
437
+ category: 'Architecture',
438
+ },
439
+
440
+ // ─────────────────────────────────────────
441
+ // RULE: flask-validate-request-data
442
+ // ROLE: Enforce input validation before use
443
+ // PURPOSE: request.json and request.form return raw dicts with no type
444
+ // coercion or validation. A missing or malformed field will cause
445
+ // an unhandled KeyError or TypeError at runtime.
446
+ // Use Flask-Marshmallow, Pydantic, or wtforms validation.
447
+ // EXAMPLE:
448
+ // WRONG:
449
+ // data = request.json
450
+ // user = User(name=data['name'], email=data['email'])
451
+ // RIGHT:
452
+ // schema = CreateUserSchema()
453
+ // data = schema.load(request.json) # raises ValidationError on bad input
454
+ // user = User(**data)
455
+ // ─────────────────────────────────────────
456
+ {
457
+ id: 'flask-validate-request-data',
458
+ label: 'Validate request.json / request.form before use',
459
+ description:
460
+ 'Do not use request.json or request.form directly. Validate with a schema library (Marshmallow, Pydantic) to catch malformed input before it reaches business logic.',
461
+ severity: 'error',
462
+ fileExtensions: ['py'],
463
+ pattern: null,
464
+ customCheck: (file) => {
465
+ const violations: Array<{ line: number | null; message: string }> = [];
466
+ for (let i = 0; i < file.lines.length; i++) {
467
+ const line = file.lines[i];
468
+ if (/request\s*\.\s*(?:json|form)\b/.test(line)) {
469
+ // Check surrounding lines for a schema.load / validate call
470
+ const context = file.lines.slice(Math.max(0, i - 5), i + 5).join('\n');
471
+ if (!context.includes('.load(') && !context.includes('.validate(') && !context.includes('schema')) {
472
+ violations.push({
473
+ line: i + 1,
474
+ message:
475
+ 'request.json/form used without schema validation. Run input through a Marshmallow schema or Pydantic model before use.',
476
+ });
477
+ }
478
+ }
479
+ }
480
+ return violations;
481
+ },
482
+ applicableTo: ['flask'],
483
+ category: 'Validation',
484
+ },
485
+
486
+ // ─────────────────────────────────────────
487
+ // RULE: flask-no-debug-in-production
488
+ // ROLE: Prevent debug mode in production
489
+ // PURPOSE: app.run(debug=True) exposes the Werkzeug interactive debugger,
490
+ // which allows arbitrary Python code execution in the browser.
491
+ // It must never run in production.
492
+ // EXAMPLE:
493
+ // WRONG: app.run(debug=True)
494
+ // RIGHT: app.run(debug=os.getenv("FLASK_DEBUG", "false").lower() == "true")
495
+ // ─────────────────────────────────────────
496
+ {
497
+ id: 'flask-no-debug-in-production',
498
+ label: 'Do not hardcode debug=True in app.run()',
499
+ description:
500
+ 'app.run(debug=True) exposes the Werkzeug interactive debugger — a remote code execution vulnerability. Read the debug flag from an environment variable.',
501
+ severity: 'error',
502
+ fileExtensions: ['py'],
503
+ pattern: /app\.run\s*\([^)]*debug\s*=\s*True/,
504
+ applicableTo: ['flask'],
505
+ category: 'Security',
506
+ },
507
+ ];
508
+
509
+ registerRules('python', pythonRules.filter(r => r.applicableTo?.includes('python')));
510
+ registerRules('django', pythonRules.filter(r => r.applicableTo?.includes('django')));
511
+ registerRules('fastapi', pythonRules.filter(r => r.applicableTo?.includes('fastapi')));
512
+ registerRules('flask', pythonRules.filter(r => r.applicableTo?.includes('flask')));
@@ -0,0 +1,17 @@
1
+ /**
2
+ * ============================================================================
3
+ * fastify.config.ts — Fastify-specific coding rules
4
+ * ============================================================================
5
+ *
6
+ * Registers rules that apply to Fastify projects:
7
+ * - Schema-first validation enforcement
8
+ * - Plugin scope correctness
9
+ * - Async handler patterns
10
+ * - Security: CORS, helmet, rate limiting
11
+ * - Error response shape
12
+ * - Hook lifecycle usage
13
+ */
14
+ import { Rule } from './guidelines.config';
15
+ declare const fastifyRules: Rule[];
16
+ export { fastifyRules };
17
+ //# sourceMappingURL=fastify.config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fastify.config.d.ts","sourceRoot":"","sources":["../../config/fastify.config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAiB,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAE1D,QAAA,MAAM,YAAY,EAAE,IAAI,EAgTvB,CAAC;AAKF,OAAO,EAAE,YAAY,EAAE,CAAC"}