tryassay 0.33.0 → 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/parse-utils.d.ts +6 -0
- package/dist/hunt/parse-utils.js +28 -1
- package/dist/hunt/parse-utils.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
package/dist/hunt/discovery.js
CHANGED
|
@@ -16,7 +16,7 @@ const LOW_PRIORITY_DIRS = [
|
|
|
16
16
|
'node_modules/', '.git/', 'dist/', 'build/',
|
|
17
17
|
];
|
|
18
18
|
export function discoverSecurityFiles(targetPath, options = {}) {
|
|
19
|
-
const { maxLines =
|
|
19
|
+
const { maxLines = 2000, maxFiles = 500 } = options;
|
|
20
20
|
const allFiles = walkDirectory(targetPath);
|
|
21
21
|
const discovered = [];
|
|
22
22
|
for (const absPath of allFiles) {
|
|
@@ -28,7 +28,7 @@ export function discoverSecurityFiles(targetPath, options = {}) {
|
|
|
28
28
|
const matchesKeyword = SECURITY_KEYWORDS.some(kw => lowerPath.includes(kw));
|
|
29
29
|
if (!matchesKeyword) {
|
|
30
30
|
try {
|
|
31
|
-
const preview = readFileSync(absPath, 'utf-8').split('\n').slice(0,
|
|
31
|
+
const preview = readFileSync(absPath, 'utf-8').split('\n').slice(0, 100).join('\n').toLowerCase();
|
|
32
32
|
const contentMatch = SECURITY_KEYWORDS.some(kw => preview.includes(kw));
|
|
33
33
|
if (!contentMatch)
|
|
34
34
|
continue;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"discovery.js","sourceRoot":"","sources":["../../src/hunt/discovery.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAY,MAAM,IAAI,CAAC;AACzD,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAkBpC,MAAM,iBAAiB,GAAG;IACxB,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS;IAC1E,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,YAAY;IACjE,gBAAgB,EAAE,YAAY,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,UAAU;IACtE,QAAQ,EAAE,QAAQ,EAAE,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,OAAO;CAClE,CAAC;AAEF,MAAM,oBAAoB,GAAG,IAAI,GAAG,CAAC;IACnC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM;CAC1E,CAAC,CAAC;AAEH,MAAM,iBAAiB,GAAG;IACxB,WAAW,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,YAAY;IACvE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW;IACzE,eAAe,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ;CAC5C,CAAC;AAEF,MAAM,UAAU,qBAAqB,CACnC,UAAkB,EAClB,UAA4B,EAAE;IAE9B,MAAM,EAAE,QAAQ,GAAG,
|
|
1
|
+
{"version":3,"file":"discovery.js","sourceRoot":"","sources":["../../src/hunt/discovery.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finding-to-Template Pipeline
|
|
3
|
+
*
|
|
4
|
+
* Converts confirmed HuntFindings into VulnerabilityTemplates that can
|
|
5
|
+
* augment future hunt scans. This closes the learning loop:
|
|
6
|
+
* hunt finds bugs -> bounty confirms them -> confirmed findings become new templates
|
|
7
|
+
*
|
|
8
|
+
* Learned templates are stored as JSON on disk (project-local or global)
|
|
9
|
+
* and merged into the template registry at load time.
|
|
10
|
+
*/
|
|
11
|
+
import type { HuntFinding, VulnerabilityTemplate } from '../types.js';
|
|
12
|
+
export interface LearnedTemplate {
|
|
13
|
+
id: string;
|
|
14
|
+
source: 'bounty-finding';
|
|
15
|
+
sourceFinding: string;
|
|
16
|
+
createdAt: string;
|
|
17
|
+
template: VulnerabilityTemplate;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Extract file-matching keywords from a finding's file path, evidence,
|
|
21
|
+
* and attack scenario. These become the `filePatterns` on the learned template.
|
|
22
|
+
*/
|
|
23
|
+
export declare function extractFilePatterns(finding: HuntFinding): string[];
|
|
24
|
+
/**
|
|
25
|
+
* Extract bypass techniques mentioned in the attack scenario.
|
|
26
|
+
*/
|
|
27
|
+
export declare function extractBypasses(finding: HuntFinding): string[];
|
|
28
|
+
/**
|
|
29
|
+
* Convert a confirmed HuntFinding into a LearnedTemplate.
|
|
30
|
+
* The template captures the vulnerability pattern so future scans
|
|
31
|
+
* can detect similar issues without re-discovering from scratch.
|
|
32
|
+
*/
|
|
33
|
+
export declare function findingToTemplate(finding: HuntFinding): LearnedTemplate;
|
|
34
|
+
/**
|
|
35
|
+
* Save a learned template to disk.
|
|
36
|
+
* Deduplicates by template ID — if the same ID exists, it's replaced.
|
|
37
|
+
*/
|
|
38
|
+
export declare function saveLearnedTemplate(template: LearnedTemplate, options?: {
|
|
39
|
+
global?: boolean;
|
|
40
|
+
projectRoot?: string;
|
|
41
|
+
}): void;
|
|
42
|
+
/**
|
|
43
|
+
* Load all learned templates from disk.
|
|
44
|
+
* Merges global + project-local, deduplicating by ID
|
|
45
|
+
* (project-local wins over global).
|
|
46
|
+
*/
|
|
47
|
+
export declare function loadLearnedTemplates(projectRoot?: string): VulnerabilityTemplate[];
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finding-to-Template Pipeline
|
|
3
|
+
*
|
|
4
|
+
* Converts confirmed HuntFindings into VulnerabilityTemplates that can
|
|
5
|
+
* augment future hunt scans. This closes the learning loop:
|
|
6
|
+
* hunt finds bugs -> bounty confirms them -> confirmed findings become new templates
|
|
7
|
+
*
|
|
8
|
+
* Learned templates are stored as JSON on disk (project-local or global)
|
|
9
|
+
* and merged into the template registry at load time.
|
|
10
|
+
*/
|
|
11
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'node:fs';
|
|
12
|
+
import { join, basename, dirname, extname } from 'node:path';
|
|
13
|
+
import { homedir } from 'node:os';
|
|
14
|
+
// ── Pattern extraction helpers ─────────────────────────────────
|
|
15
|
+
/** Technology keywords to detect from attack scenarios and evidence. */
|
|
16
|
+
const TECH_KEYWORDS = [
|
|
17
|
+
'postmessage', 'csrf', 'oauth', 'jwt', 'cors', 'cookie', 'session',
|
|
18
|
+
'redirect', 'iframe', 'sandbox', 'csp', 'xss', 'sqli', 'ssrf',
|
|
19
|
+
'graphql', 'websocket', 'sse', 'fetch', 'xmlhttprequest', 'ajax',
|
|
20
|
+
'formdata', 'multipart', 'json', 'xml', 'yaml', 'toml',
|
|
21
|
+
'express', 'nextjs', 'next', 'react', 'vue', 'angular', 'svelte',
|
|
22
|
+
'middleware', 'proxy', 'nginx', 'apache', 'cloudflare', 'vercel',
|
|
23
|
+
'supabase', 'firebase', 'auth0', 'passport', 'bcrypt', 'argon2',
|
|
24
|
+
'hmac', 'sha256', 'sha1', 'md5', 'aes', 'rsa', 'ecdsa',
|
|
25
|
+
'localStorage', 'sessionStorage', 'indexeddb',
|
|
26
|
+
'header', 'origin', 'referer', 'host', 'authorization', 'bearer',
|
|
27
|
+
'token', 'nonce', 'state', 'pkce', 'saml', 'oidc',
|
|
28
|
+
'wildcard', 'regex', 'pattern', 'validation', 'sanitize', 'escape',
|
|
29
|
+
'prototype', 'constructor', '__proto__', 'pollution',
|
|
30
|
+
'path', 'traversal', 'directory', 'file', 'upload', 'download',
|
|
31
|
+
'injection', 'command', 'exec', 'spawn', 'eval', 'template',
|
|
32
|
+
'deserialization', 'pickle', 'marshal', 'serialize',
|
|
33
|
+
];
|
|
34
|
+
/**
|
|
35
|
+
* Extract file-matching keywords from a finding's file path, evidence,
|
|
36
|
+
* and attack scenario. These become the `filePatterns` on the learned template.
|
|
37
|
+
*/
|
|
38
|
+
export function extractFilePatterns(finding) {
|
|
39
|
+
const patterns = new Set();
|
|
40
|
+
// 1. Path-derived keywords
|
|
41
|
+
const filePath = finding.file.toLowerCase();
|
|
42
|
+
const base = basename(filePath, extname(filePath));
|
|
43
|
+
const dir = dirname(filePath);
|
|
44
|
+
// Split path segments into keywords (skip short/generic ones)
|
|
45
|
+
const pathSegments = [...dir.split('/'), base]
|
|
46
|
+
.map(s => s.replace(/[^a-z0-9-_]/gi, ''))
|
|
47
|
+
.filter(s => s.length >= 3 && !['src', 'lib', 'app', 'index', 'dist', 'node_modules'].includes(s));
|
|
48
|
+
for (const seg of pathSegments) {
|
|
49
|
+
patterns.add(seg.toLowerCase());
|
|
50
|
+
}
|
|
51
|
+
// 2. Technology keywords from text fields
|
|
52
|
+
const combinedText = [
|
|
53
|
+
finding.attackScenario,
|
|
54
|
+
finding.evidence,
|
|
55
|
+
finding.title,
|
|
56
|
+
finding.recommendation,
|
|
57
|
+
].join(' ').toLowerCase();
|
|
58
|
+
for (const kw of TECH_KEYWORDS) {
|
|
59
|
+
if (combinedText.includes(kw.toLowerCase())) {
|
|
60
|
+
patterns.add(kw.toLowerCase());
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// 3. Import/function names from evidence
|
|
64
|
+
// Matches: import { x } from 'module', import 'module', require('module')
|
|
65
|
+
const importMatches = finding.evidence.matchAll(/(?:from\s+['"]([^'"]+)['"]|import\s+['"]([^'"]+)['"]|require\s*\(\s*['"]([^'"]+)['"]\s*\))/g);
|
|
66
|
+
for (const m of importMatches) {
|
|
67
|
+
const modulePath = m[1] ?? m[2] ?? m[3];
|
|
68
|
+
if (!modulePath)
|
|
69
|
+
continue;
|
|
70
|
+
const moduleName = basename(modulePath).replace(/\.[^.]+$/, '');
|
|
71
|
+
if (moduleName.length >= 3) {
|
|
72
|
+
patterns.add(moduleName.toLowerCase());
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const functionMatches = finding.evidence.matchAll(/(?:function|const|let|var)\s+(\w{3,})/g);
|
|
76
|
+
for (const m of functionMatches) {
|
|
77
|
+
patterns.add(m[1].toLowerCase());
|
|
78
|
+
}
|
|
79
|
+
// Ensure at least 2 patterns (required for matching threshold)
|
|
80
|
+
if (patterns.size < 2) {
|
|
81
|
+
patterns.add(finding.cwe.toLowerCase());
|
|
82
|
+
}
|
|
83
|
+
if (patterns.size < 2) {
|
|
84
|
+
patterns.add(finding.templateId.toLowerCase());
|
|
85
|
+
}
|
|
86
|
+
return [...patterns];
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Extract bypass techniques mentioned in the attack scenario.
|
|
90
|
+
*/
|
|
91
|
+
export function extractBypasses(finding) {
|
|
92
|
+
const bypasses = [];
|
|
93
|
+
const text = finding.attackScenario + '\n' + finding.reproductionSteps;
|
|
94
|
+
// Look for numbered steps or bullet points that describe bypass
|
|
95
|
+
const lines = text.split('\n');
|
|
96
|
+
for (const line of lines) {
|
|
97
|
+
const trimmed = line.trim();
|
|
98
|
+
// Lines starting with "- " or numbered that mention bypass/exploit/attack/craft
|
|
99
|
+
if (trimmed.length > 10 &&
|
|
100
|
+
/^(?:\d+[\.\)]\s*|-\s*|\*\s*)/.test(trimmed) &&
|
|
101
|
+
/bypass|exploit|attack|craft|inject|spoof|forge|tamper|override|manipulat/i.test(trimmed)) {
|
|
102
|
+
bypasses.push(trimmed.replace(/^(?:\d+[\.\)]\s*|-\s*|\*\s*)/, '').trim());
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// If no structured bypasses found, use the first sentence of attackScenario
|
|
106
|
+
if (bypasses.length === 0 && finding.attackScenario.length > 0) {
|
|
107
|
+
const firstSentence = finding.attackScenario.split(/[.!?\n]/)[0].trim();
|
|
108
|
+
if (firstSentence.length > 10) {
|
|
109
|
+
bypasses.push(firstSentence);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return bypasses;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Extract spec references from the recommendation text.
|
|
116
|
+
*/
|
|
117
|
+
function extractSpecReferences(finding) {
|
|
118
|
+
const refs = [];
|
|
119
|
+
const text = finding.recommendation + '\n' + finding.attackScenario;
|
|
120
|
+
// Match RFC references
|
|
121
|
+
const rfcMatches = text.matchAll(/RFC\s*\d+(?:\s*(?:Section|§)\s*[\d.]+)?/gi);
|
|
122
|
+
for (const m of rfcMatches) {
|
|
123
|
+
refs.push(m[0]);
|
|
124
|
+
}
|
|
125
|
+
// Match CWE references (beyond the finding's own CWE)
|
|
126
|
+
const cweMatches = text.matchAll(/CWE-\d+/gi);
|
|
127
|
+
for (const m of cweMatches) {
|
|
128
|
+
if (m[0].toUpperCase() !== finding.cwe.toUpperCase()) {
|
|
129
|
+
refs.push(m[0].toUpperCase());
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
// Match OWASP references
|
|
133
|
+
const owaspMatches = text.matchAll(/OWASP\s+[\w\s-]+(?:\d{4})?/gi);
|
|
134
|
+
for (const m of owaspMatches) {
|
|
135
|
+
refs.push(m[0].trim());
|
|
136
|
+
}
|
|
137
|
+
return [...new Set(refs)];
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Map finding severity to a template severity range.
|
|
141
|
+
*/
|
|
142
|
+
function severityToRange(severity) {
|
|
143
|
+
switch (severity) {
|
|
144
|
+
case 'critical': return ['high', 'critical'];
|
|
145
|
+
case 'high': return ['medium', 'critical'];
|
|
146
|
+
case 'medium': return ['low', 'high'];
|
|
147
|
+
case 'low': return ['low', 'medium'];
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
// ── Core conversion ────────────────────────────────────────────
|
|
151
|
+
/**
|
|
152
|
+
* Convert a confirmed HuntFinding into a LearnedTemplate.
|
|
153
|
+
* The template captures the vulnerability pattern so future scans
|
|
154
|
+
* can detect similar issues without re-discovering from scratch.
|
|
155
|
+
*/
|
|
156
|
+
export function findingToTemplate(finding) {
|
|
157
|
+
const slug = finding.title
|
|
158
|
+
.toLowerCase()
|
|
159
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
160
|
+
.replace(/^-|-$/g, '')
|
|
161
|
+
.slice(0, 50);
|
|
162
|
+
const id = `learned-${slug}-${Date.now().toString(36)}`;
|
|
163
|
+
const filePatterns = extractFilePatterns(finding);
|
|
164
|
+
const bypasses = extractBypasses(finding);
|
|
165
|
+
const specRefs = extractSpecReferences(finding);
|
|
166
|
+
const triagePrompt = `You are a security researcher hunting for vulnerabilities similar to a confirmed finding.
|
|
167
|
+
|
|
168
|
+
CONFIRMED FINDING: ${finding.title}
|
|
169
|
+
CWE: ${finding.cwe}
|
|
170
|
+
|
|
171
|
+
ATTACK SCENARIO (proven to work):
|
|
172
|
+
${finding.attackScenario}
|
|
173
|
+
|
|
174
|
+
EVIDENCE (from confirmed exploit):
|
|
175
|
+
${finding.evidence}
|
|
176
|
+
|
|
177
|
+
Look for code that exhibits similar patterns:
|
|
178
|
+
1. Same vulnerability class (${finding.cwe})
|
|
179
|
+
2. Similar code constructs to the evidence above
|
|
180
|
+
3. Missing or weak validation that enables the attack scenario
|
|
181
|
+
|
|
182
|
+
Focus on whether the code is vulnerable to the SPECIFIC attack technique that was confirmed.`;
|
|
183
|
+
const deepDivePrompt = `You are an expert security researcher verifying a vulnerability similar to a confirmed finding.
|
|
184
|
+
|
|
185
|
+
ORIGINAL CONFIRMED FINDING: ${finding.title}
|
|
186
|
+
CWE: ${finding.cwe}
|
|
187
|
+
|
|
188
|
+
KNOWN REPRODUCTION STEPS:
|
|
189
|
+
${finding.reproductionSteps}
|
|
190
|
+
|
|
191
|
+
RECOMMENDATION FROM ORIGINAL FINDING:
|
|
192
|
+
${finding.recommendation}
|
|
193
|
+
|
|
194
|
+
Your task:
|
|
195
|
+
1. Verify if this code has the same vulnerability class
|
|
196
|
+
2. Adapt the reproduction steps to this specific codebase
|
|
197
|
+
3. Build a concrete proof-of-concept (curl command, HTML form, or code snippet)
|
|
198
|
+
4. Explain the exact code path that enables the attack
|
|
199
|
+
5. Provide a specific fix recommendation`;
|
|
200
|
+
const template = {
|
|
201
|
+
id,
|
|
202
|
+
name: `[Learned] ${finding.title}`,
|
|
203
|
+
cwe: finding.cwe,
|
|
204
|
+
filePatterns,
|
|
205
|
+
triagePrompt,
|
|
206
|
+
deepDivePrompt,
|
|
207
|
+
knownBypasses: bypasses,
|
|
208
|
+
specReferences: specRefs,
|
|
209
|
+
severityRange: severityToRange(finding.severity),
|
|
210
|
+
negativePatterns: [],
|
|
211
|
+
minMatchScore: 2,
|
|
212
|
+
};
|
|
213
|
+
return {
|
|
214
|
+
id,
|
|
215
|
+
source: 'bounty-finding',
|
|
216
|
+
sourceFinding: finding.title,
|
|
217
|
+
createdAt: new Date().toISOString(),
|
|
218
|
+
template,
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
// ── Persistence ────────────────────────────────────────────────
|
|
222
|
+
function getGlobalStorePath() {
|
|
223
|
+
return join(homedir(), '.assay', 'learned', 'templates.json');
|
|
224
|
+
}
|
|
225
|
+
function getProjectStorePath(projectRoot) {
|
|
226
|
+
return join(projectRoot, '.assay', 'learned', 'templates.json');
|
|
227
|
+
}
|
|
228
|
+
function readStore(path) {
|
|
229
|
+
if (!existsSync(path)) {
|
|
230
|
+
return { version: 1, templates: [] };
|
|
231
|
+
}
|
|
232
|
+
try {
|
|
233
|
+
const raw = readFileSync(path, 'utf-8');
|
|
234
|
+
const parsed = JSON.parse(raw);
|
|
235
|
+
if (parsed.version !== 1 || !Array.isArray(parsed.templates)) {
|
|
236
|
+
return { version: 1, templates: [] };
|
|
237
|
+
}
|
|
238
|
+
return parsed;
|
|
239
|
+
}
|
|
240
|
+
catch {
|
|
241
|
+
return { version: 1, templates: [] };
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
function writeStore(path, store) {
|
|
245
|
+
const dir = dirname(path);
|
|
246
|
+
mkdirSync(dir, { recursive: true });
|
|
247
|
+
writeFileSync(path, JSON.stringify(store, null, 2) + '\n', 'utf-8');
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Save a learned template to disk.
|
|
251
|
+
* Deduplicates by template ID — if the same ID exists, it's replaced.
|
|
252
|
+
*/
|
|
253
|
+
export function saveLearnedTemplate(template, options = {}) {
|
|
254
|
+
const storePath = options.global
|
|
255
|
+
? getGlobalStorePath()
|
|
256
|
+
: getProjectStorePath(options.projectRoot ?? process.cwd());
|
|
257
|
+
const store = readStore(storePath);
|
|
258
|
+
// Replace existing template with same ID, or append
|
|
259
|
+
const existingIdx = store.templates.findIndex(t => t.id === template.id);
|
|
260
|
+
if (existingIdx >= 0) {
|
|
261
|
+
store.templates[existingIdx] = template;
|
|
262
|
+
}
|
|
263
|
+
else {
|
|
264
|
+
store.templates.push(template);
|
|
265
|
+
}
|
|
266
|
+
writeStore(storePath, store);
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Load all learned templates from disk.
|
|
270
|
+
* Merges global + project-local, deduplicating by ID
|
|
271
|
+
* (project-local wins over global).
|
|
272
|
+
*/
|
|
273
|
+
export function loadLearnedTemplates(projectRoot) {
|
|
274
|
+
const globalStore = readStore(getGlobalStorePath());
|
|
275
|
+
const projectStore = projectRoot
|
|
276
|
+
? readStore(getProjectStorePath(projectRoot))
|
|
277
|
+
: { version: 1, templates: [] };
|
|
278
|
+
// Project-local templates override global ones with same ID
|
|
279
|
+
const byId = new Map();
|
|
280
|
+
for (const lt of globalStore.templates) {
|
|
281
|
+
byId.set(lt.id, lt.template);
|
|
282
|
+
}
|
|
283
|
+
for (const lt of projectStore.templates) {
|
|
284
|
+
byId.set(lt.id, lt.template);
|
|
285
|
+
}
|
|
286
|
+
return [...byId.values()];
|
|
287
|
+
}
|
|
288
|
+
//# sourceMappingURL=finding-to-template.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finding-to-template.js","sourceRoot":"","sources":["../../src/hunt/finding-to-template.ts"],"names":[],"mappings":"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"}
|
|
@@ -20,6 +20,9 @@ export declare class HuntOrchestrator {
|
|
|
20
20
|
private opts;
|
|
21
21
|
private files;
|
|
22
22
|
private templates;
|
|
23
|
+
private importGraph;
|
|
24
|
+
private taintSources;
|
|
25
|
+
private taintSinks;
|
|
23
26
|
constructor(opts: HuntOptions);
|
|
24
27
|
loadFiles(files: DiscoveredFile[]): void;
|
|
25
28
|
triage(): Promise<TriageResult>;
|
|
@@ -4,10 +4,14 @@ import { runTriage } from './triage.js';
|
|
|
4
4
|
import { runDeepDive } from './deep-dive.js';
|
|
5
5
|
import { getAllTemplates, getTemplateById } from './templates/index.js';
|
|
6
6
|
import { retryWithBackoff } from '../lib/retry.js';
|
|
7
|
+
import { buildImportGraph, findSources, findSinks, buildTaintContext, formatTaintContext } from './taint-analysis.js';
|
|
7
8
|
export class HuntOrchestrator {
|
|
8
9
|
opts;
|
|
9
10
|
files = [];
|
|
10
11
|
templates = [];
|
|
12
|
+
importGraph = [];
|
|
13
|
+
taintSources = [];
|
|
14
|
+
taintSinks = [];
|
|
11
15
|
constructor(opts) {
|
|
12
16
|
this.opts = opts;
|
|
13
17
|
}
|
|
@@ -21,9 +25,14 @@ export class HuntOrchestrator {
|
|
|
21
25
|
maxFiles: this.opts.maxFiles,
|
|
22
26
|
});
|
|
23
27
|
console.log(` Found ${this.files.length} security-relevant files`);
|
|
28
|
+
// Build cross-file taint analysis
|
|
29
|
+
this.importGraph = buildImportGraph(this.files);
|
|
30
|
+
this.taintSources = findSources(this.files);
|
|
31
|
+
this.taintSinks = findSinks(this.files);
|
|
32
|
+
console.log(` Taint analysis: ${this.importGraph.length} import edges, ${this.taintSources.length} sources, ${this.taintSinks.length} sinks`);
|
|
24
33
|
this.templates = this.opts.templateFilter?.length
|
|
25
|
-
? this.opts.templateFilter.map(id => getTemplateById(id)).filter((t) => t !== undefined)
|
|
26
|
-
: getAllTemplates();
|
|
34
|
+
? this.opts.templateFilter.map(id => getTemplateById(id, this.opts.targetPath)).filter((t) => t !== undefined)
|
|
35
|
+
: getAllTemplates(this.opts.targetPath);
|
|
27
36
|
console.log(` Using ${this.templates.length} vulnerability templates`);
|
|
28
37
|
const pairs = [];
|
|
29
38
|
for (const file of this.files) {
|
|
@@ -39,7 +48,10 @@ export class HuntOrchestrator {
|
|
|
39
48
|
const runOne = async (pair, index) => {
|
|
40
49
|
const id = nextId++;
|
|
41
50
|
console.log(` [${index + 1}/${pairs.length}] Triaging ${pair.file.relativePath} (${pair.match.template.id})`);
|
|
42
|
-
|
|
51
|
+
// Build taint context for this file
|
|
52
|
+
const tCtx = buildTaintContext(pair.file, this.files, this.importGraph, this.taintSources, this.taintSinks);
|
|
53
|
+
const taintContextStr = formatTaintContext(tCtx) || undefined;
|
|
54
|
+
const result = await retryWithBackoff(() => runTriage(pair.file, pair.match.template, id, this.opts.provider, taintContextStr), { maxRetries: 2, baseDelayMs: 1000 });
|
|
43
55
|
if (result)
|
|
44
56
|
hypotheses.push(result);
|
|
45
57
|
};
|
|
@@ -65,7 +77,7 @@ export class HuntOrchestrator {
|
|
|
65
77
|
async deepDive(hypotheses) {
|
|
66
78
|
const findings = [];
|
|
67
79
|
for (const hypothesis of hypotheses) {
|
|
68
|
-
const template = getTemplateById(hypothesis.templateId);
|
|
80
|
+
const template = getTemplateById(hypothesis.templateId, this.opts.targetPath);
|
|
69
81
|
if (!template) {
|
|
70
82
|
console.error(` Warning: Template ${hypothesis.templateId} not found, skipping`);
|
|
71
83
|
continue;
|
|
@@ -76,7 +88,10 @@ export class HuntOrchestrator {
|
|
|
76
88
|
continue;
|
|
77
89
|
}
|
|
78
90
|
console.log(` Deep-diving hypothesis #${hypothesis.id}: ${hypothesis.summary}`);
|
|
79
|
-
|
|
91
|
+
// Build taint context for deep dive
|
|
92
|
+
const tCtx = buildTaintContext(file, this.files, this.importGraph, this.taintSources, this.taintSinks);
|
|
93
|
+
const taintContextStr = formatTaintContext(tCtx) || undefined;
|
|
94
|
+
const finding = await retryWithBackoff(() => runDeepDive(template, hypothesis, file, this.opts.provider, taintContextStr), { maxRetries: 2, baseDelayMs: 2000 });
|
|
80
95
|
if (finding) {
|
|
81
96
|
findings.push(finding);
|
|
82
97
|
console.log(` CONFIRMED: ${finding.title} (${finding.severity})`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orchestrator.js","sourceRoot":"","sources":["../../src/hunt/orchestrator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAA8C,MAAM,gBAAgB,CAAC;AACnG,OAAO,EAAE,cAAc,EAAsB,MAAM,cAAc,CAAC;AAClE,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACxE,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"orchestrator.js","sourceRoot":"","sources":["../../src/hunt/orchestrator.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,2 +1,8 @@
|
|
|
1
1
|
export declare function stripCodeFences(text: string): string;
|
|
2
|
+
/**
|
|
3
|
+
* Extract JSON from text that may contain code fences and trailing prose.
|
|
4
|
+
* Handles the common LLM pattern of returning:
|
|
5
|
+
* ```json\n{...}\n```\n\n**Reasoning:**\n...
|
|
6
|
+
*/
|
|
7
|
+
export declare function extractJSON(text: string): string;
|
|
2
8
|
export declare function safeParseJSON(text: string): any | null;
|
package/dist/hunt/parse-utils.js
CHANGED
|
@@ -6,9 +6,36 @@ export function stripCodeFences(text) {
|
|
|
6
6
|
lines.pop();
|
|
7
7
|
return lines.join('\n').trim();
|
|
8
8
|
}
|
|
9
|
+
/**
|
|
10
|
+
* Extract JSON from text that may contain code fences and trailing prose.
|
|
11
|
+
* Handles the common LLM pattern of returning:
|
|
12
|
+
* ```json\n{...}\n```\n\n**Reasoning:**\n...
|
|
13
|
+
*/
|
|
14
|
+
export function extractJSON(text) {
|
|
15
|
+
// First try: extract content between code fences
|
|
16
|
+
const fenceMatch = text.match(/```(?:json)?\s*\n([\s\S]*?)\n```/);
|
|
17
|
+
if (fenceMatch)
|
|
18
|
+
return fenceMatch[1].trim();
|
|
19
|
+
// Second try: find the first { and its matching closing }
|
|
20
|
+
const start = text.indexOf('{');
|
|
21
|
+
if (start === -1)
|
|
22
|
+
return text.trim();
|
|
23
|
+
let depth = 0;
|
|
24
|
+
for (let i = start; i < text.length; i++) {
|
|
25
|
+
if (text[i] === '{')
|
|
26
|
+
depth++;
|
|
27
|
+
else if (text[i] === '}') {
|
|
28
|
+
depth--;
|
|
29
|
+
if (depth === 0)
|
|
30
|
+
return text.slice(start, i + 1);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
// Fallback: return from first { to end (old behavior via stripCodeFences)
|
|
34
|
+
return stripCodeFences(text);
|
|
35
|
+
}
|
|
9
36
|
export function safeParseJSON(text) {
|
|
10
37
|
try {
|
|
11
|
-
return JSON.parse(
|
|
38
|
+
return JSON.parse(extractJSON(text));
|
|
12
39
|
}
|
|
13
40
|
catch {
|
|
14
41
|
return null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parse-utils.js","sourceRoot":"","sources":["../../src/hunt/parse-utils.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACtC,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,KAAK,CAAC;QAAE,KAAK,CAAC,KAAK,EAAE,CAAC;IAC/C,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,UAAU,CAAC,KAAK,CAAC;QAAE,KAAK,CAAC,GAAG,EAAE,CAAC;IAC5D,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;AACjC,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,
|
|
1
|
+
{"version":3,"file":"parse-utils.js","sourceRoot":"","sources":["../../src/hunt/parse-utils.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACtC,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,KAAK,CAAC;QAAE,KAAK,CAAC,KAAK,EAAE,CAAC;IAC/C,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,UAAU,CAAC,KAAK,CAAC;QAAE,KAAK,CAAC,GAAG,EAAE,CAAC;IAC5D,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;AACjC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,iDAAiD;IACjD,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,kCAAkC,CAAC,CAAC;IAClE,IAAI,UAAU;QAAE,OAAO,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAE5C,0DAA0D;IAC1D,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAChC,IAAI,KAAK,KAAK,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC;IAErC,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,IAAI,CAAC,GAAG,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACzC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG;YAAE,KAAK,EAAE,CAAC;aACxB,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;YACzB,KAAK,EAAE,CAAC;YACR,IAAI,KAAK,KAAK,CAAC;gBAAE,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;QACnD,CAAC;IACH,CAAC;IAED,0EAA0E;IAC1E,OAAO,eAAe,CAAC,IAAI,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;IACvC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { DiscoveredFile } from './discovery.js';
|
|
2
|
+
export interface TaintSource {
|
|
3
|
+
file: string;
|
|
4
|
+
line?: number;
|
|
5
|
+
type: 'request-param' | 'request-body' | 'query-string' | 'url-param' | 'file-upload' | 'websocket' | 'env-var' | 'user-input';
|
|
6
|
+
identifier: string;
|
|
7
|
+
}
|
|
8
|
+
export interface TaintSink {
|
|
9
|
+
file: string;
|
|
10
|
+
line?: number;
|
|
11
|
+
type: 'sql-query' | 'command-exec' | 'file-op' | 'redirect' | 'template-render' | 'response-write' | 'eval' | 'inner-html';
|
|
12
|
+
identifier: string;
|
|
13
|
+
}
|
|
14
|
+
export interface ImportEdge {
|
|
15
|
+
from: string;
|
|
16
|
+
to: string;
|
|
17
|
+
symbols: string[];
|
|
18
|
+
}
|
|
19
|
+
export interface TaintContext {
|
|
20
|
+
file: string;
|
|
21
|
+
sources: TaintSource[];
|
|
22
|
+
sinks: TaintSink[];
|
|
23
|
+
dataFlowPaths: string[];
|
|
24
|
+
relatedFiles: {
|
|
25
|
+
path: string;
|
|
26
|
+
relevance: string;
|
|
27
|
+
snippet: string;
|
|
28
|
+
}[];
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Build a graph of import edges between discovered files.
|
|
32
|
+
* Resolves relative imports (./foo, ../bar) to discovered file paths.
|
|
33
|
+
*/
|
|
34
|
+
export declare function buildImportGraph(files: DiscoveredFile[]): ImportEdge[];
|
|
35
|
+
export declare function findSources(files: DiscoveredFile[]): TaintSource[];
|
|
36
|
+
export declare function findSinks(files: DiscoveredFile[]): TaintSink[];
|
|
37
|
+
/**
|
|
38
|
+
* Build a TaintContext for a target file, showing:
|
|
39
|
+
* - Sources and sinks within the file
|
|
40
|
+
* - Related files that import from or are imported by the target
|
|
41
|
+
* - Data flow paths tracing sources through imports to sinks
|
|
42
|
+
*/
|
|
43
|
+
export declare function buildTaintContext(targetFile: DiscoveredFile, allFiles: DiscoveredFile[], importGraph: ImportEdge[], sources: TaintSource[], sinks: TaintSink[]): TaintContext;
|
|
44
|
+
/**
|
|
45
|
+
* Format a TaintContext into a human-readable string for injection into
|
|
46
|
+
* triage/deep-dive LLM prompts.
|
|
47
|
+
* Returns empty string if there's no cross-file context worth showing.
|
|
48
|
+
*/
|
|
49
|
+
export declare function formatTaintContext(ctx: TaintContext): string;
|