tryassay 0.31.0 → 0.33.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/cli.js +55 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/assess.js +73 -0
- package/dist/commands/assess.js.map +1 -1
- package/dist/commands/bounty-chain.d.ts +1 -0
- package/dist/commands/bounty-chain.js +34 -0
- package/dist/commands/bounty-chain.js.map +1 -0
- package/dist/commands/bounty-check.d.ts +10 -0
- package/dist/commands/bounty-check.js +104 -0
- package/dist/commands/bounty-check.js.map +1 -0
- package/dist/commands/bounty-discover.d.ts +6 -0
- package/dist/commands/bounty-discover.js +45 -0
- package/dist/commands/bounty-discover.js.map +1 -0
- package/dist/commands/bounty-scan.d.ts +7 -0
- package/dist/commands/bounty-scan.js +312 -0
- package/dist/commands/bounty-scan.js.map +1 -0
- package/dist/commands/bounty-watch.d.ts +9 -0
- package/dist/commands/bounty-watch.js +210 -0
- package/dist/commands/bounty-watch.js.map +1 -0
- package/dist/commands/hunt.d.ts +11 -0
- package/dist/commands/hunt.js +216 -0
- package/dist/commands/hunt.js.map +1 -0
- package/dist/hunt/__tests__/deep-dive.test.d.ts +1 -0
- package/dist/hunt/__tests__/deep-dive.test.js +102 -0
- package/dist/hunt/__tests__/deep-dive.test.js.map +1 -0
- package/dist/hunt/__tests__/discovery.test.d.ts +1 -0
- package/dist/hunt/__tests__/discovery.test.js +55 -0
- package/dist/hunt/__tests__/discovery.test.js.map +1 -0
- package/dist/hunt/__tests__/e2e.test.d.ts +1 -0
- package/dist/hunt/__tests__/e2e.test.js +261 -0
- package/dist/hunt/__tests__/e2e.test.js.map +1 -0
- package/dist/hunt/__tests__/matcher.test.d.ts +1 -0
- package/dist/hunt/__tests__/matcher.test.js +63 -0
- package/dist/hunt/__tests__/matcher.test.js.map +1 -0
- package/dist/hunt/__tests__/orchestrator.test.d.ts +1 -0
- package/dist/hunt/__tests__/orchestrator.test.js +73 -0
- package/dist/hunt/__tests__/orchestrator.test.js.map +1 -0
- package/dist/hunt/__tests__/parse-utils.test.d.ts +1 -0
- package/dist/hunt/__tests__/parse-utils.test.js +28 -0
- package/dist/hunt/__tests__/parse-utils.test.js.map +1 -0
- package/dist/hunt/__tests__/state.test.d.ts +1 -0
- package/dist/hunt/__tests__/state.test.js +49 -0
- package/dist/hunt/__tests__/state.test.js.map +1 -0
- package/dist/hunt/__tests__/templates.test.d.ts +1 -0
- package/dist/hunt/__tests__/templates.test.js +32 -0
- package/dist/hunt/__tests__/templates.test.js.map +1 -0
- package/dist/hunt/__tests__/triage.test.d.ts +1 -0
- package/dist/hunt/__tests__/triage.test.js +91 -0
- package/dist/hunt/__tests__/triage.test.js.map +1 -0
- package/dist/hunt/__tests__/types.test.d.ts +1 -0
- package/dist/hunt/__tests__/types.test.js +65 -0
- package/dist/hunt/__tests__/types.test.js.map +1 -0
- package/dist/hunt/deep-dive.d.ts +8 -0
- package/dist/hunt/deep-dive.js +86 -0
- package/dist/hunt/deep-dive.js.map +1 -0
- package/dist/hunt/discovery.d.ts +15 -0
- package/dist/hunt/discovery.js +116 -0
- package/dist/hunt/discovery.js.map +1 -0
- package/dist/hunt/matcher.d.ts +8 -0
- package/dist/hunt/matcher.js +27 -0
- package/dist/hunt/matcher.js.map +1 -0
- package/dist/hunt/orchestrator.d.ts +27 -0
- package/dist/hunt/orchestrator.js +91 -0
- package/dist/hunt/orchestrator.js.map +1 -0
- package/dist/hunt/parse-utils.d.ts +2 -0
- package/dist/hunt/parse-utils.js +17 -0
- package/dist/hunt/parse-utils.js.map +1 -0
- package/dist/hunt/state.d.ts +5 -0
- package/dist/hunt/state.js +35 -0
- package/dist/hunt/state.js.map +1 -0
- package/dist/hunt/templates/auth-bypass.d.ts +2 -0
- package/dist/hunt/templates/auth-bypass.js +80 -0
- package/dist/hunt/templates/auth-bypass.js.map +1 -0
- package/dist/hunt/templates/cors-misconfig.d.ts +2 -0
- package/dist/hunt/templates/cors-misconfig.js +88 -0
- package/dist/hunt/templates/cors-misconfig.js.map +1 -0
- package/dist/hunt/templates/csrf-bypass.d.ts +2 -0
- package/dist/hunt/templates/csrf-bypass.js +65 -0
- package/dist/hunt/templates/csrf-bypass.js.map +1 -0
- package/dist/hunt/templates/index.d.ts +3 -0
- package/dist/hunt/templates/index.js +29 -0
- package/dist/hunt/templates/index.js.map +1 -0
- package/dist/hunt/templates/injection.d.ts +2 -0
- package/dist/hunt/templates/injection.js +103 -0
- package/dist/hunt/templates/injection.js.map +1 -0
- package/dist/hunt/templates/open-redirect.d.ts +2 -0
- package/dist/hunt/templates/open-redirect.js +93 -0
- package/dist/hunt/templates/open-redirect.js.map +1 -0
- package/dist/hunt/templates/path-traversal.d.ts +2 -0
- package/dist/hunt/templates/path-traversal.js +94 -0
- package/dist/hunt/templates/path-traversal.js.map +1 -0
- package/dist/hunt/templates/prototype-pollution.d.ts +2 -0
- package/dist/hunt/templates/prototype-pollution.js +108 -0
- package/dist/hunt/templates/prototype-pollution.js.map +1 -0
- package/dist/hunt/templates/ssrf.d.ts +2 -0
- package/dist/hunt/templates/ssrf.js +75 -0
- package/dist/hunt/templates/ssrf.js.map +1 -0
- package/dist/hunt/templates/timing-attack.d.ts +2 -0
- package/dist/hunt/templates/timing-attack.js +108 -0
- package/dist/hunt/templates/timing-attack.js.map +1 -0
- package/dist/hunt/templates/weak-random.d.ts +2 -0
- package/dist/hunt/templates/weak-random.js +73 -0
- package/dist/hunt/templates/weak-random.js.map +1 -0
- package/dist/hunt/triage.d.ts +8 -0
- package/dist/hunt/triage.js +78 -0
- package/dist/hunt/triage.js.map +1 -0
- package/dist/lib/__tests__/bounty-scan.test.d.ts +1 -0
- package/dist/lib/__tests__/bounty-scan.test.js +15 -0
- package/dist/lib/__tests__/bounty-scan.test.js.map +1 -0
- package/dist/lib/__tests__/chain-analyzer.test.d.ts +1 -0
- package/dist/lib/__tests__/chain-analyzer.test.js +47 -0
- package/dist/lib/__tests__/chain-analyzer.test.js.map +1 -0
- package/dist/lib/__tests__/change-classifier.test.d.ts +1 -0
- package/dist/lib/__tests__/change-classifier.test.js +55 -0
- package/dist/lib/__tests__/change-classifier.test.js.map +1 -0
- package/dist/lib/__tests__/finding-dedup.test.d.ts +1 -0
- package/dist/lib/__tests__/finding-dedup.test.js +30 -0
- package/dist/lib/__tests__/finding-dedup.test.js.map +1 -0
- package/dist/lib/__tests__/learned-rules.test.js +25 -0
- package/dist/lib/__tests__/learned-rules.test.js.map +1 -1
- package/dist/lib/__tests__/novelty-checker.test.d.ts +1 -0
- package/dist/lib/__tests__/novelty-checker.test.js +57 -0
- package/dist/lib/__tests__/novelty-checker.test.js.map +1 -0
- package/dist/lib/__tests__/program-registry.test.d.ts +1 -0
- package/dist/lib/__tests__/program-registry.test.js +40 -0
- package/dist/lib/__tests__/program-registry.test.js.map +1 -0
- package/dist/lib/__tests__/retry.test.d.ts +1 -0
- package/dist/lib/__tests__/retry.test.js +23 -0
- package/dist/lib/__tests__/retry.test.js.map +1 -0
- package/dist/lib/__tests__/watchlist.test.d.ts +1 -0
- package/dist/lib/__tests__/watchlist.test.js +88 -0
- package/dist/lib/__tests__/watchlist.test.js.map +1 -0
- package/dist/lib/chain-analyzer.d.ts +25 -0
- package/dist/lib/chain-analyzer.js +105 -0
- package/dist/lib/chain-analyzer.js.map +1 -0
- package/dist/lib/change-classifier.d.ts +3 -0
- package/dist/lib/change-classifier.js +97 -0
- package/dist/lib/change-classifier.js.map +1 -0
- package/dist/lib/finding-dedup.d.ts +2 -0
- package/dist/lib/finding-dedup.js +9 -0
- package/dist/lib/finding-dedup.js.map +1 -0
- package/dist/lib/issue-reporter.d.ts +13 -0
- package/dist/lib/issue-reporter.js +51 -0
- package/dist/lib/issue-reporter.js.map +1 -0
- package/dist/lib/novelty-checker.d.ts +60 -0
- package/dist/lib/novelty-checker.js +223 -0
- package/dist/lib/novelty-checker.js.map +1 -0
- package/dist/lib/program-registry.d.ts +12 -0
- package/dist/lib/program-registry.js +18 -0
- package/dist/lib/program-registry.js.map +1 -0
- package/dist/lib/retry.d.ts +5 -0
- package/dist/lib/retry.js +19 -0
- package/dist/lib/retry.js.map +1 -0
- package/dist/lib/watchlist.d.ts +23 -0
- package/dist/lib/watchlist.js +31 -0
- package/dist/lib/watchlist.js.map +1 -0
- package/dist/runtime/safe-executor.js +1 -1
- package/dist/runtime/safe-executor.js.map +1 -1
- package/dist/runtime/types.d.ts +1 -1
- package/dist/sdk/forward-verify.d.ts +2 -2
- package/dist/sdk/forward-verify.js +31 -2
- package/dist/sdk/forward-verify.js.map +1 -1
- package/dist/sdk/types.d.ts +8 -0
- package/dist/types.d.ts +45 -0
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chain-analyzer.js","sourceRoot":"","sources":["../../src/lib/chain-analyzer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export type ChangeCategory = 'string_literal' | 'comment' | 'css_style' | 'numeric_constant' | 'import_export' | 'type_definition' | 'logic' | 'unknown';
|
|
2
|
+
export declare function classifyChange(oldText: string, newText: string, filePath: string): ChangeCategory;
|
|
3
|
+
export declare function mapExperimentCategory(experimentCategory: string): ChangeCategory;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
const PRIORITY = {
|
|
2
|
+
string_literal: 0, comment: 1, css_style: 2, numeric_constant: 3,
|
|
3
|
+
logic: 4, type_definition: 5, import_export: 6, unknown: 4,
|
|
4
|
+
};
|
|
5
|
+
export function classifyChange(oldText, newText, filePath) {
|
|
6
|
+
if (!oldText && !newText)
|
|
7
|
+
return 'unknown';
|
|
8
|
+
// Diff-level check: if texts are structurally identical except for values, classify by content type
|
|
9
|
+
const oldNormalized = oldText.replace(/\d+/g, 'NUM');
|
|
10
|
+
const newNormalized = newText.replace(/\d+/g, 'NUM');
|
|
11
|
+
const onlyNumericDiff = oldNormalized === newNormalized && oldText !== newText;
|
|
12
|
+
const oldStrNorm = oldText.replace(/"[^"]*"/g, '"STR"').replace(/'[^']*'/g, "'STR'");
|
|
13
|
+
const newStrNorm = newText.replace(/"[^"]*"/g, '"STR"').replace(/'[^']*'/g, "'STR'");
|
|
14
|
+
const onlyStringDiff = oldStrNorm === newStrNorm && oldText !== newText;
|
|
15
|
+
if (onlyNumericDiff || onlyStringDiff) {
|
|
16
|
+
// Check if context is CSS before returning generic category
|
|
17
|
+
const isCssContext = /\b(Height|Width|Margin|Padding|Size|Color|Border|Font|Display|Position|style|className)\b/i.test(oldText) ||
|
|
18
|
+
/\d+(px|em|rem|%|vh|vw)/.test(oldText) || /\d+(px|em|rem|%|vh|vw)/.test(newText);
|
|
19
|
+
if (isCssContext)
|
|
20
|
+
return 'css_style';
|
|
21
|
+
if (onlyNumericDiff)
|
|
22
|
+
return 'numeric_constant';
|
|
23
|
+
return 'string_literal';
|
|
24
|
+
}
|
|
25
|
+
const oldLines = oldText.split('\n');
|
|
26
|
+
const newLines = newText.split('\n');
|
|
27
|
+
const addedLines = newLines.filter(l => !oldLines.includes(l));
|
|
28
|
+
const removedLines = oldLines.filter(l => !newLines.includes(l));
|
|
29
|
+
const changedContent = [...addedLines, ...removedLines].join('\n');
|
|
30
|
+
if (!changedContent.trim())
|
|
31
|
+
return 'unknown';
|
|
32
|
+
const categories = new Set();
|
|
33
|
+
for (const line of [...addedLines, ...removedLines]) {
|
|
34
|
+
const trimmed = line.trim();
|
|
35
|
+
if (!trimmed)
|
|
36
|
+
continue;
|
|
37
|
+
categories.add(classifyLine(trimmed, filePath));
|
|
38
|
+
}
|
|
39
|
+
if (categories.size === 0)
|
|
40
|
+
return 'unknown';
|
|
41
|
+
let highest = 'string_literal';
|
|
42
|
+
let highestPriority = -1;
|
|
43
|
+
for (const cat of categories) {
|
|
44
|
+
if (PRIORITY[cat] > highestPriority) {
|
|
45
|
+
highest = cat;
|
|
46
|
+
highestPriority = PRIORITY[cat];
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return highest;
|
|
50
|
+
}
|
|
51
|
+
function classifyLine(line, filePath) {
|
|
52
|
+
if (/^\s*(import\s|export\s|from\s)/.test(line))
|
|
53
|
+
return 'import_export';
|
|
54
|
+
if (/^\s*export\s+(default\s+)?/.test(line))
|
|
55
|
+
return 'import_export';
|
|
56
|
+
if (/^\s*(type|interface)\s+\w+/.test(line))
|
|
57
|
+
return 'type_definition';
|
|
58
|
+
if (/:\s*(string|number|boolean|null|undefined|void|never|any)\s*[;,}]?\s*$/.test(line) && !line.includes('=') && !line.includes('('))
|
|
59
|
+
return 'type_definition';
|
|
60
|
+
if (/^\s*(\/\/|\/\*|\*\/|\*)/.test(line))
|
|
61
|
+
return 'comment';
|
|
62
|
+
if (/^\s*\w*(Height|Width|Margin|Padding|Size|Color|Border|Font|Display|Position)\s*[:=]/.test(line) ||
|
|
63
|
+
/^\s*(min|max)?(Height|Width)\s*[:=]/.test(line) ||
|
|
64
|
+
/^\s*style\s*[:=]/.test(line) ||
|
|
65
|
+
/^\s*className\s*[:=]/.test(line))
|
|
66
|
+
return 'css_style';
|
|
67
|
+
if (/^\s*['"]?\w+['"]?\s*:\s*['"]?\d+(px|em|rem|%|vh|vw)['"]?\s*[,;}]?$/.test(line))
|
|
68
|
+
return 'css_style';
|
|
69
|
+
if (/`[^`]*\$\{/.test(line))
|
|
70
|
+
return 'logic';
|
|
71
|
+
if (/^\s*"[^"]*"\s*[;,]?\s*$/.test(line) || /^\s*'[^']*'\s*[;,]?\s*$/.test(line))
|
|
72
|
+
return 'string_literal';
|
|
73
|
+
if (/^\s*(return\s+)?"[^"]*"\s*;?\s*$/.test(line) || /^\s*(return\s+)?'[^']*'\s*;?\s*$/.test(line))
|
|
74
|
+
return 'string_literal';
|
|
75
|
+
if (/^\s*(const|let|var)?\s*\w+\s*=\s*"[^"]*"\s*;?\s*$/.test(line) || /^\s*(const|let|var)?\s*\w+\s*=\s*'[^']*'\s*;?\s*$/.test(line))
|
|
76
|
+
return 'string_literal';
|
|
77
|
+
if (/^\s*(const|let|var)?\s*\w+\s*=\s*\d+/.test(line))
|
|
78
|
+
return 'numeric_constant';
|
|
79
|
+
if (/\b\d+\b/.test(line) && !/[a-zA-Z_]\w*\s*\(/.test(line) && !line.includes('import') && !line.includes('export')) {
|
|
80
|
+
const stripped = line.replace(/\d+/g, 'NUM');
|
|
81
|
+
if (/^\s*(return\s+)?.*NUM.*$/.test(stripped))
|
|
82
|
+
return 'numeric_constant';
|
|
83
|
+
}
|
|
84
|
+
if (/console\.(log|warn|error|info|debug)/.test(line))
|
|
85
|
+
return 'logic';
|
|
86
|
+
return 'logic';
|
|
87
|
+
}
|
|
88
|
+
export function mapExperimentCategory(experimentCategory) {
|
|
89
|
+
const mapping = {
|
|
90
|
+
string_constant: 'string_literal', css_styling: 'css_style', console_log: 'logic',
|
|
91
|
+
numeric_constant: 'numeric_constant', comment: 'comment',
|
|
92
|
+
missing_module: 'import_export', renamed_export: 'import_export', removed_export: 'import_export',
|
|
93
|
+
type_deletion: 'type_definition', type_incompatible: 'type_definition', generic_constraint: 'type_definition',
|
|
94
|
+
};
|
|
95
|
+
return mapping[experimentCategory] ?? 'unknown';
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=change-classifier.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"change-classifier.js","sourceRoot":"","sources":["../../src/lib/change-classifier.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
export function hashFinding(repo, file, claimText) {
|
|
3
|
+
const normalized = [repo, file, claimText.replace(/\s+/g, ' ').trim().toLowerCase()].join('|');
|
|
4
|
+
return createHash('sha256').update(normalized).digest('hex').slice(0, 16);
|
|
5
|
+
}
|
|
6
|
+
export function isDuplicate(hash, reportedFindings) {
|
|
7
|
+
return reportedFindings.includes(hash);
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=finding-dedup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"finding-dedup.js","sourceRoot":"","sources":["../../src/lib/finding-dedup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,IAAY,EAAE,SAAiB;IACvE,MAAM,UAAU,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,SAAS,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC/F,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAC5E,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,gBAA0B;IAClE,OAAO,gBAAgB,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AACzC,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface FindingIssue {
|
|
2
|
+
repo: string;
|
|
3
|
+
program: string;
|
|
4
|
+
asset: string;
|
|
5
|
+
file: string;
|
|
6
|
+
claim: string;
|
|
7
|
+
severity: string;
|
|
8
|
+
reasoning: string;
|
|
9
|
+
chainId?: string;
|
|
10
|
+
chainImpact?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function findExistingIssue(hashPrefix: string): boolean;
|
|
13
|
+
export declare function createFindingIssue(finding: FindingIssue, hashPrefix: string): void;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { safeExecSync } from '../runtime/safe-executor.js';
|
|
2
|
+
export function findExistingIssue(hashPrefix) {
|
|
3
|
+
const result = safeExecSync('gh', [
|
|
4
|
+
'issue', 'list',
|
|
5
|
+
'--label', 'bounty-finding',
|
|
6
|
+
'--state', 'open',
|
|
7
|
+
'--search', `[BOUNTY-${hashPrefix}]`,
|
|
8
|
+
'--json', 'number',
|
|
9
|
+
'--jq', 'length',
|
|
10
|
+
]);
|
|
11
|
+
if (result.exitCode !== 0)
|
|
12
|
+
return false;
|
|
13
|
+
return parseInt(result.stdout.trim(), 10) > 0;
|
|
14
|
+
}
|
|
15
|
+
export function createFindingIssue(finding, hashPrefix) {
|
|
16
|
+
const labels = ['bounty-finding'];
|
|
17
|
+
if (finding.chainId)
|
|
18
|
+
labels.push('chain-finding');
|
|
19
|
+
const chainLine = finding.chainId
|
|
20
|
+
? `**Chain:** ${finding.chainId} — ${finding.chainImpact}\n`
|
|
21
|
+
: '';
|
|
22
|
+
const body = `## [BOUNTY-${hashPrefix}] ${finding.claim.slice(0, 60)}
|
|
23
|
+
|
|
24
|
+
**Program:** ${finding.program}
|
|
25
|
+
**Asset:** ${finding.asset}
|
|
26
|
+
**Severity:** ${finding.severity}
|
|
27
|
+
${chainLine}
|
|
28
|
+
### Finding
|
|
29
|
+
**File:** \`${finding.file}\`
|
|
30
|
+
**Claim:** ${finding.claim}
|
|
31
|
+
**Reasoning:** ${finding.reasoning}
|
|
32
|
+
|
|
33
|
+
### HackerOne Form Fields
|
|
34
|
+
| Field | Value |
|
|
35
|
+
|-------|-------|
|
|
36
|
+
| Asset | ${finding.asset} |
|
|
37
|
+
| Severity | ${finding.severity} |
|
|
38
|
+
|
|
39
|
+
### Next Steps
|
|
40
|
+
1. Run \`npx tryassay bounty-check\` for Layer 2 verification
|
|
41
|
+
2. Run \`/bounty-report\` to generate submission-ready markdown
|
|
42
|
+
3. Submit to HackerOne`;
|
|
43
|
+
const title = `[BOUNTY-${hashPrefix}] ${finding.severity.toUpperCase()}: ${finding.claim.slice(0, 70)}`;
|
|
44
|
+
safeExecSync('gh', [
|
|
45
|
+
'issue', 'create',
|
|
46
|
+
'--title', title,
|
|
47
|
+
'--body', body,
|
|
48
|
+
'--label', labels.join(','),
|
|
49
|
+
]);
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=issue-reporter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"issue-reporter.js","sourceRoot":"","sources":["../../src/lib/issue-reporter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAc3D,MAAM,UAAU,iBAAiB,CAAC,UAAkB;IAClD,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,EAAE;QAChC,OAAO,EAAE,MAAM;QACf,SAAS,EAAE,gBAAgB;QAC3B,SAAS,EAAE,MAAM;QACjB,UAAU,EAAE,WAAW,UAAU,GAAG;QACpC,QAAQ,EAAE,QAAQ;QAClB,MAAM,EAAE,QAAQ;KACjB,CAAC,CAAC;IACH,IAAI,MAAM,CAAC,QAAQ,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACxC,OAAO,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC;AAChD,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,OAAqB,EAAE,UAAkB;IAC1E,MAAM,MAAM,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAClC,IAAI,OAAO,CAAC,OAAO;QAAE,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IAElD,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO;QAC/B,CAAC,CAAC,cAAc,OAAO,CAAC,OAAO,MAAM,OAAO,CAAC,WAAW,IAAI;QAC5D,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,IAAI,GAAG,cAAc,UAAU,KAAK,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;;eAEvD,OAAO,CAAC,OAAO;aACjB,OAAO,CAAC,KAAK;gBACV,OAAO,CAAC,QAAQ;EAC9B,SAAS;;cAEG,OAAO,CAAC,IAAI;aACb,OAAO,CAAC,KAAK;iBACT,OAAO,CAAC,SAAS;;;;;YAKtB,OAAO,CAAC,KAAK;eACV,OAAO,CAAC,QAAQ;;;;;uBAKR,CAAC;IAEtB,MAAM,KAAK,GAAG,WAAW,UAAU,KAAK,OAAO,CAAC,QAAQ,CAAC,WAAW,EAAE,KAAK,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;IAExG,YAAY,CAAC,IAAI,EAAE;QACjB,OAAO,EAAE,QAAQ;QACjB,SAAS,EAAE,KAAK;QAChB,QAAQ,EAAE,IAAI;QACd,SAAS,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;KAC5B,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Novelty Checker — determines if a bounty finding has already been reported/patched.
|
|
3
|
+
*
|
|
4
|
+
* Layer 1 (fast, local): Git recency + release notes keyword scan.
|
|
5
|
+
* Layer 2 (deep, API): GitHub Security Advisories + code comparison.
|
|
6
|
+
*/
|
|
7
|
+
export type NoveltyStatus = 'likely-novel' | 'likely-patched' | 'check-manually';
|
|
8
|
+
export interface NoveltyResult {
|
|
9
|
+
status: NoveltyStatus;
|
|
10
|
+
reason: string;
|
|
11
|
+
}
|
|
12
|
+
export interface GitSecurityResult {
|
|
13
|
+
hasSecurityFixes: boolean;
|
|
14
|
+
matchingCommits: string[];
|
|
15
|
+
}
|
|
16
|
+
export interface DeepCheckResult {
|
|
17
|
+
status: NoveltyStatus;
|
|
18
|
+
reason: string;
|
|
19
|
+
advisories: string[];
|
|
20
|
+
hacktivityMatches: string[];
|
|
21
|
+
codeStillVulnerable: boolean | null;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Extract security-relevant keywords from a claim description.
|
|
25
|
+
*/
|
|
26
|
+
export declare function extractSecurityKeywords(claim: string): string[];
|
|
27
|
+
/**
|
|
28
|
+
* Parse git log output and check for security-related commits
|
|
29
|
+
* that mention any of the given keywords.
|
|
30
|
+
*/
|
|
31
|
+
export declare function parseGitLogForSecurityFixes(gitLog: string, keywords: string[]): GitSecurityResult;
|
|
32
|
+
/**
|
|
33
|
+
* Parse git tag output into a clean array of tag names.
|
|
34
|
+
*/
|
|
35
|
+
export declare function parseGitTags(output: string): string[];
|
|
36
|
+
/**
|
|
37
|
+
* Layer 1: Quick novelty check using only local git data.
|
|
38
|
+
*/
|
|
39
|
+
export declare function checkNoveltyLocal(targetPath: string, file: string, claim: string): NoveltyResult;
|
|
40
|
+
/**
|
|
41
|
+
* Check GitHub Security Advisories for a repo.
|
|
42
|
+
* Requires `gh` CLI to be installed and authenticated.
|
|
43
|
+
*/
|
|
44
|
+
export declare function checkGitHubAdvisories(owner: string, repo: string, keywords: string[]): {
|
|
45
|
+
found: boolean;
|
|
46
|
+
matches: string[];
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Check if the vulnerable code still exists on the main branch.
|
|
50
|
+
* Now actually checks file existence + optionally greps for claim pattern.
|
|
51
|
+
*/
|
|
52
|
+
export declare function checkCodeOnMain(targetPath: string, file: string, claimKeywords?: string[]): {
|
|
53
|
+
exists: boolean;
|
|
54
|
+
lastModified: string | null;
|
|
55
|
+
reason: string;
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Layer 2: Full deep novelty check with API calls.
|
|
59
|
+
*/
|
|
60
|
+
export declare function deepNoveltyCheck(targetPath: string, file: string, claim: string, repoUrl: string): DeepCheckResult;
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Novelty Checker — determines if a bounty finding has already been reported/patched.
|
|
3
|
+
*
|
|
4
|
+
* Layer 1 (fast, local): Git recency + release notes keyword scan.
|
|
5
|
+
* Layer 2 (deep, API): GitHub Security Advisories + code comparison.
|
|
6
|
+
*/
|
|
7
|
+
import { safeExecSync } from '../runtime/safe-executor.js';
|
|
8
|
+
// ── Security Keywords ────────────────────────────────────────
|
|
9
|
+
const SECURITY_TERMS = new Set([
|
|
10
|
+
'fix', 'vuln', 'vulnerability', 'security', 'cve', 'patch',
|
|
11
|
+
'csrf', 'xss', 'ssrf', 'injection', 'bypass', 'sanitize',
|
|
12
|
+
'sanitization', 'auth', 'authentication', 'authorization',
|
|
13
|
+
'token', 'session', 'cookie', 'cors', 'origin', 'redirect',
|
|
14
|
+
'traversal', 'overflow', 'exploit', 'attack', 'malicious',
|
|
15
|
+
'rce', 'dos', 'denial', 'privilege', 'escalation', 'sqli',
|
|
16
|
+
]);
|
|
17
|
+
/**
|
|
18
|
+
* Extract security-relevant keywords from a claim description.
|
|
19
|
+
*/
|
|
20
|
+
export function extractSecurityKeywords(claim) {
|
|
21
|
+
const words = claim.toLowerCase()
|
|
22
|
+
.replace(/[^a-z0-9\s-]/g, ' ')
|
|
23
|
+
.split(/\s+/)
|
|
24
|
+
.filter(w => w.length > 2);
|
|
25
|
+
const commonWords = new Set([
|
|
26
|
+
'the', 'and', 'for', 'that', 'this', 'with', 'from', 'are', 'was',
|
|
27
|
+
'not', 'but', 'has', 'have', 'does', 'can', 'may', 'should', 'would',
|
|
28
|
+
'will', 'all', 'any', 'each', 'when', 'where', 'which', 'while',
|
|
29
|
+
'function', 'method', 'class', 'file', 'code', 'value', 'return',
|
|
30
|
+
'input', 'output', 'error', 'check', 'use', 'using', 'used',
|
|
31
|
+
]);
|
|
32
|
+
return [...new Set(words.filter(w => SECURITY_TERMS.has(w) || (!commonWords.has(w) && w.length > 3)))];
|
|
33
|
+
}
|
|
34
|
+
// ── Layer 1: Git-Based Checks ────────────────────────────────
|
|
35
|
+
/**
|
|
36
|
+
* Parse git log output and check for security-related commits
|
|
37
|
+
* that mention any of the given keywords.
|
|
38
|
+
*/
|
|
39
|
+
export function parseGitLogForSecurityFixes(gitLog, keywords) {
|
|
40
|
+
if (!gitLog.trim()) {
|
|
41
|
+
return { hasSecurityFixes: false, matchingCommits: [] };
|
|
42
|
+
}
|
|
43
|
+
const lines = gitLog.trim().split('\n');
|
|
44
|
+
const matchingCommits = [];
|
|
45
|
+
const keywordsLower = keywords.map(k => k.toLowerCase());
|
|
46
|
+
for (const line of lines) {
|
|
47
|
+
const lineLower = line.toLowerCase();
|
|
48
|
+
const hasKeyword = keywordsLower.some(kw => lineLower.includes(kw));
|
|
49
|
+
const hasSecurityIndicator = lineLower.includes('fix') ||
|
|
50
|
+
lineLower.includes('security') ||
|
|
51
|
+
lineLower.includes('cve') ||
|
|
52
|
+
lineLower.includes('patch') ||
|
|
53
|
+
lineLower.includes('vuln');
|
|
54
|
+
if (hasKeyword || (hasSecurityIndicator && hasKeyword)) {
|
|
55
|
+
matchingCommits.push(lineLower.trim());
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
hasSecurityFixes: matchingCommits.length > 0,
|
|
60
|
+
matchingCommits,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Parse git tag output into a clean array of tag names.
|
|
65
|
+
*/
|
|
66
|
+
export function parseGitTags(output) {
|
|
67
|
+
if (!output.trim())
|
|
68
|
+
return [];
|
|
69
|
+
return output.trim().split('\n').map(t => t.trim()).filter(t => t.length > 0);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Layer 1: Quick novelty check using only local git data.
|
|
73
|
+
*/
|
|
74
|
+
export function checkNoveltyLocal(targetPath, file, claim) {
|
|
75
|
+
const keywords = extractSecurityKeywords(claim);
|
|
76
|
+
// Check 1: Recent commits touching this file
|
|
77
|
+
const logResult = safeExecSync('git', [
|
|
78
|
+
'-C', targetPath,
|
|
79
|
+
'log', '--since=90 days ago', '--oneline', '--', file,
|
|
80
|
+
]);
|
|
81
|
+
if (logResult.exitCode === 0 && logResult.stdout.trim()) {
|
|
82
|
+
const gitCheck = parseGitLogForSecurityFixes(logResult.stdout, keywords);
|
|
83
|
+
if (gitCheck.hasSecurityFixes) {
|
|
84
|
+
return {
|
|
85
|
+
status: 'likely-patched',
|
|
86
|
+
reason: `Security-related commit found: ${gitCheck.matchingCommits[0]}`,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// Check 2: Recent release tags mentioning security keywords
|
|
91
|
+
const tagsResult = safeExecSync('git', [
|
|
92
|
+
'-C', targetPath,
|
|
93
|
+
'tag', '--sort=-creatordate',
|
|
94
|
+
]);
|
|
95
|
+
if (tagsResult.exitCode === 0 && tagsResult.stdout.trim()) {
|
|
96
|
+
const tags = parseGitTags(tagsResult.stdout).slice(0, 5);
|
|
97
|
+
for (const tag of tags) {
|
|
98
|
+
const tagMsgResult = safeExecSync('git', [
|
|
99
|
+
'-C', targetPath,
|
|
100
|
+
'tag', '-l', '--format=%(contents:subject)', tag,
|
|
101
|
+
]);
|
|
102
|
+
if (tagMsgResult.exitCode === 0) {
|
|
103
|
+
const tagMsg = tagMsgResult.stdout.toLowerCase();
|
|
104
|
+
const hasMatch = keywords.some(kw => tagMsg.includes(kw));
|
|
105
|
+
if (hasMatch) {
|
|
106
|
+
return {
|
|
107
|
+
status: 'likely-patched',
|
|
108
|
+
reason: `Release ${tag} mentions: ${keywords.filter(kw => tagMsg.includes(kw)).join(', ')}`,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return {
|
|
115
|
+
status: 'likely-novel',
|
|
116
|
+
reason: 'No recent security commits or releases match this finding',
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
// ── Layer 2: Deep Checks (API-based) ─────────────────────────
|
|
120
|
+
/**
|
|
121
|
+
* Check GitHub Security Advisories for a repo.
|
|
122
|
+
* Requires `gh` CLI to be installed and authenticated.
|
|
123
|
+
*/
|
|
124
|
+
export function checkGitHubAdvisories(owner, repo, keywords) {
|
|
125
|
+
const result = safeExecSync('gh', [
|
|
126
|
+
'api', `repos/${owner}/${repo}/security-advisories`,
|
|
127
|
+
'--jq', '.[].summary',
|
|
128
|
+
]);
|
|
129
|
+
if (result.exitCode !== 0) {
|
|
130
|
+
return { found: false, matches: [] };
|
|
131
|
+
}
|
|
132
|
+
const summaries = result.stdout.trim().split('\n').filter(s => s.length > 0);
|
|
133
|
+
const keywordsLower = keywords.map(k => k.toLowerCase());
|
|
134
|
+
const matches = summaries.filter(summary => {
|
|
135
|
+
const summaryLower = summary.toLowerCase();
|
|
136
|
+
return keywordsLower.some(kw => summaryLower.includes(kw));
|
|
137
|
+
});
|
|
138
|
+
return { found: matches.length > 0, matches };
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Check if the vulnerable code still exists on the main branch.
|
|
142
|
+
* Now actually checks file existence + optionally greps for claim pattern.
|
|
143
|
+
*/
|
|
144
|
+
export function checkCodeOnMain(targetPath, file, claimKeywords) {
|
|
145
|
+
// Check if file exists in working tree
|
|
146
|
+
const catResult = safeExecSync('git', [
|
|
147
|
+
'-C', targetPath,
|
|
148
|
+
'show', `HEAD:${file}`,
|
|
149
|
+
]);
|
|
150
|
+
if (catResult.exitCode !== 0) {
|
|
151
|
+
return { exists: false, lastModified: null, reason: 'File not found on HEAD' };
|
|
152
|
+
}
|
|
153
|
+
// File exists — check last modification
|
|
154
|
+
const logResult = safeExecSync('git', [
|
|
155
|
+
'-C', targetPath,
|
|
156
|
+
'log', '-1', '--format=%H %ai %s', '--', file,
|
|
157
|
+
]);
|
|
158
|
+
const lastModified = logResult.exitCode === 0 ? logResult.stdout.trim() : null;
|
|
159
|
+
// If keywords provided, grep for them in current file content
|
|
160
|
+
if (claimKeywords && claimKeywords.length > 0) {
|
|
161
|
+
const fileContent = catResult.stdout.toLowerCase();
|
|
162
|
+
const hasPattern = claimKeywords.some(kw => fileContent.includes(kw.toLowerCase()));
|
|
163
|
+
if (!hasPattern) {
|
|
164
|
+
return {
|
|
165
|
+
exists: false,
|
|
166
|
+
lastModified,
|
|
167
|
+
reason: 'File exists but vulnerable pattern not found in current HEAD',
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return { exists: true, lastModified, reason: 'File exists and pattern present' };
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Layer 2: Full deep novelty check with API calls.
|
|
175
|
+
*/
|
|
176
|
+
export function deepNoveltyCheck(targetPath, file, claim, repoUrl) {
|
|
177
|
+
const keywords = extractSecurityKeywords(claim);
|
|
178
|
+
// Parse owner/repo from URL
|
|
179
|
+
let owner = '';
|
|
180
|
+
let repo = '';
|
|
181
|
+
const ghMatch = repoUrl.match(/github\.com\/([^/]+)\/([^/.]+)/);
|
|
182
|
+
if (ghMatch) {
|
|
183
|
+
owner = ghMatch[1];
|
|
184
|
+
repo = ghMatch[2];
|
|
185
|
+
}
|
|
186
|
+
// Check 1: GitHub Security Advisories
|
|
187
|
+
let advisories = [];
|
|
188
|
+
if (owner && repo) {
|
|
189
|
+
const advisory = checkGitHubAdvisories(owner, repo, keywords);
|
|
190
|
+
advisories = advisory.matches;
|
|
191
|
+
}
|
|
192
|
+
// Check 2: Code still exists on main
|
|
193
|
+
const codeCheck = checkCodeOnMain(targetPath, file, keywords);
|
|
194
|
+
// Check 3: Git recency (reuse Layer 1)
|
|
195
|
+
const localCheck = checkNoveltyLocal(targetPath, file, claim);
|
|
196
|
+
// Synthesize verdict
|
|
197
|
+
if (advisories.length > 0) {
|
|
198
|
+
return {
|
|
199
|
+
status: 'likely-patched',
|
|
200
|
+
reason: `GitHub advisory matches: ${advisories[0]}`,
|
|
201
|
+
advisories,
|
|
202
|
+
hacktivityMatches: [],
|
|
203
|
+
codeStillVulnerable: codeCheck.exists,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
if (localCheck.status === 'likely-patched') {
|
|
207
|
+
return {
|
|
208
|
+
status: 'likely-patched',
|
|
209
|
+
reason: localCheck.reason,
|
|
210
|
+
advisories: [],
|
|
211
|
+
hacktivityMatches: [],
|
|
212
|
+
codeStillVulnerable: codeCheck.exists,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
return {
|
|
216
|
+
status: 'likely-novel',
|
|
217
|
+
reason: 'No advisories found, no security commits match, code unchanged',
|
|
218
|
+
advisories: [],
|
|
219
|
+
hacktivityMatches: [],
|
|
220
|
+
codeStillVulnerable: codeCheck.exists,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
//# sourceMappingURL=novelty-checker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"novelty-checker.js","sourceRoot":"","sources":["../../src/lib/novelty-checker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { WatchlistRepo } from './watchlist.js';
|
|
2
|
+
export interface BountyProgram {
|
|
3
|
+
handle: string;
|
|
4
|
+
name: string;
|
|
5
|
+
repos: string[];
|
|
6
|
+
asset: string;
|
|
7
|
+
}
|
|
8
|
+
export interface ProgramRegistry {
|
|
9
|
+
programs: BountyProgram[];
|
|
10
|
+
}
|
|
11
|
+
export declare function loadPrograms(path: string): Promise<ProgramRegistry>;
|
|
12
|
+
export declare function findNewRepos(registry: ProgramRegistry, existingUrls: string[]): WatchlistRepo[];
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
export async function loadPrograms(path) {
|
|
3
|
+
const raw = await readFile(path, 'utf-8');
|
|
4
|
+
return JSON.parse(raw);
|
|
5
|
+
}
|
|
6
|
+
export function findNewRepos(registry, existingUrls) {
|
|
7
|
+
const existing = new Set(existingUrls);
|
|
8
|
+
const newRepos = [];
|
|
9
|
+
for (const program of registry.programs) {
|
|
10
|
+
for (const repoUrl of program.repos) {
|
|
11
|
+
if (!existing.has(repoUrl)) {
|
|
12
|
+
newRepos.push({ url: repoUrl, program: program.handle, asset: program.asset, source: 'program-discovery', priority: 'fresh' });
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return newRepos;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=program-registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"program-registry.js","sourceRoot":"","sources":["../../src/lib/program-registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAc5C,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAAY;IAC7C,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAC1C,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAoB,CAAC;AAC5C,CAAC;AAED,MAAM,UAAU,YAAY,CAC1B,QAAyB,EACzB,YAAsB;IAEtB,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,YAAY,CAAC,CAAC;IACvC,MAAM,QAAQ,GAAoB,EAAE,CAAC;IACrC,KAAK,MAAM,OAAO,IAAI,QAAQ,CAAC,QAAQ,EAAE,CAAC;QACxC,KAAK,MAAM,OAAO,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;YACpC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC3B,QAAQ,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,mBAAmB,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;YACjI,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export async function retryWithBackoff(fn, options) {
|
|
2
|
+
const maxRetries = options?.maxRetries ?? 3;
|
|
3
|
+
const baseDelay = options?.baseDelayMs ?? 1000;
|
|
4
|
+
let lastError;
|
|
5
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
6
|
+
try {
|
|
7
|
+
return await fn();
|
|
8
|
+
}
|
|
9
|
+
catch (err) {
|
|
10
|
+
lastError = err instanceof Error ? err : new Error(String(err));
|
|
11
|
+
if (attempt < maxRetries) {
|
|
12
|
+
const delay = baseDelay * Math.pow(4, attempt);
|
|
13
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
throw lastError;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=retry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry.js","sourceRoot":"","sources":["../../src/lib/retry.ts"],"names":[],"mappings":"AAKA,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,EAAoB,EACpB,OAAsB;IAEtB,MAAM,UAAU,GAAG,OAAO,EAAE,UAAU,IAAI,CAAC,CAAC;IAC5C,MAAM,SAAS,GAAG,OAAO,EAAE,WAAW,IAAI,IAAI,CAAC;IAC/C,IAAI,SAA4B,CAAC;IAEjC,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,UAAU,EAAE,OAAO,EAAE,EAAE,CAAC;QACvD,IAAI,CAAC;YACH,OAAO,MAAM,EAAE,EAAE,CAAC;QACpB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,SAAS,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;YAChE,IAAI,OAAO,GAAG,UAAU,EAAE,CAAC;gBACzB,MAAM,KAAK,GAAG,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;gBAC/C,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;YAC3D,CAAC;QACH,CAAC;IACH,CAAC;IACD,MAAM,SAAS,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface WatchlistRepo {
|
|
2
|
+
url: string;
|
|
3
|
+
program: string;
|
|
4
|
+
asset: string;
|
|
5
|
+
source: 'seed' | 'program-discovery' | 'manual';
|
|
6
|
+
priority: 'normal' | 'fresh';
|
|
7
|
+
}
|
|
8
|
+
export interface WatchlistConfig {
|
|
9
|
+
repos: WatchlistRepo[];
|
|
10
|
+
}
|
|
11
|
+
export interface RepoState {
|
|
12
|
+
lastSha: string;
|
|
13
|
+
lastScanned: string;
|
|
14
|
+
}
|
|
15
|
+
export interface ScanState {
|
|
16
|
+
repoStates: Record<string, RepoState>;
|
|
17
|
+
reportedFindings: string[];
|
|
18
|
+
}
|
|
19
|
+
export declare function loadWatchlist(path: string): Promise<WatchlistConfig>;
|
|
20
|
+
export declare function loadScanState(path: string): Promise<ScanState>;
|
|
21
|
+
export declare function saveScanState(path: string, state: ScanState): Promise<void>;
|
|
22
|
+
export declare function addRepo(configPath: string, repo: WatchlistRepo): Promise<void>;
|
|
23
|
+
export declare function removeRepo(configPath: string, url: string): Promise<void>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { readFile, writeFile } from 'node:fs/promises';
|
|
2
|
+
export async function loadWatchlist(path) {
|
|
3
|
+
const raw = await readFile(path, 'utf-8');
|
|
4
|
+
return JSON.parse(raw);
|
|
5
|
+
}
|
|
6
|
+
export async function loadScanState(path) {
|
|
7
|
+
try {
|
|
8
|
+
const raw = await readFile(path, 'utf-8');
|
|
9
|
+
return JSON.parse(raw);
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return { repoStates: {}, reportedFindings: [] };
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export async function saveScanState(path, state) {
|
|
16
|
+
await writeFile(path, JSON.stringify(state, null, 2));
|
|
17
|
+
}
|
|
18
|
+
export async function addRepo(configPath, repo) {
|
|
19
|
+
const config = await loadWatchlist(configPath);
|
|
20
|
+
const exists = config.repos.some(r => r.url === repo.url);
|
|
21
|
+
if (exists)
|
|
22
|
+
return;
|
|
23
|
+
config.repos.push(repo);
|
|
24
|
+
await writeFile(configPath, JSON.stringify(config, null, 2));
|
|
25
|
+
}
|
|
26
|
+
export async function removeRepo(configPath, url) {
|
|
27
|
+
const config = await loadWatchlist(configPath);
|
|
28
|
+
config.repos = config.repos.filter(r => r.url !== url);
|
|
29
|
+
await writeFile(configPath, JSON.stringify(config, null, 2));
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=watchlist.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watchlist.js","sourceRoot":"","sources":["../../src/lib/watchlist.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAwBvD,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAAY;IAC9C,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAC1C,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAoB,CAAC;AAC5C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAAY;IAC9C,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAC1C,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAc,CAAC;IACtC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,UAAU,EAAE,EAAE,EAAE,gBAAgB,EAAE,EAAE,EAAE,CAAC;IAClD,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAAY,EAAE,KAAgB;IAChE,MAAM,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AACxD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,UAAkB,EAAE,IAAmB;IACnE,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,UAAU,CAAC,CAAC;IAC/C,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,IAAI,CAAC,GAAG,CAAC,CAAC;IAC1D,IAAI,MAAM;QAAE,OAAO;IACnB,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxB,MAAM,SAAS,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AAC/D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,UAAkB,EAAE,GAAW;IAC9D,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,UAAU,CAAC,CAAC;IAC/C,MAAM,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC;IACvD,MAAM,SAAS,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AAC/D,CAAC"}
|