@korayda4/vibe-security 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +240 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +333 -0
- package/dist/cli.js.map +1 -0
- package/dist/engine/baseline.d.ts +19 -0
- package/dist/engine/baseline.js +48 -0
- package/dist/engine/baseline.js.map +1 -0
- package/dist/engine/config.d.ts +26 -0
- package/dist/engine/config.js +41 -0
- package/dist/engine/config.js.map +1 -0
- package/dist/engine/fixer.d.ts +22 -0
- package/dist/engine/fixer.js +87 -0
- package/dist/engine/fixer.js.map +1 -0
- package/dist/engine/language.d.ts +27 -0
- package/dist/engine/language.js +364 -0
- package/dist/engine/language.js.map +1 -0
- package/dist/engine/output.d.ts +6 -0
- package/dist/engine/output.js +155 -0
- package/dist/engine/output.js.map +1 -0
- package/dist/engine/reporter.d.ts +3 -0
- package/dist/engine/reporter.js +145 -0
- package/dist/engine/reporter.js.map +1 -0
- package/dist/engine/rules/_helpers.d.ts +9 -0
- package/dist/engine/rules/_helpers.js +64 -0
- package/dist/engine/rules/_helpers.js.map +1 -0
- package/dist/engine/rules/backend/fileUpload.d.ts +3 -0
- package/dist/engine/rules/backend/fileUpload.js +40 -0
- package/dist/engine/rules/backend/fileUpload.js.map +1 -0
- package/dist/engine/rules/backend/hashing.d.ts +3 -0
- package/dist/engine/rules/backend/hashing.js +40 -0
- package/dist/engine/rules/backend/hashing.js.map +1 -0
- package/dist/engine/rules/backend/idor.d.ts +3 -0
- package/dist/engine/rules/backend/idor.js +40 -0
- package/dist/engine/rules/backend/idor.js.map +1 -0
- package/dist/engine/rules/backend/index.d.ts +12 -0
- package/dist/engine/rules/backend/index.js +22 -0
- package/dist/engine/rules/backend/index.js.map +1 -0
- package/dist/engine/rules/backend/inputValidation.d.ts +3 -0
- package/dist/engine/rules/backend/inputValidation.js +37 -0
- package/dist/engine/rules/backend/inputValidation.js.map +1 -0
- package/dist/engine/rules/backend/jwt.d.ts +3 -0
- package/dist/engine/rules/backend/jwt.js +42 -0
- package/dist/engine/rules/backend/jwt.js.map +1 -0
- package/dist/engine/rules/backend/rateLimit.d.ts +3 -0
- package/dist/engine/rules/backend/rateLimit.js +45 -0
- package/dist/engine/rules/backend/rateLimit.js.map +1 -0
- package/dist/engine/rules/backend/sqlInjection.d.ts +3 -0
- package/dist/engine/rules/backend/sqlInjection.js +41 -0
- package/dist/engine/rules/backend/sqlInjection.js.map +1 -0
- package/dist/engine/rules/backend/tokenExpiry.d.ts +3 -0
- package/dist/engine/rules/backend/tokenExpiry.js +85 -0
- package/dist/engine/rules/backend/tokenExpiry.js.map +1 -0
- package/dist/engine/rules/backend/tokenInUrl.d.ts +3 -0
- package/dist/engine/rules/backend/tokenInUrl.js +112 -0
- package/dist/engine/rules/backend/tokenInUrl.js.map +1 -0
- package/dist/engine/rules/cicd/dependencies.d.ts +3 -0
- package/dist/engine/rules/cicd/dependencies.js +44 -0
- package/dist/engine/rules/cicd/dependencies.js.map +1 -0
- package/dist/engine/rules/cicd/dockerfile.d.ts +3 -0
- package/dist/engine/rules/cicd/dockerfile.js +42 -0
- package/dist/engine/rules/cicd/dockerfile.js.map +1 -0
- package/dist/engine/rules/cicd/index.d.ts +6 -0
- package/dist/engine/rules/cicd/index.js +10 -0
- package/dist/engine/rules/cicd/index.js.map +1 -0
- package/dist/engine/rules/cicd/secrets.d.ts +3 -0
- package/dist/engine/rules/cicd/secrets.js +41 -0
- package/dist/engine/rules/cicd/secrets.js.map +1 -0
- package/dist/engine/rules/database/encryption.d.ts +3 -0
- package/dist/engine/rules/database/encryption.js +37 -0
- package/dist/engine/rules/database/encryption.js.map +1 -0
- package/dist/engine/rules/database/index.d.ts +5 -0
- package/dist/engine/rules/database/index.js +8 -0
- package/dist/engine/rules/database/index.js.map +1 -0
- package/dist/engine/rules/database/privileges.d.ts +3 -0
- package/dist/engine/rules/database/privileges.js +40 -0
- package/dist/engine/rules/database/privileges.js.map +1 -0
- package/dist/engine/rules/frontend/clickjacking.d.ts +3 -0
- package/dist/engine/rules/frontend/clickjacking.js +41 -0
- package/dist/engine/rules/frontend/clickjacking.js.map +1 -0
- package/dist/engine/rules/frontend/cookies.d.ts +3 -0
- package/dist/engine/rules/frontend/cookies.js +35 -0
- package/dist/engine/rules/frontend/cookies.js.map +1 -0
- package/dist/engine/rules/frontend/envVars.d.ts +3 -0
- package/dist/engine/rules/frontend/envVars.js +34 -0
- package/dist/engine/rules/frontend/envVars.js.map +1 -0
- package/dist/engine/rules/frontend/eval.d.ts +3 -0
- package/dist/engine/rules/frontend/eval.js +35 -0
- package/dist/engine/rules/frontend/eval.js.map +1 -0
- package/dist/engine/rules/frontend/index.d.ts +9 -0
- package/dist/engine/rules/frontend/index.js +16 -0
- package/dist/engine/rules/frontend/index.js.map +1 -0
- package/dist/engine/rules/frontend/sourceMaps.d.ts +3 -0
- package/dist/engine/rules/frontend/sourceMaps.js +79 -0
- package/dist/engine/rules/frontend/sourceMaps.js.map +1 -0
- package/dist/engine/rules/frontend/xss.d.ts +3 -0
- package/dist/engine/rules/frontend/xss.js +70 -0
- package/dist/engine/rules/frontend/xss.js.map +1 -0
- package/dist/engine/rules/index.d.ts +8 -0
- package/dist/engine/rules/index.js +38 -0
- package/dist/engine/rules/index.js.map +1 -0
- package/dist/engine/rules/kotlin/index.d.ts +5 -0
- package/dist/engine/rules/kotlin/index.js +8 -0
- package/dist/engine/rules/kotlin/index.js.map +1 -0
- package/dist/engine/rules/kotlin/kotlinxHtml.d.ts +3 -0
- package/dist/engine/rules/kotlin/kotlinxHtml.js +54 -0
- package/dist/engine/rules/kotlin/kotlinxHtml.js.map +1 -0
- package/dist/engine/rules/kotlin/sqlInjection.d.ts +3 -0
- package/dist/engine/rules/kotlin/sqlInjection.js +57 -0
- package/dist/engine/rules/kotlin/sqlInjection.js.map +1 -0
- package/dist/engine/rules/network/cors.d.ts +3 -0
- package/dist/engine/rules/network/cors.js +106 -0
- package/dist/engine/rules/network/cors.js.map +1 -0
- package/dist/engine/rules/network/headers.d.ts +3 -0
- package/dist/engine/rules/network/headers.js +51 -0
- package/dist/engine/rules/network/headers.js.map +1 -0
- package/dist/engine/rules/network/index.d.ts +6 -0
- package/dist/engine/rules/network/index.js +10 -0
- package/dist/engine/rules/network/index.js.map +1 -0
- package/dist/engine/rules/network/tls.d.ts +3 -0
- package/dist/engine/rules/network/tls.js +40 -0
- package/dist/engine/rules/network/tls.js.map +1 -0
- package/dist/engine/rules/observability/errorHandler.d.ts +3 -0
- package/dist/engine/rules/observability/errorHandler.js +34 -0
- package/dist/engine/rules/observability/errorHandler.js.map +1 -0
- package/dist/engine/rules/observability/index.d.ts +5 -0
- package/dist/engine/rules/observability/index.js +8 -0
- package/dist/engine/rules/observability/index.js.map +1 -0
- package/dist/engine/rules/observability/logging.d.ts +3 -0
- package/dist/engine/rules/observability/logging.js +38 -0
- package/dist/engine/rules/observability/logging.js.map +1 -0
- package/dist/engine/rules/php/index.d.ts +6 -0
- package/dist/engine/rules/php/index.js +10 -0
- package/dist/engine/rules/php/index.js.map +1 -0
- package/dist/engine/rules/php/sqlInjection.d.ts +3 -0
- package/dist/engine/rules/php/sqlInjection.js +58 -0
- package/dist/engine/rules/php/sqlInjection.js.map +1 -0
- package/dist/engine/rules/php/weakHashing.d.ts +3 -0
- package/dist/engine/rules/php/weakHashing.js +36 -0
- package/dist/engine/rules/php/weakHashing.js.map +1 -0
- package/dist/engine/rules/php/xss.d.ts +3 -0
- package/dist/engine/rules/php/xss.js +56 -0
- package/dist/engine/rules/php/xss.js.map +1 -0
- package/dist/engine/rules/ruby/index.d.ts +6 -0
- package/dist/engine/rules/ruby/index.js +10 -0
- package/dist/engine/rules/ruby/index.js.map +1 -0
- package/dist/engine/rules/ruby/sqlInjection.d.ts +3 -0
- package/dist/engine/rules/ruby/sqlInjection.js +56 -0
- package/dist/engine/rules/ruby/sqlInjection.js.map +1 -0
- package/dist/engine/rules/ruby/strongParams.d.ts +3 -0
- package/dist/engine/rules/ruby/strongParams.js +35 -0
- package/dist/engine/rules/ruby/strongParams.js.map +1 -0
- package/dist/engine/rules/ruby/xss.d.ts +3 -0
- package/dist/engine/rules/ruby/xss.js +55 -0
- package/dist/engine/rules/ruby/xss.js.map +1 -0
- package/dist/engine/rules/rust/index.d.ts +6 -0
- package/dist/engine/rules/rust/index.js +10 -0
- package/dist/engine/rules/rust/index.js.map +1 -0
- package/dist/engine/rules/rust/secretInCode.d.ts +3 -0
- package/dist/engine/rules/rust/secretInCode.js +36 -0
- package/dist/engine/rules/rust/secretInCode.js.map +1 -0
- package/dist/engine/rules/rust/sqlInjection.d.ts +3 -0
- package/dist/engine/rules/rust/sqlInjection.js +55 -0
- package/dist/engine/rules/rust/sqlInjection.js.map +1 -0
- package/dist/engine/rules/rust/unsafeExpect.d.ts +3 -0
- package/dist/engine/rules/rust/unsafeExpect.js +56 -0
- package/dist/engine/rules/rust/unsafeExpect.js.map +1 -0
- package/dist/engine/scanner.d.ts +5 -0
- package/dist/engine/scanner.js +135 -0
- package/dist/engine/scanner.js.map +1 -0
- package/dist/engine/walker.d.ts +11 -0
- package/dist/engine/walker.js +90 -0
- package/dist/engine/walker.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +36 -0
- package/dist/server.js +360 -0
- package/dist/server.js.map +1 -0
- package/dist/types.d.ts +92 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +85 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { lineColumnFromIndex, snippetAt } from '../_helpers.js';
|
|
2
|
+
const rule = {
|
|
3
|
+
id: 'PHP-002',
|
|
4
|
+
title: 'PHP: XSS via echo of unescaped input',
|
|
5
|
+
layer: 'frontend',
|
|
6
|
+
severity: 'high',
|
|
7
|
+
description: '`echo $_GET["..."]` or `print $user_input` renders raw user input as HTML. Attackers inject `<script>` tags for session hijacking. The same applies to Laravel `{{ ... }}` raw output and Symfony `{{ var|raw }}`.',
|
|
8
|
+
threat: 'Reflected / Stored XSS → account takeover, session hijack, data exfiltration',
|
|
9
|
+
remediation: '1) Use `htmlspecialchars($input, ENT_QUOTES, "UTF-8")` before echo: `echo htmlspecialchars($_GET["q"])`. ' +
|
|
10
|
+
'2) Laravel: `{{ $input }}` auto-escapes; `{{!! $input !!}}` is raw (avoid). ' +
|
|
11
|
+
'3) Use Twig auto-escape; avoid `|raw` filter on user input. ' +
|
|
12
|
+
'4) Set `Content-Security-Policy` header. ' +
|
|
13
|
+
'5) Use a templating engine that escapes by default (Twig, Blade).',
|
|
14
|
+
references: [
|
|
15
|
+
'https://www.php.net/manual/en/function.htmlspecialchars.php',
|
|
16
|
+
'https://laravel.com/docs/blade#displaying-data',
|
|
17
|
+
'https://owasp.org/www-community/attacks/xss/',
|
|
18
|
+
],
|
|
19
|
+
cwe: 'CWE-79',
|
|
20
|
+
owasp: 'A03:2021 Injection',
|
|
21
|
+
languages: ['php'],
|
|
22
|
+
check: (ctx) => {
|
|
23
|
+
const findings = [];
|
|
24
|
+
const patterns = [
|
|
25
|
+
/\b(?:echo|print)\s+\$_(?:GET|POST|REQUEST|COOKIE)\s*\[/g,
|
|
26
|
+
/\b(?:echo|print)\s+\$this->input->/g,
|
|
27
|
+
/\{\{!!\s+[^}]+\s+!!\}\}/g,
|
|
28
|
+
/\{\{[^}]+\s*\|\s*raw\s*\}\}/g,
|
|
29
|
+
];
|
|
30
|
+
for (const re of patterns) {
|
|
31
|
+
for (const m of ctx.source.matchAll(re)) {
|
|
32
|
+
if (m.index === undefined)
|
|
33
|
+
continue;
|
|
34
|
+
const { line } = lineColumnFromIndex(ctx.source, m.index);
|
|
35
|
+
findings.push({
|
|
36
|
+
id: `${rule.id}-${ctx.relativePath}-${line}`,
|
|
37
|
+
ruleId: rule.id,
|
|
38
|
+
title: rule.title,
|
|
39
|
+
layer: rule.layer,
|
|
40
|
+
severity: rule.severity,
|
|
41
|
+
file: ctx.relativePath,
|
|
42
|
+
match: { snippet: snippetAt(ctx.source, line), line, column: m.index },
|
|
43
|
+
description: rule.description,
|
|
44
|
+
impact: rule.threat,
|
|
45
|
+
remediation: rule.remediation,
|
|
46
|
+
references: rule.references,
|
|
47
|
+
cwe: rule.cwe,
|
|
48
|
+
owasp: rule.owasp,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return findings;
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
export default rule;
|
|
56
|
+
//# sourceMappingURL=xss.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"xss.js","sourceRoot":"","sources":["../../../../src/engine/rules/php/xss.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhE,MAAM,IAAI,GAAS;IACjB,EAAE,EAAE,SAAS;IACb,KAAK,EAAE,sCAAsC;IAC7C,KAAK,EAAE,UAAU;IACjB,QAAQ,EAAE,MAAM;IAChB,WAAW,EACT,oNAAoN;IACtN,MAAM,EAAE,8EAA8E;IACtF,WAAW,EACT,2GAA2G;QAC3G,8EAA8E;QAC9E,8DAA8D;QAC9D,2CAA2C;QAC3C,mEAAmE;IACrE,UAAU,EAAE;QACV,6DAA6D;QAC7D,gDAAgD;QAChD,8CAA8C;KAC/C;IACD,GAAG,EAAE,QAAQ;IACb,KAAK,EAAE,oBAAoB;IAC3B,SAAS,EAAE,CAAC,KAAK,CAAC;IAClB,KAAK,EAAE,CAAC,GAAG,EAAE,EAAE;QACb,MAAM,QAAQ,GAAG,EAAE,CAAC;QACpB,MAAM,QAAQ,GAAG;YACf,yDAAyD;YACzD,qCAAqC;YACrC,0BAA0B;YAC1B,8BAA8B;SAC/B,CAAC;QACF,KAAK,MAAM,EAAE,IAAI,QAAQ,EAAE,CAAC;YAC1B,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;gBACxC,IAAI,CAAC,CAAC,KAAK,KAAK,SAAS;oBAAE,SAAS;gBACpC,MAAM,EAAE,IAAI,EAAE,GAAG,mBAAmB,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC;gBAC1D,QAAQ,CAAC,IAAI,CAAC;oBACZ,EAAE,EAAE,GAAG,IAAI,CAAC,EAAE,IAAI,GAAG,CAAC,YAAY,IAAI,IAAI,EAAE;oBAC5C,MAAM,EAAE,IAAI,CAAC,EAAE;oBACf,KAAK,EAAE,IAAI,CAAC,KAAK;oBACjB,KAAK,EAAE,IAAI,CAAC,KAAK;oBACjB,QAAQ,EAAE,IAAI,CAAC,QAAQ;oBACvB,IAAI,EAAE,GAAG,CAAC,YAAY;oBACtB,KAAK,EAAE,EAAE,OAAO,EAAE,SAAS,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,KAAK,EAAE;oBACtE,WAAW,EAAE,IAAI,CAAC,WAAW;oBAC7B,MAAM,EAAE,IAAI,CAAC,MAAM;oBACnB,WAAW,EAAE,IAAI,CAAC,WAAW;oBAC7B,UAAU,EAAE,IAAI,CAAC,UAAU;oBAC3B,GAAG,EAAE,IAAI,CAAC,GAAG;oBACb,KAAK,EAAE,IAAI,CAAC,KAAK;iBAClB,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF,CAAC;AAEF,eAAe,IAAI,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/engine/rules/ruby/index.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,mBAAmB,CAAC;AAC7C,OAAO,YAAY,MAAM,mBAAmB,CAAC;AAC7C,OAAO,GAAG,MAAM,UAAU,CAAC;AAE3B,MAAM,CAAC,MAAM,KAAK,GAAG;IACnB,YAAY;IACZ,YAAY;IACZ,GAAG;CACJ,CAAC;AAEF,eAAe,KAAK,CAAC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { lineColumnFromIndex, snippetAt } from '../_helpers.js';
|
|
2
|
+
const rule = {
|
|
3
|
+
id: 'RB-001',
|
|
4
|
+
title: 'Ruby: SQL injection via string interpolation',
|
|
5
|
+
layer: 'backend',
|
|
6
|
+
severity: 'critical',
|
|
7
|
+
description: 'Ruby on Rails ActiveRecord parameterizes queries by default. However, raw `where("...")` with string interpolation, `sanitize_sql` with concatenation, or find_by_sql bypass that protection.',
|
|
8
|
+
threat: 'Database exfiltration, authentication bypass, data destruction',
|
|
9
|
+
remediation: '1) Use ActiveRecord: `User.where(email: params[:email])` — auto-parameterized. ' +
|
|
10
|
+
'2) For raw SQL: `where("email = ?", params[:email])` with `?` placeholders. ' +
|
|
11
|
+
'3) Use `sanitize_sql_array` for IN clauses: `where("id IN (?)", ids)`. ' +
|
|
12
|
+
'4) Hash conditions: `where("name = :name", name: input)` — named placeholders. ' +
|
|
13
|
+
"5) NEVER use string interpolation in raw SQL: where(\"email = '#{params[:email]}'\") is vulnerable.",
|
|
14
|
+
references: [
|
|
15
|
+
'https://guides.rubyonrails.org/security.html#sql-injection',
|
|
16
|
+
'https://api.rubyonrails.org/classes/ActiveRecord/Sanitization/ClassMethods.html',
|
|
17
|
+
'https://owasp.org/www-community/attacks/SQL_Injection',
|
|
18
|
+
],
|
|
19
|
+
cwe: 'CWE-89',
|
|
20
|
+
owasp: 'A03:2021 Injection',
|
|
21
|
+
languages: ['ruby'],
|
|
22
|
+
check: (ctx) => {
|
|
23
|
+
const findings = [];
|
|
24
|
+
const patterns = [
|
|
25
|
+
/where\s*\(\s*["'][^"']*#\{[^}]*(?:params|request|input|params\[|@user\.input)/g,
|
|
26
|
+
/find_by_sql\s*\(\s*["'][^"']*#\{/g,
|
|
27
|
+
/execute\s*\(\s*["'][^"']*#\{/g,
|
|
28
|
+
/sanitize_sql\s*\(\s*["'][^"']*#\{/g,
|
|
29
|
+
];
|
|
30
|
+
for (const re of patterns) {
|
|
31
|
+
for (const m of ctx.source.matchAll(re)) {
|
|
32
|
+
if (m.index === undefined)
|
|
33
|
+
continue;
|
|
34
|
+
const { line } = lineColumnFromIndex(ctx.source, m.index);
|
|
35
|
+
findings.push({
|
|
36
|
+
id: `${rule.id}-${ctx.relativePath}-${line}`,
|
|
37
|
+
ruleId: rule.id,
|
|
38
|
+
title: rule.title,
|
|
39
|
+
layer: rule.layer,
|
|
40
|
+
severity: rule.severity,
|
|
41
|
+
file: ctx.relativePath,
|
|
42
|
+
match: { snippet: snippetAt(ctx.source, line), line, column: m.index },
|
|
43
|
+
description: rule.description,
|
|
44
|
+
impact: rule.threat,
|
|
45
|
+
remediation: rule.remediation,
|
|
46
|
+
references: rule.references,
|
|
47
|
+
cwe: rule.cwe,
|
|
48
|
+
owasp: rule.owasp,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return findings;
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
export default rule;
|
|
56
|
+
//# sourceMappingURL=sqlInjection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlInjection.js","sourceRoot":"","sources":["../../../../src/engine/rules/ruby/sqlInjection.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhE,MAAM,IAAI,GAAS;IACjB,EAAE,EAAE,QAAQ;IACZ,KAAK,EAAE,8CAA8C;IACrD,KAAK,EAAE,SAAS;IAChB,QAAQ,EAAE,UAAU;IACpB,WAAW,EACT,+LAA+L;IACjM,MAAM,EAAE,gEAAgE;IACxE,WAAW,EACT,iFAAiF;QACjF,8EAA8E;QAC9E,yEAAyE;QACzE,iFAAiF;QACjF,qGAAqG;IACvG,UAAU,EAAE;QACV,4DAA4D;QAC5D,iFAAiF;QACjF,uDAAuD;KACxD;IACD,GAAG,EAAE,QAAQ;IACb,KAAK,EAAE,oBAAoB;IAC3B,SAAS,EAAE,CAAC,MAAM,CAAC;IACnB,KAAK,EAAE,CAAC,GAAG,EAAE,EAAE;QACb,MAAM,QAAQ,GAAG,EAAE,CAAC;QACpB,MAAM,QAAQ,GAAG;YACf,gFAAgF;YAChF,mCAAmC;YACnC,+BAA+B;YAC/B,oCAAoC;SACrC,CAAC;QACF,KAAK,MAAM,EAAE,IAAI,QAAQ,EAAE,CAAC;YAC1B,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;gBACxC,IAAI,CAAC,CAAC,KAAK,KAAK,SAAS;oBAAE,SAAS;gBACpC,MAAM,EAAE,IAAI,EAAE,GAAG,mBAAmB,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC;gBAC1D,QAAQ,CAAC,IAAI,CAAC;oBACZ,EAAE,EAAE,GAAG,IAAI,CAAC,EAAE,IAAI,GAAG,CAAC,YAAY,IAAI,IAAI,EAAE;oBAC5C,MAAM,EAAE,IAAI,CAAC,EAAE;oBACf,KAAK,EAAE,IAAI,CAAC,KAAK;oBACjB,KAAK,EAAE,IAAI,CAAC,KAAK;oBACjB,QAAQ,EAAE,IAAI,CAAC,QAAQ;oBACvB,IAAI,EAAE,GAAG,CAAC,YAAY;oBACtB,KAAK,EAAE,EAAE,OAAO,EAAE,SAAS,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,KAAK,EAAE;oBACtE,WAAW,EAAE,IAAI,CAAC,WAAW;oBAC7B,MAAM,EAAE,IAAI,CAAC,MAAM;oBACnB,WAAW,EAAE,IAAI,CAAC,WAAW;oBAC7B,UAAU,EAAE,IAAI,CAAC,UAAU;oBAC3B,GAAG,EAAE,IAAI,CAAC,GAAG;oBACb,KAAK,EAAE,IAAI,CAAC,KAAK;iBAClB,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF,CAAC;AAEF,eAAe,IAAI,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { grepRule } from '../_helpers.js';
|
|
2
|
+
const rule = {
|
|
3
|
+
id: 'RB-002',
|
|
4
|
+
title: 'Ruby on Rails: mass assignment — params.require only',
|
|
5
|
+
layer: 'backend',
|
|
6
|
+
severity: 'high',
|
|
7
|
+
description: 'In Rails, `User.create(params[:user])` or `User.update(params[:user])` passes the entire params hash to the model. Attackers can set `admin: true`, `role: \'admin\`, etc. Rails 4+ requires using strong parameters (`params.require(:user).permit(...)`).',
|
|
8
|
+
threat: 'Privilege escalation (become admin), business logic bypass',
|
|
9
|
+
remediation: '1) Use strong parameters in every controller: `def user_params; params.require(:user).permit(:name, :email); end`. ' +
|
|
10
|
+
'2) Never call `User.create(params[:user])` directly. ' +
|
|
11
|
+
'3) Use `before_action :authenticate_user!` for protected actions. ' +
|
|
12
|
+
'4) For nested attributes: `.permit(:name, emails_attributes: [:address])`. ' +
|
|
13
|
+
'5) In Rails 5+, `config.load_defaults 5.0` enables `permit_all_parameters = false` by default.',
|
|
14
|
+
references: [
|
|
15
|
+
'https://guides.rubyonrails.org/action_controller_overview.html#strong-parameters',
|
|
16
|
+
'https://guides.rubyonrails.org/security.html#mass-assignment',
|
|
17
|
+
],
|
|
18
|
+
cwe: 'CWE-915',
|
|
19
|
+
owasp: 'A04:2021 Insecure Design',
|
|
20
|
+
languages: ['ruby'],
|
|
21
|
+
check: (ctx) => {
|
|
22
|
+
const patterns = [
|
|
23
|
+
/\.(?:create|update|update_attributes)\s*\(\s*params\s*[,:]/g,
|
|
24
|
+
/\.(?:create|update|update_attributes)\s*\(\s*params\[:/g,
|
|
25
|
+
/User\.create\s*\(\s*params/g,
|
|
26
|
+
];
|
|
27
|
+
const findings = [];
|
|
28
|
+
for (const re of patterns) {
|
|
29
|
+
findings.push(...grepRule(rule, ctx, re));
|
|
30
|
+
}
|
|
31
|
+
return findings;
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
export default rule;
|
|
35
|
+
//# sourceMappingURL=strongParams.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"strongParams.js","sourceRoot":"","sources":["../../../../src/engine/rules/ruby/strongParams.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE1C,MAAM,IAAI,GAAS;IACjB,EAAE,EAAE,QAAQ;IACZ,KAAK,EAAE,sDAAsD;IAC7D,KAAK,EAAE,SAAS;IAChB,QAAQ,EAAE,MAAM;IAChB,WAAW,EACT,6PAA6P;IAC/P,MAAM,EAAE,4DAA4D;IACpE,WAAW,EACT,qHAAqH;QACrH,uDAAuD;QACvD,oEAAoE;QACpE,6EAA6E;QAC7E,gGAAgG;IAClG,UAAU,EAAE;QACV,kFAAkF;QAClF,8DAA8D;KAC/D;IACD,GAAG,EAAE,SAAS;IACd,KAAK,EAAE,0BAA0B;IACjC,SAAS,EAAE,CAAC,MAAM,CAAC;IACnB,KAAK,EAAE,CAAC,GAAG,EAAE,EAAE;QACb,MAAM,QAAQ,GAAG;YACf,6DAA6D;YAC7D,yDAAyD;YACzD,6BAA6B;SAC9B,CAAC;QACF,MAAM,QAAQ,GAAG,EAAE,CAAC;QACpB,KAAK,MAAM,EAAE,IAAI,QAAQ,EAAE,CAAC;YAC1B,QAAQ,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC;QAC5C,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF,CAAC;AAEF,eAAe,IAAI,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { lineColumnFromIndex, snippetAt } from '../_helpers.js';
|
|
2
|
+
const rule = {
|
|
3
|
+
id: 'RB-003',
|
|
4
|
+
title: 'Ruby: XSS via raw / html_safe in views',
|
|
5
|
+
layer: 'frontend',
|
|
6
|
+
severity: 'high',
|
|
7
|
+
description: 'In Rails, `<%= ... %>` escapes HTML by default. However `<%= raw ... %>` or `<%== ... %>` (or `html_safe` in controllers) bypasses escaping and renders raw HTML — XSS.',
|
|
8
|
+
threat: 'Stored / Reflected XSS → session hijack, account takeover, data exfiltration',
|
|
9
|
+
remediation: '1) NEVER use `<%= raw user_input %>` or `<%== user_input %>`. ' +
|
|
10
|
+
'2) Use `<%= user_input %>` which auto-escapes. ' +
|
|
11
|
+
'3) For rich text, sanitize via `sanitize(user_input)` or a gem like `loofah` / `sanitize`. ' +
|
|
12
|
+
'4) In controllers, NEVER call `.html_safe` on user-controlled data. ' +
|
|
13
|
+
'5) Set `Content-Security-Policy` headers to add defense in depth.',
|
|
14
|
+
references: [
|
|
15
|
+
'https://guides.rubyonrails.org/security.html#cross-site-scripting-xss',
|
|
16
|
+
'https://owasp.org/www-community/attacks/xss/',
|
|
17
|
+
],
|
|
18
|
+
cwe: 'CWE-79',
|
|
19
|
+
owasp: 'A03:2021 Injection',
|
|
20
|
+
languages: ['ruby'],
|
|
21
|
+
check: (ctx) => {
|
|
22
|
+
const findings = [];
|
|
23
|
+
const patterns = [
|
|
24
|
+
/<%=\s*raw\s+/g,
|
|
25
|
+
/<%==\s*/g,
|
|
26
|
+
/<%=\s*\w+\.html_safe\s*%>/g,
|
|
27
|
+
/\.html_safe\s*(?:!|\b)/g,
|
|
28
|
+
];
|
|
29
|
+
for (const re of patterns) {
|
|
30
|
+
for (const m of ctx.source.matchAll(re)) {
|
|
31
|
+
if (m.index === undefined)
|
|
32
|
+
continue;
|
|
33
|
+
const { line } = lineColumnFromIndex(ctx.source, m.index);
|
|
34
|
+
findings.push({
|
|
35
|
+
id: `${rule.id}-${ctx.relativePath}-${line}`,
|
|
36
|
+
ruleId: rule.id,
|
|
37
|
+
title: rule.title,
|
|
38
|
+
layer: rule.layer,
|
|
39
|
+
severity: rule.severity,
|
|
40
|
+
file: ctx.relativePath,
|
|
41
|
+
match: { snippet: snippetAt(ctx.source, line), line, column: m.index },
|
|
42
|
+
description: rule.description,
|
|
43
|
+
impact: rule.threat,
|
|
44
|
+
remediation: rule.remediation,
|
|
45
|
+
references: rule.references,
|
|
46
|
+
cwe: rule.cwe,
|
|
47
|
+
owasp: rule.owasp,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return findings;
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
export default rule;
|
|
55
|
+
//# sourceMappingURL=xss.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"xss.js","sourceRoot":"","sources":["../../../../src/engine/rules/ruby/xss.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhE,MAAM,IAAI,GAAS;IACjB,EAAE,EAAE,QAAQ;IACZ,KAAK,EAAE,wCAAwC;IAC/C,KAAK,EAAE,UAAU;IACjB,QAAQ,EAAE,MAAM;IAChB,WAAW,EACT,yKAAyK;IAC3K,MAAM,EAAE,8EAA8E;IACtF,WAAW,EACT,gEAAgE;QAChE,iDAAiD;QACjD,6FAA6F;QAC7F,sEAAsE;QACtE,mEAAmE;IACrE,UAAU,EAAE;QACV,uEAAuE;QACvE,8CAA8C;KAC/C;IACD,GAAG,EAAE,QAAQ;IACb,KAAK,EAAE,oBAAoB;IAC3B,SAAS,EAAE,CAAC,MAAM,CAAC;IACnB,KAAK,EAAE,CAAC,GAAG,EAAE,EAAE;QACb,MAAM,QAAQ,GAAG,EAAE,CAAC;QACpB,MAAM,QAAQ,GAAG;YACf,eAAe;YACf,UAAU;YACV,4BAA4B;YAC5B,yBAAyB;SAC1B,CAAC;QACF,KAAK,MAAM,EAAE,IAAI,QAAQ,EAAE,CAAC;YAC1B,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;gBACxC,IAAI,CAAC,CAAC,KAAK,KAAK,SAAS;oBAAE,SAAS;gBACpC,MAAM,EAAE,IAAI,EAAE,GAAG,mBAAmB,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC;gBAC1D,QAAQ,CAAC,IAAI,CAAC;oBACZ,EAAE,EAAE,GAAG,IAAI,CAAC,EAAE,IAAI,GAAG,CAAC,YAAY,IAAI,IAAI,EAAE;oBAC5C,MAAM,EAAE,IAAI,CAAC,EAAE;oBACf,KAAK,EAAE,IAAI,CAAC,KAAK;oBACjB,KAAK,EAAE,IAAI,CAAC,KAAK;oBACjB,QAAQ,EAAE,IAAI,CAAC,QAAQ;oBACvB,IAAI,EAAE,GAAG,CAAC,YAAY;oBACtB,KAAK,EAAE,EAAE,OAAO,EAAE,SAAS,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,KAAK,EAAE;oBACtE,WAAW,EAAE,IAAI,CAAC,WAAW;oBAC7B,MAAM,EAAE,IAAI,CAAC,MAAM;oBACnB,WAAW,EAAE,IAAI,CAAC,WAAW;oBAC7B,UAAU,EAAE,IAAI,CAAC,UAAU;oBAC3B,GAAG,EAAE,IAAI,CAAC,GAAG;oBACb,KAAK,EAAE,IAAI,CAAC,KAAK;iBAClB,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF,CAAC;AAEF,eAAe,IAAI,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import unsafeExpect from './unsafeExpect.js';
|
|
2
|
+
import sqlInjection from './sqlInjection.js';
|
|
3
|
+
import secretInCode from './secretInCode.js';
|
|
4
|
+
export const rules = {
|
|
5
|
+
unsafeExpect,
|
|
6
|
+
sqlInjection,
|
|
7
|
+
secretInCode,
|
|
8
|
+
};
|
|
9
|
+
export default rules;
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/engine/rules/rust/index.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,mBAAmB,CAAC;AAC7C,OAAO,YAAY,MAAM,mBAAmB,CAAC;AAC7C,OAAO,YAAY,MAAM,mBAAmB,CAAC;AAE7C,MAAM,CAAC,MAAM,KAAK,GAAG;IACnB,YAAY;IACZ,YAAY;IACZ,YAAY;CACb,CAAC;AAEF,eAAe,KAAK,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { grepRule } from '../_helpers.js';
|
|
2
|
+
const rule = {
|
|
3
|
+
id: 'RS-003',
|
|
4
|
+
title: 'Rust: hardcoded secret / credential',
|
|
5
|
+
layer: 'cicd',
|
|
6
|
+
severity: 'critical',
|
|
7
|
+
description: 'Hardcoded API keys, database passwords, JWT secrets, or private keys in Rust source code or `.env` files. Once committed to Git, they are public.',
|
|
8
|
+
threat: 'Cloud account takeover, financial fraud, lateral movement',
|
|
9
|
+
remediation: '1) Move all secrets to environment variables + secret manager (Vault, AWS Secrets Manager). ' +
|
|
10
|
+
'2) Use `std::env::var("API_KEY")` with proper error handling. ' +
|
|
11
|
+
'3) Add `dotenvy` only for local development; never commit `.env`. ' +
|
|
12
|
+
'4) Add `git-secrets` or `gitleaks` pre-commit hook. ' +
|
|
13
|
+
'5) If a secret leaked, rotate immediately.',
|
|
14
|
+
references: [
|
|
15
|
+
'https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html',
|
|
16
|
+
'https://github.com/YoyoFate/secretfinder-rs',
|
|
17
|
+
],
|
|
18
|
+
cwe: 'CWE-798',
|
|
19
|
+
owasp: 'A05:2021 Security Misconfiguration',
|
|
20
|
+
languages: ['rust'],
|
|
21
|
+
check: (ctx) => {
|
|
22
|
+
const patterns = [
|
|
23
|
+
/AKIA[0-9A-Z]{16}/g,
|
|
24
|
+
/sk_live_[0-9a-zA-Z]{24,}/g,
|
|
25
|
+
/-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----/g,
|
|
26
|
+
/(?:SECRET|API_KEY|PASSWORD|TOKEN|PRIVATE_KEY)\s*:\s*&?\[\s*u?8\s*\][\s\S]{0,200}=\s*["'][a-zA-Z0-9_\-/+=]{20,}["']/g,
|
|
27
|
+
];
|
|
28
|
+
const findings = [];
|
|
29
|
+
for (const re of patterns) {
|
|
30
|
+
findings.push(...grepRule(rule, ctx, re));
|
|
31
|
+
}
|
|
32
|
+
return findings;
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
export default rule;
|
|
36
|
+
//# sourceMappingURL=secretInCode.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secretInCode.js","sourceRoot":"","sources":["../../../../src/engine/rules/rust/secretInCode.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE1C,MAAM,IAAI,GAAS;IACjB,EAAE,EAAE,QAAQ;IACZ,KAAK,EAAE,qCAAqC;IAC5C,KAAK,EAAE,MAAM;IACb,QAAQ,EAAE,UAAU;IACpB,WAAW,EACT,mJAAmJ;IACrJ,MAAM,EAAE,2DAA2D;IACnE,WAAW,EACT,8FAA8F;QAC9F,gEAAgE;QAChE,oEAAoE;QACpE,sDAAsD;QACtD,4CAA4C;IAC9C,UAAU,EAAE;QACV,oFAAoF;QACpF,6CAA6C;KAC9C;IACD,GAAG,EAAE,SAAS;IACd,KAAK,EAAE,oCAAoC;IAC3C,SAAS,EAAE,CAAC,MAAM,CAAC;IACnB,KAAK,EAAE,CAAC,GAAG,EAAE,EAAE;QACb,MAAM,QAAQ,GAAG;YACf,mBAAmB;YACnB,2BAA2B;YAC3B,oDAAoD;YACpD,qHAAqH;SACtH,CAAC;QACF,MAAM,QAAQ,GAAG,EAAE,CAAC;QACpB,KAAK,MAAM,EAAE,IAAI,QAAQ,EAAE,CAAC;YAC1B,QAAQ,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC;QAC5C,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF,CAAC;AAEF,eAAe,IAAI,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { lineColumnFromIndex, snippetAt } from '../_helpers.js';
|
|
2
|
+
const rule = {
|
|
3
|
+
id: 'RS-002',
|
|
4
|
+
title: 'Rust: SQL/NoSQL injection via string concatenation',
|
|
5
|
+
layer: 'backend',
|
|
6
|
+
severity: 'critical',
|
|
7
|
+
description: 'Using `format!` or string concatenation to build SQL queries lets attackers inject SQL via untrusted input. While sqlx and diesel parameterize by default, raw `query_as` / `sqlx::query` with concatenation bypasses that protection.',
|
|
8
|
+
threat: 'Database exfiltration, authentication bypass, data destruction',
|
|
9
|
+
remediation: '1) Use sqlx::query with bind parameters: `query("SELECT * FROM users WHERE id = $1").bind(id)`. ' +
|
|
10
|
+
'2) Diesel: `users.filter(id.eq(&given_id)).first(&conn)`. ' +
|
|
11
|
+
'3) SeaORM / SQLAlchemy: parameter binding via `.bind()` or placeholder. ' +
|
|
12
|
+
'4) NEVER use `format!("SELECT ... WHERE id = {}", user_input)`. ' +
|
|
13
|
+
'5) For complex dynamic queries, build with sqlx::QueryBuilder and `.push_bind`.',
|
|
14
|
+
references: [
|
|
15
|
+
'https://docs.rs/sqlx/latest/sqlx/struct.Query.html',
|
|
16
|
+
'https://github.com/launchbadge/sqlx-secure-ext',
|
|
17
|
+
'https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html',
|
|
18
|
+
],
|
|
19
|
+
cwe: 'CWE-89',
|
|
20
|
+
owasp: 'A03:2021 Injection',
|
|
21
|
+
languages: ['rust'],
|
|
22
|
+
check: (ctx) => {
|
|
23
|
+
const patterns = [
|
|
24
|
+
/format!\s*\(\s*['"`](?:SELECT|INSERT|UPDATE|DELETE|DROP)[^'"`]*\{\s*\w+/gi,
|
|
25
|
+
/format!\s*\(\s*['"`](?:SELECT|INSERT|UPDATE|DELETE|FROM|WHERE)[^'"`]*\{\s*\w+/gi,
|
|
26
|
+
/\.exec\(\s*format!\(/g,
|
|
27
|
+
];
|
|
28
|
+
const findings = [];
|
|
29
|
+
for (const re of patterns) {
|
|
30
|
+
for (const m of ctx.source.matchAll(re)) {
|
|
31
|
+
if (m.index === undefined)
|
|
32
|
+
continue;
|
|
33
|
+
const { line } = lineColumnFromIndex(ctx.source, m.index);
|
|
34
|
+
findings.push({
|
|
35
|
+
id: `${rule.id}-${ctx.relativePath}-${line}`,
|
|
36
|
+
ruleId: rule.id,
|
|
37
|
+
title: rule.title,
|
|
38
|
+
layer: rule.layer,
|
|
39
|
+
severity: rule.severity,
|
|
40
|
+
file: ctx.relativePath,
|
|
41
|
+
match: { snippet: snippetAt(ctx.source, line), line, column: m.index },
|
|
42
|
+
description: rule.description,
|
|
43
|
+
impact: rule.threat,
|
|
44
|
+
remediation: rule.remediation,
|
|
45
|
+
references: rule.references,
|
|
46
|
+
cwe: rule.cwe,
|
|
47
|
+
owasp: rule.owasp,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return findings;
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
export default rule;
|
|
55
|
+
//# sourceMappingURL=sqlInjection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlInjection.js","sourceRoot":"","sources":["../../../../src/engine/rules/rust/sqlInjection.ts"],"names":[],"mappings":"AACA,OAAO,EAAY,mBAAmB,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE1E,MAAM,IAAI,GAAS;IACjB,EAAE,EAAE,QAAQ;IACZ,KAAK,EAAE,oDAAoD;IAC3D,KAAK,EAAE,SAAS;IAChB,QAAQ,EAAE,UAAU;IACpB,WAAW,EACT,wOAAwO;IAC1O,MAAM,EAAE,gEAAgE;IACxE,WAAW,EACT,kGAAkG;QAClG,4DAA4D;QAC5D,0EAA0E;QAC1E,kEAAkE;QAClE,iFAAiF;IACnF,UAAU,EAAE;QACV,oDAAoD;QACpD,gDAAgD;QAChD,0FAA0F;KAC3F;IACD,GAAG,EAAE,QAAQ;IACb,KAAK,EAAE,oBAAoB;IAC3B,SAAS,EAAE,CAAC,MAAM,CAAC;IACnB,KAAK,EAAE,CAAC,GAAG,EAAE,EAAE;QACb,MAAM,QAAQ,GAAG;YACf,2EAA2E;YAC3E,iFAAiF;YACjF,uBAAuB;SACxB,CAAC;QACF,MAAM,QAAQ,GAAG,EAAE,CAAC;QACpB,KAAK,MAAM,EAAE,IAAI,QAAQ,EAAE,CAAC;YAC1B,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;gBACxC,IAAI,CAAC,CAAC,KAAK,KAAK,SAAS;oBAAE,SAAS;gBACpC,MAAM,EAAE,IAAI,EAAE,GAAG,mBAAmB,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC;gBAC1D,QAAQ,CAAC,IAAI,CAAC;oBACZ,EAAE,EAAE,GAAG,IAAI,CAAC,EAAE,IAAI,GAAG,CAAC,YAAY,IAAI,IAAI,EAAE;oBAC5C,MAAM,EAAE,IAAI,CAAC,EAAE;oBACf,KAAK,EAAE,IAAI,CAAC,KAAK;oBACjB,KAAK,EAAE,IAAI,CAAC,KAAK;oBACjB,QAAQ,EAAE,IAAI,CAAC,QAAQ;oBACvB,IAAI,EAAE,GAAG,CAAC,YAAY;oBACtB,KAAK,EAAE,EAAE,OAAO,EAAE,SAAS,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,KAAK,EAAE;oBACtE,WAAW,EAAE,IAAI,CAAC,WAAW;oBAC7B,MAAM,EAAE,IAAI,CAAC,MAAM;oBACnB,WAAW,EAAE,IAAI,CAAC,WAAW;oBAC7B,UAAU,EAAE,IAAI,CAAC,UAAU;oBAC3B,GAAG,EAAE,IAAI,CAAC,GAAG;oBACb,KAAK,EAAE,IAAI,CAAC,KAAK;iBAClB,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF,CAAC;AAEF,eAAe,IAAI,CAAC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { lineColumnFromIndex, snippetAt } from '../_helpers.js';
|
|
2
|
+
const rule = {
|
|
3
|
+
id: 'RS-001',
|
|
4
|
+
title: 'Rust: unwrap() / expect() / panic! in production code',
|
|
5
|
+
layer: 'backend',
|
|
6
|
+
severity: 'medium',
|
|
7
|
+
description: 'Calling .unwrap(), .expect(), or panic!() in production code causes a panic (crash) on unexpected states. In a web handler or critical path this leads to DoS via attacker-controlled input.',
|
|
8
|
+
threat: 'Denial of service via panic on untrusted input; information disclosure from stack trace',
|
|
9
|
+
remediation: '1) Use proper error handling with `Result<T, E>` and the `?` operator. ' +
|
|
10
|
+
'2) For invariant assertions, document why the value cannot fail; consider `debug_assert!` for non-critical checks. ' +
|
|
11
|
+
'3) Wrap input parsing in dedicated `try_from` / `parse` and return HTTP 400. ' +
|
|
12
|
+
'4) Catch panics at the boundary (e.g. actix_web::middleware::panic_handler, tower_http::catch_panic). ' +
|
|
13
|
+
'5) Add a global catch_unwind or tokio::spawn wrapper for resilience.',
|
|
14
|
+
references: [
|
|
15
|
+
'https://doc.rust-lang.org/book/ch09-00-error-handling.html',
|
|
16
|
+
'https://github.com/tokio-rs/axum/blob/main/examples/testing-errors/src/main.rs',
|
|
17
|
+
],
|
|
18
|
+
cwe: 'CWE-754',
|
|
19
|
+
owasp: 'A04:2021 Insecure Design',
|
|
20
|
+
languages: ['rust'],
|
|
21
|
+
check: (ctx) => {
|
|
22
|
+
const patterns = [
|
|
23
|
+
/\.unwrap\(\)/g,
|
|
24
|
+
/\.expect\([^)]*\)/g,
|
|
25
|
+
/\bpanic!\(/g,
|
|
26
|
+
/unimplemented!\(/g,
|
|
27
|
+
/todo!\(/g,
|
|
28
|
+
];
|
|
29
|
+
const findings = [];
|
|
30
|
+
for (const re of patterns) {
|
|
31
|
+
for (const m of ctx.source.matchAll(re)) {
|
|
32
|
+
if (m.index === undefined)
|
|
33
|
+
continue;
|
|
34
|
+
const { line } = lineColumnFromIndex(ctx.source, m.index);
|
|
35
|
+
findings.push({
|
|
36
|
+
id: `${rule.id}-${ctx.relativePath}-${line}`,
|
|
37
|
+
ruleId: rule.id,
|
|
38
|
+
title: rule.title,
|
|
39
|
+
layer: rule.layer,
|
|
40
|
+
severity: rule.severity,
|
|
41
|
+
file: ctx.relativePath,
|
|
42
|
+
match: { snippet: snippetAt(ctx.source, line), line, column: m.index },
|
|
43
|
+
description: rule.description,
|
|
44
|
+
impact: rule.threat,
|
|
45
|
+
remediation: rule.remediation,
|
|
46
|
+
references: rule.references,
|
|
47
|
+
cwe: rule.cwe,
|
|
48
|
+
owasp: rule.owasp,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return findings;
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
export default rule;
|
|
56
|
+
//# sourceMappingURL=unsafeExpect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unsafeExpect.js","sourceRoot":"","sources":["../../../../src/engine/rules/rust/unsafeExpect.ts"],"names":[],"mappings":"AACA,OAAO,EAAY,mBAAmB,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE1E,MAAM,IAAI,GAAS;IACjB,EAAE,EAAE,QAAQ;IACZ,KAAK,EAAE,uDAAuD;IAC9D,KAAK,EAAE,SAAS;IAChB,QAAQ,EAAE,QAAQ;IAClB,WAAW,EACT,8LAA8L;IAChM,MAAM,EAAE,yFAAyF;IACjG,WAAW,EACT,yEAAyE;QACzE,qHAAqH;QACrH,+EAA+E;QAC/E,wGAAwG;QACxG,sEAAsE;IACxE,UAAU,EAAE;QACV,4DAA4D;QAC5D,gFAAgF;KACjF;IACD,GAAG,EAAE,SAAS;IACd,KAAK,EAAE,0BAA0B;IACjC,SAAS,EAAE,CAAC,MAAM,CAAC;IACnB,KAAK,EAAE,CAAC,GAAG,EAAE,EAAE;QACb,MAAM,QAAQ,GAAG;YACf,eAAe;YACf,oBAAoB;YACpB,aAAa;YACb,mBAAmB;YACnB,UAAU;SACX,CAAC;QACF,MAAM,QAAQ,GAAG,EAAE,CAAC;QACpB,KAAK,MAAM,EAAE,IAAI,QAAQ,EAAE,CAAC;YAC1B,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;gBACxC,IAAI,CAAC,CAAC,KAAK,KAAK,SAAS;oBAAE,SAAS;gBACpC,MAAM,EAAE,IAAI,EAAE,GAAG,mBAAmB,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC;gBAC1D,QAAQ,CAAC,IAAI,CAAC;oBACZ,EAAE,EAAE,GAAG,IAAI,CAAC,EAAE,IAAI,GAAG,CAAC,YAAY,IAAI,IAAI,EAAE;oBAC5C,MAAM,EAAE,IAAI,CAAC,EAAE;oBACf,KAAK,EAAE,IAAI,CAAC,KAAK;oBACjB,KAAK,EAAE,IAAI,CAAC,KAAK;oBACjB,QAAQ,EAAE,IAAI,CAAC,QAAQ;oBACvB,IAAI,EAAE,GAAG,CAAC,YAAY;oBACtB,KAAK,EAAE,EAAE,OAAO,EAAE,SAAS,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,KAAK,EAAE;oBACtE,WAAW,EAAE,IAAI,CAAC,WAAW;oBAC7B,MAAM,EAAE,IAAI,CAAC,MAAM;oBACnB,WAAW,EAAE,IAAI,CAAC,WAAW;oBAC7B,UAAU,EAAE,IAAI,CAAC,UAAU;oBAC3B,GAAG,EAAE,IAAI,CAAC,GAAG;oBACb,KAAK,EAAE,IAAI,CAAC,KAAK;iBAClB,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF,CAAC;AAEF,eAAe,IAAI,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type WalkedFile } from './walker.js';
|
|
2
|
+
import type { Finding, Language, ScanOptions, ScanResult, ScanSummary } from '../types.js';
|
|
3
|
+
export declare function scan(options: ScanOptions): Promise<ScanResult>;
|
|
4
|
+
export declare function summarize(findings: readonly Finding[]): ScanSummary;
|
|
5
|
+
export type { WalkedFile, Language };
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import { detectLanguage, buildProjectProfile } from './language.js';
|
|
3
|
+
import { walkProject } from './walker.js';
|
|
4
|
+
import { getRulesForLanguage } from './rules/index.js';
|
|
5
|
+
import { loadConfig, applyConfig } from './config.js';
|
|
6
|
+
import { detectFrameworks } from './language.js';
|
|
7
|
+
const ALL_LAYERS = ['frontend', 'backend', 'network', 'database', 'cicd', 'observability'];
|
|
8
|
+
const PER_FILE_FINDING_CAP = 50;
|
|
9
|
+
const GLOBAL_FINDING_CAP = 5000;
|
|
10
|
+
export async function scan(options) {
|
|
11
|
+
const startedAt = new Date();
|
|
12
|
+
const rootDir = options.rootDir ?? process.cwd();
|
|
13
|
+
const config = await loadConfig(rootDir);
|
|
14
|
+
const effective = applyConfig(config, {
|
|
15
|
+
layers: options.layers,
|
|
16
|
+
ignore: options.ignore,
|
|
17
|
+
ruleIds: options.ruleIds,
|
|
18
|
+
});
|
|
19
|
+
const layers = (effective.layers && effective.layers.length > 0 ? effective.layers : ALL_LAYERS);
|
|
20
|
+
const disabledRuleIds = new Set(Object.entries(config.rules ?? {})
|
|
21
|
+
.filter(([, v]) => v.enabled === false)
|
|
22
|
+
.map(([k]) => k));
|
|
23
|
+
const severityOverrides = new Map();
|
|
24
|
+
for (const [id, v] of Object.entries(config.rules ?? {})) {
|
|
25
|
+
if (v.severity)
|
|
26
|
+
severityOverrides.set(id, v.severity);
|
|
27
|
+
}
|
|
28
|
+
const files = await walkProject({
|
|
29
|
+
rootDir,
|
|
30
|
+
ignore: effective.ignore,
|
|
31
|
+
});
|
|
32
|
+
const rulesEvaluated = new Set();
|
|
33
|
+
const findings = [];
|
|
34
|
+
const detectedLanguages = new Set();
|
|
35
|
+
const applicableRules = new Set();
|
|
36
|
+
const maxPerFile = options.maxMatchesPerFile ?? PER_FILE_FINDING_CAP;
|
|
37
|
+
const sourcesForProfile = [];
|
|
38
|
+
for (const file of files) {
|
|
39
|
+
let source;
|
|
40
|
+
try {
|
|
41
|
+
source = await fs.readFile(file.absolutePath, 'utf8');
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
source = '';
|
|
45
|
+
}
|
|
46
|
+
const language = detectLanguage(file.relativePath, source);
|
|
47
|
+
sourcesForProfile.push({ relativePath: file.relativePath, source });
|
|
48
|
+
if (language !== 'unknown')
|
|
49
|
+
detectedLanguages.add(language);
|
|
50
|
+
const candidates = getRulesForLanguage(language);
|
|
51
|
+
const applicable = candidates.filter((r) => {
|
|
52
|
+
if (disabledRuleIds.has(r.id))
|
|
53
|
+
return false;
|
|
54
|
+
if (effective.ruleIds && effective.ruleIds.length > 0) {
|
|
55
|
+
if (!effective.ruleIds.includes(r.id))
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
if (!layers.includes(r.layer))
|
|
59
|
+
return false;
|
|
60
|
+
return true;
|
|
61
|
+
});
|
|
62
|
+
if (applicable.length === 0)
|
|
63
|
+
continue;
|
|
64
|
+
const lines = source.split(/\r?\n/);
|
|
65
|
+
for (const rule of applicable) {
|
|
66
|
+
rulesEvaluated.add(rule.id);
|
|
67
|
+
applicableRules.add(rule.id);
|
|
68
|
+
let ruleFindings;
|
|
69
|
+
try {
|
|
70
|
+
ruleFindings = rule.check({
|
|
71
|
+
filePath: file.absolutePath,
|
|
72
|
+
relativePath: file.relativePath,
|
|
73
|
+
language,
|
|
74
|
+
source,
|
|
75
|
+
lines,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
catch (err) {
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
for (let finding of ruleFindings) {
|
|
82
|
+
if (findings.length >= maxPerFile * 100)
|
|
83
|
+
break;
|
|
84
|
+
const override = severityOverrides.get(finding.ruleId);
|
|
85
|
+
if (override) {
|
|
86
|
+
finding = { ...finding, severity: override };
|
|
87
|
+
}
|
|
88
|
+
findings.push(finding);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
const finishedAt = new Date();
|
|
93
|
+
const summary = summarize(findings);
|
|
94
|
+
const profileBase = buildProjectProfile(sourcesForProfile);
|
|
95
|
+
const frameworks = await detectFrameworks(rootDir);
|
|
96
|
+
const profile = {
|
|
97
|
+
...profileBase,
|
|
98
|
+
applicableRules: Array.from(applicableRules).sort(),
|
|
99
|
+
frameworks,
|
|
100
|
+
};
|
|
101
|
+
return {
|
|
102
|
+
rootDir,
|
|
103
|
+
startedAt: startedAt.toISOString(),
|
|
104
|
+
finishedAt: finishedAt.toISOString(),
|
|
105
|
+
durationMs: finishedAt.getTime() - startedAt.getTime(),
|
|
106
|
+
filesScanned: files.length,
|
|
107
|
+
rulesEvaluated: rulesEvaluated.size,
|
|
108
|
+
findings: findings.slice(0, GLOBAL_FINDING_CAP),
|
|
109
|
+
summary,
|
|
110
|
+
profile,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
export function summarize(findings) {
|
|
114
|
+
const bySeverity = {
|
|
115
|
+
critical: 0,
|
|
116
|
+
high: 0,
|
|
117
|
+
medium: 0,
|
|
118
|
+
low: 0,
|
|
119
|
+
info: 0,
|
|
120
|
+
};
|
|
121
|
+
const byLayer = {
|
|
122
|
+
frontend: 0,
|
|
123
|
+
backend: 0,
|
|
124
|
+
network: 0,
|
|
125
|
+
database: 0,
|
|
126
|
+
cicd: 0,
|
|
127
|
+
observability: 0,
|
|
128
|
+
};
|
|
129
|
+
for (const f of findings) {
|
|
130
|
+
bySeverity[f.severity]++;
|
|
131
|
+
byLayer[f.layer]++;
|
|
132
|
+
}
|
|
133
|
+
return { totalFindings: findings.length, bySeverity, byLayer };
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=scanner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanner.js","sourceRoot":"","sources":["../../src/engine/scanner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface WalkedFile {
|
|
2
|
+
readonly absolutePath: string;
|
|
3
|
+
readonly relativePath: string;
|
|
4
|
+
readonly sizeBytes: number;
|
|
5
|
+
}
|
|
6
|
+
export interface WalkOptions {
|
|
7
|
+
readonly rootDir: string;
|
|
8
|
+
readonly ignore?: readonly string[];
|
|
9
|
+
readonly maxFiles?: number;
|
|
10
|
+
}
|
|
11
|
+
export declare function walkProject(options: WalkOptions): Promise<readonly WalkedFile[]>;
|