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.
Files changed (3) hide show
  1. package/README.md +16 -5
  2. package/cli.mjs +110 -11
  3. 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 (`ANTHROPIC_API_KEY` or `OPENAI_API_KEY`) |
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 **Anthropic (Claude)** and **OpenAI (GPT-4o)**. Set one of these environment variables:
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-... # Recommended
473
- export OPENAI_API_KEY=sk-... # Alternative
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
- **Priority:** If both are set, Anthropic is used. The active provider is shown during the audit.
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
- const activeProvider = anthropicKey ? 'Anthropic (Claude)' : openaiKey ? 'OpenAI (GPT-4o)' : openrouterKey ? `OpenRouter (${openrouterModel})` : null;
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 (!anthropicKey && !openaiKey && !openrouterKey) {
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 (anthropicKey) {
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': anthropicKey,
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 if (openaiKey || openrouterKey) {
1426
- const isOpenRouter = !openaiKey && !!openrouterKey;
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 ${apiToken}`,
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}(or use OPENAI_API_KEY instead)${c.reset}`);
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} ${c.dim}(or OPENAI_API_KEY)${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');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentaudit",
3
- "version": "3.9.16",
3
+ "version": "3.9.18",
4
4
  "description": "Security scanner for AI packages — MCP server + CLI",
5
5
  "type": "module",
6
6
  "bin": {