agentaudit 3.9.16 → 3.9.18
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/README.md +16 -5
- package/cli.mjs +110 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -207,6 +207,7 @@ Then ask your agent: *"Check which MCP servers I have installed and audit any un
|
|
|
207
207
|
| `agentaudit scan <url> --deep` | Deep audit (same as `audit`) | `agentaudit scan https://github.com/owner/repo --deep` |
|
|
208
208
|
| `agentaudit audit <url>` | Deep LLM-powered 3-pass audit (~30s) | `agentaudit audit https://github.com/owner/repo` |
|
|
209
209
|
| `agentaudit lookup <name>` | Look up package in trust registry | `agentaudit lookup fastmcp` |
|
|
210
|
+
| `agentaudit check <name\|url>` | Lookup + auto-audit if not found | `agentaudit check https://github.com/owner/repo` |
|
|
210
211
|
| `agentaudit setup` | Register agent + configure API key | `agentaudit setup` |
|
|
211
212
|
|
|
212
213
|
### Global Flags
|
|
@@ -235,7 +236,7 @@ Then ask your agent: *"Check which MCP servers I have installed and audit any un
|
|
|
235
236
|
|---|---------------------|---------------------|
|
|
236
237
|
| **Speed** | ~2 seconds | ~30 seconds |
|
|
237
238
|
| **Method** | Regex pattern matching | LLM-powered 3-pass analysis |
|
|
238
|
-
| **API key needed** | No | Yes (
|
|
239
|
+
| **API key needed** | No | Yes (Anthropic, OpenAI, or OpenRouter) |
|
|
239
240
|
| **False positives** | Higher (regex limitations) | Very low (context-aware) |
|
|
240
241
|
| **Detects** | Common patterns (injection, secrets, eval) | Complex attack chains, AI-specific threats, obfuscation |
|
|
241
242
|
| **Best for** | Quick triage, CI pipelines | Critical packages, pre-production review |
|
|
@@ -434,6 +435,8 @@ export AGENTAUDIT_API_KEY=asf_your_key_here
|
|
|
434
435
|
| `AGENTAUDIT_API_KEY` | API key for registry access |
|
|
435
436
|
| `ANTHROPIC_API_KEY` | Anthropic API key for deep audits (Claude) |
|
|
436
437
|
| `OPENAI_API_KEY` | OpenAI API key for deep audits (GPT-4o) |
|
|
438
|
+
| `OPENROUTER_API_KEY` | OpenRouter API key (access 200+ models) |
|
|
439
|
+
| `OPENROUTER_MODEL` | Model to use via OpenRouter (default: `anthropic/claude-sonnet-4`) |
|
|
437
440
|
| `NO_COLOR` | Disable ANSI colors ([no-color.org](https://no-color.org)) |
|
|
438
441
|
|
|
439
442
|
---
|
|
@@ -465,23 +468,31 @@ Or use without installing: `npx agentaudit`
|
|
|
465
468
|
|
|
466
469
|
### Setting up your LLM key for deep audits
|
|
467
470
|
|
|
468
|
-
The `audit` command supports **
|
|
471
|
+
The `audit` command supports **three LLM providers**. Set one of these environment variables:
|
|
469
472
|
|
|
470
473
|
```bash
|
|
471
474
|
# Linux / macOS
|
|
472
|
-
export ANTHROPIC_API_KEY=sk-ant-...
|
|
473
|
-
export OPENAI_API_KEY=sk-...
|
|
475
|
+
export ANTHROPIC_API_KEY=sk-ant-... # Recommended (Claude Sonnet)
|
|
476
|
+
export OPENAI_API_KEY=sk-... # Alternative (GPT-4o)
|
|
477
|
+
export OPENROUTER_API_KEY=sk-or-... # 200+ models via OpenRouter
|
|
474
478
|
|
|
475
479
|
# Windows (PowerShell)
|
|
476
480
|
$env:ANTHROPIC_API_KEY = "sk-ant-..."
|
|
477
481
|
$env:OPENAI_API_KEY = "sk-..."
|
|
482
|
+
$env:OPENROUTER_API_KEY = "sk-or-..."
|
|
478
483
|
|
|
479
484
|
# Windows (CMD)
|
|
480
485
|
set ANTHROPIC_API_KEY=sk-ant-...
|
|
481
486
|
set OPENAI_API_KEY=sk-...
|
|
487
|
+
set OPENROUTER_API_KEY=sk-or-...
|
|
482
488
|
```
|
|
483
489
|
|
|
484
|
-
**
|
|
490
|
+
**Provider priority:** Anthropic > OpenAI > OpenRouter. The active provider is shown during the audit.
|
|
491
|
+
|
|
492
|
+
**OpenRouter model selection:** By default, OpenRouter uses `anthropic/claude-sonnet-4`. Override with:
|
|
493
|
+
```bash
|
|
494
|
+
export OPENROUTER_MODEL=google/gemini-2.5-pro # or any model on openrouter.ai
|
|
495
|
+
```
|
|
485
496
|
|
|
486
497
|
**Troubleshooting:** If you see `API error: Incorrect API key`, double-check your key is valid and has credits. Use `--debug` to see the full API response.
|
|
487
498
|
|
package/cli.mjs
CHANGED
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
* agentaudit scan <repo-url> [--deep] Quick scan (or deep audit with --deep)
|
|
9
9
|
* agentaudit audit <repo-url> Deep LLM-powered security audit
|
|
10
10
|
* agentaudit lookup <name> Look up package in registry
|
|
11
|
+
* agentaudit check <name|url> Lookup + auto-audit if not found
|
|
12
|
+
* agentaudit status Check configured API keys + providers
|
|
11
13
|
* agentaudit setup Register + configure API key
|
|
12
14
|
*
|
|
13
15
|
* Global flags: --json, --quiet, --no-color
|
|
@@ -24,6 +26,26 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
|
24
26
|
const SKILL_DIR = path.resolve(__dirname);
|
|
25
27
|
const REGISTRY_URL = 'https://agentaudit.dev';
|
|
26
28
|
|
|
29
|
+
// ── Provider resolution ────
|
|
30
|
+
function resolveProvider(preferred, keys) {
|
|
31
|
+
const providers = {
|
|
32
|
+
anthropic: keys.anthropicKey ? { id: 'anthropic', label: 'Anthropic (Claude)', key: keys.anthropicKey } : null,
|
|
33
|
+
openai: keys.openaiKey ? { id: 'openai', label: 'OpenAI (GPT-4o)', key: keys.openaiKey } : null,
|
|
34
|
+
openrouter: keys.openrouterKey ? { id: 'openrouter', label: `OpenRouter (${process.env.OPENROUTER_MODEL || 'anthropic/claude-sonnet-4'})`, key: keys.openrouterKey } : null,
|
|
35
|
+
};
|
|
36
|
+
// Aliases
|
|
37
|
+
const aliases = { claude: 'anthropic', gpt: 'openai', 'gpt-4o': 'openai', 'gpt4': 'openai', or: 'openrouter' };
|
|
38
|
+
|
|
39
|
+
if (preferred) {
|
|
40
|
+
const resolved = aliases[preferred] || preferred;
|
|
41
|
+
const p = providers[resolved];
|
|
42
|
+
if (!p) return null; // requested provider not available
|
|
43
|
+
return p;
|
|
44
|
+
}
|
|
45
|
+
// Auto-detect: Anthropic > OpenAI > OpenRouter
|
|
46
|
+
return providers.anthropic || providers.openai || providers.openrouter || null;
|
|
47
|
+
}
|
|
48
|
+
|
|
27
49
|
// ── Global flags (set in main before command routing) ────
|
|
28
50
|
let jsonMode = false;
|
|
29
51
|
let quietMode = false;
|
|
@@ -1311,9 +1333,15 @@ async function auditRepo(url) {
|
|
|
1311
1333
|
const openaiKey = process.env.OPENAI_API_KEY;
|
|
1312
1334
|
const openrouterKey = process.env.OPENROUTER_API_KEY;
|
|
1313
1335
|
const openrouterModel = process.env.OPENROUTER_MODEL || 'anthropic/claude-sonnet-4';
|
|
1314
|
-
|
|
1336
|
+
|
|
1337
|
+
// --provider flag overrides auto-detection
|
|
1338
|
+
const providerFlag = process.argv.find(a => a.startsWith('--provider='))?.split('=')[1]?.toLowerCase()
|
|
1339
|
+
|| (process.argv.includes('--provider') ? process.argv[process.argv.indexOf('--provider') + 1]?.toLowerCase() : null);
|
|
1340
|
+
|
|
1341
|
+
const resolvedProvider = resolveProvider(providerFlag, { anthropicKey, openaiKey, openrouterKey });
|
|
1342
|
+
const activeProvider = resolvedProvider?.label || null;
|
|
1315
1343
|
|
|
1316
|
-
if (!
|
|
1344
|
+
if (!resolvedProvider) {
|
|
1317
1345
|
// No LLM API key — clear explanation
|
|
1318
1346
|
console.log();
|
|
1319
1347
|
console.log(` ${c.yellow}No LLM API key found.${c.reset} The ${c.bold}audit${c.reset} command needs an LLM to analyze code.`);
|
|
@@ -1396,11 +1424,11 @@ async function auditRepo(url) {
|
|
|
1396
1424
|
let _lastLlmText = '';
|
|
1397
1425
|
|
|
1398
1426
|
try {
|
|
1399
|
-
if (
|
|
1427
|
+
if (resolvedProvider.id === 'anthropic') {
|
|
1400
1428
|
const res = await fetch('https://api.anthropic.com/v1/messages', {
|
|
1401
1429
|
method: 'POST',
|
|
1402
1430
|
headers: {
|
|
1403
|
-
'x-api-key':
|
|
1431
|
+
'x-api-key': resolvedProvider.key,
|
|
1404
1432
|
'anthropic-version': '2023-06-01',
|
|
1405
1433
|
'content-type': 'application/json',
|
|
1406
1434
|
},
|
|
@@ -1422,17 +1450,17 @@ async function auditRepo(url) {
|
|
|
1422
1450
|
const text = data.content?.[0]?.text || '';
|
|
1423
1451
|
_lastLlmText = text;
|
|
1424
1452
|
report = extractJSON(text);
|
|
1425
|
-
} else
|
|
1426
|
-
|
|
1453
|
+
} else {
|
|
1454
|
+
// OpenAI or OpenRouter (both use OpenAI-compatible API)
|
|
1455
|
+
const isOpenRouter = resolvedProvider.id === 'openrouter';
|
|
1427
1456
|
const apiUrl = isOpenRouter ? 'https://openrouter.ai/api/v1/chat/completions' : 'https://api.openai.com/v1/chat/completions';
|
|
1428
|
-
const apiToken = isOpenRouter ? openrouterKey : openaiKey;
|
|
1429
1457
|
const modelName = isOpenRouter ? (process.env.OPENROUTER_MODEL || 'anthropic/claude-sonnet-4') : 'gpt-4o';
|
|
1430
1458
|
const extraHeaders = isOpenRouter ? { 'HTTP-Referer': 'https://agentaudit.dev', 'X-Title': 'AgentAudit' } : {};
|
|
1431
1459
|
|
|
1432
1460
|
const res = await fetch(apiUrl, {
|
|
1433
1461
|
method: 'POST',
|
|
1434
1462
|
headers: {
|
|
1435
|
-
'Authorization': `Bearer ${
|
|
1463
|
+
'Authorization': `Bearer ${resolvedProvider.key}`,
|
|
1436
1464
|
'Content-Type': 'application/json',
|
|
1437
1465
|
...extraHeaders,
|
|
1438
1466
|
},
|
|
@@ -1654,12 +1682,15 @@ async function main() {
|
|
|
1654
1682
|
console.log(` ${c.cyan}agentaudit scan${c.reset} <url> ${c.dim}--deep${c.reset} Deep audit (same as audit)`);
|
|
1655
1683
|
console.log(` ${c.cyan}agentaudit audit${c.reset} <url> [url...] Deep LLM-powered security audit`);
|
|
1656
1684
|
console.log(` ${c.cyan}agentaudit lookup${c.reset} <name> Look up package in registry`);
|
|
1685
|
+
console.log(` ${c.cyan}agentaudit check${c.reset} <name|url> Lookup + auto-audit if not found`);
|
|
1686
|
+
console.log(` ${c.cyan}agentaudit status${c.reset} Check API keys + active provider`);
|
|
1657
1687
|
console.log(` ${c.cyan}agentaudit setup${c.reset} Register + configure API key`);
|
|
1658
1688
|
console.log();
|
|
1659
1689
|
console.log(` ${c.bold}Global flags:${c.reset}`);
|
|
1660
1690
|
console.log(` ${c.dim}--json Output JSON to stdout (machine-readable)${c.reset}`);
|
|
1661
1691
|
console.log(` ${c.dim}--quiet Suppress banner and tree visualization${c.reset}`);
|
|
1662
1692
|
console.log(` ${c.dim}--no-color Disable ANSI colors (also: NO_COLOR env)${c.reset}`);
|
|
1693
|
+
console.log(` ${c.dim}--provider Force LLM provider (anthropic, openai, openrouter)${c.reset}`);
|
|
1663
1694
|
console.log();
|
|
1664
1695
|
console.log(` ${c.bold}Quick Scan${c.reset} vs ${c.bold}Deep Audit${c.reset}:`);
|
|
1665
1696
|
console.log(` ${c.dim}scan = fast regex-based static analysis (~2s)${c.reset}`);
|
|
@@ -1675,13 +1706,18 @@ async function main() {
|
|
|
1675
1706
|
console.log(` agentaudit audit https://github.com/owner/repo`);
|
|
1676
1707
|
console.log(` agentaudit lookup fastmcp --json`);
|
|
1677
1708
|
console.log();
|
|
1678
|
-
console.log(` ${c.bold}For deep audits,${c.reset} set an LLM API key:`);
|
|
1709
|
+
console.log(` ${c.bold}For deep audits,${c.reset} set an LLM API key (any one):`);
|
|
1679
1710
|
if (process.platform === 'win32') {
|
|
1680
1711
|
console.log(` ${c.dim}PowerShell: $env:ANTHROPIC_API_KEY = "sk-ant-..."${c.reset}`);
|
|
1712
|
+
console.log(` ${c.dim} $env:OPENAI_API_KEY = "sk-..."${c.reset}`);
|
|
1713
|
+
console.log(` ${c.dim} $env:OPENROUTER_API_KEY = "sk-or-..."${c.reset}`);
|
|
1681
1714
|
console.log(` ${c.dim}CMD: set ANTHROPIC_API_KEY=sk-ant-...${c.reset}`);
|
|
1682
|
-
console.log(` ${c.dim}
|
|
1715
|
+
console.log(` ${c.dim} set OPENAI_API_KEY=sk-...${c.reset}`);
|
|
1716
|
+
console.log(` ${c.dim} set OPENROUTER_API_KEY=sk-or-...${c.reset}`);
|
|
1683
1717
|
} else {
|
|
1684
|
-
console.log(` ${c.dim}export ANTHROPIC_API_KEY=sk-ant-...${c.reset}
|
|
1718
|
+
console.log(` ${c.dim}export ANTHROPIC_API_KEY=sk-ant-...${c.reset}`);
|
|
1719
|
+
console.log(` ${c.dim}export OPENAI_API_KEY=sk-...${c.reset}`);
|
|
1720
|
+
console.log(` ${c.dim}export OPENROUTER_API_KEY=sk-or-...${c.reset} ${c.dim}(200+ models, set OPENROUTER_MODEL to pick)${c.reset}`);
|
|
1685
1721
|
}
|
|
1686
1722
|
console.log();
|
|
1687
1723
|
console.log(` ${c.bold}Or use as MCP server${c.reset} in Cursor/Claude ${c.dim}(no extra API key needed):${c.reset}`);
|
|
@@ -1702,6 +1738,69 @@ async function main() {
|
|
|
1702
1738
|
return;
|
|
1703
1739
|
}
|
|
1704
1740
|
|
|
1741
|
+
if (command === 'status') {
|
|
1742
|
+
console.log(` ${c.bold}LLM Providers:${c.reset}`);
|
|
1743
|
+
console.log();
|
|
1744
|
+
const keys = {
|
|
1745
|
+
anthropicKey: process.env.ANTHROPIC_API_KEY,
|
|
1746
|
+
openaiKey: process.env.OPENAI_API_KEY,
|
|
1747
|
+
openrouterKey: process.env.OPENROUTER_API_KEY,
|
|
1748
|
+
};
|
|
1749
|
+
const checks = [
|
|
1750
|
+
{ name: 'Anthropic', env: 'ANTHROPIC_API_KEY', key: keys.anthropicKey, testUrl: 'https://api.anthropic.com/v1/messages', testHeaders: (k) => ({ 'x-api-key': k, 'anthropic-version': '2023-06-01', 'content-type': 'application/json' }), testBody: JSON.stringify({ model: 'claude-sonnet-4-20250514', max_tokens: 1, messages: [{ role: 'user', content: 'hi' }] }) },
|
|
1751
|
+
{ name: 'OpenAI', env: 'OPENAI_API_KEY', key: keys.openaiKey, testUrl: 'https://api.openai.com/v1/models', testHeaders: (k) => ({ 'Authorization': `Bearer ${k}` }), testBody: null },
|
|
1752
|
+
{ name: 'OpenRouter', env: 'OPENROUTER_API_KEY', key: keys.openrouterKey, testUrl: 'https://openrouter.ai/api/v1/models', testHeaders: (k) => ({ 'Authorization': `Bearer ${k}` }), testBody: null },
|
|
1753
|
+
];
|
|
1754
|
+
|
|
1755
|
+
for (const p of checks) {
|
|
1756
|
+
if (!p.key) {
|
|
1757
|
+
console.log(` ${c.dim}○${c.reset} ${p.name.padEnd(12)} ${c.dim}not set${c.reset} ${c.dim}(${p.env})${c.reset}`);
|
|
1758
|
+
continue;
|
|
1759
|
+
}
|
|
1760
|
+
const masked = p.key.substring(0, 8) + '...' + p.key.substring(p.key.length - 4);
|
|
1761
|
+
process.stdout.write(` ${c.yellow}●${c.reset} ${p.name.padEnd(12)} ${c.dim}${masked}${c.reset} checking...`);
|
|
1762
|
+
try {
|
|
1763
|
+
const res = await fetch(p.testUrl, {
|
|
1764
|
+
method: p.testBody ? 'POST' : 'GET',
|
|
1765
|
+
headers: p.testHeaders(p.key),
|
|
1766
|
+
...(p.testBody ? { body: p.testBody } : {}),
|
|
1767
|
+
signal: AbortSignal.timeout(10_000),
|
|
1768
|
+
});
|
|
1769
|
+
if (res.ok || res.status === 200 || res.status === 201) {
|
|
1770
|
+
process.stdout.write(`\r ${c.green}●${c.reset} ${p.name.padEnd(12)} ${c.dim}${masked}${c.reset} ${c.green}valid ✓${c.reset} \n`);
|
|
1771
|
+
} else {
|
|
1772
|
+
const body = await res.json().catch(() => ({}));
|
|
1773
|
+
const errMsg = body?.error?.message || `HTTP ${res.status}`;
|
|
1774
|
+
process.stdout.write(`\r ${c.red}●${c.reset} ${p.name.padEnd(12)} ${c.dim}${masked}${c.reset} ${c.red}invalid ✗${c.reset} ${c.dim}(${errMsg})${c.reset} \n`);
|
|
1775
|
+
}
|
|
1776
|
+
} catch (e) {
|
|
1777
|
+
process.stdout.write(`\r ${c.red}●${c.reset} ${p.name.padEnd(12)} ${c.dim}${masked}${c.reset} ${c.red}error ✗${c.reset} ${c.dim}(${e.message})${c.reset} \n`);
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
|
|
1781
|
+
const resolved = resolveProvider(null, keys);
|
|
1782
|
+
console.log();
|
|
1783
|
+
if (resolved) {
|
|
1784
|
+
console.log(` ${c.bold}Active provider:${c.reset} ${resolved.label}`);
|
|
1785
|
+
console.log(` ${c.dim}Override with: --provider=openai | --provider=openrouter | --provider=anthropic${c.reset}`);
|
|
1786
|
+
} else {
|
|
1787
|
+
console.log(` ${c.yellow}No LLM provider configured.${c.reset} Set one of the API keys above for deep audits.`);
|
|
1788
|
+
}
|
|
1789
|
+
|
|
1790
|
+
// AgentAudit registry key
|
|
1791
|
+
console.log();
|
|
1792
|
+
console.log(` ${c.bold}Registry:${c.reset}`);
|
|
1793
|
+
const creds = loadCredentials();
|
|
1794
|
+
if (creds?.api_key) {
|
|
1795
|
+
const masked = creds.api_key.substring(0, 8) + '...' + creds.api_key.substring(creds.api_key.length - 4);
|
|
1796
|
+
console.log(` ${c.green}●${c.reset} AgentAudit ${c.dim}${masked}${c.reset} ${c.dim}(${creds.agent_name || 'unknown'})${c.reset}`);
|
|
1797
|
+
} else {
|
|
1798
|
+
console.log(` ${c.dim}○${c.reset} AgentAudit ${c.dim}not set${c.reset} ${c.dim}(run: agentaudit setup)${c.reset}`);
|
|
1799
|
+
}
|
|
1800
|
+
console.log();
|
|
1801
|
+
return;
|
|
1802
|
+
}
|
|
1803
|
+
|
|
1705
1804
|
if (command === 'discover') {
|
|
1706
1805
|
const scanFlag = targets.includes('--quick') || targets.includes('--scan') || targets.includes('-s');
|
|
1707
1806
|
const auditFlag = targets.includes('--deep') || targets.includes('--audit') || targets.includes('-a');
|