agentaudit 3.9.37 → 3.9.39
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/cli.mjs +55 -14
- package/package.json +1 -1
package/cli.mjs
CHANGED
|
@@ -537,6 +537,22 @@ const SKIP_EXTENSIONS = new Set([
|
|
|
537
537
|
'.woff2', '.ttf', '.eot', '.mp3', '.mp4', '.zip', '.tar', '.gz',
|
|
538
538
|
'.map', '.min.js', '.min.css', '.d.ts', '.pyc', '.pyo', '.so',
|
|
539
539
|
'.dylib', '.dll', '.exe', '.bin', '.dat', '.db', '.sqlite',
|
|
540
|
+
'.snap', '.patch', '.diff', '.log', '.csv', '.tsv', '.parquet',
|
|
541
|
+
]);
|
|
542
|
+
// Files that are never security-relevant (config/docs/metadata)
|
|
543
|
+
const SKIP_FILES = new Set([
|
|
544
|
+
'license', 'license.md', 'license.txt', 'licence', 'licence.md',
|
|
545
|
+
'changelog.md', 'changelog', 'changes.md', 'history.md',
|
|
546
|
+
'contributing.md', 'contributors.md', 'authors', 'authors.md',
|
|
547
|
+
'code_of_conduct.md', 'security.md', 'funding.yml',
|
|
548
|
+
'.prettierrc', '.prettierrc.json', '.prettierrc.js', '.prettierignore',
|
|
549
|
+
'.eslintignore', '.gitignore', '.gitattributes', '.npmignore',
|
|
550
|
+
'.editorconfig', '.browserslistrc', '.nvmrc', '.node-version',
|
|
551
|
+
'.dockerignore', 'renovate.json', '.renovaterc',
|
|
552
|
+
'jest.config.js', 'jest.config.ts', 'vitest.config.ts', 'vitest.config.js',
|
|
553
|
+
'.babelrc', 'babel.config.js', 'babel.config.json',
|
|
554
|
+
'postcss.config.js', 'postcss.config.mjs', 'tailwind.config.js', 'tailwind.config.ts',
|
|
555
|
+
'prettier.config.js', 'prettier.config.mjs',
|
|
540
556
|
]);
|
|
541
557
|
|
|
542
558
|
function collectFiles(dir, basePath = '', collected = [], totalSize = { bytes: 0 }) {
|
|
@@ -555,6 +571,7 @@ function collectFiles(dir, basePath = '', collected = [], totalSize = { bytes: 0
|
|
|
555
571
|
} else {
|
|
556
572
|
const ext = path.extname(entry.name).toLowerCase();
|
|
557
573
|
if (SKIP_EXTENSIONS.has(ext)) continue;
|
|
574
|
+
if (SKIP_FILES.has(entry.name.toLowerCase())) continue;
|
|
558
575
|
try {
|
|
559
576
|
const stat = fs.statSync(fullPath);
|
|
560
577
|
if (stat.size > MAX_FILE_SIZE || stat.size === 0) continue;
|
|
@@ -1446,7 +1463,9 @@ async function auditRepo(url) {
|
|
|
1446
1463
|
|
|
1447
1464
|
// Build code chunks for multi-pass analysis.
|
|
1448
1465
|
// Budget ~45k tokens (~180k chars) per chunk for code, leaving room for prompt + output.
|
|
1449
|
-
|
|
1466
|
+
// ~15k tokens per chunk for code → fits comfortably in 32k+ context models
|
|
1467
|
+
// with room for system prompt (~2k tokens) + output (4k tokens)
|
|
1468
|
+
const MAX_CHUNK_CHARS = 60_000;
|
|
1450
1469
|
const chunks = []; // array of code block strings
|
|
1451
1470
|
let currentChunk = '';
|
|
1452
1471
|
let currentChars = 0;
|
|
@@ -1573,6 +1592,34 @@ async function auditRepo(url) {
|
|
|
1573
1592
|
}
|
|
1574
1593
|
|
|
1575
1594
|
// ── LLM call helper (reused for multi-pass) ──
|
|
1595
|
+
// Determine optimal max_tokens based on model context size
|
|
1596
|
+
// For large-context models (128k+) we can afford 8192 output tokens
|
|
1597
|
+
// For medium (32k-128k) use 4096, for small (<32k) use 2048
|
|
1598
|
+
let outputTokenBudget = 4096; // safe default
|
|
1599
|
+
if (resolvedProvider.id === 'openrouter') {
|
|
1600
|
+
try {
|
|
1601
|
+
const modelInfoRes = await fetch(`https://openrouter.ai/api/v1/models`, {
|
|
1602
|
+
signal: AbortSignal.timeout(5000),
|
|
1603
|
+
headers: { 'HTTP-Referer': 'https://agentaudit.dev' },
|
|
1604
|
+
});
|
|
1605
|
+
if (modelInfoRes.ok) {
|
|
1606
|
+
const modelData = await modelInfoRes.json();
|
|
1607
|
+
const modelInfo = modelData.data?.find(m => m.id === actualModel);
|
|
1608
|
+
if (modelInfo?.context_length) {
|
|
1609
|
+
const ctx = modelInfo.context_length;
|
|
1610
|
+
outputTokenBudget = ctx >= 128_000 ? 8192 : ctx >= 64_000 ? 4096 : ctx >= 32_000 ? 2048 : 1024;
|
|
1611
|
+
if (process.argv.includes('--debug')) {
|
|
1612
|
+
console.log(` ${c.dim} Model context: ${ctx.toLocaleString()} tokens → max_tokens: ${outputTokenBudget}${c.reset}`);
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
} catch { /* ignore — use default */ }
|
|
1617
|
+
} else if (resolvedProvider.id === 'anthropic') {
|
|
1618
|
+
outputTokenBudget = 8192; // Claude models have 200k context
|
|
1619
|
+
} else if (resolvedProvider.id === 'openai') {
|
|
1620
|
+
outputTokenBudget = 8192; // GPT-4o has 128k context
|
|
1621
|
+
}
|
|
1622
|
+
|
|
1576
1623
|
async function callLLM(codeContent, passLabel) {
|
|
1577
1624
|
const systemPrompt = auditPrompt || 'You are a security auditor. Analyze the code and report findings as JSON.';
|
|
1578
1625
|
const userMessage = [
|
|
@@ -1603,11 +1650,11 @@ async function auditRepo(url) {
|
|
|
1603
1650
|
},
|
|
1604
1651
|
body: JSON.stringify({
|
|
1605
1652
|
model: modelOverride || 'claude-sonnet-4-20250514',
|
|
1606
|
-
max_tokens:
|
|
1653
|
+
max_tokens: outputTokenBudget,
|
|
1607
1654
|
system: systemPrompt,
|
|
1608
1655
|
messages: [{ role: 'user', content: userMessage }],
|
|
1609
1656
|
}),
|
|
1610
|
-
signal: AbortSignal.timeout(
|
|
1657
|
+
signal: AbortSignal.timeout(180_000),
|
|
1611
1658
|
});
|
|
1612
1659
|
const data = await res.json();
|
|
1613
1660
|
if (data.error) {
|
|
@@ -1651,13 +1698,13 @@ async function auditRepo(url) {
|
|
|
1651
1698
|
headers: { 'Content-Type': 'application/json', ...authHeaders },
|
|
1652
1699
|
body: JSON.stringify({
|
|
1653
1700
|
model: modelName,
|
|
1654
|
-
max_tokens:
|
|
1701
|
+
max_tokens: outputTokenBudget,
|
|
1655
1702
|
messages: [
|
|
1656
1703
|
{ role: 'system', content: systemPrompt },
|
|
1657
1704
|
{ role: 'user', content: userMessage },
|
|
1658
1705
|
],
|
|
1659
1706
|
}),
|
|
1660
|
-
signal: AbortSignal.timeout(resolvedProvider.id === 'ollama' ? 300_000 :
|
|
1707
|
+
signal: AbortSignal.timeout(resolvedProvider.id === 'ollama' ? 300_000 : 180_000),
|
|
1661
1708
|
});
|
|
1662
1709
|
const data = await res.json();
|
|
1663
1710
|
if (data.error) {
|
|
@@ -1700,15 +1747,9 @@ async function auditRepo(url) {
|
|
|
1700
1747
|
const result = await callLLM(chunks[i], `pass ${i + 1}`);
|
|
1701
1748
|
|
|
1702
1749
|
if (result.error) {
|
|
1703
|
-
console.log(` ${c.red}failed${c.reset}`);
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
if (/context.length|maximum.*tokens|too.many.tokens/i.test(errMsg)) {
|
|
1707
|
-
console.log(` ${c.dim}This model's context window is too small even for chunked analysis.${c.reset}`);
|
|
1708
|
-
console.log(` ${c.dim}Try: --model anthropic/claude-sonnet-4 (200k) or --model openai/gpt-4o (128k)${c.reset}`);
|
|
1709
|
-
}
|
|
1710
|
-
try { fs.rmSync(tmpDir, { recursive: true, force: true }); } catch {}
|
|
1711
|
-
return null;
|
|
1750
|
+
console.log(` ${c.red}failed${c.reset} ${c.dim}(${result.error.slice(0, 80)})${c.reset}`);
|
|
1751
|
+
// Don't abort on individual pass failures — continue with remaining chunks
|
|
1752
|
+
continue;
|
|
1712
1753
|
}
|
|
1713
1754
|
|
|
1714
1755
|
if (!result.report) {
|