@trust-assurance-protocol/owaspscan 1.0.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/dist/analysis/ast-analyzer.d.ts +13 -0
- package/dist/analysis/ast-analyzer.d.ts.map +1 -0
- package/dist/analysis/ast-analyzer.js +58 -0
- package/dist/analysis/ast-analyzer.js.map +1 -0
- package/dist/analysis/llm-verifier.d.ts +17 -0
- package/dist/analysis/llm-verifier.d.ts.map +1 -0
- package/dist/analysis/llm-verifier.js +152 -0
- package/dist/analysis/llm-verifier.js.map +1 -0
- package/dist/analysis/result-cache.d.ts +20 -0
- package/dist/analysis/result-cache.d.ts.map +1 -0
- package/dist/analysis/result-cache.js +70 -0
- package/dist/analysis/result-cache.js.map +1 -0
- package/dist/analysis/sinks.d.ts +12 -0
- package/dist/analysis/sinks.d.ts.map +1 -0
- package/dist/analysis/sinks.js +142 -0
- package/dist/analysis/sinks.js.map +1 -0
- package/dist/analysis/sources.d.ts +8 -0
- package/dist/analysis/sources.d.ts.map +1 -0
- package/dist/analysis/sources.js +114 -0
- package/dist/analysis/sources.js.map +1 -0
- package/dist/analysis/taint-engine.d.ts +5 -0
- package/dist/analysis/taint-engine.d.ts.map +1 -0
- package/dist/analysis/taint-engine.js +187 -0
- package/dist/analysis/taint-engine.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +227 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/config/loader.d.ts +10 -0
- package/dist/config/loader.d.ts.map +1 -0
- package/dist/config/loader.js +81 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/schema.d.ts +23 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +17 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/mcp/server.d.ts +2 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +250 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/parsers/ast-parser.d.ts +38 -0
- package/dist/parsers/ast-parser.d.ts.map +1 -0
- package/dist/parsers/ast-parser.js +88 -0
- package/dist/parsers/ast-parser.js.map +1 -0
- package/dist/parsers/ast-queries.d.ts +63 -0
- package/dist/parsers/ast-queries.d.ts.map +1 -0
- package/dist/parsers/ast-queries.js +580 -0
- package/dist/parsers/ast-queries.js.map +1 -0
- package/dist/reporter/console.d.ts +8 -0
- package/dist/reporter/console.d.ts.map +1 -0
- package/dist/reporter/console.js +143 -0
- package/dist/reporter/console.js.map +1 -0
- package/dist/reporter/json.d.ts +3 -0
- package/dist/reporter/json.d.ts.map +1 -0
- package/dist/reporter/json.js +7 -0
- package/dist/reporter/json.js.map +1 -0
- package/dist/reporter/llm.d.ts +3 -0
- package/dist/reporter/llm.d.ts.map +1 -0
- package/dist/reporter/llm.js +66 -0
- package/dist/reporter/llm.js.map +1 -0
- package/dist/reporter/sarif.d.ts +3 -0
- package/dist/reporter/sarif.d.ts.map +1 -0
- package/dist/reporter/sarif.js +110 -0
- package/dist/reporter/sarif.js.map +1 -0
- package/dist/rules/owasp-a01/idor.d.ts +3 -0
- package/dist/rules/owasp-a01/idor.d.ts.map +1 -0
- package/dist/rules/owasp-a01/idor.js +48 -0
- package/dist/rules/owasp-a01/idor.js.map +1 -0
- package/dist/rules/owasp-a01/missing-auth-middleware.d.ts +3 -0
- package/dist/rules/owasp-a01/missing-auth-middleware.d.ts.map +1 -0
- package/dist/rules/owasp-a01/missing-auth-middleware.js +41 -0
- package/dist/rules/owasp-a01/missing-auth-middleware.js.map +1 -0
- package/dist/rules/owasp-a01/path-traversal.d.ts +3 -0
- package/dist/rules/owasp-a01/path-traversal.d.ts.map +1 -0
- package/dist/rules/owasp-a01/path-traversal.js +73 -0
- package/dist/rules/owasp-a01/path-traversal.js.map +1 -0
- package/dist/rules/owasp-a02/hardcoded-secrets.d.ts +3 -0
- package/dist/rules/owasp-a02/hardcoded-secrets.d.ts.map +1 -0
- package/dist/rules/owasp-a02/hardcoded-secrets.js +97 -0
- package/dist/rules/owasp-a02/hardcoded-secrets.js.map +1 -0
- package/dist/rules/owasp-a02/insecure-tls.d.ts +3 -0
- package/dist/rules/owasp-a02/insecure-tls.d.ts.map +1 -0
- package/dist/rules/owasp-a02/insecure-tls.js +75 -0
- package/dist/rules/owasp-a02/insecure-tls.js.map +1 -0
- package/dist/rules/owasp-a02/weak-hash.d.ts +3 -0
- package/dist/rules/owasp-a02/weak-hash.d.ts.map +1 -0
- package/dist/rules/owasp-a02/weak-hash.js +73 -0
- package/dist/rules/owasp-a02/weak-hash.js.map +1 -0
- package/dist/rules/owasp-a02/weak-random.d.ts +3 -0
- package/dist/rules/owasp-a02/weak-random.d.ts.map +1 -0
- package/dist/rules/owasp-a02/weak-random.js +70 -0
- package/dist/rules/owasp-a02/weak-random.js.map +1 -0
- package/dist/rules/owasp-a03/command-injection.d.ts +3 -0
- package/dist/rules/owasp-a03/command-injection.d.ts.map +1 -0
- package/dist/rules/owasp-a03/command-injection.js +79 -0
- package/dist/rules/owasp-a03/command-injection.js.map +1 -0
- package/dist/rules/owasp-a03/ldap-injection.d.ts +3 -0
- package/dist/rules/owasp-a03/ldap-injection.d.ts.map +1 -0
- package/dist/rules/owasp-a03/ldap-injection.js +56 -0
- package/dist/rules/owasp-a03/ldap-injection.js.map +1 -0
- package/dist/rules/owasp-a03/nosql-injection.d.ts +3 -0
- package/dist/rules/owasp-a03/nosql-injection.d.ts.map +1 -0
- package/dist/rules/owasp-a03/nosql-injection.js +61 -0
- package/dist/rules/owasp-a03/nosql-injection.js.map +1 -0
- package/dist/rules/owasp-a03/sql-injection.d.ts +3 -0
- package/dist/rules/owasp-a03/sql-injection.d.ts.map +1 -0
- package/dist/rules/owasp-a03/sql-injection.js +88 -0
- package/dist/rules/owasp-a03/sql-injection.js.map +1 -0
- package/dist/rules/owasp-a03/template-injection.d.ts +3 -0
- package/dist/rules/owasp-a03/template-injection.d.ts.map +1 -0
- package/dist/rules/owasp-a03/template-injection.js +64 -0
- package/dist/rules/owasp-a03/template-injection.js.map +1 -0
- package/dist/rules/owasp-a03/xss.d.ts +3 -0
- package/dist/rules/owasp-a03/xss.d.ts.map +1 -0
- package/dist/rules/owasp-a03/xss.js +74 -0
- package/dist/rules/owasp-a03/xss.js.map +1 -0
- package/dist/rules/owasp-a04/mass-assignment.d.ts +3 -0
- package/dist/rules/owasp-a04/mass-assignment.d.ts.map +1 -0
- package/dist/rules/owasp-a04/mass-assignment.js +63 -0
- package/dist/rules/owasp-a04/mass-assignment.js.map +1 -0
- package/dist/rules/owasp-a04/missing-rate-limit.d.ts +3 -0
- package/dist/rules/owasp-a04/missing-rate-limit.d.ts.map +1 -0
- package/dist/rules/owasp-a04/missing-rate-limit.js +48 -0
- package/dist/rules/owasp-a04/missing-rate-limit.js.map +1 -0
- package/dist/rules/owasp-a05/cors-wildcard.d.ts +3 -0
- package/dist/rules/owasp-a05/cors-wildcard.d.ts.map +1 -0
- package/dist/rules/owasp-a05/cors-wildcard.js +79 -0
- package/dist/rules/owasp-a05/cors-wildcard.js.map +1 -0
- package/dist/rules/owasp-a05/debug-mode.d.ts +3 -0
- package/dist/rules/owasp-a05/debug-mode.d.ts.map +1 -0
- package/dist/rules/owasp-a05/debug-mode.js +73 -0
- package/dist/rules/owasp-a05/debug-mode.js.map +1 -0
- package/dist/rules/owasp-a05/default-credentials.d.ts +3 -0
- package/dist/rules/owasp-a05/default-credentials.d.ts.map +1 -0
- package/dist/rules/owasp-a05/default-credentials.js +52 -0
- package/dist/rules/owasp-a05/default-credentials.js.map +1 -0
- package/dist/rules/owasp-a05/error-disclosure.d.ts +3 -0
- package/dist/rules/owasp-a05/error-disclosure.d.ts.map +1 -0
- package/dist/rules/owasp-a05/error-disclosure.js +70 -0
- package/dist/rules/owasp-a05/error-disclosure.js.map +1 -0
- package/dist/rules/owasp-a06/outdated-packages.d.ts +3 -0
- package/dist/rules/owasp-a06/outdated-packages.d.ts.map +1 -0
- package/dist/rules/owasp-a06/outdated-packages.js +75 -0
- package/dist/rules/owasp-a06/outdated-packages.js.map +1 -0
- package/dist/rules/owasp-a07/insecure-cookies.d.ts +3 -0
- package/dist/rules/owasp-a07/insecure-cookies.d.ts.map +1 -0
- package/dist/rules/owasp-a07/insecure-cookies.js +64 -0
- package/dist/rules/owasp-a07/insecure-cookies.js.map +1 -0
- package/dist/rules/owasp-a07/jwt-none-alg.d.ts +3 -0
- package/dist/rules/owasp-a07/jwt-none-alg.d.ts.map +1 -0
- package/dist/rules/owasp-a07/jwt-none-alg.js +81 -0
- package/dist/rules/owasp-a07/jwt-none-alg.js.map +1 -0
- package/dist/rules/owasp-a07/no-password-hashing.d.ts +3 -0
- package/dist/rules/owasp-a07/no-password-hashing.d.ts.map +1 -0
- package/dist/rules/owasp-a07/no-password-hashing.js +70 -0
- package/dist/rules/owasp-a07/no-password-hashing.js.map +1 -0
- package/dist/rules/owasp-a07/weak-session.d.ts +3 -0
- package/dist/rules/owasp-a07/weak-session.d.ts.map +1 -0
- package/dist/rules/owasp-a07/weak-session.js +64 -0
- package/dist/rules/owasp-a07/weak-session.js.map +1 -0
- package/dist/rules/owasp-a08/unsafe-deserialization.d.ts +3 -0
- package/dist/rules/owasp-a08/unsafe-deserialization.d.ts.map +1 -0
- package/dist/rules/owasp-a08/unsafe-deserialization.js +78 -0
- package/dist/rules/owasp-a08/unsafe-deserialization.js.map +1 -0
- package/dist/rules/owasp-a08/unsafe-eval.d.ts +3 -0
- package/dist/rules/owasp-a08/unsafe-eval.d.ts.map +1 -0
- package/dist/rules/owasp-a08/unsafe-eval.js +73 -0
- package/dist/rules/owasp-a08/unsafe-eval.js.map +1 -0
- package/dist/rules/owasp-a09/log-sensitive-data.d.ts +3 -0
- package/dist/rules/owasp-a09/log-sensitive-data.d.ts.map +1 -0
- package/dist/rules/owasp-a09/log-sensitive-data.js +73 -0
- package/dist/rules/owasp-a09/log-sensitive-data.js.map +1 -0
- package/dist/rules/owasp-a09/missing-error-handling.d.ts +3 -0
- package/dist/rules/owasp-a09/missing-error-handling.d.ts.map +1 -0
- package/dist/rules/owasp-a09/missing-error-handling.js +84 -0
- package/dist/rules/owasp-a09/missing-error-handling.js.map +1 -0
- package/dist/rules/owasp-a10/open-redirect.d.ts +3 -0
- package/dist/rules/owasp-a10/open-redirect.d.ts.map +1 -0
- package/dist/rules/owasp-a10/open-redirect.js +67 -0
- package/dist/rules/owasp-a10/open-redirect.js.map +1 -0
- package/dist/rules/owasp-a10/unvalidated-fetch.d.ts +3 -0
- package/dist/rules/owasp-a10/unvalidated-fetch.d.ts.map +1 -0
- package/dist/rules/owasp-a10/unvalidated-fetch.js +85 -0
- package/dist/rules/owasp-a10/unvalidated-fetch.js.map +1 -0
- package/dist/rules/registry.d.ts +20 -0
- package/dist/rules/registry.d.ts.map +1 -0
- package/dist/rules/registry.js +142 -0
- package/dist/rules/registry.js.map +1 -0
- package/dist/scanner/engine.d.ts +21 -0
- package/dist/scanner/engine.d.ts.map +1 -0
- package/dist/scanner/engine.js +260 -0
- package/dist/scanner/engine.js.map +1 -0
- package/dist/scanner/file-walker.d.ts +7 -0
- package/dist/scanner/file-walker.d.ts.map +1 -0
- package/dist/scanner/file-walker.js +81 -0
- package/dist/scanner/file-walker.js.map +1 -0
- package/dist/scanner/language-detect.d.ts +5 -0
- package/dist/scanner/language-detect.d.ts.map +1 -0
- package/dist/scanner/language-detect.js +91 -0
- package/dist/scanner/language-detect.js.map +1 -0
- package/dist/scanner/sca-scanner.d.ts +38 -0
- package/dist/scanner/sca-scanner.d.ts.map +1 -0
- package/dist/scanner/sca-scanner.js +223 -0
- package/dist/scanner/sca-scanner.js.map +1 -0
- package/dist/types/index.d.ts +114 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +25 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/pattern-matcher.d.ts +4 -0
- package/dist/utils/pattern-matcher.d.ts.map +1 -0
- package/dist/utils/pattern-matcher.js +72 -0
- package/dist/utils/pattern-matcher.js.map +1 -0
- package/dist/utils/scoring.d.ts +8 -0
- package/dist/utils/scoring.d.ts.map +1 -0
- package/dist/utils/scoring.js +76 -0
- package/dist/utils/scoring.js.map +1 -0
- package/dist/utils/suppression.d.ts +3 -0
- package/dist/utils/suppression.d.ts.map +1 -0
- package/dist/utils/suppression.js +33 -0
- package/dist/utils/suppression.js.map +1 -0
- package/package.json +94 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// OWASP A03:2021 — Injection
|
|
2
|
+
// Rule: SQL Injection via string concatenation or template literals
|
|
3
|
+
export const SqlInjection = {
|
|
4
|
+
id: 'OWASP-A03-001',
|
|
5
|
+
name: 'SQL Injection via String Concatenation or Template Literal',
|
|
6
|
+
owasp: 'A03:2021',
|
|
7
|
+
cwe: 'CWE-89',
|
|
8
|
+
severity: 'CRITICAL',
|
|
9
|
+
languages: ['javascript', 'typescript', 'python', 'php', 'java', 'ruby', 'go', 'csharp'],
|
|
10
|
+
description: 'SQL query built by concatenating or interpolating user-controlled input. ' +
|
|
11
|
+
'Attackers can break out of the query string and execute arbitrary SQL commands, ' +
|
|
12
|
+
'reading, modifying, or deleting any data in the database. ' +
|
|
13
|
+
'The #1 most common AI-generated security bug — AI assistants use string concat for convenience.',
|
|
14
|
+
patterns: [
|
|
15
|
+
{
|
|
16
|
+
// SQL keyword in a string concatenated with user input — broad pattern
|
|
17
|
+
pattern: /(?:SELECT|INSERT|UPDATE|DELETE|DROP|ALTER|WHERE|UNION)\b[^\n]{0,200}\+[^\n]{0,100}(?:req\.|request\.)(?:params|body|query)\b/gi,
|
|
18
|
+
snippetLines: 3,
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
// db/client.query with concat directly
|
|
22
|
+
pattern: /(?:db|pool|client|conn|connection)\s*\.\s*(?:query|execute|run|exec)\s*\([^\n]*\+[^\n]*(?:req\.|request\.)(?:params|body|query)\b/gi,
|
|
23
|
+
snippetLines: 3,
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
// Template literal SQL with user input (JS/TS)
|
|
27
|
+
pattern: /`\s*(?:SELECT|INSERT|UPDATE|DELETE|DROP|ALTER|WHERE|UNION)\b[^`]*\$\{[^}]*(?:req\.|request\.|params\.|query\.|body\.|user\.|input\.|data\.)/gi,
|
|
28
|
+
snippetLines: 3,
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
// db.query/execute/run with string concat (JS/TS)
|
|
32
|
+
pattern: /(?:db|conn|pool|client|connection|knex)\s*\.\s*(?:query|execute|run|exec|raw)\s*\(\s*['"`][^'"`]*['"`]\s*\+/gi,
|
|
33
|
+
snippetLines: 3,
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
// Python: cursor.execute with format string or % operator
|
|
37
|
+
pattern: /cursor\s*\.\s*execute\s*\(\s*['"`][^'"`]*(?:SELECT|INSERT|UPDATE|DELETE|WHERE)[^'"`]*['"`]\s*%\s*(?:request\.|req\.)/gi,
|
|
38
|
+
snippetLines: 3,
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
// Python: f-string SQL
|
|
42
|
+
pattern: /cursor\s*\.\s*execute\s*\(\s*f['"`][^'"`]*(?:SELECT|INSERT|UPDATE|DELETE|WHERE)[^'"`]*\{[^}]*(?:request\.|req\.)/gi,
|
|
43
|
+
snippetLines: 3,
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
// PHP: mysql_query / mysqli_query with string concat
|
|
47
|
+
pattern: /(?:mysql_query|mysqli_query|pg_query)\s*\(\s*[^,]*['"`][^'"`]*(?:SELECT|INSERT|UPDATE|DELETE)[^'"`]*['"`]\s*\./gi,
|
|
48
|
+
snippetLines: 3,
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
// Java: Statement.executeQuery with string concat
|
|
52
|
+
pattern: /(?:stmt|statement|conn)\s*\.\s*execute(?:Query|Update)?\s*\(\s*['"`][^'"`]*['"`]\s*\+/gi,
|
|
53
|
+
snippetLines: 3,
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
// Go: db.Query with Sprintf
|
|
57
|
+
pattern: /(?:db|tx)\s*\.\s*(?:Query|Exec|QueryRow)\s*\(\s*fmt\.Sprintf\s*\(\s*['"`][^'"`]*(?:SELECT|INSERT|UPDATE|DELETE|WHERE)/gi,
|
|
58
|
+
snippetLines: 3,
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
fix: 'ALWAYS use parameterized queries (prepared statements) — never concatenate user input into SQL.\n\n' +
|
|
62
|
+
'// Node.js + mysql2:\n' +
|
|
63
|
+
'const [rows] = await db.query(\n' +
|
|
64
|
+
' "SELECT * FROM users WHERE id = ? AND email = ?",\n' +
|
|
65
|
+
' [req.params.id, req.body.email] // parameters passed separately\n' +
|
|
66
|
+
');\n\n' +
|
|
67
|
+
'// Node.js + pg:\n' +
|
|
68
|
+
'const result = await client.query(\n' +
|
|
69
|
+
' "SELECT * FROM users WHERE id = $1",\n' +
|
|
70
|
+
' [req.params.id]\n' +
|
|
71
|
+
');\n\n' +
|
|
72
|
+
'// Python:\n' +
|
|
73
|
+
'cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))\n\n' +
|
|
74
|
+
'// PHP + PDO:\n' +
|
|
75
|
+
'$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");\n' +
|
|
76
|
+
'$stmt->execute([$_GET["id"]]);\n\n' +
|
|
77
|
+
'// Java:\n' +
|
|
78
|
+
'PreparedStatement ps = conn.prepareStatement("SELECT * FROM users WHERE id = ?");\n' +
|
|
79
|
+
'ps.setInt(1, userId);',
|
|
80
|
+
references: [
|
|
81
|
+
'https://owasp.org/Top10/A03_2021-Injection/',
|
|
82
|
+
'https://cwe.mitre.org/data/definitions/89.html',
|
|
83
|
+
'https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html',
|
|
84
|
+
'https://portswigger.net/web-security/sql-injection',
|
|
85
|
+
],
|
|
86
|
+
tags: ['injection', 'sql', 'database', 'sqli'],
|
|
87
|
+
};
|
|
88
|
+
//# sourceMappingURL=sql-injection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sql-injection.js","sourceRoot":"","sources":["../../../src/rules/owasp-a03/sql-injection.ts"],"names":[],"mappings":"AAAA,6BAA6B;AAC7B,oEAAoE;AAIpE,MAAM,CAAC,MAAM,YAAY,GAAS;IAChC,EAAE,EAAE,eAAe;IACnB,IAAI,EAAE,4DAA4D;IAClE,KAAK,EAAE,UAAU;IACjB,GAAG,EAAE,QAAQ;IACb,QAAQ,EAAE,UAAU;IACpB,SAAS,EAAE,CAAC,YAAY,EAAE,YAAY,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,CAAC;IACxF,WAAW,EACT,2EAA2E;QAC3E,kFAAkF;QAClF,4DAA4D;QAC5D,iGAAiG;IACnG,QAAQ,EAAE;QACR;YACE,uEAAuE;YACvE,OAAO,EACL,gIAAgI;YAClI,YAAY,EAAE,CAAC;SAChB;QACD;YACE,uCAAuC;YACvC,OAAO,EACL,qIAAqI;YACvI,YAAY,EAAE,CAAC;SAChB;QACD;YACE,+CAA+C;YAC/C,OAAO,EACL,+IAA+I;YACjJ,YAAY,EAAE,CAAC;SAChB;QACD;YACE,kDAAkD;YAClD,OAAO,EACL,+GAA+G;YACjH,YAAY,EAAE,CAAC;SAChB;QACD;YACE,0DAA0D;YAC1D,OAAO,EACL,wHAAwH;YAC1H,YAAY,EAAE,CAAC;SAChB;QACD;YACE,uBAAuB;YACvB,OAAO,EACL,oHAAoH;YACtH,YAAY,EAAE,CAAC;SAChB;QACD;YACE,qDAAqD;YACrD,OAAO,EACL,kHAAkH;YACpH,YAAY,EAAE,CAAC;SAChB;QACD;YACE,kDAAkD;YAClD,OAAO,EACL,yFAAyF;YAC3F,YAAY,EAAE,CAAC;SAChB;QACD;YACE,4BAA4B;YAC5B,OAAO,EACL,yHAAyH;YAC3H,YAAY,EAAE,CAAC;SAChB;KACF;IACD,GAAG,EACD,qGAAqG;QACrG,wBAAwB;QACxB,kCAAkC;QAClC,uDAAuD;QACvD,qEAAqE;QACrE,QAAQ;QACR,oBAAoB;QACpB,sCAAsC;QACtC,0CAA0C;QAC1C,qBAAqB;QACrB,QAAQ;QACR,cAAc;QACd,qEAAqE;QACrE,iBAAiB;QACjB,8DAA8D;QAC9D,oCAAoC;QACpC,YAAY;QACZ,qFAAqF;QACrF,uBAAuB;IACzB,UAAU,EAAE;QACV,6CAA6C;QAC7C,gDAAgD;QAChD,0FAA0F;QAC1F,oDAAoD;KACrD;IACD,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,CAAC;CAC/C,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template-injection.d.ts","sourceRoot":"","sources":["../../../src/rules/owasp-a03/template-injection.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAEjD,eAAO,MAAM,iBAAiB,EAAE,IAoE/B,CAAC"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// OWASP A03:2021 — Injection
|
|
2
|
+
// Rule: Server-Side Template Injection (SSTI)
|
|
3
|
+
export const TemplateInjection = {
|
|
4
|
+
id: 'OWASP-A03-005',
|
|
5
|
+
name: 'Server-Side Template Injection (SSTI)',
|
|
6
|
+
owasp: 'A03:2021',
|
|
7
|
+
cwe: 'CWE-94',
|
|
8
|
+
severity: 'CRITICAL',
|
|
9
|
+
languages: ['javascript', 'typescript', 'python', 'php', 'ruby', 'java'],
|
|
10
|
+
description: 'User input passed to a template engine as a template string (not as data) allows ' +
|
|
11
|
+
'attackers to execute arbitrary code on the server. A classic example: passing ' +
|
|
12
|
+
'req.body.template to pug.render() or env.from_string() with user content.',
|
|
13
|
+
patterns: [
|
|
14
|
+
{
|
|
15
|
+
// Pug/Jade: render/compile with user input as template
|
|
16
|
+
pattern: /(?:pug|jade)\s*\.\s*(?:render|compile|renderFile)\s*\([^)]*(?:req\.|request\.)(?:body|params|query)/gi,
|
|
17
|
+
snippetLines: 3,
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
// Handlebars: compile/precompile with user template
|
|
21
|
+
pattern: /(?:Handlebars|hbs)\s*\.\s*(?:compile|precompile)\s*\([^)]*(?:req\.|request\.)(?:body|params|query)/gi,
|
|
22
|
+
snippetLines: 3,
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
// EJS: render with user input as template string
|
|
26
|
+
pattern: /ejs\s*\.\s*render\s*\([^)]*(?:req\.|request\.)(?:body|params|query)/gi,
|
|
27
|
+
snippetLines: 3,
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
// Python Jinja2: from_string with user input
|
|
31
|
+
pattern: /(?:env|environment|jinja2)\s*\.\s*from_string\s*\([^)]*(?:request\.|req\.)(?:args|form|json|data)/gi,
|
|
32
|
+
snippetLines: 3,
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
// Python Jinja2: Template() with user content
|
|
36
|
+
pattern: /jinja2\s*\.\s*Template\s*\([^)]*(?:request\.|req\.)(?:args|form|json|data)/gi,
|
|
37
|
+
snippetLines: 3,
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
// Mako/Genshi template from user input
|
|
41
|
+
pattern: /Template\s*\(\s*text\s*=\s*(?:request\.|req\.)(?:args|form|json|data)/gi,
|
|
42
|
+
snippetLines: 3,
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
fix: 'Never use user input AS a template. Only pass user data INTO templates as context variables.\n\n' +
|
|
46
|
+
'// BAD:\n' +
|
|
47
|
+
'const html = pug.render(req.body.template, { user }); // SSTI!\n\n' +
|
|
48
|
+
'// GOOD — load template from filesystem, pass user data as context:\n' +
|
|
49
|
+
'const html = pug.renderFile("views/profile.pug", {\n' +
|
|
50
|
+
' username: req.body.username, // data, not template code\n' +
|
|
51
|
+
'});\n\n' +
|
|
52
|
+
'// Python Jinja2 — BAD:\n' +
|
|
53
|
+
'template = jinja2.Template(request.args["tmpl"]) # SSTI!\n\n' +
|
|
54
|
+
'// GOOD:\n' +
|
|
55
|
+
'template = env.get_template("profile.html") # from filesystem\n' +
|
|
56
|
+
'html = template.render(username=request.args.get("username", ""))',
|
|
57
|
+
references: [
|
|
58
|
+
'https://owasp.org/Top10/A03_2021-Injection/',
|
|
59
|
+
'https://cwe.mitre.org/data/definitions/94.html',
|
|
60
|
+
'https://portswigger.net/web-security/server-side-template-injection',
|
|
61
|
+
],
|
|
62
|
+
tags: ['injection', 'ssti', 'template', 'rce'],
|
|
63
|
+
};
|
|
64
|
+
//# sourceMappingURL=template-injection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template-injection.js","sourceRoot":"","sources":["../../../src/rules/owasp-a03/template-injection.ts"],"names":[],"mappings":"AAAA,6BAA6B;AAC7B,8CAA8C;AAI9C,MAAM,CAAC,MAAM,iBAAiB,GAAS;IACrC,EAAE,EAAE,eAAe;IACnB,IAAI,EAAE,uCAAuC;IAC7C,KAAK,EAAE,UAAU;IACjB,GAAG,EAAE,QAAQ;IACb,QAAQ,EAAE,UAAU;IACpB,SAAS,EAAE,CAAC,YAAY,EAAE,YAAY,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC;IACxE,WAAW,EACT,mFAAmF;QACnF,gFAAgF;QAChF,2EAA2E;IAC7E,QAAQ,EAAE;QACR;YACE,uDAAuD;YACvD,OAAO,EACL,uGAAuG;YACzG,YAAY,EAAE,CAAC;SAChB;QACD;YACE,oDAAoD;YACpD,OAAO,EACL,sGAAsG;YACxG,YAAY,EAAE,CAAC;SAChB;QACD;YACE,iDAAiD;YACjD,OAAO,EACL,uEAAuE;YACzE,YAAY,EAAE,CAAC;SAChB;QACD;YACE,6CAA6C;YAC7C,OAAO,EACL,qGAAqG;YACvG,YAAY,EAAE,CAAC;SAChB;QACD;YACE,8CAA8C;YAC9C,OAAO,EACL,8EAA8E;YAChF,YAAY,EAAE,CAAC;SAChB;QACD;YACE,uCAAuC;YACvC,OAAO,EACL,yEAAyE;YAC3E,YAAY,EAAE,CAAC;SAChB;KACF;IACD,GAAG,EACD,kGAAkG;QAClG,WAAW;QACX,oEAAoE;QACpE,uEAAuE;QACvE,sDAAsD;QACtD,6DAA6D;QAC7D,SAAS;QACT,2BAA2B;QAC3B,+DAA+D;QAC/D,YAAY;QACZ,kEAAkE;QAClE,mEAAmE;IACrE,UAAU,EAAE;QACV,6CAA6C;QAC7C,gDAAgD;QAChD,qEAAqE;KACtE;IACD,IAAI,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,CAAC;CAC/C,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"xss.d.ts","sourceRoot":"","sources":["../../../src/rules/owasp-a03/xss.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAEjD,eAAO,MAAM,YAAY,EAAE,IA6E1B,CAAC"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// OWASP A03:2021 — Injection
|
|
2
|
+
// Rule: Reflected / Stored XSS via unsanitized user input in HTML response
|
|
3
|
+
export const XssReflected = {
|
|
4
|
+
id: 'OWASP-A03-004',
|
|
5
|
+
name: 'Cross-Site Scripting (XSS) via Unsanitized User Input',
|
|
6
|
+
owasp: 'A03:2021',
|
|
7
|
+
cwe: 'CWE-79',
|
|
8
|
+
severity: 'HIGH',
|
|
9
|
+
languages: ['javascript', 'typescript', 'python', 'php', 'ruby', 'java'],
|
|
10
|
+
description: 'User-controlled input rendered directly into HTML without escaping. Attackers inject ' +
|
|
11
|
+
'JavaScript that executes in victims\' browsers — stealing cookies, tokens, or performing ' +
|
|
12
|
+
'actions on the user\'s behalf. AI tools often use innerHTML, dangerouslySetInnerHTML, ' +
|
|
13
|
+
'or res.send() with unescaped user data.',
|
|
14
|
+
patterns: [
|
|
15
|
+
{
|
|
16
|
+
// JS: element.innerHTML with user input
|
|
17
|
+
pattern: /(?:\w+)\s*\.\s*innerHTML\s*=\s*(?!['"`][^'"`]*['"`])[^;]*(?:req\.|request\.|params\.|query\.|body\.|input\.|data\.)/gi,
|
|
18
|
+
snippetLines: 3,
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
// JS: document.write with user input
|
|
22
|
+
pattern: /document\s*\.\s*write\s*\([^)]*(?:req\.|request\.|location\.|search\.|hash\.|params\.)/gi,
|
|
23
|
+
snippetLines: 3,
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
// React: dangerouslySetInnerHTML with user content
|
|
27
|
+
pattern: /dangerouslySetInnerHTML\s*=\s*\{\s*\{\s*__html\s*:\s*(?!DOMPurify|sanitize)[^}]*(?:req\.|props\.|state\.|user\.|data\.)/gi,
|
|
28
|
+
suppressIf: /DOMPurify|sanitizeHtml|xss-filters|sanitize/i,
|
|
29
|
+
snippetLines: 3,
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
// Express: res.send() / res.write() with user input (reflected)
|
|
33
|
+
pattern: /res\s*\.\s*(?:send|write)\s*\([^)]*(?:req\.|request\.)(?:body|params|query|headers)/gi,
|
|
34
|
+
snippetLines: 3,
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
// PHP: echo/print with $_GET/$_POST without htmlspecialchars
|
|
38
|
+
pattern: /(?:echo|print)\s+(?!\s*htmlspecialchars|\s*htmlentities|\s*strip_tags)\s*\$_(?:GET|POST|REQUEST|COOKIE)\s*\[/gi,
|
|
39
|
+
snippetLines: 3,
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
// Python/Flask: Markup() — bypasses Jinja2 auto-escaping
|
|
43
|
+
pattern: /Markup\s*\(\s*(?!.*escape\(|.*markupsafe)/gi,
|
|
44
|
+
requiresContext: /(?:request\.|form\.|args\.|json\.|data\.)/i,
|
|
45
|
+
snippetLines: 3,
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
// Template: |safe filter in Jinja2/Django with user data
|
|
49
|
+
pattern: /\{\{\s*[^}]*(?:request\.|form\.|args\.|data\.|user\.)[^|]*\|\s*safe\s*\}\}/gi,
|
|
50
|
+
snippetLines: 3,
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
fix: 'Always escape user input before rendering in HTML context.\n\n' +
|
|
54
|
+
'// Node.js — use a sanitization library:\n' +
|
|
55
|
+
'import DOMPurify from "dompurify";\n' +
|
|
56
|
+
'import { JSDOM } from "jsdom";\n' +
|
|
57
|
+
'const window = new JSDOM("").window;\n' +
|
|
58
|
+
'const purify = DOMPurify(window);\n' +
|
|
59
|
+
'element.innerHTML = purify.sanitize(userInput); // sanitized\n\n' +
|
|
60
|
+
'// Express — escape output:\n' +
|
|
61
|
+
'import escapeHtml from "escape-html";\n' +
|
|
62
|
+
'res.send(`<p>${escapeHtml(req.query.name)}</p>`);\n\n' +
|
|
63
|
+
'// PHP:\n' +
|
|
64
|
+
'echo htmlspecialchars($_GET["name"], ENT_QUOTES, "UTF-8");\n\n' +
|
|
65
|
+
'// Prefer template engines with auto-escaping (Jinja2, EJS, Handlebars)\n' +
|
|
66
|
+
'// and avoid |safe, {{{ }}}, or Markup() with user data.',
|
|
67
|
+
references: [
|
|
68
|
+
'https://owasp.org/Top10/A03_2021-Injection/',
|
|
69
|
+
'https://cwe.mitre.org/data/definitions/79.html',
|
|
70
|
+
'https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html',
|
|
71
|
+
],
|
|
72
|
+
tags: ['injection', 'xss', 'cross-site-scripting', 'html', 'output-encoding'],
|
|
73
|
+
};
|
|
74
|
+
//# sourceMappingURL=xss.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"xss.js","sourceRoot":"","sources":["../../../src/rules/owasp-a03/xss.ts"],"names":[],"mappings":"AAAA,6BAA6B;AAC7B,2EAA2E;AAI3E,MAAM,CAAC,MAAM,YAAY,GAAS;IAChC,EAAE,EAAE,eAAe;IACnB,IAAI,EAAE,uDAAuD;IAC7D,KAAK,EAAE,UAAU;IACjB,GAAG,EAAE,QAAQ;IACb,QAAQ,EAAE,MAAM;IAChB,SAAS,EAAE,CAAC,YAAY,EAAE,YAAY,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC;IACxE,WAAW,EACT,uFAAuF;QACvF,2FAA2F;QAC3F,wFAAwF;QACxF,yCAAyC;IAC3C,QAAQ,EAAE;QACR;YACE,wCAAwC;YACxC,OAAO,EACL,uHAAuH;YACzH,YAAY,EAAE,CAAC;SAChB;QACD;YACE,qCAAqC;YACrC,OAAO,EACL,0FAA0F;YAC5F,YAAY,EAAE,CAAC;SAChB;QACD;YACE,mDAAmD;YACnD,OAAO,EACL,2HAA2H;YAC7H,UAAU,EAAE,8CAA8C;YAC1D,YAAY,EAAE,CAAC;SAChB;QACD;YACE,gEAAgE;YAChE,OAAO,EACL,uFAAuF;YACzF,YAAY,EAAE,CAAC;SAChB;QACD;YACE,6DAA6D;YAC7D,OAAO,EACL,gHAAgH;YAClH,YAAY,EAAE,CAAC;SAChB;QACD;YACE,yDAAyD;YACzD,OAAO,EAAE,6CAA6C;YACtD,eAAe,EAAE,4CAA4C;YAC7D,YAAY,EAAE,CAAC;SAChB;QACD;YACE,yDAAyD;YACzD,OAAO,EAAE,8EAA8E;YACvF,YAAY,EAAE,CAAC;SAChB;KACF;IACD,GAAG,EACD,gEAAgE;QAChE,4CAA4C;QAC5C,sCAAsC;QACtC,kCAAkC;QAClC,wCAAwC;QACxC,qCAAqC;QACrC,kEAAkE;QAClE,+BAA+B;QAC/B,yCAAyC;QACzC,uDAAuD;QACvD,WAAW;QACX,gEAAgE;QAChE,2EAA2E;QAC3E,0DAA0D;IAC5D,UAAU,EAAE;QACV,6CAA6C;QAC7C,gDAAgD;QAChD,iGAAiG;KAClG;IACD,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,EAAE,sBAAsB,EAAE,MAAM,EAAE,iBAAiB,CAAC;CAC9E,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mass-assignment.d.ts","sourceRoot":"","sources":["../../../src/rules/owasp-a04/mass-assignment.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAEjD,eAAO,MAAM,cAAc,EAAE,IAiE5B,CAAC"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// OWASP A04:2021 — Insecure Design
|
|
2
|
+
// Rule: Mass Assignment vulnerability (binding all request fields to model)
|
|
3
|
+
export const MassAssignment = {
|
|
4
|
+
id: 'OWASP-A04-002',
|
|
5
|
+
name: 'Mass Assignment Vulnerability',
|
|
6
|
+
owasp: 'A04:2021',
|
|
7
|
+
cwe: 'CWE-915',
|
|
8
|
+
severity: 'HIGH',
|
|
9
|
+
languages: ['javascript', 'typescript', 'python', 'ruby', 'php'],
|
|
10
|
+
description: 'Binding all request body fields directly to a model object (e.g., User.create(req.body)) ' +
|
|
11
|
+
'allows attackers to set fields they should not control — such as isAdmin, role, balance, ' +
|
|
12
|
+
'or verified. AI tools commonly generate Model.create(req.body) for convenience.',
|
|
13
|
+
patterns: [
|
|
14
|
+
{
|
|
15
|
+
// Mongoose/Sequelize: Model.create(req.body) or new Model(req.body)
|
|
16
|
+
pattern: /(?:\w+)\s*\.\s*(?:create|build|insert|insertMany)\s*\(\s*req\s*\.\s*body\s*\)/gi,
|
|
17
|
+
snippetLines: 3,
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
// new Model(req.body) — passing entire body to constructor
|
|
21
|
+
pattern: /new\s+\w+\s*\(\s*req\s*\.\s*body\s*\)/g,
|
|
22
|
+
suppressIf: /(?:error|Error|exception|Exception|event|Event|stream|Stream)/i,
|
|
23
|
+
snippetLines: 3,
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
// Object spread: { ...req.body } into DB operations
|
|
27
|
+
pattern: /(?:create|update|save|insert|upsert)\s*\(\s*\{\s*\.\.\.\s*req\s*\.\s*body/gi,
|
|
28
|
+
snippetLines: 3,
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
// Object.assign(model, req.body)
|
|
32
|
+
pattern: /Object\s*\.\s*assign\s*\([^,]+,\s*req\s*\.\s*body\s*\)/gi,
|
|
33
|
+
snippetLines: 3,
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
// Python Django: Model(**request.data) or form.save() without field restriction
|
|
37
|
+
pattern: /\w+\s*\(\s*\*\*\s*request\.(?:data|POST|json)\s*\)/gi,
|
|
38
|
+
snippetLines: 3,
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
fix: 'Explicitly whitelist the fields you accept from user input. Never pass req.body directly to a model.\n\n' +
|
|
42
|
+
'// BAD:\n' +
|
|
43
|
+
'const user = await User.create(req.body); // attacker sets isAdmin: true\n\n' +
|
|
44
|
+
'// GOOD — explicit field extraction:\n' +
|
|
45
|
+
'const { name, email, password } = req.body;\n' +
|
|
46
|
+
'const user = await User.create({\n' +
|
|
47
|
+
' name,\n' +
|
|
48
|
+
' email,\n' +
|
|
49
|
+
' password: await bcrypt.hash(password, 12),\n' +
|
|
50
|
+
' // isAdmin NOT included — cannot be set by user\n' +
|
|
51
|
+
'});\n\n' +
|
|
52
|
+
'// Alternatively, use a validation library like Zod:\n' +
|
|
53
|
+
'import { z } from "zod";\n' +
|
|
54
|
+
'const CreateUserSchema = z.object({ name: z.string(), email: z.string().email() });\n' +
|
|
55
|
+
'const data = CreateUserSchema.parse(req.body); // strips unknown fields',
|
|
56
|
+
references: [
|
|
57
|
+
'https://owasp.org/Top10/A04_2021-Insecure_Design/',
|
|
58
|
+
'https://cwe.mitre.org/data/definitions/915.html',
|
|
59
|
+
'https://cheatsheetseries.owasp.org/cheatsheets/Mass_Assignment_Cheat_Sheet.html',
|
|
60
|
+
],
|
|
61
|
+
tags: ['mass-assignment', 'input-validation', 'design', 'authorization'],
|
|
62
|
+
};
|
|
63
|
+
//# sourceMappingURL=mass-assignment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mass-assignment.js","sourceRoot":"","sources":["../../../src/rules/owasp-a04/mass-assignment.ts"],"names":[],"mappings":"AAAA,mCAAmC;AACnC,4EAA4E;AAI5E,MAAM,CAAC,MAAM,cAAc,GAAS;IAClC,EAAE,EAAE,eAAe;IACnB,IAAI,EAAE,+BAA+B;IACrC,KAAK,EAAE,UAAU;IACjB,GAAG,EAAE,SAAS;IACd,QAAQ,EAAE,MAAM;IAChB,SAAS,EAAE,CAAC,YAAY,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC;IAChE,WAAW,EACT,2FAA2F;QAC3F,2FAA2F;QAC3F,iFAAiF;IACnF,QAAQ,EAAE;QACR;YACE,oEAAoE;YACpE,OAAO,EACL,iFAAiF;YACnF,YAAY,EAAE,CAAC;SAChB;QACD;YACE,2DAA2D;YAC3D,OAAO,EACL,wCAAwC;YAC1C,UAAU,EAAE,gEAAgE;YAC5E,YAAY,EAAE,CAAC;SAChB;QACD;YACE,oDAAoD;YACpD,OAAO,EACL,6EAA6E;YAC/E,YAAY,EAAE,CAAC;SAChB;QACD;YACE,iCAAiC;YACjC,OAAO,EAAE,0DAA0D;YACnE,YAAY,EAAE,CAAC;SAChB;QACD;YACE,gFAAgF;YAChF,OAAO,EACL,sDAAsD;YACxD,YAAY,EAAE,CAAC;SAChB;KACF;IACD,GAAG,EACD,0GAA0G;QAC1G,WAAW;QACX,8EAA8E;QAC9E,wCAAwC;QACxC,+CAA+C;QAC/C,oCAAoC;QACpC,WAAW;QACX,YAAY;QACZ,gDAAgD;QAChD,qDAAqD;QACrD,SAAS;QACT,wDAAwD;QACxD,4BAA4B;QAC5B,uFAAuF;QACvF,yEAAyE;IAC3E,UAAU,EAAE;QACV,mDAAmD;QACnD,iDAAiD;QACjD,iFAAiF;KAClF;IACD,IAAI,EAAE,CAAC,iBAAiB,EAAE,kBAAkB,EAAE,QAAQ,EAAE,eAAe,CAAC;CACzE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"missing-rate-limit.d.ts","sourceRoot":"","sources":["../../../src/rules/owasp-a04/missing-rate-limit.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAEjD,eAAO,MAAM,gBAAgB,EAAE,IAkD9B,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// OWASP A04:2021 — Insecure Design
|
|
2
|
+
// Rule: Missing rate limiting on sensitive endpoints
|
|
3
|
+
export const MissingRateLimit = {
|
|
4
|
+
id: 'OWASP-A04-001',
|
|
5
|
+
name: 'Missing Rate Limiting on Sensitive Endpoint',
|
|
6
|
+
owasp: 'A04:2021',
|
|
7
|
+
cwe: 'CWE-770',
|
|
8
|
+
severity: 'MEDIUM',
|
|
9
|
+
languages: ['javascript', 'typescript'],
|
|
10
|
+
description: 'Authentication, password reset, OTP verification, and account registration endpoints ' +
|
|
11
|
+
'without rate limiting are vulnerable to brute-force attacks, credential stuffing, and ' +
|
|
12
|
+
'account enumeration. AI tools rarely add rate limiting when generating auth routes.',
|
|
13
|
+
patterns: [
|
|
14
|
+
{
|
|
15
|
+
// Express login route without rate limiter
|
|
16
|
+
pattern: /(?:app|router)\s*\.\s*post\s*\(\s*['"`][^'"`]*(?:login|signin|sign-in|auth|authenticate|token|otp|verify|reset-password|forgot-password)[^'"`]*['"`]/gi,
|
|
17
|
+
suppressIf: /(?:rateLimit|rateLimiter|rate_limit|throttle|limiter|slowDown|express-rate-limit|@nestjs\/throttler|throttler)/i,
|
|
18
|
+
snippetLines: 3,
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
// Registration endpoint without rate limiting
|
|
22
|
+
pattern: /(?:app|router)\s*\.\s*post\s*\(\s*['"`][^'"`]*(?:register|signup|sign-up|create-account|new-user)[^'"`]*['"`]/gi,
|
|
23
|
+
suppressIf: /(?:rateLimit|rateLimiter|rate_limit|throttle|limiter|slowDown|express-rate-limit)/i,
|
|
24
|
+
snippetLines: 3,
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
fix: 'Add rate limiting to all authentication and sensitive endpoints.\n\n' +
|
|
28
|
+
'// Express with express-rate-limit:\n' +
|
|
29
|
+
'import rateLimit from "express-rate-limit";\n\n' +
|
|
30
|
+
'const loginLimiter = rateLimit({\n' +
|
|
31
|
+
' windowMs: 15 * 60 * 1000, // 15 minutes\n' +
|
|
32
|
+
' max: 5, // max 5 login attempts per IP per window\n' +
|
|
33
|
+
' message: { error: "Too many login attempts. Try again in 15 minutes." },\n' +
|
|
34
|
+
' standardHeaders: true,\n' +
|
|
35
|
+
' legacyHeaders: false,\n' +
|
|
36
|
+
'});\n\n' +
|
|
37
|
+
'app.post("/api/login", loginLimiter, async (req, res) => { ... });\n\n' +
|
|
38
|
+
'// For distributed systems use Redis store:\n' +
|
|
39
|
+
'import RedisStore from "rate-limit-redis";\n' +
|
|
40
|
+
'const limiter = rateLimit({ store: new RedisStore({ client: redisClient }) });',
|
|
41
|
+
references: [
|
|
42
|
+
'https://owasp.org/Top10/A04_2021-Insecure_Design/',
|
|
43
|
+
'https://cwe.mitre.org/data/definitions/770.html',
|
|
44
|
+
'https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html',
|
|
45
|
+
],
|
|
46
|
+
tags: ['rate-limiting', 'brute-force', 'auth', 'design'],
|
|
47
|
+
};
|
|
48
|
+
//# sourceMappingURL=missing-rate-limit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"missing-rate-limit.js","sourceRoot":"","sources":["../../../src/rules/owasp-a04/missing-rate-limit.ts"],"names":[],"mappings":"AAAA,mCAAmC;AACnC,qDAAqD;AAIrD,MAAM,CAAC,MAAM,gBAAgB,GAAS;IACpC,EAAE,EAAE,eAAe;IACnB,IAAI,EAAE,6CAA6C;IACnD,KAAK,EAAE,UAAU;IACjB,GAAG,EAAE,SAAS;IACd,QAAQ,EAAE,QAAQ;IAClB,SAAS,EAAE,CAAC,YAAY,EAAE,YAAY,CAAC;IACvC,WAAW,EACT,uFAAuF;QACvF,wFAAwF;QACxF,qFAAqF;IACvF,QAAQ,EAAE;QACR;YACE,2CAA2C;YAC3C,OAAO,EACL,wJAAwJ;YAC1J,UAAU,EACR,iHAAiH;YACnH,YAAY,EAAE,CAAC;SAChB;QACD;YACE,8CAA8C;YAC9C,OAAO,EACL,iHAAiH;YACnH,UAAU,EACR,oFAAoF;YACtF,YAAY,EAAE,CAAC;SAChB;KACF;IACD,GAAG,EACD,sEAAsE;QACtE,uCAAuC;QACvC,iDAAiD;QACjD,oCAAoC;QACpC,6CAA6C;QAC7C,uDAAuD;QACvD,8EAA8E;QAC9E,4BAA4B;QAC5B,2BAA2B;QAC3B,SAAS;QACT,wEAAwE;QACxE,+CAA+C;QAC/C,8CAA8C;QAC9C,gFAAgF;IAClF,UAAU,EAAE;QACV,mDAAmD;QACnD,iDAAiD;QACjD,gFAAgF;KACjF;IACD,IAAI,EAAE,CAAC,eAAe,EAAE,aAAa,EAAE,MAAM,EAAE,QAAQ,CAAC;CACzD,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cors-wildcard.d.ts","sourceRoot":"","sources":["../../../src/rules/owasp-a05/cors-wildcard.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAEjD,eAAO,MAAM,YAAY,EAAE,IAiF1B,CAAC"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// OWASP A05:2021 — Security Misconfiguration
|
|
2
|
+
// Rule: CORS wildcard origin — allows any domain to make credentialed requests
|
|
3
|
+
export const CorsWildcard = {
|
|
4
|
+
id: 'OWASP-A05-002',
|
|
5
|
+
name: 'CORS Wildcard Origin (Access-Control-Allow-Origin: *)',
|
|
6
|
+
owasp: 'A05:2021',
|
|
7
|
+
cwe: 'CWE-942',
|
|
8
|
+
severity: 'HIGH',
|
|
9
|
+
languages: ['javascript', 'typescript', 'python', 'php', 'java', 'go', 'ruby'],
|
|
10
|
+
description: 'CORS configured with wildcard origin (*) or reflecting any origin without validation. ' +
|
|
11
|
+
'Combined with Access-Control-Allow-Credentials: true, this allows any website to make ' +
|
|
12
|
+
'credentialed cross-origin requests to your API, effectively defeating same-origin protection. ' +
|
|
13
|
+
'AI tools commonly generate cors() with no options, which defaults to wildcard.',
|
|
14
|
+
patterns: [
|
|
15
|
+
{
|
|
16
|
+
// Express cors() with no arguments (defaults to *) — direct or via require()
|
|
17
|
+
pattern: /app\s*\.\s*use\s*\(\s*(?:require\s*\(\s*['"`]cors['"`]\s*\)\s*\(\s*\)|cors\s*\(\s*\))\s*\)/gi,
|
|
18
|
+
snippetLines: 3,
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
// cors({ origin: '*' }) or cors({ origin: true }) — too permissive
|
|
22
|
+
pattern: /cors\s*\(\s*\{\s*[^}]*origin\s*:\s*(?:['"`]\*['"`]|true)\s*[^}]*\}\s*\)/gi,
|
|
23
|
+
snippetLines: 3,
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
// Direct header: res.setHeader('Access-Control-Allow-Origin', '*')
|
|
27
|
+
pattern: /(?:res|response)\s*\.\s*(?:setHeader|header)\s*\(\s*['"`]Access-Control-Allow-Origin['"`]\s*,\s*['"`]\*['"`]\s*\)/gi,
|
|
28
|
+
snippetLines: 3,
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
// Wildcard + credentials (especially dangerous combination)
|
|
32
|
+
pattern: /Access-Control-Allow-Origin['"]\s*,\s*['"]\*/gi,
|
|
33
|
+
snippetLines: 5,
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
// Python Flask-CORS: CORS(app) with no origins restriction
|
|
37
|
+
pattern: /CORS\s*\(\s*app\s*\)/gi,
|
|
38
|
+
suppressIf: /origins\s*=/i,
|
|
39
|
+
snippetLines: 3,
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
// Django CORS: CORS_ALLOW_ALL_ORIGINS = True
|
|
43
|
+
pattern: /CORS_ALLOW_ALL_ORIGINS\s*=\s*True/gi,
|
|
44
|
+
snippetLines: 1,
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
// Reflecting request origin directly
|
|
48
|
+
pattern: /(?:req|request)\s*\.\s*(?:headers|header)\s*(?:\[['"`]origin['"`]\]|\.origin)\s*\|\|?\s*['"`]\*/gi,
|
|
49
|
+
snippetLines: 3,
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
fix: 'Restrict CORS to a specific allowlist of trusted origins.\n\n' +
|
|
53
|
+
'// Express — explicit origin whitelist:\n' +
|
|
54
|
+
'import cors from "cors";\n' +
|
|
55
|
+
'const ALLOWED_ORIGINS = [\n' +
|
|
56
|
+
' "https://myapp.com",\n' +
|
|
57
|
+
' "https://www.myapp.com",\n' +
|
|
58
|
+
' process.env.FRONTEND_URL,\n' +
|
|
59
|
+
'].filter(Boolean);\n\n' +
|
|
60
|
+
'app.use(cors({\n' +
|
|
61
|
+
' origin: (origin, callback) => {\n' +
|
|
62
|
+
' if (!origin || ALLOWED_ORIGINS.includes(origin)) {\n' +
|
|
63
|
+
' callback(null, true);\n' +
|
|
64
|
+
' } else {\n' +
|
|
65
|
+
' callback(new Error("Not allowed by CORS"));\n' +
|
|
66
|
+
' }\n' +
|
|
67
|
+
' },\n' +
|
|
68
|
+
' credentials: true,\n' +
|
|
69
|
+
'}));\n\n' +
|
|
70
|
+
'// Django:\n' +
|
|
71
|
+
'CORS_ALLOWED_ORIGINS = ["https://myapp.com"] # explicit list',
|
|
72
|
+
references: [
|
|
73
|
+
'https://owasp.org/Top10/A05_2021-Security_Misconfiguration/',
|
|
74
|
+
'https://cwe.mitre.org/data/definitions/942.html',
|
|
75
|
+
'https://cheatsheetseries.owasp.org/cheatsheets/CORS_Cheat_Sheet.html',
|
|
76
|
+
],
|
|
77
|
+
tags: ['cors', 'misconfiguration', 'cross-origin', 'headers'],
|
|
78
|
+
};
|
|
79
|
+
//# sourceMappingURL=cors-wildcard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cors-wildcard.js","sourceRoot":"","sources":["../../../src/rules/owasp-a05/cors-wildcard.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,+EAA+E;AAI/E,MAAM,CAAC,MAAM,YAAY,GAAS;IAChC,EAAE,EAAE,eAAe;IACnB,IAAI,EAAE,uDAAuD;IAC7D,KAAK,EAAE,UAAU;IACjB,GAAG,EAAE,SAAS;IACd,QAAQ,EAAE,MAAM;IAChB,SAAS,EAAE,CAAC,YAAY,EAAE,YAAY,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC;IAC9E,WAAW,EACT,wFAAwF;QACxF,wFAAwF;QACxF,gGAAgG;QAChG,gFAAgF;IAClF,QAAQ,EAAE;QACR;YACE,6EAA6E;YAC7E,OAAO,EAAE,8FAA8F;YACvG,YAAY,EAAE,CAAC;SAChB;QACD;YACE,mEAAmE;YACnE,OAAO,EACL,2EAA2E;YAC7E,YAAY,EAAE,CAAC;SAChB;QACD;YACE,mEAAmE;YACnE,OAAO,EACL,qHAAqH;YACvH,YAAY,EAAE,CAAC;SAChB;QACD;YACE,4DAA4D;YAC5D,OAAO,EACL,gDAAgD;YAClD,YAAY,EAAE,CAAC;SAChB;QACD;YACE,2DAA2D;YAC3D,OAAO,EAAE,wBAAwB;YACjC,UAAU,EAAE,cAAc;YAC1B,YAAY,EAAE,CAAC;SAChB;QACD;YACE,6CAA6C;YAC7C,OAAO,EAAE,qCAAqC;YAC9C,YAAY,EAAE,CAAC;SAChB;QACD;YACE,qCAAqC;YACrC,OAAO,EACL,mGAAmG;YACrG,YAAY,EAAE,CAAC;SAChB;KACF;IACD,GAAG,EACD,+DAA+D;QAC/D,2CAA2C;QAC3C,4BAA4B;QAC5B,6BAA6B;QAC7B,0BAA0B;QAC1B,8BAA8B;QAC9B,+BAA+B;QAC/B,wBAAwB;QACxB,kBAAkB;QAClB,qCAAqC;QACrC,0DAA0D;QAC1D,+BAA+B;QAC/B,gBAAgB;QAChB,qDAAqD;QACrD,SAAS;QACT,QAAQ;QACR,wBAAwB;QACxB,UAAU;QACV,cAAc;QACd,+DAA+D;IACjE,UAAU,EAAE;QACV,6DAA6D;QAC7D,iDAAiD;QACjD,sEAAsE;KACvE;IACD,IAAI,EAAE,CAAC,MAAM,EAAE,kBAAkB,EAAE,cAAc,EAAE,SAAS,CAAC;CAC9D,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"debug-mode.d.ts","sourceRoot":"","sources":["../../../src/rules/owasp-a05/debug-mode.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAEjD,eAAO,MAAM,SAAS,EAAE,IAuEvB,CAAC"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// OWASP A05:2021 — Security Misconfiguration
|
|
2
|
+
// Rule: Debug mode enabled in production configuration
|
|
3
|
+
export const DebugMode = {
|
|
4
|
+
id: 'OWASP-A05-001',
|
|
5
|
+
name: 'Debug Mode Enabled in Production',
|
|
6
|
+
owasp: 'A05:2021',
|
|
7
|
+
cwe: 'CWE-94',
|
|
8
|
+
severity: 'HIGH',
|
|
9
|
+
languages: ['javascript', 'typescript', 'python', 'php', 'java', 'ruby'],
|
|
10
|
+
description: 'Debug mode or development environment settings left active in production code. ' +
|
|
11
|
+
'This exposes stack traces, internal paths, environment variables, and source code ' +
|
|
12
|
+
'to users — providing attackers with valuable reconnaissance information. ' +
|
|
13
|
+
'AI tools commonly generate DEBUG=True from tutorial templates.',
|
|
14
|
+
patterns: [
|
|
15
|
+
{
|
|
16
|
+
// Python/Django: DEBUG = True in settings file
|
|
17
|
+
pattern: /\bDEBUG\s*=\s*True\b/g,
|
|
18
|
+
suppressIf: /(?:test|spec|debug_setting|if.*DEBUG|DEBUG_OVERRIDE|dev|development)/i,
|
|
19
|
+
snippetLines: 1,
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
// Flask: app.run(debug=True) or app.debug = True
|
|
23
|
+
pattern: /(?:app\.run\s*\([^)]*debug\s*=\s*True|app\.debug\s*=\s*True)/gi,
|
|
24
|
+
snippetLines: 3,
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
// Node.js: NODE_ENV = development (hardcoded)
|
|
28
|
+
pattern: /NODE_ENV\s*=\s*['"`]development['"`]/g,
|
|
29
|
+
suppressIf: /(?:test|spec|dev-only|if.*NODE_ENV)/i,
|
|
30
|
+
snippetLines: 1,
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
// Express: app.set('env', 'development')
|
|
34
|
+
pattern: /app\s*\.\s*set\s*\(\s*['"`]env['"`]\s*,\s*['"`]development['"`]\s*\)/gi,
|
|
35
|
+
snippetLines: 3,
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
// PHP: ini_set('display_errors', '1') or error_reporting(E_ALL)
|
|
39
|
+
pattern: /ini_set\s*\(\s*['"`]display_errors['"`]\s*,\s*['"`]?1['"`]?\s*\)/gi,
|
|
40
|
+
snippetLines: 3,
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
// Spring Boot: spring.jpa.show-sql=true or logging.level=DEBUG
|
|
44
|
+
pattern: /(?:spring\.jpa\.show-sql\s*=\s*true|logging\.level\s*=\s*DEBUG)/gi,
|
|
45
|
+
snippetLines: 1,
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
// Generic: verbose error output to client
|
|
49
|
+
pattern: /res\s*\.\s*(?:send|json)\s*\([^)]*(?:error\.stack|err\.stack|e\.stack)\s*\)/gi,
|
|
50
|
+
snippetLines: 3,
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
fix: 'Use environment variables to control debug settings. Never hardcode debug=true.\n\n' +
|
|
54
|
+
'// Python/Django:\n' +
|
|
55
|
+
'DEBUG = os.environ.get("DEBUG", "False") == "True" # False in production\n\n' +
|
|
56
|
+
'// Flask:\n' +
|
|
57
|
+
'app.run(debug=os.environ.get("FLASK_DEBUG", "0") == "1")\n\n' +
|
|
58
|
+
'// Node.js — never expose stack traces:\n' +
|
|
59
|
+
'app.use((err, req, res, next) => {\n' +
|
|
60
|
+
' const isDev = process.env.NODE_ENV === "development";\n' +
|
|
61
|
+
' res.status(500).json({\n' +
|
|
62
|
+
' error: "Internal Server Error",\n' +
|
|
63
|
+
' ...(isDev && { stack: err.stack }), // only in dev\n' +
|
|
64
|
+
' });\n' +
|
|
65
|
+
'});',
|
|
66
|
+
references: [
|
|
67
|
+
'https://owasp.org/Top10/A05_2021-Security_Misconfiguration/',
|
|
68
|
+
'https://cwe.mitre.org/data/definitions/94.html',
|
|
69
|
+
'https://cheatsheetseries.owasp.org/cheatsheets/Error_Handling_Cheat_Sheet.html',
|
|
70
|
+
],
|
|
71
|
+
tags: ['misconfiguration', 'debug', 'information-disclosure', 'production'],
|
|
72
|
+
};
|
|
73
|
+
//# sourceMappingURL=debug-mode.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"debug-mode.js","sourceRoot":"","sources":["../../../src/rules/owasp-a05/debug-mode.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,uDAAuD;AAIvD,MAAM,CAAC,MAAM,SAAS,GAAS;IAC7B,EAAE,EAAE,eAAe;IACnB,IAAI,EAAE,kCAAkC;IACxC,KAAK,EAAE,UAAU;IACjB,GAAG,EAAE,QAAQ;IACb,QAAQ,EAAE,MAAM;IAChB,SAAS,EAAE,CAAC,YAAY,EAAE,YAAY,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC;IACxE,WAAW,EACT,iFAAiF;QACjF,oFAAoF;QACpF,2EAA2E;QAC3E,gEAAgE;IAClE,QAAQ,EAAE;QACR;YACE,+CAA+C;YAC/C,OAAO,EAAE,uBAAuB;YAChC,UAAU,EAAE,uEAAuE;YACnF,YAAY,EAAE,CAAC;SAChB;QACD;YACE,iDAAiD;YACjD,OAAO,EAAE,gEAAgE;YACzE,YAAY,EAAE,CAAC;SAChB;QACD;YACE,8CAA8C;YAC9C,OAAO,EAAE,uCAAuC;YAChD,UAAU,EAAE,sCAAsC;YAClD,YAAY,EAAE,CAAC;SAChB;QACD;YACE,yCAAyC;YACzC,OAAO,EAAE,wEAAwE;YACjF,YAAY,EAAE,CAAC;SAChB;QACD;YACE,gEAAgE;YAChE,OAAO,EAAE,oEAAoE;YAC7E,YAAY,EAAE,CAAC;SAChB;QACD;YACE,+DAA+D;YAC/D,OAAO,EAAE,mEAAmE;YAC5E,YAAY,EAAE,CAAC;SAChB;QACD;YACE,0CAA0C;YAC1C,OAAO,EAAE,+EAA+E;YACxF,YAAY,EAAE,CAAC;SAChB;KACF;IACD,GAAG,EACD,qFAAqF;QACrF,qBAAqB;QACrB,+EAA+E;QAC/E,aAAa;QACb,8DAA8D;QAC9D,2CAA2C;QAC3C,sCAAsC;QACtC,2DAA2D;QAC3D,4BAA4B;QAC5B,uCAAuC;QACvC,0DAA0D;QAC1D,SAAS;QACT,KAAK;IACP,UAAU,EAAE;QACV,6DAA6D;QAC7D,gDAAgD;QAChD,gFAAgF;KACjF;IACD,IAAI,EAAE,CAAC,kBAAkB,EAAE,OAAO,EAAE,wBAAwB,EAAE,YAAY,CAAC;CAC5E,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"default-credentials.d.ts","sourceRoot":"","sources":["../../../src/rules/owasp-a05/default-credentials.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAEjD,eAAO,MAAM,kBAAkB,EAAE,IAsDhC,CAAC"}
|