@securecode-ai/mcp 0.1.0 → 0.2.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/README.md +5 -3
- package/dist/api/client.d.ts +4 -0
- package/dist/api/client.js +49 -0
- package/dist/api/client.js.map +1 -1
- package/dist/api/types.d.ts +40 -0
- package/dist/index.js +64 -7
- package/dist/index.js.map +1 -1
- package/dist/poc/httpPocExecutor.d.ts +20 -0
- package/dist/poc/httpPocExecutor.js +247 -0
- package/dist/poc/httpPocExecutor.js.map +1 -0
- package/dist/poc/lightpandaExecutor.d.ts +41 -0
- package/dist/poc/lightpandaExecutor.js +263 -0
- package/dist/poc/lightpandaExecutor.js.map +1 -0
- package/dist/poc/pocExecutor.d.ts +46 -0
- package/dist/poc/pocExecutor.js +74 -0
- package/dist/poc/pocExecutor.js.map +1 -0
- package/dist/poc/pocRouter.d.ts +20 -0
- package/dist/poc/pocRouter.js +40 -0
- package/dist/poc/pocRouter.js.map +1 -0
- package/dist/poc/pocTypes.d.ts +54 -0
- package/dist/poc/pocTypes.js +16 -0
- package/dist/poc/pocTypes.js.map +1 -0
- package/dist/project-map/fixTester.d.ts +55 -0
- package/dist/project-map/fixTester.js +338 -0
- package/dist/project-map/fixTester.js.map +1 -0
- package/dist/project-map/guardEvaluator.d.ts +40 -0
- package/dist/project-map/guardEvaluator.js +216 -0
- package/dist/project-map/guardEvaluator.js.map +1 -0
- package/dist/project-map/guardPatterns.d.ts +43 -0
- package/dist/project-map/guardPatterns.js +173 -0
- package/dist/project-map/guardPatterns.js.map +1 -0
- package/dist/project-map/sanitizers.d.ts +34 -0
- package/dist/project-map/sanitizers.js +76 -0
- package/dist/project-map/sanitizers.js.map +1 -0
- package/dist/project-map/sinkFinder.d.ts +48 -0
- package/dist/project-map/sinkFinder.js +340 -0
- package/dist/project-map/sinkFinder.js.map +1 -0
- package/dist/project-map/sinkRegistry.d.ts +60 -0
- package/dist/project-map/sinkRegistry.js +246 -0
- package/dist/project-map/sinkRegistry.js.map +1 -0
- package/dist/project-map/taintSources.d.ts +30 -0
- package/dist/project-map/taintSources.js +77 -0
- package/dist/project-map/taintSources.js.map +1 -0
- package/dist/project-map/taintTracker.d.ts +42 -0
- package/dist/project-map/taintTracker.js +571 -0
- package/dist/project-map/taintTracker.js.map +1 -0
- package/dist/tools/scan.js +44 -0
- package/dist/tools/scan.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase E — guard pattern library.
|
|
3
|
+
*
|
|
4
|
+
* Maps guard types to the attacks they do and don't stop. The guard
|
|
5
|
+
* evaluator uses this to answer "does this guard actually defend against
|
|
6
|
+
* this attack?" — a question the Juror would otherwise have to guess at.
|
|
7
|
+
*
|
|
8
|
+
* The patterns are conservative: when we can't determine effectiveness, we
|
|
9
|
+
* return `unknown` rather than `effective` (a false "effective" is far more
|
|
10
|
+
* dangerous than a false "unknown" — it would let the Juror mark a real
|
|
11
|
+
* vulnerability as SAFE).
|
|
12
|
+
*/
|
|
13
|
+
/** Attack types that guards are evaluated against (matches canonical vuln types). */
|
|
14
|
+
export type AttackType = 'sql_injection' | 'nosql_injection' | 'command_injection' | 'xss' | 'ssrf' | 'path_traversal' | 'open_redirect' | 'prototype_pollution' | 'insecure_deserialization' | 'broken_access_control';
|
|
15
|
+
/** Guard types we can identify and evaluate. */
|
|
16
|
+
export type GuardType = 'sanitizer-numeric' | 'sanitizer-html' | 'sanitizer-url' | 'parameterized-query' | 'allowlist-literal' | 'allowlist-dynamic' | 'auth-jwt-verify' | 'auth-jwt-verify-noalg' | 'auth-session' | 'auth-api-key' | 'auth-none' | 'rate-limit' | 'helmet' | 'cors-strict' | 'cors-permissive' | 'validator' | 'unknown';
|
|
17
|
+
export interface GuardEvaluation {
|
|
18
|
+
/** The guard's display name. */
|
|
19
|
+
guardName: string;
|
|
20
|
+
/** Type of guard identified. */
|
|
21
|
+
guardType: GuardType;
|
|
22
|
+
/** The attack type being evaluated against. */
|
|
23
|
+
attackType: AttackType;
|
|
24
|
+
/** Does this guard stop this attack? */
|
|
25
|
+
effective: boolean;
|
|
26
|
+
/** Why it is or isn't effective. */
|
|
27
|
+
reason: string;
|
|
28
|
+
/** Example bypass when not effective. */
|
|
29
|
+
bypassExample?: string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Pattern: which GuardType stops which AttackType.
|
|
33
|
+
*
|
|
34
|
+
* `true` = effective, `false` = NOT effective, `undefined` = unknown.
|
|
35
|
+
* When unknown, the evaluator returns `effective: false` with a reason
|
|
36
|
+
* explaining the guard was detected but its effectiveness against this
|
|
37
|
+
* attack is not determinable.
|
|
38
|
+
*/
|
|
39
|
+
export declare const GUARD_EFFECTIVENESS: Record<GuardType, Partial<Record<AttackType, boolean>>>;
|
|
40
|
+
/** Bypass examples for known ineffective guard×attack pairs. */
|
|
41
|
+
export declare const GUARD_BYPASS_EXAMPLES: Partial<Record<GuardType, Partial<Record<AttackType, string>>>>;
|
|
42
|
+
/** Reasons explaining why a guard is effective. */
|
|
43
|
+
export declare const GUARD_EFFECTIVE_REASONS: Partial<Record<GuardType, Partial<Record<AttackType, string>>>>;
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Phase E — guard pattern library.
|
|
4
|
+
*
|
|
5
|
+
* Maps guard types to the attacks they do and don't stop. The guard
|
|
6
|
+
* evaluator uses this to answer "does this guard actually defend against
|
|
7
|
+
* this attack?" — a question the Juror would otherwise have to guess at.
|
|
8
|
+
*
|
|
9
|
+
* The patterns are conservative: when we can't determine effectiveness, we
|
|
10
|
+
* return `unknown` rather than `effective` (a false "effective" is far more
|
|
11
|
+
* dangerous than a false "unknown" — it would let the Juror mark a real
|
|
12
|
+
* vulnerability as SAFE).
|
|
13
|
+
*/
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.GUARD_EFFECTIVE_REASONS = exports.GUARD_BYPASS_EXAMPLES = exports.GUARD_EFFECTIVENESS = void 0;
|
|
16
|
+
/**
|
|
17
|
+
* Pattern: which GuardType stops which AttackType.
|
|
18
|
+
*
|
|
19
|
+
* `true` = effective, `false` = NOT effective, `undefined` = unknown.
|
|
20
|
+
* When unknown, the evaluator returns `effective: false` with a reason
|
|
21
|
+
* explaining the guard was detected but its effectiveness against this
|
|
22
|
+
* attack is not determinable.
|
|
23
|
+
*/
|
|
24
|
+
exports.GUARD_EFFECTIVENESS = {
|
|
25
|
+
'sanitizer-numeric': {
|
|
26
|
+
sql_injection: true, // numbers can't carry SQL syntax
|
|
27
|
+
nosql_injection: true, // numbers can't carry NoSQL operators
|
|
28
|
+
command_injection: true, // numbers can't carry shell metachars
|
|
29
|
+
xss: true, // numbers can't carry script tags
|
|
30
|
+
open_redirect: true, // numbers can't be URLs
|
|
31
|
+
path_traversal: true, // numbers can't carry ../
|
|
32
|
+
ssrf: true, // numbers can't be URLs
|
|
33
|
+
prototype_pollution: true, // numbers can't carry __proto__
|
|
34
|
+
},
|
|
35
|
+
'sanitizer-html': {
|
|
36
|
+
xss: true, // HTML escaping stops XSS
|
|
37
|
+
sql_injection: false, // HTML escaping does NOT stop SQLi
|
|
38
|
+
command_injection: false,
|
|
39
|
+
open_redirect: false,
|
|
40
|
+
path_traversal: false,
|
|
41
|
+
ssrf: false,
|
|
42
|
+
prototype_pollution: false,
|
|
43
|
+
},
|
|
44
|
+
'sanitizer-url': {
|
|
45
|
+
open_redirect: true, // URL encoding stops redirect injection
|
|
46
|
+
xss: false, // URL encoding does NOT stop XSS
|
|
47
|
+
sql_injection: false,
|
|
48
|
+
command_injection: false,
|
|
49
|
+
ssrf: true, // URL encoding limits SSRF target manipulation
|
|
50
|
+
path_traversal: false,
|
|
51
|
+
},
|
|
52
|
+
'parameterized-query': {
|
|
53
|
+
sql_injection: true, // parameterized queries stop SQLi
|
|
54
|
+
nosql_injection: false,
|
|
55
|
+
command_injection: false,
|
|
56
|
+
xss: false,
|
|
57
|
+
},
|
|
58
|
+
'allowlist-literal': {
|
|
59
|
+
sql_injection: true,
|
|
60
|
+
nosql_injection: true,
|
|
61
|
+
command_injection: true,
|
|
62
|
+
xss: true,
|
|
63
|
+
open_redirect: true,
|
|
64
|
+
path_traversal: true,
|
|
65
|
+
ssrf: true,
|
|
66
|
+
prototype_pollution: true,
|
|
67
|
+
},
|
|
68
|
+
'allowlist-dynamic': {
|
|
69
|
+
// An allowlist with a user-controlled key is NOT effective against anything
|
|
70
|
+
sql_injection: false,
|
|
71
|
+
nosql_injection: false,
|
|
72
|
+
command_injection: false,
|
|
73
|
+
xss: false,
|
|
74
|
+
open_redirect: false,
|
|
75
|
+
path_traversal: false,
|
|
76
|
+
ssrf: false,
|
|
77
|
+
},
|
|
78
|
+
'auth-jwt-verify': {
|
|
79
|
+
broken_access_control: true, // verified JWT = authenticated
|
|
80
|
+
},
|
|
81
|
+
'auth-jwt-verify-noalg': {
|
|
82
|
+
broken_access_control: false, // algorithm confusion bypass
|
|
83
|
+
},
|
|
84
|
+
'auth-session': {
|
|
85
|
+
broken_access_control: true,
|
|
86
|
+
},
|
|
87
|
+
'auth-api-key': {
|
|
88
|
+
broken_access_control: true,
|
|
89
|
+
},
|
|
90
|
+
'auth-none': {
|
|
91
|
+
broken_access_control: false,
|
|
92
|
+
},
|
|
93
|
+
'rate-limit': {
|
|
94
|
+
// Rate limiting stops brute force but not the vuln types in our taxonomy
|
|
95
|
+
broken_access_control: false,
|
|
96
|
+
},
|
|
97
|
+
'helmet': {
|
|
98
|
+
xss: false, // helmet sets headers, doesn't sanitize output
|
|
99
|
+
ssrf: false,
|
|
100
|
+
},
|
|
101
|
+
'cors-strict': {
|
|
102
|
+
// CORS strict doesn't stop any server-side vuln
|
|
103
|
+
},
|
|
104
|
+
'cors-permissive': {
|
|
105
|
+
broken_access_control: false,
|
|
106
|
+
},
|
|
107
|
+
'validator': {
|
|
108
|
+
// Schema validation (zod/joi/pydantic) is effective when the schema
|
|
109
|
+
// constrains the type (e.g. z.number() stops SQLi). Without seeing
|
|
110
|
+
// the schema, we can't say — return unknown (false + reason).
|
|
111
|
+
sql_injection: false,
|
|
112
|
+
xss: false,
|
|
113
|
+
},
|
|
114
|
+
'unknown': {},
|
|
115
|
+
};
|
|
116
|
+
/** Bypass examples for known ineffective guard×attack pairs. */
|
|
117
|
+
exports.GUARD_BYPASS_EXAMPLES = {
|
|
118
|
+
'sanitizer-html': {
|
|
119
|
+
sql_injection: "escapeHtml doesn't stop SQLi: q' OR 1=1-- is valid HTML",
|
|
120
|
+
command_injection: "escapeHtml doesn't stop cmd injection: ; cat /etc/passwd",
|
|
121
|
+
},
|
|
122
|
+
'sanitizer-url': {
|
|
123
|
+
xss: "encodeURIComponent doesn't stop XSS: <script> stays as %3Cscript%3E (decodes in HTML)",
|
|
124
|
+
},
|
|
125
|
+
'sanitizer-numeric': {
|
|
126
|
+
insecure_deserialization: "parseInt doesn't validate serialized objects",
|
|
127
|
+
},
|
|
128
|
+
'auth-jwt-verify-noalg': {
|
|
129
|
+
broken_access_control: "Algorithm confusion: sign with HS256 using the public RSA key as the secret",
|
|
130
|
+
},
|
|
131
|
+
'allowlist-dynamic': {
|
|
132
|
+
sql_injection: "User-controlled allowlist key: attacker sets key to their payload",
|
|
133
|
+
xss: "User-controlled allowlist key: attacker sets key to <script>",
|
|
134
|
+
},
|
|
135
|
+
'rate-limit': {
|
|
136
|
+
broken_access_control: "Rate limiting slows brute force but doesn't stop auth bypass",
|
|
137
|
+
},
|
|
138
|
+
'helmet': {
|
|
139
|
+
xss: "helmet sets CSP/X-Frame-Options but doesn't sanitize response body content",
|
|
140
|
+
},
|
|
141
|
+
'cors-permissive': {
|
|
142
|
+
broken_access_control: "CORS origin:'*' allows any origin to read responses",
|
|
143
|
+
},
|
|
144
|
+
'validator': {
|
|
145
|
+
sql_injection: "Schema validation only stops SQLi if the schema enforces numeric/enum type",
|
|
146
|
+
xss: "Schema validation only stops XSS if the schema enforces no HTML or sanitizes",
|
|
147
|
+
},
|
|
148
|
+
};
|
|
149
|
+
/** Reasons explaining why a guard is effective. */
|
|
150
|
+
exports.GUARD_EFFECTIVE_REASONS = {
|
|
151
|
+
'sanitizer-numeric': {
|
|
152
|
+
sql_injection: 'parseInt/Number produces a numeric value — no SQL syntax can survive',
|
|
153
|
+
xss: 'parseInt/Number produces a numeric value — no script tags can survive',
|
|
154
|
+
command_injection: 'parseInt/Number produces a numeric value — no shell metacharacters',
|
|
155
|
+
},
|
|
156
|
+
'sanitizer-html': {
|
|
157
|
+
xss: 'HTML escaping escapes <, >, ", \', & — prevents script injection',
|
|
158
|
+
},
|
|
159
|
+
'parameterized-query': {
|
|
160
|
+
sql_injection: 'Parameterized queries separate code from data — injection impossible',
|
|
161
|
+
},
|
|
162
|
+
'allowlist-literal': {
|
|
163
|
+
sql_injection: 'Literal allowlist rejects any value not in the set',
|
|
164
|
+
xss: 'Literal allowlist rejects any value not in the set',
|
|
165
|
+
},
|
|
166
|
+
'auth-jwt-verify': {
|
|
167
|
+
broken_access_control: 'jwt.verify with algorithm pinning rejects forged tokens',
|
|
168
|
+
},
|
|
169
|
+
'auth-session': {
|
|
170
|
+
broken_access_control: 'Session check rejects unauthenticated requests',
|
|
171
|
+
},
|
|
172
|
+
};
|
|
173
|
+
//# sourceMappingURL=guardPatterns.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guardPatterns.js","sourceRoot":"","sources":["../../src/project-map/guardPatterns.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase C — sanitizer registry.
|
|
3
|
+
*
|
|
4
|
+
* Functions that remove taint from data. When the taint tracker encounters
|
|
5
|
+
* a call to a sanitizer, the result is NOT tainted (the sanitizer removes
|
|
6
|
+
* the taint). Note: `String()` alone does NOT remove taint — it's a type
|
|
7
|
+
* coercion, not a sanitizer.
|
|
8
|
+
*/
|
|
9
|
+
import type { SinkLanguage } from './sinkRegistry';
|
|
10
|
+
export interface SanitizerDef {
|
|
11
|
+
/** Method name: 'parseInt', 'escape', 'sanitize', etc. */
|
|
12
|
+
method: string;
|
|
13
|
+
/** Optional receiver: 'DOMPurify' for DOMPurify.sanitize. */
|
|
14
|
+
receiver?: string;
|
|
15
|
+
languages: SinkLanguage[];
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Known sanitizers. A call to any of these with a tainted argument produces
|
|
19
|
+
* a non-tainted result.
|
|
20
|
+
*
|
|
21
|
+
* Type coercions that reject non-numeric input (parseInt, parseFloat, Number)
|
|
22
|
+
* ARE sanitizers for SQL injection — the result is always a number.
|
|
23
|
+
* `String()` is NOT a sanitizer — it accepts anything.
|
|
24
|
+
*/
|
|
25
|
+
export declare const SANITIZERS: SanitizerDef[];
|
|
26
|
+
/**
|
|
27
|
+
* Check if a call expression is a sanitizer call.
|
|
28
|
+
* Returns the sanitizer name, or null if not a sanitizer.
|
|
29
|
+
*
|
|
30
|
+
* @param method the method name from callParts
|
|
31
|
+
* @param receiver the base identifier from callParts (or null for bare calls)
|
|
32
|
+
* @param language the source language
|
|
33
|
+
*/
|
|
34
|
+
export declare function matchSanitizer(method: string, receiver: string | null, language: SinkLanguage): string | null;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Phase C — sanitizer registry.
|
|
4
|
+
*
|
|
5
|
+
* Functions that remove taint from data. When the taint tracker encounters
|
|
6
|
+
* a call to a sanitizer, the result is NOT tainted (the sanitizer removes
|
|
7
|
+
* the taint). Note: `String()` alone does NOT remove taint — it's a type
|
|
8
|
+
* coercion, not a sanitizer.
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.SANITIZERS = void 0;
|
|
12
|
+
exports.matchSanitizer = matchSanitizer;
|
|
13
|
+
/**
|
|
14
|
+
* Known sanitizers. A call to any of these with a tainted argument produces
|
|
15
|
+
* a non-tainted result.
|
|
16
|
+
*
|
|
17
|
+
* Type coercions that reject non-numeric input (parseInt, parseFloat, Number)
|
|
18
|
+
* ARE sanitizers for SQL injection — the result is always a number.
|
|
19
|
+
* `String()` is NOT a sanitizer — it accepts anything.
|
|
20
|
+
*/
|
|
21
|
+
exports.SANITIZERS = [
|
|
22
|
+
// ── JS/TS — type coercions that reject invalid input ─────────────────
|
|
23
|
+
{ method: 'parseInt', languages: ['javascript', 'typescript', 'tsx'] },
|
|
24
|
+
{ method: 'parseFloat', languages: ['javascript', 'typescript', 'tsx'] },
|
|
25
|
+
{ method: 'Number', languages: ['javascript', 'typescript', 'tsx'] },
|
|
26
|
+
{ method: 'Boolean', languages: ['javascript', 'typescript', 'tsx'] },
|
|
27
|
+
// ── JS/TS — encoding functions ───────────────────────────────────────
|
|
28
|
+
{ method: 'encodeURIComponent', languages: ['javascript', 'typescript', 'tsx'] },
|
|
29
|
+
{ method: 'encodeURI', languages: ['javascript', 'typescript', 'tsx'] },
|
|
30
|
+
// ── JS/TS — HTML/XSS sanitizers ──────────────────────────────────────
|
|
31
|
+
{ method: 'escape', languages: ['javascript', 'typescript', 'tsx'] },
|
|
32
|
+
{ method: 'escapeHtml', languages: ['javascript', 'typescript', 'tsx'] },
|
|
33
|
+
{ method: 'sanitize', languages: ['javascript', 'typescript', 'tsx'] },
|
|
34
|
+
{ method: 'sanitizeHtml', languages: ['javascript', 'typescript', 'tsx'] },
|
|
35
|
+
{ method: 'sanitize', receiver: 'DOMPurify', languages: ['javascript', 'typescript', 'tsx'] },
|
|
36
|
+
{ method: 'xss', languages: ['javascript', 'typescript', 'tsx'] },
|
|
37
|
+
{ method: 'clean', receiver: 'xss', languages: ['javascript', 'typescript', 'tsx'] },
|
|
38
|
+
// ── JS/TS — validator library ────────────────────────────────────────
|
|
39
|
+
// validator.isInt(x) etc. are validators, not sanitizers — they return
|
|
40
|
+
// boolean, not cleaned data. The sanitizer is validator.toString(x, true)
|
|
41
|
+
// etc. But `escape` from validator IS a sanitizer.
|
|
42
|
+
{ method: 'escape', receiver: 'validator', languages: ['javascript', 'typescript', 'tsx'] },
|
|
43
|
+
{ method: 'ltrim', receiver: 'validator', languages: ['javascript', 'typescript', 'tsx'] },
|
|
44
|
+
{ method: 'rtrim', receiver: 'validator', languages: ['javascript', 'typescript', 'tsx'] },
|
|
45
|
+
{ method: 'trim', receiver: 'validator', languages: ['javascript', 'typescript', 'tsx'] },
|
|
46
|
+
// ── Python — type coercions ──────────────────────────────────────────
|
|
47
|
+
{ method: 'int', languages: ['python'] },
|
|
48
|
+
{ method: 'float', languages: ['python'] },
|
|
49
|
+
{ method: 'bool', languages: ['python'] },
|
|
50
|
+
// ── Python — HTML/XSS sanitizers ─────────────────────────────────────
|
|
51
|
+
{ method: 'escape', languages: ['python'] },
|
|
52
|
+
{ method: 'escape', receiver: 'markupsafe', languages: ['python'] },
|
|
53
|
+
{ method: 'clean', receiver: 'bleach', languages: ['python'] },
|
|
54
|
+
{ method: 'clean', receiver: 'nh3', languages: ['python'] },
|
|
55
|
+
];
|
|
56
|
+
/**
|
|
57
|
+
* Check if a call expression is a sanitizer call.
|
|
58
|
+
* Returns the sanitizer name, or null if not a sanitizer.
|
|
59
|
+
*
|
|
60
|
+
* @param method the method name from callParts
|
|
61
|
+
* @param receiver the base identifier from callParts (or null for bare calls)
|
|
62
|
+
* @param language the source language
|
|
63
|
+
*/
|
|
64
|
+
function matchSanitizer(method, receiver, language) {
|
|
65
|
+
for (const san of exports.SANITIZERS) {
|
|
66
|
+
if (!san.languages.includes(language))
|
|
67
|
+
continue;
|
|
68
|
+
if (san.method !== method)
|
|
69
|
+
continue;
|
|
70
|
+
if (san.receiver && receiver !== san.receiver)
|
|
71
|
+
continue;
|
|
72
|
+
return san.receiver ? `${san.receiver}.${san.method}` : san.method;
|
|
73
|
+
}
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=sanitizers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sanitizers.js","sourceRoot":"","sources":["../../src/project-map/sanitizers.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase B — AST-based sink finder.
|
|
3
|
+
*
|
|
4
|
+
* Walks a parsed file once and matches every call/assignment/jsx-attribute
|
|
5
|
+
* against the sink registry. Returns findings with argument shapes and
|
|
6
|
+
* enclosing-function context so the Taint Tracker (Phase C) and the Juror
|
|
7
|
+
* can reason about exploitability without re-parsing.
|
|
8
|
+
*
|
|
9
|
+
* Replaces the regex `sinkFloorService` with zero false positives on
|
|
10
|
+
* variable names that happen to match sink names (`const exec = ...`),
|
|
11
|
+
* sinks inside comments (AST skips them), and sinks inside string literals.
|
|
12
|
+
*/
|
|
13
|
+
import { SinkLanguage } from './sinkRegistry';
|
|
14
|
+
export interface ArgInfo {
|
|
15
|
+
kind: 'literal' | 'template' | 'identifier' | 'binary' | 'call' | 'other';
|
|
16
|
+
/** For literal: the string value. For identifier: the variable name. For call: the call name. */
|
|
17
|
+
value?: string;
|
|
18
|
+
/** For template: whether the template has interpolation. */
|
|
19
|
+
interpolated?: boolean;
|
|
20
|
+
}
|
|
21
|
+
export interface SinkFinding {
|
|
22
|
+
/** 1-indexed start line. */
|
|
23
|
+
line: number;
|
|
24
|
+
/** 1-indexed end line (inclusive). */
|
|
25
|
+
endLine: number;
|
|
26
|
+
/** Sink id from the registry: 'exec', 'eval', 'innerHTML'. */
|
|
27
|
+
sink: string;
|
|
28
|
+
/** Canonical vulnerability type (matches API taxonomy). */
|
|
29
|
+
canonicalType: string;
|
|
30
|
+
severity: 'Critical' | 'High' | 'Medium';
|
|
31
|
+
/** The full call/assignment expression text (for display). */
|
|
32
|
+
callExpression: string;
|
|
33
|
+
/** Each argument's shape — empty for member-assignment / jsx-attribute. */
|
|
34
|
+
arguments: ArgInfo[];
|
|
35
|
+
/** Name of the enclosing function, or null at module level. */
|
|
36
|
+
enclosingFunction: string | null;
|
|
37
|
+
/** Whether the sink is inside a try/catch block. */
|
|
38
|
+
isInsideTryCatch: boolean;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Find all security sinks in source code via AST analysis.
|
|
42
|
+
*
|
|
43
|
+
* @param source the file's source text
|
|
44
|
+
* @param language the grammar to parse with
|
|
45
|
+
* @returns sink findings, ordered by line. Empty when the grammar is missing
|
|
46
|
+
* or the file fails to parse (best-effort: never throws).
|
|
47
|
+
*/
|
|
48
|
+
export declare function findSinks(source: string, language: SinkLanguage): Promise<SinkFinding[]>;
|