agentaudit 3.10.9 → 3.10.10

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/cli.mjs +59 -67
  2. package/index.mjs +8 -8
  3. package/package.json +3 -3
package/cli.mjs CHANGED
@@ -435,26 +435,31 @@ function singleSelect(items, { title = 'Select', hint = '↑↓=move Enter=sele
435
435
  });
436
436
  }
437
437
 
438
- async function registerAgent(agentName) {
439
- const res = await fetch(`${REGISTRY_URL}/api/register`, {
440
- method: 'POST',
441
- headers: { 'Content-Type': 'application/json' },
442
- body: JSON.stringify({ agent_name: agentName }),
443
- signal: AbortSignal.timeout(15_000),
444
- });
445
- if (!res.ok) throw new Error(`Registration failed (HTTP ${res.status}): ${await res.text()}`);
446
- return res.json();
438
+ async function validateApiKey(apiKey) {
439
+ try {
440
+ const res = await fetch(`${REGISTRY_URL}/api/auth/validate`, {
441
+ headers: { 'Authorization': `Bearer ${apiKey}` },
442
+ signal: AbortSignal.timeout(10_000),
443
+ });
444
+ if (res.ok) {
445
+ const data = await res.json();
446
+ return { valid: true, agent_name: data.agent_name || null };
447
+ }
448
+ return { valid: false, agent_name: null };
449
+ } catch {
450
+ return { valid: false, agent_name: null };
451
+ }
447
452
  }
448
453
 
449
454
  async function setupCommand() {
450
- console.log(` ${c.bold}AgentAudit Login${c.reset}`);
451
- console.log(` ${c.dim}Create an account to upload audit reports to agentaudit.dev${c.reset}`);
455
+ console.log(` ${c.bold}AgentAudit Setup${c.reset}`);
456
+ console.log(` ${c.dim}Link your API key to upload audit reports to agentaudit.dev${c.reset}`);
452
457
  console.log();
453
458
 
454
459
  const existing = loadCredentials();
455
460
  if (existing) {
456
- console.log(` ${icons.safe} Already logged in as ${c.bold}${existing.agent_name}${c.reset}`);
457
- console.log(` ${c.dim}Key: ${existing.api_key.slice(0, 8)}...${c.reset}`);
461
+ console.log(` ${icons.safe} Already configured as ${c.bold}${existing.agent_name}${c.reset}`);
462
+ console.log(` ${c.dim}Key: ${existing.api_key.slice(0, 12)}...${c.reset}`);
458
463
  console.log();
459
464
  const answer = await askQuestion(` Reconfigure? ${c.dim}(y/N)${c.reset} `);
460
465
  if (answer.toLowerCase() !== 'y') {
@@ -464,39 +469,28 @@ async function setupCommand() {
464
469
  console.log();
465
470
  }
466
471
 
467
- console.log(` ${c.bold}1)${c.reset} Register new agent ${c.dim}(free, creates API key automatically)${c.reset}`);
468
- console.log(` ${c.bold}2)${c.reset} Enter existing API key`);
469
- console.log();
470
- const choice = await askQuestion(` Choice ${c.dim}(1/2)${c.reset}: `);
472
+ console.log(` ${c.bold}Step 1:${c.reset} Create an API key at ${c.cyan}${REGISTRY_URL}/profile${c.reset}`);
473
+ console.log(` ${c.dim}Sign in with GitHub, then click "Create API Key".${c.reset}`);
471
474
  console.log();
475
+ const key = await askQuestion(` ${c.bold}Step 2:${c.reset} Paste your API key here: `);
476
+ if (!key || !key.trim()) {
477
+ console.log(` ${c.red}No key entered.${c.reset}`);
478
+ return;
479
+ }
472
480
 
473
- if (choice === '2') {
474
- const key = await askQuestion(` API Key: `);
475
- if (!key) { console.log(` ${c.red}No key entered.${c.reset}`); return; }
476
- const name = await askQuestion(` Agent name ${c.dim}(optional)${c.reset}: `);
477
- saveCredentials({ api_key: key, agent_name: name || 'custom' });
481
+ process.stdout.write(` Validating...`);
482
+ const validation = await validateApiKey(key.trim());
483
+ if (validation.valid) {
484
+ const agentName = validation.agent_name || 'agent';
485
+ saveCredentials({ api_key: key.trim(), agent_name: agentName });
486
+ console.log(` ${c.green}valid!${c.reset}`);
478
487
  console.log();
479
- console.log(` ${icons.safe} Saved! Key stored in ${c.dim}${USER_CRED_FILE}${c.reset}`);
488
+ console.log(` ${icons.safe} Logged in as ${c.bold}${agentName}${c.reset}`);
489
+ console.log(` ${c.dim}Key saved to: ${USER_CRED_FILE}${c.reset}`);
480
490
  } else {
481
- const name = await askQuestion(` Agent name ${c.dim}(e.g. my-scanner, claude-desktop)${c.reset}: `);
482
- if (!name || !/^[a-zA-Z0-9._-]{2,64}$/.test(name)) {
483
- console.log(` ${c.red}Invalid name. Use 2-64 chars: letters, numbers, dash, underscore, dot.${c.reset}`);
484
- return;
485
- }
486
- process.stdout.write(` Registering ${c.bold}${name}${c.reset}...`);
487
- try {
488
- const data = await registerAgent(name);
489
- saveCredentials({ api_key: data.api_key, agent_name: data.agent_name });
490
- console.log(` ${c.green}done!${c.reset}`);
491
- console.log();
492
- console.log(` ${icons.safe} Registered as ${c.bold}${data.agent_name}${c.reset}`);
493
- console.log(` ${c.dim}Key: ${data.api_key.slice(0, 12)}...${c.reset}`);
494
- console.log(` ${c.dim}Saved to: ${USER_CRED_FILE}${c.reset}`);
495
- } catch (err) {
496
- console.log(` ${c.red}failed${c.reset}`);
497
- console.log(` ${c.red}${err.message}${c.reset}`);
498
- return;
499
- }
491
+ console.log(` ${c.red}invalid${c.reset}`);
492
+ console.log(` ${c.red}Key not recognized. Make sure you copied the full key from ${REGISTRY_URL}/profile${c.reset}`);
493
+ return;
500
494
  }
501
495
 
502
496
  console.log();
@@ -1056,7 +1050,7 @@ function quickChecks(files) {
1056
1050
 
1057
1051
  async function checkRegistry(slug) {
1058
1052
  try {
1059
- const res = await fetch(`${REGISTRY_URL}/api/skills/${encodeURIComponent(slug)}`, {
1053
+ const res = await fetch(`${REGISTRY_URL}/api/packages/${encodeURIComponent(slug)}`, {
1060
1054
  signal: AbortSignal.timeout(5000),
1061
1055
  });
1062
1056
  if (res.ok) return await res.json();
@@ -1146,7 +1140,7 @@ function printScanResult(url, info, files, findings, registryData, duration) {
1146
1140
  if (registryData) {
1147
1141
  const rd = registryData;
1148
1142
  const riskScore = rd.risk_score ?? rd.latest_risk_score ?? 0;
1149
- console.log(`${icons.treeLast} ${c.dim}registry${c.reset} ${riskBadge(riskScore)} Risk ${riskScore} ${c.dim}${REGISTRY_URL}/skills/${slug}${c.reset}`);
1143
+ console.log(`${icons.treeLast} ${c.dim}registry${c.reset} ${riskBadge(riskScore)} Risk ${riskScore} ${c.dim}${REGISTRY_URL}/packages/${slug}${c.reset}`);
1150
1144
  } else {
1151
1145
  console.log(`${icons.treeLast} ${c.dim}registry${c.reset} ${c.dim}not audited yet${c.reset}`);
1152
1146
  }
@@ -1533,7 +1527,7 @@ async function discoverCommand(options = {}) {
1533
1527
  const riskScore = regData.risk_score ?? regData.latest_risk_score ?? 0;
1534
1528
  const hasOfficial = regData.has_official_audit;
1535
1529
  console.log(`${branch} ${c.bold}${server.name}${c.reset} ${sourceLabel}`);
1536
- console.log(`${pipe} ${riskBadge(riskScore)} Risk ${riskScore} ${hasOfficial ? `${c.green}✔ official${c.reset} ` : ''}${c.dim}${REGISTRY_URL}/skills/${slug}${c.reset}`);
1530
+ console.log(`${pipe} ${riskBadge(riskScore)} Risk ${riskScore} ${hasOfficial ? `${c.green}✔ official${c.reset} ` : ''}${c.dim}${REGISTRY_URL}/packages/${slug}${c.reset}`);
1537
1531
  if (resolvedUrl) allServersWithUrls.push({ name: server.name, sourceUrl: resolvedUrl, hasAudit: true, regData });
1538
1532
  } else {
1539
1533
  unauditedServers++;
@@ -2046,7 +2040,7 @@ async function auditRepo(url) {
2046
2040
  if (res.ok) {
2047
2041
  const data = await res.json();
2048
2042
  console.log(` ${c.green}done${c.reset}`);
2049
- console.log(` ${c.dim}Report: ${REGISTRY_URL}/skills/${slug}${c.reset}`);
2043
+ console.log(` ${c.dim}Report: ${REGISTRY_URL}/packages/${slug}${c.reset}`);
2050
2044
  } else {
2051
2045
  let errBody = '';
2052
2046
  try { errBody = await res.text(); } catch {}
@@ -2059,27 +2053,22 @@ async function auditRepo(url) {
2059
2053
  console.log(` ${c.yellow}failed${c.reset}`);
2060
2054
  }
2061
2055
  } else if (process.stdin.isTTY) {
2062
- // No credentials — offer inline registration
2063
- console.log();
2064
- console.log(` ${c.bold}Share this audit with the community?${c.reset}`);
2065
- console.log(` ${c.dim}Uploading helps others assess package security. Account is free.${c.reset}`);
2056
+ // No credentials — prompt to paste key or set up
2066
2057
  console.log();
2067
- console.log(` ${c.bold}1)${c.reset} Create account + upload ${c.dim}(free)${c.reset}`);
2068
- console.log(` ${c.bold}2)${c.reset} Skip`);
2058
+ console.log(` ${c.bold}Want to upload this report to agentaudit.dev?${c.reset}`);
2059
+ console.log(` ${c.dim}Create an API key at ${c.cyan}${REGISTRY_URL}/profile${c.dim} (sign in with GitHub)${c.reset}`);
2069
2060
  console.log();
2070
- const uploadChoice = await askQuestion(` Choice ${c.dim}(1/2)${c.reset}: `);
2071
- if (uploadChoice === '1') {
2072
- const name = await askQuestion(` Agent name ${c.dim}(e.g. my-scanner, claude-desktop)${c.reset}: `);
2073
- if (!name || !/^[a-zA-Z0-9._-]{2,64}$/.test(name)) {
2074
- console.log(` ${c.red}Invalid name. Use 2-64 chars: letters, numbers, dash, underscore, dot.${c.reset}`);
2075
- } else {
2061
+ const pastedKey = await askQuestion(` Paste API key ${c.dim}(or Enter to skip)${c.reset}: `);
2062
+ if (pastedKey && pastedKey.trim()) {
2063
+ process.stdout.write(` Validating...`);
2064
+ const validation = await validateApiKey(pastedKey.trim());
2065
+ if (validation.valid) {
2066
+ const agentName = validation.agent_name || 'agent';
2067
+ saveCredentials({ api_key: pastedKey.trim(), agent_name: agentName });
2068
+ creds = { api_key: pastedKey.trim(), agent_name: agentName };
2069
+ console.log(` ${c.green}valid!${c.reset}`);
2070
+ process.stdout.write(` Uploading report...`);
2076
2071
  try {
2077
- process.stdout.write(` Registering ${c.bold}${name}${c.reset}...`);
2078
- const regData = await registerAgent(name);
2079
- saveCredentials({ api_key: regData.api_key, agent_name: regData.agent_name });
2080
- console.log(` ${c.green}done!${c.reset}`);
2081
- creds = { api_key: regData.api_key, agent_name: regData.agent_name };
2082
- process.stdout.write(` Uploading report...`);
2083
2072
  const res = await fetch(`${REGISTRY_URL}/api/reports`, {
2084
2073
  method: 'POST',
2085
2074
  headers: {
@@ -2091,7 +2080,7 @@ async function auditRepo(url) {
2091
2080
  });
2092
2081
  if (res.ok) {
2093
2082
  console.log(` ${c.green}done${c.reset}`);
2094
- console.log(` ${c.dim}Report: ${REGISTRY_URL}/skills/${slug}${c.reset}`);
2083
+ console.log(` ${c.dim}Report: ${REGISTRY_URL}/packages/${slug}${c.reset}`);
2095
2084
  } else {
2096
2085
  console.log(` ${c.yellow}failed (HTTP ${res.status})${c.reset}`);
2097
2086
  }
@@ -2099,10 +2088,13 @@ async function auditRepo(url) {
2099
2088
  console.log(` ${c.red}failed${c.reset}`);
2100
2089
  console.log(` ${c.dim}${err.message}${c.reset}`);
2101
2090
  }
2091
+ } else {
2092
+ console.log(` ${c.red}invalid key${c.reset}`);
2093
+ console.log(` ${c.dim}Run ${c.cyan}agentaudit setup${c.dim} to configure.${c.reset}`);
2102
2094
  }
2103
2095
  }
2104
2096
  } else {
2105
- console.log(` ${c.dim}Run ${c.cyan}agentaudit setup${c.dim} to create an account and upload reports${c.reset}`);
2097
+ console.log(` ${c.dim}Run ${c.cyan}agentaudit setup${c.dim} to configure your API key and upload reports${c.reset}`);
2106
2098
  }
2107
2099
 
2108
2100
  console.log();
@@ -2131,7 +2123,7 @@ async function checkPackage(name) {
2131
2123
  console.log(` ${c.bold}${name}${c.reset} ${riskBadge(riskScore)}`);
2132
2124
  console.log(` ${c.dim}Risk Score: ${riskScore}/100${c.reset}`);
2133
2125
  if (data.source_url) console.log(` ${c.dim}Source: ${data.source_url}${c.reset}`);
2134
- console.log(` ${c.dim}Registry: ${REGISTRY_URL}/skills/${name}${c.reset}`);
2126
+ console.log(` ${c.dim}Registry: ${REGISTRY_URL}/packages/${name}${c.reset}`);
2135
2127
  if (data.has_official_audit) console.log(` ${c.green}✔ Officially audited${c.reset}`);
2136
2128
  console.log();
2137
2129
  }
package/index.mjs CHANGED
@@ -292,7 +292,7 @@ async function resolveSourceUrl(server) {
292
292
 
293
293
  async function checkRegistry(slug) {
294
294
  try {
295
- const res = await fetch(`${REGISTRY_URL}/api/skills/${encodeURIComponent(slug)}`, {
295
+ const res = await fetch(`${REGISTRY_URL}/api/packages/${encodeURIComponent(slug)}`, {
296
296
  signal: AbortSignal.timeout(5000),
297
297
  });
298
298
  if (res.ok) return await res.json();
@@ -303,7 +303,7 @@ async function checkRegistry(slug) {
303
303
  // ── MCP Server ───────────────────────────────────────────
304
304
 
305
305
  const server = new Server(
306
- { name: 'agentaudit', version: '3.9.8' },
306
+ { name: 'agentaudit', version: '3.10.9' },
307
307
  { capabilities: { tools: {} } }
308
308
  );
309
309
 
@@ -413,7 +413,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
413
413
  const risk = regData.risk_score ?? regData.latest_risk_score ?? 0;
414
414
  const official = regData.has_official_audit ? ' (official)' : '';
415
415
  text += `- **Registry: ✅ Audited** — Risk ${risk}/100${official}\n`;
416
- text += `- Report: ${REGISTRY_URL}/skills/${slug}\n`;
416
+ text += `- Report: ${REGISTRY_URL}/packages/${slug}\n`;
417
417
  } else {
418
418
  const sourceUrl = await resolveSourceUrl(srv);
419
419
  text += `- **Registry: ⚠️ Not audited** — no audit report found\n`;
@@ -583,7 +583,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
583
583
  try { data = JSON.parse(body); } catch { data = { raw: body }; }
584
584
 
585
585
  if (res.ok) {
586
- return { content: [{ type: 'text', text: `✅ Report submitted!\n\nReport ID: ${data.report_id || 'unknown'}\nURL: ${REGISTRY_URL}/skills/${report.skill_slug}\nRisk: ${report.risk_score}/100 (${report.result})\nFindings: ${report.findings_count}` }] };
586
+ return { content: [{ type: 'text', text: `✅ Report submitted!\n\nReport ID: ${data.report_id || 'unknown'}\nURL: ${REGISTRY_URL}/packages/${report.skill_slug}\nRisk: ${report.risk_score}/100 (${report.result})\nFindings: ${report.findings_count}` }] };
587
587
  } else {
588
588
  return { content: [{ type: 'text', text: `Upload failed (HTTP ${res.status}): ${JSON.stringify(data, null, 2)}` }] };
589
589
  }
@@ -600,7 +600,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
600
600
  }
601
601
 
602
602
  try {
603
- const res = await fetch(`${REGISTRY_URL}/api/skills/${encodeURIComponent(package_name)}`, {
603
+ const res = await fetch(`${REGISTRY_URL}/api/packages/${encodeURIComponent(package_name)}`, {
604
604
  signal: AbortSignal.timeout(10_000),
605
605
  });
606
606
 
@@ -622,9 +622,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
622
622
 
623
623
  // Risk assessment at the very top — this is what the agent needs to act on
624
624
  if (typeof risk === 'number' && risk >= 40) {
625
- summary += `🚨 UNSAFE — DO NOT INSTALL WITHOUT WARNING THE USER!\nRisk Score: ${risk}/100 | ${findings} finding(s)\n\nYou MUST tell the user: "${package_name}" has been flagged as HIGH RISK in a security audit. It has ${findings} security finding(s) and a risk score of ${risk}/100. Installing this package is NOT recommended. Ask the user if they want to proceed despite the risk, and link them to the full report: ${REGISTRY_URL}/skills/${package_name}\n\n`;
625
+ summary += `🚨 UNSAFE — DO NOT INSTALL WITHOUT WARNING THE USER!\nRisk Score: ${risk}/100 | ${findings} finding(s)\n\nYou MUST tell the user: "${package_name}" has been flagged as HIGH RISK in a security audit. It has ${findings} security finding(s) and a risk score of ${risk}/100. Installing this package is NOT recommended. Ask the user if they want to proceed despite the risk, and link them to the full report: ${REGISTRY_URL}/packages/${package_name}\n\n`;
626
626
  } else if (typeof risk === 'number' && risk >= 20) {
627
- summary += `⚠️ MODERATE RISK — Inform the user before installing.\nRisk Score: ${risk}/100 | ${findings} finding(s)\n\nTell the user: "${package_name}" has some security findings (risk ${risk}/100). Recommend they review the report before installing: ${REGISTRY_URL}/skills/${package_name}\n\n`;
627
+ summary += `⚠️ MODERATE RISK — Inform the user before installing.\nRisk Score: ${risk}/100 | ${findings} finding(s)\n\nTell the user: "${package_name}" has some security findings (risk ${risk}/100). Recommend they review the report before installing: ${REGISTRY_URL}/packages/${package_name}\n\n`;
628
628
  } else if (typeof risk === 'number') {
629
629
  summary += `✅ LOW RISK — Safe to install.\nRisk Score: ${risk}/100 | ${findings} finding(s)\n\nThis package has been audited and appears safe. You can proceed with installation.\n\n`;
630
630
  }
@@ -640,7 +640,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
640
640
  summary += `Last Audited: ${auditedAt}\n`;
641
641
  if (version) summary += `Audited Version: ${version}\n`;
642
642
  if (data.source_url) summary += `Source: ${data.source_url}\n`;
643
- summary += `Registry: ${REGISTRY_URL}/skills/${package_name}\n`;
643
+ summary += `Registry: ${REGISTRY_URL}/packages/${package_name}\n`;
644
644
 
645
645
  return { content: [{ type: 'text', text: summary }] };
646
646
  } catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentaudit",
3
- "version": "3.10.9",
3
+ "version": "3.10.10",
4
4
  "description": "Security scanner for AI packages — MCP server + CLI",
5
5
  "type": "module",
6
6
  "bin": {
@@ -31,11 +31,11 @@
31
31
  "prompt-injection",
32
32
  "agent-security"
33
33
  ],
34
- "author": "starbuck100",
34
+ "author": "agentaudit-dev",
35
35
  "license": "AGPL-3.0",
36
36
  "repository": {
37
37
  "type": "git",
38
- "url": "git+https://github.com/starbuck100/agentaudit-mcp.git"
38
+ "url": "git+https://github.com/agentaudit-dev/agentaudit-mcp.git"
39
39
  },
40
40
  "homepage": "https://agentaudit.dev",
41
41
  "engines": {