@kernlang/review 2.0.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/concept-rules/boundary-mutation.d.ts +13 -0
- package/dist/concept-rules/boundary-mutation.js +40 -0
- package/dist/concept-rules/boundary-mutation.js.map +1 -0
- package/dist/concept-rules/ignored-error.d.ts +13 -0
- package/dist/concept-rules/ignored-error.js +40 -0
- package/dist/concept-rules/ignored-error.js.map +1 -0
- package/dist/concept-rules/illegal-dependency.d.ts +13 -0
- package/dist/concept-rules/illegal-dependency.js +49 -0
- package/dist/concept-rules/illegal-dependency.js.map +1 -0
- package/dist/concept-rules/index.d.ts +15 -0
- package/dist/concept-rules/index.js +27 -0
- package/dist/concept-rules/index.js.map +1 -0
- package/dist/concept-rules/unguarded-effect.d.ts +13 -0
- package/dist/concept-rules/unguarded-effect.js +58 -0
- package/dist/concept-rules/unguarded-effect.js.map +1 -0
- package/dist/concept-rules/unrecovered-effect.d.ts +13 -0
- package/dist/concept-rules/unrecovered-effect.js +61 -0
- package/dist/concept-rules/unrecovered-effect.js.map +1 -0
- package/dist/confidence.d.ts +92 -0
- package/dist/confidence.js +263 -0
- package/dist/confidence.js.map +1 -0
- package/dist/differ.js +4 -2
- package/dist/differ.js.map +1 -1
- package/dist/external-tools.js +7 -3
- package/dist/external-tools.js.map +1 -1
- package/dist/file-role.d.ts +10 -0
- package/dist/file-role.js +80 -0
- package/dist/file-role.js.map +1 -0
- package/dist/graph.d.ts +11 -0
- package/dist/graph.js +152 -0
- package/dist/graph.js.map +1 -0
- package/dist/index.d.ts +46 -3
- package/dist/index.js +313 -27
- package/dist/index.js.map +1 -1
- package/dist/inferrer.js +123 -25
- package/dist/inferrer.js.map +1 -1
- package/dist/kern-lint.d.ts +18 -0
- package/dist/kern-lint.js +24 -0
- package/dist/kern-lint.js.map +1 -0
- package/dist/llm-bridge.d.ts +42 -0
- package/dist/llm-bridge.js +176 -0
- package/dist/llm-bridge.js.map +1 -0
- package/dist/llm-review.d.ts +8 -1
- package/dist/llm-review.js +20 -7
- package/dist/llm-review.js.map +1 -1
- package/dist/mappers/ts-concepts.d.ts +9 -0
- package/dist/mappers/ts-concepts.js +518 -0
- package/dist/mappers/ts-concepts.js.map +1 -0
- package/dist/quality-rules.d.ts +3 -3
- package/dist/quality-rules.js +3 -11
- package/dist/quality-rules.js.map +1 -1
- package/dist/reporter.d.ts +19 -3
- package/dist/reporter.js +232 -20
- package/dist/reporter.js.map +1 -1
- package/dist/rules/base.js +167 -15
- package/dist/rules/base.js.map +1 -1
- package/dist/rules/confidence.d.ts +37 -0
- package/dist/rules/confidence.js +159 -0
- package/dist/rules/confidence.js.map +1 -0
- package/dist/rules/dead-logic.d.ts +13 -0
- package/dist/rules/dead-logic.js +393 -0
- package/dist/rules/dead-logic.js.map +1 -0
- package/dist/rules/express.js +69 -2
- package/dist/rules/express.js.map +1 -1
- package/dist/rules/ground-layer.d.ts +23 -0
- package/dist/rules/ground-layer.js +132 -0
- package/dist/rules/ground-layer.js.map +1 -0
- package/dist/rules/index.d.ts +1 -1
- package/dist/rules/index.js +8 -2
- package/dist/rules/index.js.map +1 -1
- package/dist/rules/kern-source.d.ts +16 -0
- package/dist/rules/kern-source.js +726 -0
- package/dist/rules/kern-source.js.map +1 -0
- package/dist/rules/nextjs.js +38 -10
- package/dist/rules/nextjs.js.map +1 -1
- package/dist/rules/null-safety.d.ts +12 -0
- package/dist/rules/null-safety.js +123 -0
- package/dist/rules/null-safety.js.map +1 -0
- package/dist/rules/react.js +64 -1
- package/dist/rules/react.js.map +1 -1
- package/dist/rules/security-v2.d.ts +12 -0
- package/dist/rules/security-v2.js +415 -0
- package/dist/rules/security-v2.js.map +1 -0
- package/dist/rules/security-v3.d.ts +12 -0
- package/dist/rules/security-v3.js +397 -0
- package/dist/rules/security-v3.js.map +1 -0
- package/dist/rules/security-v4.d.ts +22 -0
- package/dist/rules/security-v4.js +688 -0
- package/dist/rules/security-v4.js.map +1 -0
- package/dist/rules/security.d.ts +12 -0
- package/dist/rules/security.js +286 -0
- package/dist/rules/security.js.map +1 -0
- package/dist/rules/utils.d.ts +7 -0
- package/dist/rules/utils.js +21 -0
- package/dist/rules/utils.js.map +1 -0
- package/dist/rules/vue.js +1 -1
- package/dist/rules/vue.js.map +1 -1
- package/dist/spec-checker.d.ts +83 -0
- package/dist/spec-checker.js +405 -0
- package/dist/spec-checker.js.map +1 -0
- package/dist/suppression/apply-suppression.d.ts +17 -0
- package/dist/suppression/apply-suppression.js +94 -0
- package/dist/suppression/apply-suppression.js.map +1 -0
- package/dist/suppression/index.d.ts +6 -0
- package/dist/suppression/index.js +6 -0
- package/dist/suppression/index.js.map +1 -0
- package/dist/suppression/parse-directives.d.ts +25 -0
- package/dist/suppression/parse-directives.js +161 -0
- package/dist/suppression/parse-directives.js.map +1 -0
- package/dist/suppression/types.d.ts +32 -0
- package/dist/suppression/types.js +5 -0
- package/dist/suppression/types.js.map +1 -0
- package/dist/taint.d.ts +115 -0
- package/dist/taint.js +1052 -0
- package/dist/taint.js.map +1 -0
- package/dist/types.d.ts +71 -0
- package/dist/types.js.map +1 -1
- package/package.json +7 -4
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security v3 rules — OWASP gap closure.
|
|
3
|
+
*
|
|
4
|
+
* Covers: Regex DoS, missing input validation, prototype pollution,
|
|
5
|
+
* information exposure (stack traces in responses).
|
|
6
|
+
*
|
|
7
|
+
* All AST-based. Always active regardless of target.
|
|
8
|
+
*/
|
|
9
|
+
import { SyntaxKind } from 'ts-morph';
|
|
10
|
+
import { createFingerprint } from '../types.js';
|
|
11
|
+
function span(file, line, col = 1) {
|
|
12
|
+
return { file, startLine: line, startCol: col, endLine: line, endCol: col };
|
|
13
|
+
}
|
|
14
|
+
function finding(ruleId, severity, category, message, file, line, extra) {
|
|
15
|
+
return {
|
|
16
|
+
source: 'kern',
|
|
17
|
+
ruleId,
|
|
18
|
+
severity,
|
|
19
|
+
category,
|
|
20
|
+
message,
|
|
21
|
+
primarySpan: span(file, line),
|
|
22
|
+
fingerprint: createFingerprint(ruleId, line, 1),
|
|
23
|
+
...extra,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
// ── Rule S9: regex-dos ────────────────────────────────────────────────
|
|
27
|
+
// Regex literals with nested quantifiers or unbounded repetition that
|
|
28
|
+
// can cause catastrophic backtracking (ReDoS).
|
|
29
|
+
//
|
|
30
|
+
// Detects: (a+)+, (a|a)+, (.*a){n}, nested quantifiers in groups
|
|
31
|
+
// CWE-1333
|
|
32
|
+
/**
|
|
33
|
+
* Check if a regex pattern contains ReDoS-vulnerable constructs.
|
|
34
|
+
* Looks for: nested quantifiers, overlapping alternation with quantifiers,
|
|
35
|
+
* and ambiguous repetition patterns.
|
|
36
|
+
*/
|
|
37
|
+
function isReDoSVulnerable(pattern) {
|
|
38
|
+
// Nested quantifiers: (x+)+ , (x*)* , (x+)* , (x*)+, (x{n,})+ etc.
|
|
39
|
+
// These cause exponential backtracking
|
|
40
|
+
if (/\([^)]*[+*]\)[+*{]/.test(pattern)) {
|
|
41
|
+
return 'nested quantifier — causes exponential backtracking';
|
|
42
|
+
}
|
|
43
|
+
// Quantified group containing alternation with overlap: (a|a)+, (a|ab)+
|
|
44
|
+
// Simplified: group with | and outer quantifier
|
|
45
|
+
if (/\([^)]*\|[^)]*\)[+*]{1,2}/.test(pattern)) {
|
|
46
|
+
// Check for character overlap in alternation branches
|
|
47
|
+
const groupMatch = pattern.match(/\(([^)]*\|[^)]*)\)[+*]/);
|
|
48
|
+
if (groupMatch) {
|
|
49
|
+
const branches = groupMatch[1].split('|');
|
|
50
|
+
if (branches.length >= 2) {
|
|
51
|
+
const first = branches[0].replace(/[+*?{}\\[\]()]/g, '');
|
|
52
|
+
const second = branches[1].replace(/[+*?{}\\[\]()]/g, '');
|
|
53
|
+
// If branches share starting characters, it's ambiguous
|
|
54
|
+
if (first.length > 0 && second.length > 0 && first[0] === second[0]) {
|
|
55
|
+
return 'overlapping alternation with quantifier — ambiguous matching causes backtracking';
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
// Quantified group with .* or .+ inside: (.*something)+ or (.+x){2,}
|
|
61
|
+
if (/\([^)]*\.\*[^)]*\)[+*{]/.test(pattern) || /\([^)]*\.\+[^)]*\)[+*{]/.test(pattern)) {
|
|
62
|
+
return '.* or .+ inside quantified group — unbounded matching causes backtracking';
|
|
63
|
+
}
|
|
64
|
+
// Adjacent overlapping quantifiers without separator: \s*\s*, \d+\d+
|
|
65
|
+
if (/\\[dswDSW][+*]\\[dswDSW][+*]/.test(pattern)) {
|
|
66
|
+
return 'adjacent overlapping quantifiers — ambiguous boundary causes backtracking';
|
|
67
|
+
}
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
function regexDos(ctx) {
|
|
71
|
+
const findings = [];
|
|
72
|
+
// Check regex literals: /pattern/flags
|
|
73
|
+
for (const node of ctx.sourceFile.getDescendantsOfKind(SyntaxKind.RegularExpressionLiteral)) {
|
|
74
|
+
const text = node.getText();
|
|
75
|
+
// Extract pattern (between first / and last /)
|
|
76
|
+
const lastSlash = text.lastIndexOf('/');
|
|
77
|
+
if (lastSlash <= 0)
|
|
78
|
+
continue;
|
|
79
|
+
const pattern = text.slice(1, lastSlash);
|
|
80
|
+
const vulnerability = isReDoSVulnerable(pattern);
|
|
81
|
+
if (vulnerability) {
|
|
82
|
+
findings.push(finding('regex-dos', 'warning', 'bug', `Regex vulnerable to ReDoS: ${vulnerability}`, ctx.filePath, node.getStartLineNumber(), { suggestion: 'Rewrite regex to avoid nested quantifiers, or use a linear-time regex engine (RE2)' }));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// Check new RegExp('pattern') constructors
|
|
86
|
+
for (const newExpr of ctx.sourceFile.getDescendantsOfKind(SyntaxKind.NewExpression)) {
|
|
87
|
+
if (newExpr.getExpression().getText() !== 'RegExp')
|
|
88
|
+
continue;
|
|
89
|
+
const args = newExpr.getArguments();
|
|
90
|
+
if (args.length === 0)
|
|
91
|
+
continue;
|
|
92
|
+
const firstArg = args[0];
|
|
93
|
+
if (firstArg.getKind() !== SyntaxKind.StringLiteral)
|
|
94
|
+
continue;
|
|
95
|
+
const pattern = firstArg.getLiteralValue();
|
|
96
|
+
const vulnerability = isReDoSVulnerable(pattern);
|
|
97
|
+
if (vulnerability) {
|
|
98
|
+
findings.push(finding('regex-dos', 'warning', 'bug', `RegExp constructor vulnerable to ReDoS: ${vulnerability}`, ctx.filePath, newExpr.getStartLineNumber(), { suggestion: 'Rewrite regex to avoid nested quantifiers, or use a linear-time regex engine (RE2)' }));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return findings;
|
|
102
|
+
}
|
|
103
|
+
// ── Rule S10: missing-input-validation ────────────────────────────────
|
|
104
|
+
// HTTP handler params (req.body, req.query, req.params) used directly
|
|
105
|
+
// in logic without validation (no schema.parse, no if-check, no
|
|
106
|
+
// parseInt/Number, no sanitize call).
|
|
107
|
+
// CWE-20
|
|
108
|
+
const USER_INPUT_PATTERNS = /req\.(body|query|params|headers)\b/;
|
|
109
|
+
const VALIDATION_PATTERNS = /\.parse\(|\.validate\(|\.safeParse\(|parseInt\(|Number\(|Boolean\(|sanitize|validator\.|zod\.|yup\.|joi\.|ajv\.|superstruct|valibot/;
|
|
110
|
+
function missingInputValidation(ctx) {
|
|
111
|
+
const findings = [];
|
|
112
|
+
// Find Express-style route handlers: (req, res) => { ... } or function(req, res) { ... }
|
|
113
|
+
for (const fn of [
|
|
114
|
+
...ctx.sourceFile.getDescendantsOfKind(SyntaxKind.ArrowFunction),
|
|
115
|
+
...ctx.sourceFile.getDescendantsOfKind(SyntaxKind.FunctionExpression),
|
|
116
|
+
]) {
|
|
117
|
+
const params = fn.getParameters();
|
|
118
|
+
if (params.length < 2)
|
|
119
|
+
continue;
|
|
120
|
+
// Heuristic: first param named req/request, second named res/response
|
|
121
|
+
const firstName = params[0].getName();
|
|
122
|
+
const secondName = params[1].getName();
|
|
123
|
+
if (!/^req(uest)?$/.test(firstName) || !/^res(ponse)?$/.test(secondName))
|
|
124
|
+
continue;
|
|
125
|
+
const body = fn.getBody();
|
|
126
|
+
if (!body)
|
|
127
|
+
continue;
|
|
128
|
+
const bodyText = body.getText();
|
|
129
|
+
// Check if handler uses user input
|
|
130
|
+
if (!USER_INPUT_PATTERNS.test(bodyText))
|
|
131
|
+
continue;
|
|
132
|
+
// Check if any validation is present in the handler
|
|
133
|
+
if (VALIDATION_PATTERNS.test(bodyText))
|
|
134
|
+
continue;
|
|
135
|
+
// Extract which input sources are used
|
|
136
|
+
const sources = [];
|
|
137
|
+
const bodyMatch = bodyText.match(/req\.body/);
|
|
138
|
+
const queryMatch = bodyText.match(/req\.query/);
|
|
139
|
+
const paramsMatch = bodyText.match(/req\.params/);
|
|
140
|
+
if (bodyMatch)
|
|
141
|
+
sources.push('req.body');
|
|
142
|
+
if (queryMatch)
|
|
143
|
+
sources.push('req.query');
|
|
144
|
+
if (paramsMatch)
|
|
145
|
+
sources.push('req.params');
|
|
146
|
+
findings.push(finding('missing-input-validation', 'warning', 'bug', `HTTP handler uses ${sources.join(', ')} without input validation`, ctx.filePath, fn.getStartLineNumber(), { suggestion: 'Validate input with zod, joi, or manual checks before using in business logic' }));
|
|
147
|
+
}
|
|
148
|
+
return findings;
|
|
149
|
+
}
|
|
150
|
+
// ── Rule S11: prototype-pollution ─────────────────────────────────────
|
|
151
|
+
// Object.assign() or spread from unvalidated external input.
|
|
152
|
+
// Detects: Object.assign(target, req.body), { ...req.body }, merge(target, userInput)
|
|
153
|
+
// CWE-1321
|
|
154
|
+
const MERGE_FUNCTIONS = new Set(['merge', 'defaults', 'defaultsDeep', 'extend', 'assign']);
|
|
155
|
+
function prototypePollution(ctx) {
|
|
156
|
+
const findings = [];
|
|
157
|
+
// Object.assign(target, untrustedSource)
|
|
158
|
+
for (const call of ctx.sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression)) {
|
|
159
|
+
const callee = call.getExpression();
|
|
160
|
+
// Object.assign(...)
|
|
161
|
+
if (callee.getKind() === SyntaxKind.PropertyAccessExpression) {
|
|
162
|
+
const pa = callee;
|
|
163
|
+
if (pa.getExpression().getText() === 'Object' && pa.getName() === 'assign') {
|
|
164
|
+
const args = call.getArguments();
|
|
165
|
+
// Check if any argument after the first is user input
|
|
166
|
+
for (let i = 1; i < args.length; i++) {
|
|
167
|
+
const text = args[i].getText();
|
|
168
|
+
if (USER_INPUT_PATTERNS.test(text) || /\bJSON\.parse\b/.test(text)) {
|
|
169
|
+
findings.push(finding('prototype-pollution', 'error', 'bug', `Object.assign() with user input (${text.substring(0, 40)}) — prototype pollution risk`, ctx.filePath, call.getStartLineNumber(), { suggestion: 'Use a safe merge utility, or validate/strip __proto__ and constructor keys first' }));
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
// Deep merge utilities: merge(target, userInput), _.defaults(target, userInput)
|
|
176
|
+
if (callee.getKind() === SyntaxKind.Identifier) {
|
|
177
|
+
const funcName = callee.getText();
|
|
178
|
+
if (MERGE_FUNCTIONS.has(funcName)) {
|
|
179
|
+
const args = call.getArguments();
|
|
180
|
+
for (const arg of args) {
|
|
181
|
+
const text = arg.getText();
|
|
182
|
+
if (USER_INPUT_PATTERNS.test(text) || /\bJSON\.parse\b/.test(text)) {
|
|
183
|
+
findings.push(finding('prototype-pollution', 'warning', 'bug', `${funcName}() with user input — potential prototype pollution`, ctx.filePath, call.getStartLineNumber(), { suggestion: 'Validate input keys or use Object.create(null) as target' }));
|
|
184
|
+
break;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
// lodash-style: _.merge, _.defaults, _.defaultsDeep, _.extend
|
|
190
|
+
if (callee.getKind() === SyntaxKind.PropertyAccessExpression) {
|
|
191
|
+
const pa = callee;
|
|
192
|
+
const objText = pa.getExpression().getText();
|
|
193
|
+
if ((objText === '_' || objText === 'lodash') && MERGE_FUNCTIONS.has(pa.getName())) {
|
|
194
|
+
const args = call.getArguments();
|
|
195
|
+
for (const arg of args) {
|
|
196
|
+
const text = arg.getText();
|
|
197
|
+
if (USER_INPUT_PATTERNS.test(text) || /\bJSON\.parse\b/.test(text)) {
|
|
198
|
+
findings.push(finding('prototype-pollution', 'warning', 'bug', `${objText}.${pa.getName()}() with user input — potential prototype pollution`, ctx.filePath, call.getStartLineNumber(), { suggestion: 'Use a prototype-safe merge, or strip __proto__/constructor from input' }));
|
|
199
|
+
break;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
// Spread from user input in object literal: { ...req.body }
|
|
206
|
+
for (const spread of ctx.sourceFile.getDescendantsOfKind(SyntaxKind.SpreadAssignment)) {
|
|
207
|
+
const expr = spread.getExpression();
|
|
208
|
+
const text = expr.getText();
|
|
209
|
+
if (USER_INPUT_PATTERNS.test(text) || /\bJSON\.parse\b/.test(text)) {
|
|
210
|
+
findings.push(finding('prototype-pollution', 'warning', 'bug', `Spread from user input (${text.substring(0, 40)}) — prototype pollution risk if input contains __proto__`, ctx.filePath, spread.getStartLineNumber(), { suggestion: 'Destructure only known fields, or strip __proto__ and constructor keys' }));
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return findings;
|
|
214
|
+
}
|
|
215
|
+
// ── Rule S12: information-exposure ────────────────────────────────────
|
|
216
|
+
// Error responses that include stack traces, internal paths, or
|
|
217
|
+
// unfiltered error objects sent to clients.
|
|
218
|
+
// CWE-209
|
|
219
|
+
function informationExposure(ctx) {
|
|
220
|
+
const findings = [];
|
|
221
|
+
for (const call of ctx.sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression)) {
|
|
222
|
+
const callee = call.getExpression();
|
|
223
|
+
if (callee.getKind() !== SyntaxKind.PropertyAccessExpression)
|
|
224
|
+
continue;
|
|
225
|
+
const pa = callee;
|
|
226
|
+
const methodName = pa.getName();
|
|
227
|
+
// res.json(), res.send(), res.status().json()
|
|
228
|
+
const isResponseMethod = methodName === 'json' || methodName === 'send';
|
|
229
|
+
if (!isResponseMethod)
|
|
230
|
+
continue;
|
|
231
|
+
// Walk up to check if this is on a response object (res.json or res.status(N).json)
|
|
232
|
+
let objText = pa.getExpression().getText();
|
|
233
|
+
// Handle chained: res.status(500).json(...)
|
|
234
|
+
if (objText.includes('.status(')) {
|
|
235
|
+
objText = objText.split('.')[0];
|
|
236
|
+
}
|
|
237
|
+
if (!/^res(ponse)?$/.test(objText))
|
|
238
|
+
continue;
|
|
239
|
+
const args = call.getArguments();
|
|
240
|
+
if (args.length === 0)
|
|
241
|
+
continue;
|
|
242
|
+
const argText = args[0].getText();
|
|
243
|
+
// Pattern 1: Sending raw error object — res.json(err), res.json({ error: err })
|
|
244
|
+
// err.stack, err.message with stack included, error.stack
|
|
245
|
+
if (/\.stack\b/.test(argText)) {
|
|
246
|
+
findings.push(finding('information-exposure', 'error', 'bug', 'Stack trace sent in response — exposes internal paths and code structure', ctx.filePath, call.getStartLineNumber(), { suggestion: 'Send a generic error message to clients; log the stack server-side' }));
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
// Pattern 2: Sending raw error in catch block — res.json(err) or res.send(err)
|
|
250
|
+
// Check if we're inside a catch clause
|
|
251
|
+
let ancestor = call.getParent();
|
|
252
|
+
let inCatch = false;
|
|
253
|
+
let catchVarName = '';
|
|
254
|
+
while (ancestor) {
|
|
255
|
+
if (ancestor.getKind() === SyntaxKind.CatchClause) {
|
|
256
|
+
inCatch = true;
|
|
257
|
+
const clause = ancestor;
|
|
258
|
+
const varDecl = clause.getVariableDeclaration();
|
|
259
|
+
if (varDecl)
|
|
260
|
+
catchVarName = varDecl.getName();
|
|
261
|
+
break;
|
|
262
|
+
}
|
|
263
|
+
ancestor = ancestor.getParent();
|
|
264
|
+
}
|
|
265
|
+
if (inCatch && catchVarName) {
|
|
266
|
+
// Sending the raw error variable: res.json(err), res.json({ error: err })
|
|
267
|
+
const errRef = new RegExp(`\\b${catchVarName}\\b`);
|
|
268
|
+
if (errRef.test(argText)) {
|
|
269
|
+
// Check it's not just err.message (which is often safe)
|
|
270
|
+
const onlyMessage = new RegExp(`^\\{?\\s*\\w+:\\s*${catchVarName}\\.message\\s*\\}?$`);
|
|
271
|
+
if (!onlyMessage.test(argText.trim())) {
|
|
272
|
+
findings.push(finding('information-exposure', 'warning', 'bug', `Raw error object '${catchVarName}' sent in response — may expose stack traces and internal details`, ctx.filePath, call.getStartLineNumber(), { suggestion: `Send only ${catchVarName}.message or a generic error string` }));
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
// Pattern 3: process.env or __dirname in response
|
|
277
|
+
if (/process\.env\b/.test(argText) || /\b__dirname\b/.test(argText) || /\b__filename\b/.test(argText)) {
|
|
278
|
+
findings.push(finding('information-exposure', 'warning', 'bug', 'Internal path or environment variable sent in response', ctx.filePath, call.getStartLineNumber(), { suggestion: 'Never expose process.env, __dirname, or __filename to clients' }));
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
return findings;
|
|
282
|
+
}
|
|
283
|
+
// ── Rule S13: prompt-injection ─────────────────────────────────────────
|
|
284
|
+
// User input embedded into LLM prompts without sanitization.
|
|
285
|
+
// Detects: template literals or string concatenation that include user input
|
|
286
|
+
// flowing to LLM API calls (generateContent, chat.completions, etc.)
|
|
287
|
+
// CWE-77 (Injection), OWASP LLM01
|
|
288
|
+
/** Known LLM API call patterns */
|
|
289
|
+
const LLM_API_PATTERNS = /\bgenerateContent\b|\bchat\.completions\b|\bcreate\b.*\bmodel\b|\bgenerate\b|\bsendMessage\b|\bcomplete\b/;
|
|
290
|
+
/** Common prompt builder function names */
|
|
291
|
+
const PROMPT_BUILDER_PATTERNS = /\bbuildPrompt\b|\bgeneratePrompt\b|\bsystemPrompt\b|\buserPrompt\b|\bcreatePrompt\b/;
|
|
292
|
+
/** Sanitization function patterns */
|
|
293
|
+
const PROMPT_SANITIZER_PATTERNS = /\bsanitizeForPrompt\b|\bescapePrompt\b|\bcleanPrompt\b|\bsanitize\w*\(/;
|
|
294
|
+
function promptInjection(ctx) {
|
|
295
|
+
const findings = [];
|
|
296
|
+
// Check template literals that embed user input and flow to LLM calls
|
|
297
|
+
for (const template of ctx.sourceFile.getDescendantsOfKind(SyntaxKind.TemplateExpression)) {
|
|
298
|
+
const text = template.getText();
|
|
299
|
+
// Does this template contain user input references?
|
|
300
|
+
const hasUserInput = USER_INPUT_PATTERNS.test(text) ||
|
|
301
|
+
/\b(question|userInput|userMessage|message|input|query|prompt|instruction|caption)\b/.test(text);
|
|
302
|
+
if (!hasUserInput)
|
|
303
|
+
continue;
|
|
304
|
+
// Is there a sanitizer wrapping the user input in this template?
|
|
305
|
+
if (PROMPT_SANITIZER_PATTERNS.test(text))
|
|
306
|
+
continue;
|
|
307
|
+
// Check if this template is used in an LLM context
|
|
308
|
+
// 1. Inside a function whose name suggests prompt building
|
|
309
|
+
let parent = template.getParent();
|
|
310
|
+
let inPromptContext = false;
|
|
311
|
+
while (parent) {
|
|
312
|
+
if (parent.getKind() === SyntaxKind.FunctionDeclaration ||
|
|
313
|
+
parent.getKind() === SyntaxKind.ArrowFunction ||
|
|
314
|
+
parent.getKind() === SyntaxKind.MethodDeclaration) {
|
|
315
|
+
const parentText = parent.getText().substring(0, 200);
|
|
316
|
+
if (PROMPT_BUILDER_PATTERNS.test(parentText) || LLM_API_PATTERNS.test(parentText)) {
|
|
317
|
+
inPromptContext = true;
|
|
318
|
+
}
|
|
319
|
+
// Check function name
|
|
320
|
+
if (parent.getKind() === SyntaxKind.FunctionDeclaration) {
|
|
321
|
+
const fnName = parent.getName() || '';
|
|
322
|
+
if (/prompt|build.*prompt|generate.*prompt|system.*prompt/i.test(fnName)) {
|
|
323
|
+
inPromptContext = true;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
break;
|
|
327
|
+
}
|
|
328
|
+
parent = parent.getParent();
|
|
329
|
+
}
|
|
330
|
+
// 2. Variable assigned to a name like "systemPrompt", "userPrompt"
|
|
331
|
+
const assignParent = template.getParent();
|
|
332
|
+
if (assignParent?.getKind() === SyntaxKind.VariableDeclaration) {
|
|
333
|
+
const varName = assignParent.getName();
|
|
334
|
+
if (/prompt|system|instruction/i.test(varName)) {
|
|
335
|
+
inPromptContext = true;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
// Also check if template is assigned via `const x = ...`
|
|
339
|
+
if (assignParent?.getKind() === SyntaxKind.BinaryExpression) {
|
|
340
|
+
const leftText = assignParent.getLeft().getText();
|
|
341
|
+
if (/prompt|system|instruction/i.test(leftText)) {
|
|
342
|
+
inPromptContext = true;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
if (!inPromptContext)
|
|
346
|
+
continue;
|
|
347
|
+
// Find which user input variable is unsanitized
|
|
348
|
+
const spans = template.getTemplateSpans();
|
|
349
|
+
for (const span of spans) {
|
|
350
|
+
const exprText = span.getExpression().getText();
|
|
351
|
+
// Skip if this specific expression is wrapped in sanitize
|
|
352
|
+
if (PROMPT_SANITIZER_PATTERNS.test(exprText))
|
|
353
|
+
continue;
|
|
354
|
+
// Skip simple property access on known safe objects
|
|
355
|
+
if (/^(intent|mixContext|analysisResults)\b/.test(exprText))
|
|
356
|
+
continue;
|
|
357
|
+
// Check if this is a user-controlled value
|
|
358
|
+
const isUserControlled = USER_INPUT_PATTERNS.test(exprText) ||
|
|
359
|
+
/^(question|userInput|userMessage|message|input|caption|instruction)\b/.test(exprText);
|
|
360
|
+
if (isUserControlled) {
|
|
361
|
+
findings.push(finding('prompt-injection', 'warning', 'bug', `User input '${exprText.substring(0, 50)}' embedded in LLM prompt without sanitization — prompt injection risk`, ctx.filePath, template.getStartLineNumber(), { suggestion: 'Wrap user input with sanitizeForPrompt() or equivalent before embedding in prompts' }));
|
|
362
|
+
break; // One finding per template
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
// Check string concatenation in prompt context: "You are... " + userInput
|
|
367
|
+
for (const bin of ctx.sourceFile.getDescendantsOfKind(SyntaxKind.BinaryExpression)) {
|
|
368
|
+
if (bin.getOperatorToken().getKind() !== SyntaxKind.PlusToken)
|
|
369
|
+
continue;
|
|
370
|
+
const rightText = bin.getRight().getText();
|
|
371
|
+
const leftText = bin.getLeft().getText();
|
|
372
|
+
const fullText = bin.getText();
|
|
373
|
+
// Is this string concat involving user input?
|
|
374
|
+
const hasUserInput = USER_INPUT_PATTERNS.test(rightText) ||
|
|
375
|
+
/^(question|userInput|message|input|caption|instruction)\b/.test(rightText);
|
|
376
|
+
if (!hasUserInput)
|
|
377
|
+
continue;
|
|
378
|
+
// Is the left side a prompt-like string?
|
|
379
|
+
const isPromptConcat = /prompt|instruction|system|you are|analyze|review/i.test(leftText);
|
|
380
|
+
if (!isPromptConcat)
|
|
381
|
+
continue;
|
|
382
|
+
// Is it sanitized?
|
|
383
|
+
if (PROMPT_SANITIZER_PATTERNS.test(fullText))
|
|
384
|
+
continue;
|
|
385
|
+
findings.push(finding('prompt-injection', 'warning', 'bug', `User input concatenated into LLM prompt without sanitization — prompt injection risk`, ctx.filePath, bin.getStartLineNumber(), { suggestion: 'Use sanitizeForPrompt() on user input before concatenating into prompts' }));
|
|
386
|
+
}
|
|
387
|
+
return findings;
|
|
388
|
+
}
|
|
389
|
+
// ── Exported Security v3 Rules ────────────────────────────────────────
|
|
390
|
+
export const securityV3Rules = [
|
|
391
|
+
regexDos,
|
|
392
|
+
missingInputValidation,
|
|
393
|
+
prototypePollution,
|
|
394
|
+
informationExposure,
|
|
395
|
+
promptInjection,
|
|
396
|
+
];
|
|
397
|
+
//# sourceMappingURL=security-v3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security-v3.js","sourceRoot":"","sources":["../../src/rules/security-v3.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAEtC,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD,SAAS,IAAI,CAAC,IAAY,EAAE,IAAY,EAAE,GAAG,GAAG,CAAC;IAC/C,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;AAC9E,CAAC;AAED,SAAS,OAAO,CACd,MAAc,EACd,QAAsC,EACtC,QAAmC,EACnC,OAAe,EACf,IAAY,EACZ,IAAY,EACZ,KAA8B;IAE9B,OAAO;QACL,MAAM,EAAE,MAAM;QACd,MAAM;QACN,QAAQ;QACR,QAAQ;QACR,OAAO;QACP,WAAW,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC;QAC7B,WAAW,EAAE,iBAAiB,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/C,GAAG,KAAK;KACT,CAAC;AACJ,CAAC;AAED,yEAAyE;AACzE,sEAAsE;AACtE,+CAA+C;AAC/C,EAAE;AACF,iEAAiE;AACjE,WAAW;AAEX;;;;GAIG;AACH,SAAS,iBAAiB,CAAC,OAAe;IACxC,mEAAmE;IACnE,uCAAuC;IACvC,IAAI,oBAAoB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QACvC,OAAO,qDAAqD,CAAC;IAC/D,CAAC;IAED,wEAAwE;IACxE,gDAAgD;IAChD,IAAI,2BAA2B,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QAC9C,sDAAsD;QACtD,MAAM,UAAU,GAAG,OAAO,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAC;QAC3D,IAAI,UAAU,EAAE,CAAC;YACf,MAAM,QAAQ,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAC1C,IAAI,QAAQ,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;gBACzB,MAAM,KAAK,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,iBAAiB,EAAE,EAAE,CAAC,CAAC;gBACzD,MAAM,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,iBAAiB,EAAE,EAAE,CAAC,CAAC;gBAC1D,wDAAwD;gBACxD,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;oBACpE,OAAO,kFAAkF,CAAC;gBAC5F,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,qEAAqE;IACrE,IAAI,yBAAyB,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,yBAAyB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QACvF,OAAO,2EAA2E,CAAC;IACrF,CAAC;IAED,qEAAqE;IACrE,IAAI,8BAA8B,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QACjD,OAAO,2EAA2E,CAAC;IACrF,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,QAAQ,CAAC,GAAgB;IAChC,MAAM,QAAQ,GAAoB,EAAE,CAAC;IAErC,uCAAuC;IACvC,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,UAAU,CAAC,oBAAoB,CAAC,UAAU,CAAC,wBAAwB,CAAC,EAAE,CAAC;QAC5F,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;QAC5B,+CAA+C;QAC/C,MAAM,SAAS,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;QACxC,IAAI,SAAS,IAAI,CAAC;YAAE,SAAS;QAC7B,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;QAEzC,MAAM,aAAa,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;QACjD,IAAI,aAAa,EAAE,CAAC;YAClB,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,SAAS,EAAE,KAAK,EACjD,8BAA8B,aAAa,EAAE,EAC7C,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,kBAAkB,EAAE,EACvC,EAAE,UAAU,EAAE,oFAAoF,EAAE,CAAC,CAAC,CAAC;QAC3G,CAAC;IACH,CAAC;IAED,2CAA2C;IAC3C,KAAK,MAAM,OAAO,IAAI,GAAG,CAAC,UAAU,CAAC,oBAAoB,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;QACpF,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC,OAAO,EAAE,KAAK,QAAQ;YAAE,SAAS;QAC7D,MAAM,IAAI,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;QACpC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QAChC,MAAM,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACzB,IAAI,QAAQ,CAAC,OAAO,EAAE,KAAK,UAAU,CAAC,aAAa;YAAE,SAAS;QAC9D,MAAM,OAAO,GAAI,QAA6C,CAAC,eAAe,EAAE,CAAC;QAEjF,MAAM,aAAa,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;QACjD,IAAI,aAAa,EAAE,CAAC;YAClB,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,SAAS,EAAE,KAAK,EACjD,2CAA2C,aAAa,EAAE,EAC1D,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,kBAAkB,EAAE,EAC1C,EAAE,UAAU,EAAE,oFAAoF,EAAE,CAAC,CAAC,CAAC;QAC3G,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,yEAAyE;AACzE,sEAAsE;AACtE,gEAAgE;AAChE,sCAAsC;AACtC,SAAS;AAET,MAAM,mBAAmB,GAAG,oCAAoC,CAAC;AACjE,MAAM,mBAAmB,GAAG,qIAAqI,CAAC;AAElK,SAAS,sBAAsB,CAAC,GAAgB;IAC9C,MAAM,QAAQ,GAAoB,EAAE,CAAC;IAErC,yFAAyF;IACzF,KAAK,MAAM,EAAE,IAAI;QACf,GAAG,GAAG,CAAC,UAAU,CAAC,oBAAoB,CAAC,UAAU,CAAC,aAAa,CAAC;QAChE,GAAG,GAAG,CAAC,UAAU,CAAC,oBAAoB,CAAC,UAAU,CAAC,kBAAkB,CAAC;KACtE,EAAE,CAAC;QACF,MAAM,MAAM,GAAG,EAAE,CAAC,aAAa,EAAE,CAAC;QAClC,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;YAAE,SAAS;QAEhC,sEAAsE;QACtE,MAAM,SAAS,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;QACtC,MAAM,UAAU,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;QACvC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,UAAU,CAAC;YAAE,SAAS;QAEnF,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC;QAC1B,IAAI,CAAC,IAAI;YAAE,SAAS;QACpB,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;QAEhC,mCAAmC;QACnC,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,QAAQ,CAAC;YAAE,SAAS;QAElD,oDAAoD;QACpD,IAAI,mBAAmB,CAAC,IAAI,CAAC,QAAQ,CAAC;YAAE,SAAS;QAEjD,uCAAuC;QACvC,MAAM,OAAO,GAAa,EAAE,CAAC;QAC7B,MAAM,SAAS,GAAG,QAAQ,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QAC9C,MAAM,UAAU,GAAG,QAAQ,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;QAChD,MAAM,WAAW,GAAG,QAAQ,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;QAClD,IAAI,SAAS;YAAE,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACxC,IAAI,UAAU;YAAE,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC1C,IAAI,WAAW;YAAE,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAE5C,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,0BAA0B,EAAE,SAAS,EAAE,KAAK,EAChE,qBAAqB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,2BAA2B,EAClE,GAAG,CAAC,QAAQ,EAAE,EAAE,CAAC,kBAAkB,EAAE,EACrC,EAAE,UAAU,EAAE,+EAA+E,EAAE,CAAC,CAAC,CAAC;IACtG,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,yEAAyE;AACzE,6DAA6D;AAC7D,sFAAsF;AACtF,WAAW;AAEX,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,CAAC,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;AAE3F,SAAS,kBAAkB,CAAC,GAAgB;IAC1C,MAAM,QAAQ,GAAoB,EAAE,CAAC;IAErC,yCAAyC;IACzC,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,UAAU,CAAC,oBAAoB,CAAC,UAAU,CAAC,cAAc,CAAC,EAAE,CAAC;QAClF,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;QAEpC,qBAAqB;QACrB,IAAI,MAAM,CAAC,OAAO,EAAE,KAAK,UAAU,CAAC,wBAAwB,EAAE,CAAC;YAC7D,MAAM,EAAE,GAAG,MAAqD,CAAC;YACjE,IAAI,EAAE,CAAC,aAAa,EAAE,CAAC,OAAO,EAAE,KAAK,QAAQ,IAAI,EAAE,CAAC,OAAO,EAAE,KAAK,QAAQ,EAAE,CAAC;gBAC3E,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;gBACjC,sDAAsD;gBACtD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;oBACrC,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;oBAC/B,IAAI,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;wBACnE,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,qBAAqB,EAAE,OAAO,EAAE,KAAK,EACzD,oCAAoC,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,8BAA8B,EACvF,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,kBAAkB,EAAE,EACvC,EAAE,UAAU,EAAE,kFAAkF,EAAE,CAAC,CAAC,CAAC;wBACvG,MAAM;oBACR,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QAED,gFAAgF;QAChF,IAAI,MAAM,CAAC,OAAO,EAAE,KAAK,UAAU,CAAC,UAAU,EAAE,CAAC;YAC/C,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC;YAClC,IAAI,eAAe,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAClC,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;gBACjC,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;oBACvB,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,EAAE,CAAC;oBAC3B,IAAI,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;wBACnE,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,qBAAqB,EAAE,SAAS,EAAE,KAAK,EAC3D,GAAG,QAAQ,oDAAoD,EAC/D,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,kBAAkB,EAAE,EACvC,EAAE,UAAU,EAAE,0DAA0D,EAAE,CAAC,CAAC,CAAC;wBAC/E,MAAM;oBACR,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QAED,8DAA8D;QAC9D,IAAI,MAAM,CAAC,OAAO,EAAE,KAAK,UAAU,CAAC,wBAAwB,EAAE,CAAC;YAC7D,MAAM,EAAE,GAAG,MAAqD,CAAC;YACjE,MAAM,OAAO,GAAG,EAAE,CAAC,aAAa,EAAE,CAAC,OAAO,EAAE,CAAC;YAC7C,IAAI,CAAC,OAAO,KAAK,GAAG,IAAI,OAAO,KAAK,QAAQ,CAAC,IAAI,eAAe,CAAC,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;gBACnF,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;gBACjC,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;oBACvB,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,EAAE,CAAC;oBAC3B,IAAI,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;wBACnE,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,qBAAqB,EAAE,SAAS,EAAE,KAAK,EAC3D,GAAG,OAAO,IAAI,EAAE,CAAC,OAAO,EAAE,oDAAoD,EAC9E,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,kBAAkB,EAAE,EACvC,EAAE,UAAU,EAAE,uEAAuE,EAAE,CAAC,CAAC,CAAC;wBAC5F,MAAM;oBACR,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,4DAA4D;IAC5D,KAAK,MAAM,MAAM,IAAI,GAAG,CAAC,UAAU,CAAC,oBAAoB,CAAC,UAAU,CAAC,gBAAgB,CAAC,EAAE,CAAC;QACtF,MAAM,IAAI,GAAG,MAAM,CAAC,aAAa,EAAE,CAAC;QACpC,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;QAC5B,IAAI,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACnE,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,qBAAqB,EAAE,SAAS,EAAE,KAAK,EAC3D,2BAA2B,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,0DAA0D,EAC1G,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,kBAAkB,EAAE,EACzC,EAAE,UAAU,EAAE,wEAAwE,EAAE,CAAC,CAAC,CAAC;QAC/F,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,yEAAyE;AACzE,gEAAgE;AAChE,4CAA4C;AAC5C,UAAU;AAEV,SAAS,mBAAmB,CAAC,GAAgB;IAC3C,MAAM,QAAQ,GAAoB,EAAE,CAAC;IAErC,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,UAAU,CAAC,oBAAoB,CAAC,UAAU,CAAC,cAAc,CAAC,EAAE,CAAC;QAClF,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;QACpC,IAAI,MAAM,CAAC,OAAO,EAAE,KAAK,UAAU,CAAC,wBAAwB;YAAE,SAAS;QACvE,MAAM,EAAE,GAAG,MAAqD,CAAC;QACjE,MAAM,UAAU,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC;QAEhC,8CAA8C;QAC9C,MAAM,gBAAgB,GAAG,UAAU,KAAK,MAAM,IAAI,UAAU,KAAK,MAAM,CAAC;QACxE,IAAI,CAAC,gBAAgB;YAAE,SAAS;QAEhC,oFAAoF;QACpF,IAAI,OAAO,GAAG,EAAE,CAAC,aAAa,EAAE,CAAC,OAAO,EAAE,CAAC;QAC3C,4CAA4C;QAC5C,IAAI,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;YACjC,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAClC,CAAC;QACD,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC;YAAE,SAAS;QAE7C,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;QACjC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QAChC,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;QAElC,gFAAgF;QAChF,0DAA0D;QAC1D,IAAI,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YAC9B,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,sBAAsB,EAAE,OAAO,EAAE,KAAK,EAC1D,0EAA0E,EAC1E,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,kBAAkB,EAAE,EACvC,EAAE,UAAU,EAAE,oEAAoE,EAAE,CAAC,CAAC,CAAC;YACzF,SAAS;QACX,CAAC;QAED,+EAA+E;QAC/E,uCAAuC;QACvC,IAAI,QAAQ,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QAChC,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,IAAI,YAAY,GAAG,EAAE,CAAC;QACtB,OAAO,QAAQ,EAAE,CAAC;YAChB,IAAI,QAAQ,CAAC,OAAO,EAAE,KAAK,UAAU,CAAC,WAAW,EAAE,CAAC;gBAClD,OAAO,GAAG,IAAI,CAAC;gBACf,MAAM,MAAM,GAAG,QAA0C,CAAC;gBAC1D,MAAM,OAAO,GAAG,MAAM,CAAC,sBAAsB,EAAE,CAAC;gBAChD,IAAI,OAAO;oBAAE,YAAY,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;gBAC9C,MAAM;YACR,CAAC;YACD,QAAQ,GAAG,QAAQ,CAAC,SAAS,EAAE,CAAC;QAClC,CAAC;QAED,IAAI,OAAO,IAAI,YAAY,EAAE,CAAC;YAC5B,0EAA0E;YAC1E,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,MAAM,YAAY,KAAK,CAAC,CAAC;YACnD,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;gBACzB,wDAAwD;gBACxD,MAAM,WAAW,GAAG,IAAI,MAAM,CAAC,qBAAqB,YAAY,qBAAqB,CAAC,CAAC;gBACvF,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;oBACtC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,sBAAsB,EAAE,SAAS,EAAE,KAAK,EAC5D,qBAAqB,YAAY,mEAAmE,EACpG,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,kBAAkB,EAAE,EACvC,EAAE,UAAU,EAAE,aAAa,YAAY,oCAAoC,EAAE,CAAC,CAAC,CAAC;gBACpF,CAAC;YACH,CAAC;QACH,CAAC;QAED,kDAAkD;QAClD,IAAI,gBAAgB,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,gBAAgB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YACtG,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,sBAAsB,EAAE,SAAS,EAAE,KAAK,EAC5D,wDAAwD,EACxD,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,kBAAkB,EAAE,EACvC,EAAE,UAAU,EAAE,+DAA+D,EAAE,CAAC,CAAC,CAAC;QACtF,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,0EAA0E;AAC1E,6DAA6D;AAC7D,6EAA6E;AAC7E,qEAAqE;AACrE,kCAAkC;AAElC,kCAAkC;AAClC,MAAM,gBAAgB,GAAG,2GAA2G,CAAC;AACrI,2CAA2C;AAC3C,MAAM,uBAAuB,GAAG,qFAAqF,CAAC;AACtH,qCAAqC;AACrC,MAAM,yBAAyB,GAAG,wEAAwE,CAAC;AAE3G,SAAS,eAAe,CAAC,GAAgB;IACvC,MAAM,QAAQ,GAAoB,EAAE,CAAC;IAErC,sEAAsE;IACtE,KAAK,MAAM,QAAQ,IAAI,GAAG,CAAC,UAAU,CAAC,oBAAoB,CAAC,UAAU,CAAC,kBAAkB,CAAC,EAAE,CAAC;QAC1F,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,EAAE,CAAC;QAEhC,oDAAoD;QACpD,MAAM,YAAY,GAAG,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC;YACjD,qFAAqF,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnG,IAAI,CAAC,YAAY;YAAE,SAAS;QAE5B,iEAAiE;QACjE,IAAI,yBAAyB,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,SAAS;QAEnD,mDAAmD;QACnD,2DAA2D;QAC3D,IAAI,MAAM,GAAwC,QAAQ,CAAC,SAAS,EAAE,CAAC;QACvE,IAAI,eAAe,GAAG,KAAK,CAAC;QAC5B,OAAO,MAAM,EAAE,CAAC;YACd,IAAI,MAAM,CAAC,OAAO,EAAE,KAAK,UAAU,CAAC,mBAAmB;gBACnD,MAAM,CAAC,OAAO,EAAE,KAAK,UAAU,CAAC,aAAa;gBAC7C,MAAM,CAAC,OAAO,EAAE,KAAK,UAAU,CAAC,iBAAiB,EAAE,CAAC;gBACtD,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC,SAAS,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;gBACtD,IAAI,uBAAuB,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,gBAAgB,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;oBAClF,eAAe,GAAG,IAAI,CAAC;gBACzB,CAAC;gBACD,sBAAsB;gBACtB,IAAI,MAAM,CAAC,OAAO,EAAE,KAAK,UAAU,CAAC,mBAAmB,EAAE,CAAC;oBACxD,MAAM,MAAM,GAAI,MAAiD,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC;oBAClF,IAAI,uDAAuD,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;wBACzE,eAAe,GAAG,IAAI,CAAC;oBACzB,CAAC;gBACH,CAAC;gBACD,MAAM;YACR,CAAC;YACD,MAAM,GAAG,MAAM,CAAC,SAAS,EAAyC,CAAC;QACrE,CAAC;QAED,mEAAmE;QACnE,MAAM,YAAY,GAAG,QAAQ,CAAC,SAAS,EAAE,CAAC;QAC1C,IAAI,YAAY,EAAE,OAAO,EAAE,KAAK,UAAU,CAAC,mBAAmB,EAAE,CAAC;YAC/D,MAAM,OAAO,GAAI,YAAuD,CAAC,OAAO,EAAE,CAAC;YACnF,IAAI,4BAA4B,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC/C,eAAe,GAAG,IAAI,CAAC;YACzB,CAAC;QACH,CAAC;QACD,yDAAyD;QACzD,IAAI,YAAY,EAAE,OAAO,EAAE,KAAK,UAAU,CAAC,gBAAgB,EAAE,CAAC;YAC5D,MAAM,QAAQ,GAAI,YAAoD,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,CAAC;YAC3F,IAAI,4BAA4B,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAChD,eAAe,GAAG,IAAI,CAAC;YACzB,CAAC;QACH,CAAC;QAED,IAAI,CAAC,eAAe;YAAE,SAAS;QAE/B,gDAAgD;QAChD,MAAM,KAAK,GAAG,QAAQ,CAAC,gBAAgB,EAAE,CAAC;QAC1C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,QAAQ,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC,OAAO,EAAE,CAAC;YAChD,0DAA0D;YAC1D,IAAI,yBAAyB,CAAC,IAAI,CAAC,QAAQ,CAAC;gBAAE,SAAS;YACvD,oDAAoD;YACpD,IAAI,wCAAwC,CAAC,IAAI,CAAC,QAAQ,CAAC;gBAAE,SAAS;YAEtE,2CAA2C;YAC3C,MAAM,gBAAgB,GAAG,mBAAmB,CAAC,IAAI,CAAC,QAAQ,CAAC;gBACzD,uEAAuE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAEzF,IAAI,gBAAgB,EAAE,CAAC;gBACrB,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,kBAAkB,EAAE,SAAS,EAAE,KAAK,EACxD,eAAe,QAAQ,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,uEAAuE,EAC/G,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAC,kBAAkB,EAAE,EAC3C,EAAE,UAAU,EAAE,oFAAoF,EAAE,CAAC,CAAC,CAAC;gBACzG,MAAM,CAAC,2BAA2B;YACpC,CAAC;QACH,CAAC;IACH,CAAC;IAED,0EAA0E;IAC1E,KAAK,MAAM,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,oBAAoB,CAAC,UAAU,CAAC,gBAAgB,CAAC,EAAE,CAAC;QACnF,IAAI,GAAG,CAAC,gBAAgB,EAAE,CAAC,OAAO,EAAE,KAAK,UAAU,CAAC,SAAS;YAAE,SAAS;QAExE,MAAM,SAAS,GAAG,GAAG,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,CAAC;QAC3C,MAAM,QAAQ,GAAG,GAAG,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,CAAC;QACzC,MAAM,QAAQ,GAAG,GAAG,CAAC,OAAO,EAAE,CAAC;QAE/B,8CAA8C;QAC9C,MAAM,YAAY,GAAG,mBAAmB,CAAC,IAAI,CAAC,SAAS,CAAC;YACtD,2DAA2D,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC9E,IAAI,CAAC,YAAY;YAAE,SAAS;QAE5B,yCAAyC;QACzC,MAAM,cAAc,GAAG,mDAAmD,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC1F,IAAI,CAAC,cAAc;YAAE,SAAS;QAE9B,mBAAmB;QACnB,IAAI,yBAAyB,CAAC,IAAI,CAAC,QAAQ,CAAC;YAAE,SAAS;QAEvD,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,kBAAkB,EAAE,SAAS,EAAE,KAAK,EACxD,sFAAsF,EACtF,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,kBAAkB,EAAE,EACtC,EAAE,UAAU,EAAE,yEAAyE,EAAE,CAAC,CAAC,CAAC;IAChG,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,yEAAyE;AAEzE,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,QAAQ;IACR,sBAAsB;IACtB,kBAAkB;IAClB,mBAAmB;IACnB,eAAe;CAChB,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security v4 rules — LLM prompt injection attack surface.
|
|
3
|
+
*
|
|
4
|
+
* Covers 10 attack vectors beyond basic prompt injection (v3):
|
|
5
|
+
* S14: indirect-prompt-injection — DB-stored data flows to LLM prompt
|
|
6
|
+
* S15: llm-output-execution — LLM output passed to eval/exec
|
|
7
|
+
* S16: system-prompt-leakage — system prompt exposed in responses
|
|
8
|
+
* S17: rag-poisoning — retrieval results flow unsanitized to prompt
|
|
9
|
+
* S18: tool-calling-manipulation — user input controls tool names/schemas
|
|
10
|
+
* S19: encoding-bypass — decoded content enters prompt unsanitized
|
|
11
|
+
* S20: delimiter-injection — user input with delimiters in prompt context
|
|
12
|
+
* S21: unsanitized-history — chat history pushed without sanitization
|
|
13
|
+
* S22: json-output-manipulation — JSON.parse on LLM output without schema
|
|
14
|
+
* S23: missing-output-validation — LLM output used without validation
|
|
15
|
+
*
|
|
16
|
+
* All AST-based. Always active regardless of target.
|
|
17
|
+
* OWASP LLM01-LLM09
|
|
18
|
+
*/
|
|
19
|
+
import type { ReviewFinding, RuleContext } from '../types.js';
|
|
20
|
+
declare function indirectPromptInjection(ctx: RuleContext): ReviewFinding[];
|
|
21
|
+
export declare const securityV4Rules: (typeof indirectPromptInjection)[];
|
|
22
|
+
export {};
|