agentaudit 3.6.0 → 3.7.1
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 +103 -6
- package/package.json +1 -1
package/cli.mjs
CHANGED
|
@@ -638,6 +638,11 @@ function findMcpConfigs() {
|
|
|
638
638
|
{ name: 'Continue', path: path.join(home, '.continue', 'config.json') },
|
|
639
639
|
];
|
|
640
640
|
|
|
641
|
+
// Also check AGENTAUDIT_TEST_CONFIG env for testing
|
|
642
|
+
if (process.env.AGENTAUDIT_TEST_CONFIG) {
|
|
643
|
+
candidates.push({ name: 'Test Config', path: process.env.AGENTAUDIT_TEST_CONFIG });
|
|
644
|
+
}
|
|
645
|
+
|
|
641
646
|
// Also scan workspace .cursor/mcp.json, .vscode/mcp.json in cwd
|
|
642
647
|
const cwd = process.cwd();
|
|
643
648
|
candidates.push(
|
|
@@ -731,6 +736,22 @@ function serverSlug(server) {
|
|
|
731
736
|
return server.name.toLowerCase().replace(/[^a-z0-9-]/gi, '-');
|
|
732
737
|
}
|
|
733
738
|
|
|
739
|
+
async function searchGitHub(query) {
|
|
740
|
+
try {
|
|
741
|
+
const res = await fetch(`https://api.github.com/search/repositories?q=${encodeURIComponent(query)}&per_page=1`, {
|
|
742
|
+
signal: AbortSignal.timeout(5000),
|
|
743
|
+
headers: { 'Accept': 'application/vnd.github+json' },
|
|
744
|
+
});
|
|
745
|
+
if (res.ok) {
|
|
746
|
+
const data = await res.json();
|
|
747
|
+
if (data.items?.length > 0) {
|
|
748
|
+
return data.items[0].html_url;
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
} catch {}
|
|
752
|
+
return null;
|
|
753
|
+
}
|
|
754
|
+
|
|
734
755
|
async function resolveSourceUrl(server) {
|
|
735
756
|
// Already have it
|
|
736
757
|
if (server.sourceUrl) return server.sourceUrl;
|
|
@@ -750,7 +771,9 @@ async function resolveSourceUrl(server) {
|
|
|
750
771
|
}
|
|
751
772
|
}
|
|
752
773
|
} catch {}
|
|
753
|
-
// Fallback:
|
|
774
|
+
// Fallback: try GitHub search for the package name
|
|
775
|
+
const ghUrl = await searchGitHub(server.npmPackage);
|
|
776
|
+
if (ghUrl) return ghUrl;
|
|
754
777
|
return `https://www.npmjs.com/package/${server.npmPackage}`;
|
|
755
778
|
}
|
|
756
779
|
|
|
@@ -767,6 +790,9 @@ async function resolveSourceUrl(server) {
|
|
|
767
790
|
if (source && source.startsWith('http')) return source;
|
|
768
791
|
}
|
|
769
792
|
} catch {}
|
|
793
|
+
// Fallback: GitHub search
|
|
794
|
+
const ghUrl = await searchGitHub(server.pyPackage);
|
|
795
|
+
if (ghUrl) return ghUrl;
|
|
770
796
|
return `https://pypi.org/project/${server.pyPackage}/`;
|
|
771
797
|
}
|
|
772
798
|
|
|
@@ -806,7 +832,9 @@ async function resolveSourceUrl(server) {
|
|
|
806
832
|
return null;
|
|
807
833
|
}
|
|
808
834
|
|
|
809
|
-
async function discoverCommand() {
|
|
835
|
+
async function discoverCommand(options = {}) {
|
|
836
|
+
const autoScan = options.scan || false;
|
|
837
|
+
|
|
810
838
|
console.log(` ${c.bold}Discovering local MCP servers...${c.reset}`);
|
|
811
839
|
console.log();
|
|
812
840
|
|
|
@@ -830,6 +858,7 @@ async function discoverCommand() {
|
|
|
830
858
|
let auditedServers = 0;
|
|
831
859
|
let unauditedServers = 0;
|
|
832
860
|
const unauditedWithUrls = [];
|
|
861
|
+
const allServersWithUrls = []; // For --scan: all servers we can scan
|
|
833
862
|
|
|
834
863
|
for (const config of configs) {
|
|
835
864
|
const servers = extractServersFromConfig(config.content);
|
|
@@ -874,20 +903,23 @@ async function discoverCommand() {
|
|
|
874
903
|
else if (server.url) sourceLabel = `${c.dim}${server.url.length > 60 ? server.url.slice(0, 57) + '...' : server.url}${c.reset}`;
|
|
875
904
|
else if (server.command) sourceLabel = `${c.dim}${[server.command, ...server.args.slice(0, 2)].join(' ')}${c.reset}`;
|
|
876
905
|
|
|
906
|
+
// Always resolve source URL (needed for --scan)
|
|
907
|
+
const resolvedUrl = await resolveSourceUrl(server);
|
|
908
|
+
|
|
877
909
|
if (regData) {
|
|
878
910
|
auditedServers++;
|
|
879
911
|
const riskScore = regData.risk_score ?? regData.latest_risk_score ?? 0;
|
|
880
912
|
const hasOfficial = regData.has_official_audit;
|
|
881
913
|
console.log(`${branch} ${c.bold}${server.name}${c.reset} ${sourceLabel}`);
|
|
882
914
|
console.log(`${pipe} ${riskBadge(riskScore)} Risk ${riskScore} ${hasOfficial ? `${c.green}✔ official${c.reset} ` : ''}${c.dim}${REGISTRY_URL}/skills/${slug}${c.reset}`);
|
|
915
|
+
if (resolvedUrl) allServersWithUrls.push({ name: server.name, sourceUrl: resolvedUrl, hasAudit: true, regData });
|
|
883
916
|
} else {
|
|
884
917
|
unauditedServers++;
|
|
885
|
-
// Resolve source URL
|
|
886
|
-
const resolvedUrl = await resolveSourceUrl(server);
|
|
887
918
|
console.log(`${branch} ${c.bold}${server.name}${c.reset} ${sourceLabel}`);
|
|
888
919
|
if (resolvedUrl) {
|
|
889
920
|
console.log(`${pipe} ${c.yellow}⚠ not audited${c.reset} ${c.dim}Run: ${c.cyan}agentaudit audit ${resolvedUrl}${c.reset}`);
|
|
890
921
|
unauditedWithUrls.push({ name: server.name, sourceUrl: resolvedUrl });
|
|
922
|
+
allServersWithUrls.push({ name: server.name, sourceUrl: resolvedUrl, hasAudit: false });
|
|
891
923
|
} else {
|
|
892
924
|
console.log(`${pipe} ${c.yellow}⚠ not audited${c.reset} ${c.dim}Source URL unknown — check the package's GitHub/npm page${c.reset}`);
|
|
893
925
|
}
|
|
@@ -909,7 +941,67 @@ async function discoverCommand() {
|
|
|
909
941
|
if (unauditedServers > 0) console.log(` ${icons.caution} ${c.yellow}${unauditedServers} not audited${c.reset}`);
|
|
910
942
|
console.log();
|
|
911
943
|
|
|
912
|
-
|
|
944
|
+
// --scan: automatically scan all servers with resolved source URLs (git-cloneable only)
|
|
945
|
+
if (autoScan) {
|
|
946
|
+
const isCloneable = (url) => /^https?:\/\/(github\.com|gitlab\.com|bitbucket\.org)\//i.test(url);
|
|
947
|
+
const scanTargets = allServersWithUrls.filter(s => s.sourceUrl && isCloneable(s.sourceUrl));
|
|
948
|
+
// Deduplicate by sourceUrl
|
|
949
|
+
const seen = new Set();
|
|
950
|
+
const dedupedTargets = scanTargets.filter(s => {
|
|
951
|
+
if (seen.has(s.sourceUrl)) return false;
|
|
952
|
+
seen.add(s.sourceUrl);
|
|
953
|
+
return true;
|
|
954
|
+
});
|
|
955
|
+
const skipped = allServersWithUrls.filter(s => s.sourceUrl && !isCloneable(s.sourceUrl));
|
|
956
|
+
if (dedupedTargets.length > 0) {
|
|
957
|
+
console.log(`${c.dim}${'─'.repeat(60)}${c.reset}`);
|
|
958
|
+
console.log(` ${c.bold}${icons.scan} Auto-scanning ${dedupedTargets.length} server${dedupedTargets.length !== 1 ? 's' : ''}...${c.reset}`);
|
|
959
|
+
if (skipped.length > 0) {
|
|
960
|
+
console.log(` ${c.dim}(${skipped.length} skipped — no cloneable source URL)${c.reset}`);
|
|
961
|
+
}
|
|
962
|
+
console.log();
|
|
963
|
+
|
|
964
|
+
const scanResults = [];
|
|
965
|
+
for (const target of dedupedTargets) {
|
|
966
|
+
const result = await scanRepo(target.sourceUrl);
|
|
967
|
+
if (result) scanResults.push({ ...result, serverName: target.name });
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
if (scanResults.length > 1) {
|
|
971
|
+
// Print combined scan summary
|
|
972
|
+
console.log(`${c.dim}${'─'.repeat(60)}${c.reset}`);
|
|
973
|
+
console.log(` ${c.bold}Scan Summary${c.reset} ${scanResults.length} server${scanResults.length !== 1 ? 's' : ''} scanned`);
|
|
974
|
+
console.log();
|
|
975
|
+
|
|
976
|
+
let totalFindings = 0;
|
|
977
|
+
let serversWithFindings = 0;
|
|
978
|
+
|
|
979
|
+
for (const r of scanResults) {
|
|
980
|
+
const findingCount = r.findings ? r.findings.length : 0;
|
|
981
|
+
totalFindings += findingCount;
|
|
982
|
+
if (findingCount > 0) serversWithFindings++;
|
|
983
|
+
|
|
984
|
+
const status = findingCount === 0
|
|
985
|
+
? `${icons.safe} ${c.green}clean${c.reset}`
|
|
986
|
+
: `${icons.caution} ${c.yellow}${findingCount} finding${findingCount !== 1 ? 's' : ''}${c.reset}`;
|
|
987
|
+
console.log(` ${status} ${c.bold}${r.serverName || r.slug}${c.reset} ${c.dim}(${r.duration})${c.reset}`);
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
console.log();
|
|
991
|
+
if (serversWithFindings > 0) {
|
|
992
|
+
console.log(` ${c.yellow}${serversWithFindings}/${scanResults.length} server${scanResults.length !== 1 ? 's' : ''} with findings (${totalFindings} total)${c.reset}`);
|
|
993
|
+
console.log(` ${c.dim}Run ${c.cyan}agentaudit audit <url>${c.dim} for deep LLM analysis on flagged servers${c.reset}`);
|
|
994
|
+
} else {
|
|
995
|
+
console.log(` ${c.green}All servers passed quick scan${c.reset}`);
|
|
996
|
+
console.log(` ${c.dim}Run ${c.cyan}agentaudit audit <url>${c.dim} for thorough LLM-powered analysis${c.reset}`);
|
|
997
|
+
}
|
|
998
|
+
console.log();
|
|
999
|
+
}
|
|
1000
|
+
} else {
|
|
1001
|
+
console.log(` ${c.dim}No scannable source URLs found.${c.reset}`);
|
|
1002
|
+
console.log();
|
|
1003
|
+
}
|
|
1004
|
+
} else if (unauditedServers > 0) {
|
|
913
1005
|
if (unauditedWithUrls.length > 0) {
|
|
914
1006
|
console.log(` ${c.dim}To audit unaudited servers:${c.reset}`);
|
|
915
1007
|
for (const { name, sourceUrl } of unauditedWithUrls) {
|
|
@@ -920,6 +1012,8 @@ async function discoverCommand() {
|
|
|
920
1012
|
console.log(` ${c.cyan}agentaudit audit <source-url>${c.reset}`);
|
|
921
1013
|
}
|
|
922
1014
|
console.log();
|
|
1015
|
+
console.log(` ${c.dim}Or run ${c.cyan}agentaudit discover --scan${c.dim} to auto-scan all servers${c.reset}`);
|
|
1016
|
+
console.log();
|
|
923
1017
|
}
|
|
924
1018
|
}
|
|
925
1019
|
|
|
@@ -1210,6 +1304,7 @@ async function main() {
|
|
|
1210
1304
|
console.log(` ${c.bold}Commands:${c.reset}`);
|
|
1211
1305
|
console.log();
|
|
1212
1306
|
console.log(` ${c.cyan}agentaudit discover${c.reset} Find local MCP servers + check registry`);
|
|
1307
|
+
console.log(` ${c.cyan}agentaudit discover --scan${c.reset} Discover + auto-scan all servers`);
|
|
1213
1308
|
console.log(` ${c.cyan}agentaudit scan${c.reset} <url> [url...] Quick static scan (regex, local)`);
|
|
1214
1309
|
console.log(` ${c.cyan}agentaudit audit${c.reset} <url> [url...] Deep LLM-powered security audit`);
|
|
1215
1310
|
console.log(` ${c.cyan}agentaudit check${c.reset} <name> Look up package in registry`);
|
|
@@ -1221,6 +1316,7 @@ async function main() {
|
|
|
1221
1316
|
console.log();
|
|
1222
1317
|
console.log(` ${c.bold}Examples:${c.reset}`);
|
|
1223
1318
|
console.log(` agentaudit discover`);
|
|
1319
|
+
console.log(` agentaudit discover --scan`);
|
|
1224
1320
|
console.log(` agentaudit scan https://github.com/owner/repo`);
|
|
1225
1321
|
console.log(` agentaudit audit https://github.com/owner/repo`);
|
|
1226
1322
|
console.log(` agentaudit check fastmcp`);
|
|
@@ -1252,7 +1348,8 @@ async function main() {
|
|
|
1252
1348
|
}
|
|
1253
1349
|
|
|
1254
1350
|
if (command === 'discover') {
|
|
1255
|
-
|
|
1351
|
+
const scanFlag = targets.includes('--scan') || targets.includes('-s');
|
|
1352
|
+
await discoverCommand({ scan: scanFlag });
|
|
1256
1353
|
return;
|
|
1257
1354
|
}
|
|
1258
1355
|
|