tryassay 0.33.1 → 0.33.2
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/cli.js +2 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/hunt.d.ts +2 -0
- package/dist/commands/hunt.js +58 -7
- package/dist/commands/hunt.js.map +1 -1
- package/dist/hunt/__tests__/finding-to-template.test.d.ts +1 -0
- package/dist/hunt/__tests__/finding-to-template.test.js +213 -0
- package/dist/hunt/__tests__/finding-to-template.test.js.map +1 -0
- package/dist/hunt/__tests__/parse-utils.test.js +28 -1
- package/dist/hunt/__tests__/parse-utils.test.js.map +1 -1
- package/dist/hunt/__tests__/taint-analysis.test.d.ts +1 -0
- package/dist/hunt/__tests__/taint-analysis.test.js +556 -0
- package/dist/hunt/__tests__/taint-analysis.test.js.map +1 -0
- package/dist/hunt/__tests__/templates.test.js +2 -2
- package/dist/hunt/__tests__/templates.test.js.map +1 -1
- package/dist/hunt/deep-dive.d.ts +2 -2
- package/dist/hunt/deep-dive.js +4 -4
- package/dist/hunt/deep-dive.js.map +1 -1
- package/dist/hunt/discovery.js +2 -2
- package/dist/hunt/discovery.js.map +1 -1
- package/dist/hunt/finding-to-template.d.ts +47 -0
- package/dist/hunt/finding-to-template.js +288 -0
- package/dist/hunt/finding-to-template.js.map +1 -0
- package/dist/hunt/orchestrator.d.ts +3 -0
- package/dist/hunt/orchestrator.js +20 -5
- package/dist/hunt/orchestrator.js.map +1 -1
- package/dist/hunt/taint-analysis.d.ts +49 -0
- package/dist/hunt/taint-analysis.js +429 -0
- package/dist/hunt/taint-analysis.js.map +1 -0
- package/dist/hunt/templates/csv-injection.d.ts +2 -0
- package/dist/hunt/templates/csv-injection.js +148 -0
- package/dist/hunt/templates/csv-injection.js.map +1 -0
- package/dist/hunt/templates/django-misconfig.d.ts +2 -0
- package/dist/hunt/templates/django-misconfig.js +172 -0
- package/dist/hunt/templates/django-misconfig.js.map +1 -0
- package/dist/hunt/templates/express-misconfig.d.ts +2 -0
- package/dist/hunt/templates/express-misconfig.js +156 -0
- package/dist/hunt/templates/express-misconfig.js.map +1 -0
- package/dist/hunt/templates/file-upload.d.ts +2 -0
- package/dist/hunt/templates/file-upload.js +131 -0
- package/dist/hunt/templates/file-upload.js.map +1 -0
- package/dist/hunt/templates/graphql-abuse.d.ts +2 -0
- package/dist/hunt/templates/graphql-abuse.js +161 -0
- package/dist/hunt/templates/graphql-abuse.js.map +1 -0
- package/dist/hunt/templates/hardcoded-credentials.d.ts +2 -0
- package/dist/hunt/templates/hardcoded-credentials.js +109 -0
- package/dist/hunt/templates/hardcoded-credentials.js.map +1 -0
- package/dist/hunt/templates/idor.d.ts +2 -0
- package/dist/hunt/templates/idor.js +102 -0
- package/dist/hunt/templates/idor.js.map +1 -0
- package/dist/hunt/templates/index.d.ts +2 -2
- package/dist/hunt/templates/index.js +38 -5
- package/dist/hunt/templates/index.js.map +1 -1
- package/dist/hunt/templates/insecure-deserialization.d.ts +2 -0
- package/dist/hunt/templates/insecure-deserialization.js +131 -0
- package/dist/hunt/templates/insecure-deserialization.js.map +1 -0
- package/dist/hunt/templates/mass-assignment.d.ts +2 -0
- package/dist/hunt/templates/mass-assignment.js +101 -0
- package/dist/hunt/templates/mass-assignment.js.map +1 -0
- package/dist/hunt/templates/nextjs-misconfig.d.ts +2 -0
- package/dist/hunt/templates/nextjs-misconfig.js +127 -0
- package/dist/hunt/templates/nextjs-misconfig.js.map +1 -0
- package/dist/hunt/templates/postmessage.d.ts +2 -0
- package/dist/hunt/templates/postmessage.js +180 -0
- package/dist/hunt/templates/postmessage.js.map +1 -0
- package/dist/hunt/templates/race-condition.d.ts +2 -0
- package/dist/hunt/templates/race-condition.js +138 -0
- package/dist/hunt/templates/race-condition.js.map +1 -0
- package/dist/hunt/templates/spring-misconfig.d.ts +2 -0
- package/dist/hunt/templates/spring-misconfig.js +177 -0
- package/dist/hunt/templates/spring-misconfig.js.map +1 -0
- package/dist/hunt/templates/xxe.d.ts +2 -0
- package/dist/hunt/templates/xxe.js +187 -0
- package/dist/hunt/templates/xxe.js.map +1 -0
- package/dist/hunt/triage.d.ts +2 -2
- package/dist/hunt/triage.js +4 -4
- package/dist/hunt/triage.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,556 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { buildImportGraph, findSources, findSinks, buildTaintContext, formatTaintContext, } from '../taint-analysis.js';
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// Helpers
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
const makeFile = (overrides) => ({
|
|
7
|
+
absolutePath: `/project/${overrides.relativePath}`,
|
|
8
|
+
content: '',
|
|
9
|
+
imports: [],
|
|
10
|
+
exports: [],
|
|
11
|
+
functions: [],
|
|
12
|
+
contentHash: 'hash',
|
|
13
|
+
isLowPriority: false,
|
|
14
|
+
...overrides,
|
|
15
|
+
});
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
// buildImportGraph
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
describe('buildImportGraph', () => {
|
|
20
|
+
it('resolves relative imports between discovered files', () => {
|
|
21
|
+
const fileA = makeFile({
|
|
22
|
+
relativePath: 'src/routes.ts',
|
|
23
|
+
content: `import { query } from './db.js';`,
|
|
24
|
+
imports: ['./db.js'],
|
|
25
|
+
});
|
|
26
|
+
const fileB = makeFile({
|
|
27
|
+
relativePath: 'src/db.ts',
|
|
28
|
+
content: `export function query(sql: string) {}`,
|
|
29
|
+
exports: ['query'],
|
|
30
|
+
});
|
|
31
|
+
const edges = buildImportGraph([fileA, fileB]);
|
|
32
|
+
expect(edges).toHaveLength(1);
|
|
33
|
+
expect(edges[0].from).toBe('src/routes.ts');
|
|
34
|
+
expect(edges[0].to).toBe('src/db.ts');
|
|
35
|
+
expect(edges[0].symbols).toContain('query');
|
|
36
|
+
});
|
|
37
|
+
it('resolves imports without extension', () => {
|
|
38
|
+
const fileA = makeFile({
|
|
39
|
+
relativePath: 'src/handler.ts',
|
|
40
|
+
content: `import db from './db';`,
|
|
41
|
+
imports: ['./db'],
|
|
42
|
+
});
|
|
43
|
+
const fileB = makeFile({
|
|
44
|
+
relativePath: 'src/db.ts',
|
|
45
|
+
content: `export default pool;`,
|
|
46
|
+
exports: [],
|
|
47
|
+
});
|
|
48
|
+
const edges = buildImportGraph([fileA, fileB]);
|
|
49
|
+
expect(edges).toHaveLength(1);
|
|
50
|
+
expect(edges[0].to).toBe('src/db.ts');
|
|
51
|
+
expect(edges[0].symbols).toContain('db');
|
|
52
|
+
});
|
|
53
|
+
it('resolves parent-directory imports', () => {
|
|
54
|
+
const fileA = makeFile({
|
|
55
|
+
relativePath: 'src/routes/api.ts',
|
|
56
|
+
content: `import { pool } from '../db.js';`,
|
|
57
|
+
imports: ['../db.js'],
|
|
58
|
+
});
|
|
59
|
+
const fileB = makeFile({
|
|
60
|
+
relativePath: 'src/db.ts',
|
|
61
|
+
content: `export const pool = {};`,
|
|
62
|
+
exports: ['pool'],
|
|
63
|
+
});
|
|
64
|
+
const edges = buildImportGraph([fileA, fileB]);
|
|
65
|
+
expect(edges).toHaveLength(1);
|
|
66
|
+
expect(edges[0].from).toBe('src/routes/api.ts');
|
|
67
|
+
expect(edges[0].to).toBe('src/db.ts');
|
|
68
|
+
});
|
|
69
|
+
it('returns empty array when no imports resolve', () => {
|
|
70
|
+
const fileA = makeFile({
|
|
71
|
+
relativePath: 'src/app.ts',
|
|
72
|
+
content: `import express from 'express';`,
|
|
73
|
+
imports: ['express'],
|
|
74
|
+
});
|
|
75
|
+
const edges = buildImportGraph([fileA]);
|
|
76
|
+
expect(edges).toHaveLength(0);
|
|
77
|
+
});
|
|
78
|
+
it('extracts named, default, and namespace import symbols', () => {
|
|
79
|
+
const fileA = makeFile({
|
|
80
|
+
relativePath: 'src/app.ts',
|
|
81
|
+
content: `import { foo, bar as baz } from './utils.js';\nimport * as helpers from './utils.js';`,
|
|
82
|
+
imports: ['./utils.js'],
|
|
83
|
+
});
|
|
84
|
+
const fileB = makeFile({
|
|
85
|
+
relativePath: 'src/utils.ts',
|
|
86
|
+
content: `export function foo() {} export function bar() {}`,
|
|
87
|
+
exports: ['foo', 'bar'],
|
|
88
|
+
});
|
|
89
|
+
const edges = buildImportGraph([fileA, fileB]);
|
|
90
|
+
expect(edges).toHaveLength(1);
|
|
91
|
+
expect(edges[0].symbols).toContain('foo');
|
|
92
|
+
expect(edges[0].symbols).toContain('baz'); // renamed
|
|
93
|
+
expect(edges[0].symbols).toContain('helpers'); // namespace
|
|
94
|
+
});
|
|
95
|
+
it('handles require() style imports', () => {
|
|
96
|
+
const fileA = makeFile({
|
|
97
|
+
relativePath: 'src/app.js',
|
|
98
|
+
content: `const { query } = require('./db');`,
|
|
99
|
+
imports: ['./db'],
|
|
100
|
+
});
|
|
101
|
+
const fileB = makeFile({
|
|
102
|
+
relativePath: 'src/db.js',
|
|
103
|
+
content: `module.exports = { query };`,
|
|
104
|
+
exports: [],
|
|
105
|
+
});
|
|
106
|
+
const edges = buildImportGraph([fileA, fileB]);
|
|
107
|
+
expect(edges).toHaveLength(1);
|
|
108
|
+
expect(edges[0].symbols).toContain('query');
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
// ---------------------------------------------------------------------------
|
|
112
|
+
// findSources
|
|
113
|
+
// ---------------------------------------------------------------------------
|
|
114
|
+
describe('findSources', () => {
|
|
115
|
+
it('detects Express request sources', () => {
|
|
116
|
+
const file = makeFile({
|
|
117
|
+
relativePath: 'src/routes.ts',
|
|
118
|
+
content: `
|
|
119
|
+
app.get('/user/:id', (req, res) => {
|
|
120
|
+
const id = req.params.id;
|
|
121
|
+
const q = req.query.search;
|
|
122
|
+
const data = req.body;
|
|
123
|
+
});`,
|
|
124
|
+
});
|
|
125
|
+
const sources = findSources([file]);
|
|
126
|
+
expect(sources.length).toBeGreaterThanOrEqual(3);
|
|
127
|
+
const types = sources.map(s => s.type);
|
|
128
|
+
expect(types).toContain('request-param');
|
|
129
|
+
expect(types).toContain('query-string');
|
|
130
|
+
expect(types).toContain('request-body');
|
|
131
|
+
});
|
|
132
|
+
it('detects Koa context sources', () => {
|
|
133
|
+
const file = makeFile({
|
|
134
|
+
relativePath: 'src/koa-handler.ts',
|
|
135
|
+
content: `
|
|
136
|
+
router.get('/item', async (ctx) => {
|
|
137
|
+
const q = ctx.query;
|
|
138
|
+
const params = ctx.params;
|
|
139
|
+
const body = ctx.request.body;
|
|
140
|
+
});`,
|
|
141
|
+
});
|
|
142
|
+
const sources = findSources([file]);
|
|
143
|
+
expect(sources.length).toBeGreaterThanOrEqual(3);
|
|
144
|
+
});
|
|
145
|
+
it('detects Lambda event sources', () => {
|
|
146
|
+
const file = makeFile({
|
|
147
|
+
relativePath: 'src/lambda.ts',
|
|
148
|
+
content: `
|
|
149
|
+
export const handler = async (event) => {
|
|
150
|
+
const body = event.body;
|
|
151
|
+
const params = event.queryStringParameters;
|
|
152
|
+
};`,
|
|
153
|
+
});
|
|
154
|
+
const sources = findSources([file]);
|
|
155
|
+
expect(sources.length).toBeGreaterThanOrEqual(2);
|
|
156
|
+
const types = sources.map(s => s.type);
|
|
157
|
+
expect(types).toContain('request-body');
|
|
158
|
+
expect(types).toContain('query-string');
|
|
159
|
+
});
|
|
160
|
+
it('detects React Router hooks', () => {
|
|
161
|
+
const file = makeFile({
|
|
162
|
+
relativePath: 'src/page.tsx',
|
|
163
|
+
content: `
|
|
164
|
+
const params = useParams();
|
|
165
|
+
const [searchParams] = useSearchParams();`,
|
|
166
|
+
});
|
|
167
|
+
const sources = findSources([file]);
|
|
168
|
+
expect(sources.length).toBeGreaterThanOrEqual(2);
|
|
169
|
+
const types = sources.map(s => s.type);
|
|
170
|
+
expect(types).toContain('url-param');
|
|
171
|
+
expect(types).toContain('query-string');
|
|
172
|
+
});
|
|
173
|
+
it('detects Python Flask sources', () => {
|
|
174
|
+
const file = makeFile({
|
|
175
|
+
relativePath: 'app/views.py',
|
|
176
|
+
content: `
|
|
177
|
+
def index():
|
|
178
|
+
name = request.args.get('name')
|
|
179
|
+
data = request.form['email']
|
|
180
|
+
payload = request.json`,
|
|
181
|
+
});
|
|
182
|
+
const sources = findSources([file]);
|
|
183
|
+
expect(sources.length).toBeGreaterThanOrEqual(3);
|
|
184
|
+
});
|
|
185
|
+
it('detects Java annotation sources', () => {
|
|
186
|
+
const file = makeFile({
|
|
187
|
+
relativePath: 'src/Controller.java',
|
|
188
|
+
content: `
|
|
189
|
+
@GetMapping("/user")
|
|
190
|
+
public User getUser(@RequestParam String name, @PathVariable Long id, @RequestBody UserDto dto) {
|
|
191
|
+
return userService.find(name, id);
|
|
192
|
+
}`,
|
|
193
|
+
});
|
|
194
|
+
const sources = findSources([file]);
|
|
195
|
+
expect(sources.length).toBeGreaterThanOrEqual(3);
|
|
196
|
+
const types = sources.map(s => s.type);
|
|
197
|
+
expect(types).toContain('request-param');
|
|
198
|
+
expect(types).toContain('url-param');
|
|
199
|
+
expect(types).toContain('request-body');
|
|
200
|
+
});
|
|
201
|
+
it('detects WebSocket message sources', () => {
|
|
202
|
+
const file = makeFile({
|
|
203
|
+
relativePath: 'src/ws.ts',
|
|
204
|
+
content: `ws.on('message', (data) => { process(data); });`,
|
|
205
|
+
});
|
|
206
|
+
const sources = findSources([file]);
|
|
207
|
+
expect(sources.length).toBeGreaterThanOrEqual(1);
|
|
208
|
+
expect(sources[0].type).toBe('websocket');
|
|
209
|
+
});
|
|
210
|
+
it('detects file upload sources', () => {
|
|
211
|
+
const file = makeFile({
|
|
212
|
+
relativePath: 'src/upload.ts',
|
|
213
|
+
content: `const upload = multer({ dest: 'uploads/' });
|
|
214
|
+
app.post('/upload', (req, res) => { const f = req.files; });`,
|
|
215
|
+
});
|
|
216
|
+
const sources = findSources([file]);
|
|
217
|
+
const types = sources.map(s => s.type);
|
|
218
|
+
expect(types).toContain('file-upload');
|
|
219
|
+
});
|
|
220
|
+
it('includes correct line numbers', () => {
|
|
221
|
+
const file = makeFile({
|
|
222
|
+
relativePath: 'src/routes.ts',
|
|
223
|
+
content: `// line 1
|
|
224
|
+
// line 2
|
|
225
|
+
const x = req.body;`,
|
|
226
|
+
});
|
|
227
|
+
const sources = findSources([file]);
|
|
228
|
+
expect(sources.length).toBeGreaterThanOrEqual(1);
|
|
229
|
+
expect(sources[0].line).toBe(3);
|
|
230
|
+
});
|
|
231
|
+
it('returns empty for files with no sources', () => {
|
|
232
|
+
const file = makeFile({
|
|
233
|
+
relativePath: 'src/utils.ts',
|
|
234
|
+
content: `export function add(a: number, b: number) { return a + b; }`,
|
|
235
|
+
});
|
|
236
|
+
const sources = findSources([file]);
|
|
237
|
+
expect(sources).toHaveLength(0);
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
// ---------------------------------------------------------------------------
|
|
241
|
+
// findSinks
|
|
242
|
+
// ---------------------------------------------------------------------------
|
|
243
|
+
describe('findSinks', () => {
|
|
244
|
+
it('detects SQL query sinks', () => {
|
|
245
|
+
const file = makeFile({
|
|
246
|
+
relativePath: 'src/db.ts',
|
|
247
|
+
content: `
|
|
248
|
+
const result = await db.query(\`SELECT * FROM users WHERE id = \${id}\`);
|
|
249
|
+
pool.query('INSERT INTO logs VALUES ($1)', [msg]);`,
|
|
250
|
+
});
|
|
251
|
+
const sinks = findSinks([file]);
|
|
252
|
+
expect(sinks.length).toBeGreaterThanOrEqual(2);
|
|
253
|
+
expect(sinks.every(s => s.type === 'sql-query')).toBe(true);
|
|
254
|
+
});
|
|
255
|
+
it('detects command execution sinks', () => {
|
|
256
|
+
const file = makeFile({
|
|
257
|
+
relativePath: 'src/exec.ts',
|
|
258
|
+
content: `
|
|
259
|
+
const { execSync } = require('child_process');
|
|
260
|
+
execSync('ls ' + userInput);
|
|
261
|
+
spawn('bash', ['-c', cmd]);`,
|
|
262
|
+
});
|
|
263
|
+
const sinks = findSinks([file]);
|
|
264
|
+
const types = sinks.map(s => s.type);
|
|
265
|
+
expect(types).toContain('command-exec');
|
|
266
|
+
});
|
|
267
|
+
it('detects file operation sinks', () => {
|
|
268
|
+
const file = makeFile({
|
|
269
|
+
relativePath: 'src/files.ts',
|
|
270
|
+
content: `
|
|
271
|
+
fs.readFile(userPath, 'utf8', cb);
|
|
272
|
+
fs.writeFileSync(outPath, data);`,
|
|
273
|
+
});
|
|
274
|
+
const sinks = findSinks([file]);
|
|
275
|
+
expect(sinks.length).toBeGreaterThanOrEqual(2);
|
|
276
|
+
expect(sinks.every(s => s.type === 'file-op')).toBe(true);
|
|
277
|
+
});
|
|
278
|
+
it('detects redirect sinks', () => {
|
|
279
|
+
const file = makeFile({
|
|
280
|
+
relativePath: 'src/redirect.ts',
|
|
281
|
+
content: `
|
|
282
|
+
res.redirect(req.query.next);
|
|
283
|
+
location.href = url;`,
|
|
284
|
+
});
|
|
285
|
+
const sinks = findSinks([file]);
|
|
286
|
+
const types = sinks.map(s => s.type);
|
|
287
|
+
expect(types).toContain('redirect');
|
|
288
|
+
});
|
|
289
|
+
it('detects XSS sinks', () => {
|
|
290
|
+
const file = makeFile({
|
|
291
|
+
relativePath: 'src/render.tsx',
|
|
292
|
+
content: `
|
|
293
|
+
el.innerHTML = userContent;
|
|
294
|
+
<div dangerouslySetInnerHTML={{ __html: html }} />`,
|
|
295
|
+
});
|
|
296
|
+
const sinks = findSinks([file]);
|
|
297
|
+
const types = sinks.map(s => s.type);
|
|
298
|
+
expect(types).toContain('inner-html');
|
|
299
|
+
});
|
|
300
|
+
it('detects eval sinks', () => {
|
|
301
|
+
const file = makeFile({
|
|
302
|
+
relativePath: 'src/eval.ts',
|
|
303
|
+
content: `
|
|
304
|
+
eval(userCode);
|
|
305
|
+
const fn = new Function('x', body);`,
|
|
306
|
+
});
|
|
307
|
+
const sinks = findSinks([file]);
|
|
308
|
+
const types = sinks.map(s => s.type);
|
|
309
|
+
expect(types).toContain('eval');
|
|
310
|
+
});
|
|
311
|
+
it('detects response write sinks', () => {
|
|
312
|
+
const file = makeFile({
|
|
313
|
+
relativePath: 'src/api.ts',
|
|
314
|
+
content: `res.send(data); res.json({ ok: true });`,
|
|
315
|
+
});
|
|
316
|
+
const sinks = findSinks([file]);
|
|
317
|
+
expect(sinks.length).toBeGreaterThanOrEqual(2);
|
|
318
|
+
expect(sinks.every(s => s.type === 'response-write')).toBe(true);
|
|
319
|
+
});
|
|
320
|
+
it('detects Python sinks', () => {
|
|
321
|
+
const file = makeFile({
|
|
322
|
+
relativePath: 'app/views.py',
|
|
323
|
+
content: `
|
|
324
|
+
cursor.execute(f"SELECT * FROM users WHERE name = '{name}'")
|
|
325
|
+
os.system('rm ' + path)
|
|
326
|
+
subprocess.run(['ls', user_dir])`,
|
|
327
|
+
});
|
|
328
|
+
const sinks = findSinks([file]);
|
|
329
|
+
expect(sinks.length).toBeGreaterThanOrEqual(3);
|
|
330
|
+
const types = sinks.map(s => s.type);
|
|
331
|
+
expect(types).toContain('sql-query');
|
|
332
|
+
expect(types).toContain('command-exec');
|
|
333
|
+
});
|
|
334
|
+
it('detects Java sinks', () => {
|
|
335
|
+
const file = makeFile({
|
|
336
|
+
relativePath: 'src/Dao.java',
|
|
337
|
+
content: `
|
|
338
|
+
statement.executeQuery("SELECT * FROM users WHERE id = " + id);
|
|
339
|
+
Runtime.getRuntime().exec(cmd);
|
|
340
|
+
response.sendRedirect(url);`,
|
|
341
|
+
});
|
|
342
|
+
const sinks = findSinks([file]);
|
|
343
|
+
expect(sinks.length).toBeGreaterThanOrEqual(3);
|
|
344
|
+
});
|
|
345
|
+
it('includes correct line numbers', () => {
|
|
346
|
+
const file = makeFile({
|
|
347
|
+
relativePath: 'src/db.ts',
|
|
348
|
+
content: `// line 1
|
|
349
|
+
// line 2
|
|
350
|
+
// line 3
|
|
351
|
+
db.query(sql);`,
|
|
352
|
+
});
|
|
353
|
+
const sinks = findSinks([file]);
|
|
354
|
+
expect(sinks.length).toBeGreaterThanOrEqual(1);
|
|
355
|
+
expect(sinks[0].line).toBe(4);
|
|
356
|
+
});
|
|
357
|
+
it('returns empty for files with no sinks', () => {
|
|
358
|
+
const file = makeFile({
|
|
359
|
+
relativePath: 'src/types.ts',
|
|
360
|
+
content: `export interface User { id: number; name: string; }`,
|
|
361
|
+
});
|
|
362
|
+
const sinks = findSinks([file]);
|
|
363
|
+
expect(sinks).toHaveLength(0);
|
|
364
|
+
});
|
|
365
|
+
});
|
|
366
|
+
// ---------------------------------------------------------------------------
|
|
367
|
+
// buildTaintContext
|
|
368
|
+
// ---------------------------------------------------------------------------
|
|
369
|
+
describe('buildTaintContext', () => {
|
|
370
|
+
it('builds context with local sources and sinks', () => {
|
|
371
|
+
const file = makeFile({
|
|
372
|
+
relativePath: 'src/handler.ts',
|
|
373
|
+
content: `
|
|
374
|
+
const id = req.params.id;
|
|
375
|
+
db.query(\`SELECT * FROM users WHERE id = \${id}\`);`,
|
|
376
|
+
});
|
|
377
|
+
const sources = findSources([file]);
|
|
378
|
+
const sinks = findSinks([file]);
|
|
379
|
+
const ctx = buildTaintContext(file, [file], [], sources, sinks);
|
|
380
|
+
expect(ctx.file).toBe('src/handler.ts');
|
|
381
|
+
expect(ctx.sources.length).toBeGreaterThan(0);
|
|
382
|
+
expect(ctx.sinks.length).toBeGreaterThan(0);
|
|
383
|
+
});
|
|
384
|
+
it('traces cross-file data flow: source in importer, sink in target', () => {
|
|
385
|
+
const routeFile = makeFile({
|
|
386
|
+
relativePath: 'src/routes.ts',
|
|
387
|
+
content: `
|
|
388
|
+
import { getUser } from './db.js';
|
|
389
|
+
app.get('/user/:id', (req, res) => {
|
|
390
|
+
const user = getUser(req.params.id);
|
|
391
|
+
res.json(user);
|
|
392
|
+
});`,
|
|
393
|
+
imports: ['./db.js'],
|
|
394
|
+
});
|
|
395
|
+
const dbFile = makeFile({
|
|
396
|
+
relativePath: 'src/db.ts',
|
|
397
|
+
content: `
|
|
398
|
+
export function getUser(id: string) {
|
|
399
|
+
return db.query(\`SELECT * FROM users WHERE id = \${id}\`);
|
|
400
|
+
}`,
|
|
401
|
+
exports: ['getUser'],
|
|
402
|
+
});
|
|
403
|
+
const allFiles = [routeFile, dbFile];
|
|
404
|
+
const importGraph = buildImportGraph(allFiles);
|
|
405
|
+
const sources = findSources(allFiles);
|
|
406
|
+
const sinks = findSinks(allFiles);
|
|
407
|
+
// Build context for the db file (which has the sink)
|
|
408
|
+
const ctx = buildTaintContext(dbFile, allFiles, importGraph, sources, sinks);
|
|
409
|
+
expect(ctx.sinks.length).toBeGreaterThan(0);
|
|
410
|
+
// The route file (which has sources) should appear as a related file
|
|
411
|
+
expect(ctx.relatedFiles.some(rf => rf.path === 'src/routes.ts')).toBe(true);
|
|
412
|
+
expect(ctx.dataFlowPaths.length).toBeGreaterThan(0);
|
|
413
|
+
});
|
|
414
|
+
it('traces cross-file data flow: source in target, sink in importer', () => {
|
|
415
|
+
const routeFile = makeFile({
|
|
416
|
+
relativePath: 'src/routes.ts',
|
|
417
|
+
content: `
|
|
418
|
+
import { sanitize } from './utils.js';
|
|
419
|
+
app.get('/search', (req, res) => {
|
|
420
|
+
const q = req.query.q;
|
|
421
|
+
res.send(sanitize(q));
|
|
422
|
+
});`,
|
|
423
|
+
imports: ['./utils.js'],
|
|
424
|
+
exports: [],
|
|
425
|
+
});
|
|
426
|
+
const utilFile = makeFile({
|
|
427
|
+
relativePath: 'src/utils.ts',
|
|
428
|
+
content: `export function sanitize(s: string) { return s; }`,
|
|
429
|
+
exports: ['sanitize'],
|
|
430
|
+
});
|
|
431
|
+
const allFiles = [routeFile, utilFile];
|
|
432
|
+
const importGraph = buildImportGraph(allFiles);
|
|
433
|
+
const sources = findSources(allFiles);
|
|
434
|
+
const sinks = findSinks(allFiles);
|
|
435
|
+
// Build context for the route file (which has both source and sink)
|
|
436
|
+
const ctx = buildTaintContext(routeFile, allFiles, importGraph, sources, sinks);
|
|
437
|
+
expect(ctx.sources.length).toBeGreaterThan(0);
|
|
438
|
+
expect(ctx.sinks.length).toBeGreaterThan(0);
|
|
439
|
+
});
|
|
440
|
+
it('returns empty related files when no cross-file connections', () => {
|
|
441
|
+
const file = makeFile({
|
|
442
|
+
relativePath: 'src/isolated.ts',
|
|
443
|
+
content: `db.query('SELECT 1');`,
|
|
444
|
+
});
|
|
445
|
+
const sinks = findSinks([file]);
|
|
446
|
+
const ctx = buildTaintContext(file, [file], [], [], sinks);
|
|
447
|
+
expect(ctx.relatedFiles).toHaveLength(0);
|
|
448
|
+
expect(ctx.dataFlowPaths).toHaveLength(0);
|
|
449
|
+
});
|
|
450
|
+
it('handles complex multi-file chains', () => {
|
|
451
|
+
const controllerFile = makeFile({
|
|
452
|
+
relativePath: 'src/controller.ts',
|
|
453
|
+
content: `
|
|
454
|
+
import { processOrder } from './service.js';
|
|
455
|
+
app.post('/order', (req, res) => {
|
|
456
|
+
const result = processOrder(req.body);
|
|
457
|
+
res.json(result);
|
|
458
|
+
});`,
|
|
459
|
+
imports: ['./service.js'],
|
|
460
|
+
});
|
|
461
|
+
const serviceFile = makeFile({
|
|
462
|
+
relativePath: 'src/service.ts',
|
|
463
|
+
content: `
|
|
464
|
+
import { saveOrder } from './repo.js';
|
|
465
|
+
export function processOrder(data: any) {
|
|
466
|
+
return saveOrder(data);
|
|
467
|
+
}`,
|
|
468
|
+
imports: ['./repo.js'],
|
|
469
|
+
exports: ['processOrder'],
|
|
470
|
+
});
|
|
471
|
+
const repoFile = makeFile({
|
|
472
|
+
relativePath: 'src/repo.ts',
|
|
473
|
+
content: `
|
|
474
|
+
export function saveOrder(data: any) {
|
|
475
|
+
return db.query(\`INSERT INTO orders VALUES (\${data.id})\`);
|
|
476
|
+
}`,
|
|
477
|
+
exports: ['saveOrder'],
|
|
478
|
+
});
|
|
479
|
+
const allFiles = [controllerFile, serviceFile, repoFile];
|
|
480
|
+
const importGraph = buildImportGraph(allFiles);
|
|
481
|
+
const sources = findSources(allFiles);
|
|
482
|
+
const sinks = findSinks(allFiles);
|
|
483
|
+
// The repo file has the sink; the controller file has the source
|
|
484
|
+
const ctx = buildTaintContext(repoFile, allFiles, importGraph, sources, sinks);
|
|
485
|
+
expect(ctx.sinks.length).toBeGreaterThan(0);
|
|
486
|
+
// Service file imports repo, and controller imports service
|
|
487
|
+
// The 2nd-degree traversal should find the controller via the service
|
|
488
|
+
expect(ctx.relatedFiles.length).toBeGreaterThan(0);
|
|
489
|
+
});
|
|
490
|
+
});
|
|
491
|
+
// ---------------------------------------------------------------------------
|
|
492
|
+
// formatTaintContext
|
|
493
|
+
// ---------------------------------------------------------------------------
|
|
494
|
+
describe('formatTaintContext', () => {
|
|
495
|
+
it('returns empty string when no cross-file context', () => {
|
|
496
|
+
const ctx = {
|
|
497
|
+
file: 'src/isolated.ts',
|
|
498
|
+
sources: [],
|
|
499
|
+
sinks: [{ file: 'src/isolated.ts', line: 5, type: 'sql-query', identifier: 'db.query(' }],
|
|
500
|
+
dataFlowPaths: [],
|
|
501
|
+
relatedFiles: [],
|
|
502
|
+
};
|
|
503
|
+
expect(formatTaintContext(ctx)).toBe('');
|
|
504
|
+
});
|
|
505
|
+
it('formats sources, sinks, data flows, and related files', () => {
|
|
506
|
+
const ctx = {
|
|
507
|
+
file: 'src/db.ts',
|
|
508
|
+
sources: [
|
|
509
|
+
{ file: 'src/db.ts', line: 3, type: 'request-param', identifier: 'req.params' },
|
|
510
|
+
],
|
|
511
|
+
sinks: [
|
|
512
|
+
{ file: 'src/db.ts', line: 10, type: 'sql-query', identifier: 'db.query(' },
|
|
513
|
+
],
|
|
514
|
+
dataFlowPaths: [
|
|
515
|
+
'request-param in src/routes.ts:5 → getUser → sql-query in src/db.ts:10',
|
|
516
|
+
],
|
|
517
|
+
relatedFiles: [
|
|
518
|
+
{
|
|
519
|
+
path: 'src/routes.ts',
|
|
520
|
+
relevance: 'calls getUser from target',
|
|
521
|
+
snippet: 'L5: const user = getUser(req.params.id);',
|
|
522
|
+
},
|
|
523
|
+
],
|
|
524
|
+
};
|
|
525
|
+
const output = formatTaintContext(ctx);
|
|
526
|
+
expect(output).toContain('SOURCES');
|
|
527
|
+
expect(output).toContain('request-param');
|
|
528
|
+
expect(output).toContain('SINKS');
|
|
529
|
+
expect(output).toContain('sql-query');
|
|
530
|
+
expect(output).toContain('DATA FLOW PATHS');
|
|
531
|
+
expect(output).toContain('request-param in src/routes.ts:5');
|
|
532
|
+
expect(output).toContain('RELATED FILES');
|
|
533
|
+
expect(output).toContain('src/routes.ts');
|
|
534
|
+
expect(output).toContain('getUser');
|
|
535
|
+
});
|
|
536
|
+
it('limits output to prevent prompt bloat', () => {
|
|
537
|
+
const manySources = Array.from({ length: 20 }, (_, i) => ({
|
|
538
|
+
file: 'src/big.ts',
|
|
539
|
+
line: i + 1,
|
|
540
|
+
type: 'request-param',
|
|
541
|
+
identifier: `req.params.p${i}`,
|
|
542
|
+
}));
|
|
543
|
+
const ctx = {
|
|
544
|
+
file: 'src/big.ts',
|
|
545
|
+
sources: manySources,
|
|
546
|
+
sinks: [],
|
|
547
|
+
dataFlowPaths: ['some flow'],
|
|
548
|
+
relatedFiles: [{ path: 'src/other.ts', relevance: 'test', snippet: 'code' }],
|
|
549
|
+
};
|
|
550
|
+
const output = formatTaintContext(ctx);
|
|
551
|
+
// Should only show at most 10 sources
|
|
552
|
+
const sourceMatches = output.match(/request-param/g) || [];
|
|
553
|
+
expect(sourceMatches.length).toBeLessThanOrEqual(10);
|
|
554
|
+
});
|
|
555
|
+
});
|
|
556
|
+
//# sourceMappingURL=taint-analysis.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"taint-analysis.test.js","sourceRoot":"","sources":["../../../src/hunt/__tests__/taint-analysis.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EACL,gBAAgB,EAChB,WAAW,EACX,SAAS,EACT,iBAAiB,EACjB,kBAAkB,GAInB,MAAM,sBAAsB,CAAC;AAG9B,8EAA8E;AAC9E,UAAU;AACV,8EAA8E;AAE9E,MAAM,QAAQ,GAAG,CAAC,SAA6D,EAAkB,EAAE,CAAC,CAAC;IACnG,YAAY,EAAE,YAAY,SAAS,CAAC,YAAY,EAAE;IAClD,OAAO,EAAE,EAAE;IACX,OAAO,EAAE,EAAE;IACX,OAAO,EAAE,EAAE;IACX,SAAS,EAAE,EAAE;IACb,WAAW,EAAE,MAAM;IACnB,aAAa,EAAE,KAAK;IACpB,GAAG,SAAS;CACb,CAAC,CAAC;AAEH,8EAA8E;AAC9E,mBAAmB;AACnB,8EAA8E;AAE9E,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;IAChC,EAAE,CAAC,oDAAoD,EAAE,GAAG,EAAE;QAC5D,MAAM,KAAK,GAAG,QAAQ,CAAC;YACrB,YAAY,EAAE,eAAe;YAC7B,OAAO,EAAE,kCAAkC;YAC3C,OAAO,EAAE,CAAC,SAAS,CAAC;SACrB,CAAC,CAAC;QACH,MAAM,KAAK,GAAG,QAAQ,CAAC;YACrB,YAAY,EAAE,WAAW;YACzB,OAAO,EAAE,uCAAuC;YAChD,OAAO,EAAE,CAAC,OAAO,CAAC;SACnB,CAAC,CAAC;QAEH,MAAM,KAAK,GAAG,gBAAgB,CAAC,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;QAC/C,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC9B,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QAC5C,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACtC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;QAC5C,MAAM,KAAK,GAAG,QAAQ,CAAC;YACrB,YAAY,EAAE,gBAAgB;YAC9B,OAAO,EAAE,wBAAwB;YACjC,OAAO,EAAE,CAAC,MAAM,CAAC;SAClB,CAAC,CAAC;QACH,MAAM,KAAK,GAAG,QAAQ,CAAC;YACrB,YAAY,EAAE,WAAW;YACzB,OAAO,EAAE,sBAAsB;YAC/B,OAAO,EAAE,EAAE;SACZ,CAAC,CAAC;QAEH,MAAM,KAAK,GAAG,gBAAgB,CAAC,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;QAC/C,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC9B,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACtC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;QAC3C,MAAM,KAAK,GAAG,QAAQ,CAAC;YACrB,YAAY,EAAE,mBAAmB;YACjC,OAAO,EAAE,kCAAkC;YAC3C,OAAO,EAAE,CAAC,UAAU,CAAC;SACtB,CAAC,CAAC;QACH,MAAM,KAAK,GAAG,QAAQ,CAAC;YACrB,YAAY,EAAE,WAAW;YACzB,OAAO,EAAE,yBAAyB;YAClC,OAAO,EAAE,CAAC,MAAM,CAAC;SAClB,CAAC,CAAC;QAEH,MAAM,KAAK,GAAG,gBAAgB,CAAC,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;QAC/C,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC9B,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;QAChD,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACrD,MAAM,KAAK,GAAG,QAAQ,CAAC;YACrB,YAAY,EAAE,YAAY;YAC1B,OAAO,EAAE,gCAAgC;YACzC,OAAO,EAAE,CAAC,SAAS,CAAC;SACrB,CAAC,CAAC;QAEH,MAAM,KAAK,GAAG,gBAAgB,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;QACxC,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAChC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uDAAuD,EAAE,GAAG,EAAE;QAC/D,MAAM,KAAK,GAAG,QAAQ,CAAC;YACrB,YAAY,EAAE,YAAY;YAC1B,OAAO,EAAE,uFAAuF;YAChG,OAAO,EAAE,CAAC,YAAY,CAAC;SACxB,CAAC,CAAC;QACH,MAAM,KAAK,GAAG,QAAQ,CAAC;YACrB,YAAY,EAAE,cAAc;YAC5B,OAAO,EAAE,mDAAmD;YAC5D,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC;SACxB,CAAC,CAAC;QAEH,MAAM,KAAK,GAAG,gBAAgB,CAAC,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;QAC/C,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC9B,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QAC1C,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU;QACrD,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,YAAY;IAC7D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;QACzC,MAAM,KAAK,GAAG,QAAQ,CAAC;YACrB,YAAY,EAAE,YAAY;YAC1B,OAAO,EAAE,oCAAoC;YAC7C,OAAO,EAAE,CAAC,MAAM,CAAC;SAClB,CAAC,CAAC;QACH,MAAM,KAAK,GAAG,QAAQ,CAAC;YACrB,YAAY,EAAE,WAAW;YACzB,OAAO,EAAE,6BAA6B;YACtC,OAAO,EAAE,EAAE;SACZ,CAAC,CAAC;QAEH,MAAM,KAAK,GAAG,gBAAgB,CAAC,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;QAC/C,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC9B,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,8EAA8E;AAC9E,cAAc;AACd,8EAA8E;AAE9E,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;IAC3B,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;QACzC,MAAM,IAAI,GAAG,QAAQ,CAAC;YACpB,YAAY,EAAE,eAAe;YAC7B,OAAO,EAAE;;;;;IAKX;SACC,CAAC,CAAC;QAEH,MAAM,OAAO,GAAG,WAAW,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QACpC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC;QAEjD,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACvC,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;QACzC,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;QACxC,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE;QACrC,MAAM,IAAI,GAAG,QAAQ,CAAC;YACpB,YAAY,EAAE,oBAAoB;YAClC,OAAO,EAAE;;;;;IAKX;SACC,CAAC,CAAC;QAEH,MAAM,OAAO,GAAG,WAAW,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QACpC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8BAA8B,EAAE,GAAG,EAAE;QACtC,MAAM,IAAI,GAAG,QAAQ,CAAC;YACpB,YAAY,EAAE,eAAe;YAC7B,OAAO,EAAE;;;;GAIZ;SACE,CAAC,CAAC;QAEH,MAAM,OAAO,GAAG,WAAW,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QACpC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC;QACjD,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACvC,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;QACxC,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;QACpC,MAAM,IAAI,GAAG,QAAQ,CAAC;YACpB,YAAY,EAAE,cAAc;YAC5B,OAAO,EAAE;;0CAE2B;SACrC,CAAC,CAAC;QAEH,MAAM,OAAO,GAAG,WAAW,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QACpC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC;QACjD,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACvC,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;QACrC,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8BAA8B,EAAE,GAAG,EAAE;QACtC,MAAM,IAAI,GAAG,QAAQ,CAAC;YACpB,YAAY,EAAE,cAAc;YAC5B,OAAO,EAAE;;;;2BAIY;SACtB,CAAC,CAAC;QAEH,MAAM,OAAO,GAAG,WAAW,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QACpC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;QACzC,MAAM,IAAI,GAAG,QAAQ,CAAC;YACpB,YAAY,EAAE,qBAAqB;YACnC,OAAO,EAAE;;;;EAIb;SACG,CAAC,CAAC;QAEH,MAAM,OAAO,GAAG,WAAW,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QACpC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC;QACjD,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACvC,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;QACzC,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;QACrC,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;QAC3C,MAAM,IAAI,GAAG,QAAQ,CAAC;YACpB,YAAY,EAAE,WAAW;YACzB,OAAO,EAAE,iDAAiD;SAC3D,CAAC,CAAC;QAEH,MAAM,OAAO,GAAG,WAAW,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QACpC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC;QACjD,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE;QACrC,MAAM,IAAI,GAAG,QAAQ,CAAC;YACpB,YAAY,EAAE,eAAe;YAC7B,OAAO,EAAE;6DAC8C;SACxD,CAAC,CAAC;QAEH,MAAM,OAAO,GAAG,WAAW,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QACpC,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACvC,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+BAA+B,EAAE,GAAG,EAAE;QACvC,MAAM,IAAI,GAAG,QAAQ,CAAC;YACpB,YAAY,EAAE,eAAe;YAC7B,OAAO,EAAE;;oBAEK;SACf,CAAC,CAAC;QAEH,MAAM,OAAO,GAAG,WAAW,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QACpC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC;QACjD,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;QACjD,MAAM,IAAI,GAAG,QAAQ,CAAC;YACpB,YAAY,EAAE,cAAc;YAC5B,OAAO,EAAE,6DAA6D;SACvE,CAAC,CAAC;QAEH,MAAM,OAAO,GAAG,WAAW,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QACpC,MAAM,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,8EAA8E;AAC9E,YAAY;AACZ,8EAA8E;AAE9E,QAAQ,CAAC,WAAW,EAAE,GAAG,EAAE;IACzB,EAAE,CAAC,yBAAyB,EAAE,GAAG,EAAE;QACjC,MAAM,IAAI,GAAG,QAAQ,CAAC;YACpB,YAAY,EAAE,WAAW;YACzB,OAAO,EAAE;;mDAEoC;SAC9C,CAAC,CAAC;QAEH,MAAM,KAAK,GAAG,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QAChC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC;QAC/C,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC9D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;QACzC,MAAM,IAAI,GAAG,QAAQ,CAAC;YACpB,YAAY,EAAE,aAAa;YAC3B,OAAO,EAAE;;;4BAGa;SACvB,CAAC,CAAC;QAEH,MAAM,KAAK,GAAG,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QAChC,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACrC,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8BAA8B,EAAE,GAAG,EAAE;QACtC,MAAM,IAAI,GAAG,QAAQ,CAAC;YACpB,YAAY,EAAE,cAAc;YAC5B,OAAO,EAAE;;iCAEkB;SAC5B,CAAC,CAAC;QAEH,MAAM,KAAK,GAAG,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QAChC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC;QAC/C,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wBAAwB,EAAE,GAAG,EAAE;QAChC,MAAM,IAAI,GAAG,QAAQ,CAAC;YACpB,YAAY,EAAE,iBAAiB;YAC/B,OAAO,EAAE;;qBAEM;SAChB,CAAC,CAAC;QAEH,MAAM,KAAK,GAAG,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QAChC,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACrC,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mBAAmB,EAAE,GAAG,EAAE;QAC3B,MAAM,IAAI,GAAG,QAAQ,CAAC;YACpB,YAAY,EAAE,gBAAgB;YAC9B,OAAO,EAAE;;mDAEoC;SAC9C,CAAC,CAAC;QAEH,MAAM,KAAK,GAAG,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QAChC,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACrC,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oBAAoB,EAAE,GAAG,EAAE;QAC5B,MAAM,IAAI,GAAG,QAAQ,CAAC;YACpB,YAAY,EAAE,aAAa;YAC3B,OAAO,EAAE;;oCAEqB;SAC/B,CAAC,CAAC;QAEH,MAAM,KAAK,GAAG,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QAChC,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACrC,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8BAA8B,EAAE,GAAG,EAAE;QACtC,MAAM,IAAI,GAAG,QAAQ,CAAC;YACpB,YAAY,EAAE,YAAY;YAC1B,OAAO,EAAE,yCAAyC;SACnD,CAAC,CAAC;QAEH,MAAM,KAAK,GAAG,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QAChC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC;QAC/C,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,gBAAgB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACnE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sBAAsB,EAAE,GAAG,EAAE;QAC9B,MAAM,IAAI,GAAG,QAAQ,CAAC;YACpB,YAAY,EAAE,cAAc;YAC5B,OAAO,EAAE;;;iCAGkB;SAC5B,CAAC,CAAC;QAEH,MAAM,KAAK,GAAG,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QAChC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC;QAC/C,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACrC,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;QACrC,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oBAAoB,EAAE,GAAG,EAAE;QAC5B,MAAM,IAAI,GAAG,QAAQ,CAAC;YACpB,YAAY,EAAE,cAAc;YAC5B,OAAO,EAAE;;;4BAGa;SACvB,CAAC,CAAC;QAEH,MAAM,KAAK,GAAG,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QAChC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+BAA+B,EAAE,GAAG,EAAE;QACvC,MAAM,IAAI,GAAG,QAAQ,CAAC;YACpB,YAAY,EAAE,WAAW;YACzB,OAAO,EAAE;;;eAGA;SACV,CAAC,CAAC;QAEH,MAAM,KAAK,GAAG,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QAChC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC;QAC/C,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAChC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAC/C,MAAM,IAAI,GAAG,QAAQ,CAAC;YACpB,YAAY,EAAE,cAAc;YAC5B,OAAO,EAAE,qDAAqD;SAC/D,CAAC,CAAC;QAEH,MAAM,KAAK,GAAG,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QAChC,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAChC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,8EAA8E;AAC9E,oBAAoB;AACpB,8EAA8E;AAE9E,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACrD,MAAM,IAAI,GAAG,QAAQ,CAAC;YACpB,YAAY,EAAE,gBAAgB;YAC9B,OAAO,EAAE;;qDAEsC;SAChD,CAAC,CAAC;QAEH,MAAM,OAAO,GAAG,WAAW,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QACpC,MAAM,KAAK,GAAG,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QAChC,MAAM,GAAG,GAAG,iBAAiB,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;QAEhE,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;QACxC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QAC9C,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iEAAiE,EAAE,GAAG,EAAE;QACzE,MAAM,SAAS,GAAG,QAAQ,CAAC;YACzB,YAAY,EAAE,eAAe;YAC7B,OAAO,EAAE;;;;;IAKX;YACE,OAAO,EAAE,CAAC,SAAS,CAAC;SACrB,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,QAAQ,CAAC;YACtB,YAAY,EAAE,WAAW;YACzB,OAAO,EAAE;;;EAGb;YACI,OAAO,EAAE,CAAC,SAAS,CAAC;SACrB,CAAC,CAAC;QAEH,MAAM,QAAQ,GAAG,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;QACrC,MAAM,WAAW,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC;QAC/C,MAAM,OAAO,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;QACtC,MAAM,KAAK,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;QAElC,qDAAqD;QACrD,MAAM,GAAG,GAAG,iBAAiB,CAAC,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;QAE7E,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QAC5C,qEAAqE;QACrE,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,KAAK,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC5E,MAAM,CAAC,GAAG,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iEAAiE,EAAE,GAAG,EAAE;QACzE,MAAM,SAAS,GAAG,QAAQ,CAAC;YACzB,YAAY,EAAE,eAAe;YAC7B,OAAO,EAAE;;;;;IAKX;YACE,OAAO,EAAE,CAAC,YAAY,CAAC;YACvB,OAAO,EAAE,EAAE;SACZ,CAAC,CAAC;QAEH,MAAM,QAAQ,GAAG,QAAQ,CAAC;YACxB,YAAY,EAAE,cAAc;YAC5B,OAAO,EAAE,mDAAmD;YAC5D,OAAO,EAAE,CAAC,UAAU,CAAC;SACtB,CAAC,CAAC;QAEH,MAAM,QAAQ,GAAG,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;QACvC,MAAM,WAAW,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC;QAC/C,MAAM,OAAO,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;QACtC,MAAM,KAAK,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;QAElC,oEAAoE;QACpE,MAAM,GAAG,GAAG,iBAAiB,CAAC,SAAS,EAAE,QAAQ,EAAE,WAAW,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;QAEhF,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QAC9C,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4DAA4D,EAAE,GAAG,EAAE;QACpE,MAAM,IAAI,GAAG,QAAQ,CAAC;YACpB,YAAY,EAAE,iBAAiB;YAC/B,OAAO,EAAE,uBAAuB;SACjC,CAAC,CAAC;QAEH,MAAM,KAAK,GAAG,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QAChC,MAAM,GAAG,GAAG,iBAAiB,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,CAAC;QAE3D,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACzC,MAAM,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;QAC3C,MAAM,cAAc,GAAG,QAAQ,CAAC;YAC9B,YAAY,EAAE,mBAAmB;YACjC,OAAO,EAAE;;;;;IAKX;YACE,OAAO,EAAE,CAAC,cAAc,CAAC;SAC1B,CAAC,CAAC;QAEH,MAAM,WAAW,GAAG,QAAQ,CAAC;YAC3B,YAAY,EAAE,gBAAgB;YAC9B,OAAO,EAAE;;;;EAIb;YACI,OAAO,EAAE,CAAC,WAAW,CAAC;YACtB,OAAO,EAAE,CAAC,cAAc,CAAC;SAC1B,CAAC,CAAC;QAEH,MAAM,QAAQ,GAAG,QAAQ,CAAC;YACxB,YAAY,EAAE,aAAa;YAC3B,OAAO,EAAE;;;EAGb;YACI,OAAO,EAAE,CAAC,WAAW,CAAC;SACvB,CAAC,CAAC;QAEH,MAAM,QAAQ,GAAG,CAAC,cAAc,EAAE,WAAW,EAAE,QAAQ,CAAC,CAAC;QACzD,MAAM,WAAW,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC;QAC/C,MAAM,OAAO,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;QACtC,MAAM,KAAK,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;QAElC,iEAAiE;QACjE,MAAM,GAAG,GAAG,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,EAAE,WAAW,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;QAE/E,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QAC5C,4DAA4D;QAC5D,sEAAsE;QACtE,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,8EAA8E;AAC9E,qBAAqB;AACrB,8EAA8E;AAE9E,QAAQ,CAAC,oBAAoB,EAAE,GAAG,EAAE;IAClC,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;QACzD,MAAM,GAAG,GAAG;YACV,IAAI,EAAE,iBAAiB;YACvB,OAAO,EAAE,EAAE;YACX,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,WAAoB,EAAE,UAAU,EAAE,WAAW,EAAE,CAAC;YAClG,aAAa,EAAE,EAAE;YACjB,YAAY,EAAE,EAAE;SACjB,CAAC;QAEF,MAAM,CAAC,kBAAkB,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uDAAuD,EAAE,GAAG,EAAE;QAC/D,MAAM,GAAG,GAAyC;YAChD,IAAI,EAAE,WAAW;YACjB,OAAO,EAAE;gBACP,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,UAAU,EAAE,YAAY,EAAE;aAChF;YACD,KAAK,EAAE;gBACL,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE;aAC5E;YACD,aAAa,EAAE;gBACb,wEAAwE;aACzE;YACD,YAAY,EAAE;gBACZ;oBACE,IAAI,EAAE,eAAe;oBACrB,SAAS,EAAE,2BAA2B;oBACtC,OAAO,EAAE,0CAA0C;iBACpD;aACF;SACF,CAAC;QAEF,MAAM,MAAM,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAC;QAEvC,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QACpC,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;QAC1C,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QAClC,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;QACtC,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;QAC5C,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,kCAAkC,CAAC,CAAC;QAC7D,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;QAC1C,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;QAC1C,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAC/C,MAAM,WAAW,GAAkB,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;YACvE,IAAI,EAAE,YAAY;YAClB,IAAI,EAAE,CAAC,GAAG,CAAC;YACX,IAAI,EAAE,eAAwB;YAC9B,UAAU,EAAE,eAAe,CAAC,EAAE;SAC/B,CAAC,CAAC,CAAC;QAEJ,MAAM,GAAG,GAAG;YACV,IAAI,EAAE,YAAY;YAClB,OAAO,EAAE,WAAW;YACpB,KAAK,EAAE,EAAE;YACT,aAAa,EAAE,CAAC,WAAW,CAAC;YAC5B,YAAY,EAAE,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;SAC7E,CAAC;QAEF,MAAM,MAAM,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAC;QACvC,sCAAsC;QACtC,MAAM,aAAa,GAAG,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,IAAI,EAAE,CAAC;QAC3D,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,mBAAmB,CAAC,EAAE,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -21,8 +21,8 @@ describe('Template registry', () => {
|
|
|
21
21
|
it('returns undefined for unknown id', () => {
|
|
22
22
|
expect(getTemplateById('nonexistent')).toBeUndefined();
|
|
23
23
|
});
|
|
24
|
-
it('has
|
|
25
|
-
expect(getAllTemplates()).toHaveLength(
|
|
24
|
+
it('has 24 built-in templates', () => {
|
|
25
|
+
expect(getAllTemplates()).toHaveLength(24);
|
|
26
26
|
});
|
|
27
27
|
it('each template has unique id', () => {
|
|
28
28
|
const ids = getAllTemplates().map(t => t.id);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"templates.test.js","sourceRoot":"","sources":["../../../src/hunt/__tests__/templates.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAEzE,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,EAAE,CAAC,qBAAqB,EAAE,GAAG,EAAE;QAC7B,MAAM,SAAS,GAAG,eAAe,EAAE,CAAC;QACpC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC;QACnD,KAAK,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;YAC1B,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,UAAU,EAAE,CAAC;YAC1B,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;YACnC,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;YACjD,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC;YAClD,MAAM,CAAC,CAAC,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC;YACpD,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QACpD,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sBAAsB,EAAE,GAAG,EAAE;QAC9B,MAAM,CAAC,GAAG,eAAe,CAAC,aAAa,CAAC,CAAC;QACzC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;QACxB,MAAM,CAAC,CAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACjC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;QAC1C,MAAM,CAAC,eAAe,CAAC,aAAa,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IACzD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"templates.test.js","sourceRoot":"","sources":["../../../src/hunt/__tests__/templates.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAEzE,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,EAAE,CAAC,qBAAqB,EAAE,GAAG,EAAE;QAC7B,MAAM,SAAS,GAAG,eAAe,EAAE,CAAC;QACpC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC;QACnD,KAAK,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;YAC1B,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,UAAU,EAAE,CAAC;YAC1B,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;YACnC,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;YACjD,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC;YAClD,MAAM,CAAC,CAAC,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC;YACpD,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QACpD,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sBAAsB,EAAE,GAAG,EAAE;QAC9B,MAAM,CAAC,GAAG,eAAe,CAAC,aAAa,CAAC,CAAC;QACzC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;QACxB,MAAM,CAAC,CAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACjC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;QAC1C,MAAM,CAAC,eAAe,CAAC,aAAa,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IACzD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2BAA2B,EAAE,GAAG,EAAE;QACnC,MAAM,CAAC,eAAe,EAAE,CAAC,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE;QACrC,MAAM,GAAG,GAAG,eAAe,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAC7C,MAAM,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
package/dist/hunt/deep-dive.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { VulnerabilityTemplate, HuntHypothesis, HuntFinding } from '../types.js';
|
|
2
2
|
import type { DiscoveredFile } from './discovery.js';
|
|
3
3
|
import type { LLMProvider } from '../runtime/types.js';
|
|
4
|
-
export declare function buildDeepDivePrompt(template: VulnerabilityTemplate, hypothesis: HuntHypothesis, file: DiscoveredFile): {
|
|
4
|
+
export declare function buildDeepDivePrompt(template: VulnerabilityTemplate, hypothesis: HuntHypothesis, file: DiscoveredFile, taintContext?: string): {
|
|
5
5
|
systemPrompt: string;
|
|
6
6
|
userPrompt: string;
|
|
7
7
|
};
|
|
8
|
-
export declare function runDeepDive(template: VulnerabilityTemplate, hypothesis: HuntHypothesis, file: DiscoveredFile, provider: LLMProvider): Promise<HuntFinding | null>;
|
|
8
|
+
export declare function runDeepDive(template: VulnerabilityTemplate, hypothesis: HuntHypothesis, file: DiscoveredFile, provider: LLMProvider, taintContext?: string): Promise<HuntFinding | null>;
|
package/dist/hunt/deep-dive.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { safeParseJSON } from './parse-utils.js';
|
|
2
|
-
export function buildDeepDivePrompt(template, hypothesis, file) {
|
|
2
|
+
export function buildDeepDivePrompt(template, hypothesis, file, taintContext) {
|
|
3
3
|
const systemPrompt = `You are an expert security researcher writing a bug bounty report.
|
|
4
4
|
|
|
5
5
|
VULNERABILITY CLASS: ${template.name} (${template.cwe})
|
|
@@ -41,11 +41,11 @@ ${context ? `CONTEXT:\n${context}\n` : ''}
|
|
|
41
41
|
${file.content}
|
|
42
42
|
</analyzed-code>
|
|
43
43
|
|
|
44
|
-
Verify whether this vulnerability is real and exploitable. If real, write a complete bug bounty report. If false positive, explain why
|
|
44
|
+
Verify whether this vulnerability is real and exploitable. If real, write a complete bug bounty report. If false positive, explain why.${taintContext ? `\n\nCROSS-FILE DATA FLOW:\n${taintContext}` : ''}`;
|
|
45
45
|
return { systemPrompt, userPrompt };
|
|
46
46
|
}
|
|
47
|
-
export async function runDeepDive(template, hypothesis, file, provider) {
|
|
48
|
-
const { systemPrompt, userPrompt } = buildDeepDivePrompt(template, hypothesis, file);
|
|
47
|
+
export async function runDeepDive(template, hypothesis, file, provider, taintContext) {
|
|
48
|
+
const { systemPrompt, userPrompt } = buildDeepDivePrompt(template, hypothesis, file, taintContext);
|
|
49
49
|
try {
|
|
50
50
|
const response = await provider.complete({
|
|
51
51
|
model: 'claude-sonnet-4-6',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deep-dive.js","sourceRoot":"","sources":["../../src/hunt/deep-dive.ts"],"names":[],"mappings":"AAGA,OAAO,EAAmB,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAElE,MAAM,UAAU,mBAAmB,CACjC,QAA+B,EAC/B,UAA0B,EAC1B,IAAoB;
|
|
1
|
+
{"version":3,"file":"deep-dive.js","sourceRoot":"","sources":["../../src/hunt/deep-dive.ts"],"names":[],"mappings":"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"}
|