agentverify 1.2.0 → 1.4.0
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/CHANGELOG.md +28 -0
- package/README.md +47 -4
- package/dist/chunk-XVA2M2HS.mjs +131 -0
- package/dist/cli.js +247 -23
- package/dist/cli.mjs +190 -22
- package/dist/sdk.d.mts +117 -9
- package/dist/sdk.d.ts +117 -9
- package/dist/sdk.js +59 -3
- package/dist/sdk.mjs +1 -1
- package/package.json +4 -3
- package/dist/chunk-YOD7H7MC.mjs +0 -75
package/dist/cli.mjs
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
scan
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-XVA2M2HS.mjs";
|
|
5
5
|
|
|
6
6
|
// src/cli.ts
|
|
7
|
-
import { readFileSync, readdirSync, statSync } from "fs";
|
|
7
|
+
import { readFileSync, readdirSync, statSync, appendFileSync, writeFileSync } from "fs";
|
|
8
8
|
import { join, relative, extname } from "path";
|
|
9
|
-
var VERSION = "1.
|
|
10
|
-
var API_URL = "https://agentverify-api.agentverify.workers.dev/v1/scan";
|
|
9
|
+
var VERSION = "1.4.0";
|
|
10
|
+
var API_URL = process.env.AGENTVERIFY_API_URL ?? "https://agentverify-api.agentverify.workers.dev/v1/scan";
|
|
11
|
+
var A2SPA_DOCS_URL = "https://aimodularity.com/A2SPA/docs";
|
|
12
|
+
var EXIT_OK = 0;
|
|
13
|
+
var EXIT_NOT_VERIFIED = 1;
|
|
14
|
+
var EXIT_NOT_ASSESSED = 2;
|
|
15
|
+
var EXIT_EXECUTION_ERROR = 3;
|
|
11
16
|
var c = {
|
|
12
17
|
reset: "\x1B[0m",
|
|
13
18
|
bold: "\x1B[1m",
|
|
@@ -22,6 +27,7 @@ var c = {
|
|
|
22
27
|
var write = (value = "") => process.stdout.write(`${value}
|
|
23
28
|
`);
|
|
24
29
|
var SUPPORTED_EXTENSIONS = /* @__PURE__ */ new Set([".js", ".ts", ".py", ".json", ".yaml", ".yml", ".md", ".mjs", ".cjs"]);
|
|
30
|
+
var MAX_SCAN_FILE_BYTES = 5 * 1024 * 1024;
|
|
25
31
|
var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", ".next", "vendor", "coverage", ".cache"]);
|
|
26
32
|
function collectFiles(dir) {
|
|
27
33
|
const files = [];
|
|
@@ -49,8 +55,13 @@ function parseArgs(argv) {
|
|
|
49
55
|
key: "",
|
|
50
56
|
file: "",
|
|
51
57
|
json: false,
|
|
58
|
+
markdown: false,
|
|
59
|
+
ci: false,
|
|
60
|
+
allowNotAssessed: false,
|
|
52
61
|
help: false,
|
|
53
|
-
version: false
|
|
62
|
+
version: false,
|
|
63
|
+
policy: "",
|
|
64
|
+
summaryFile: ""
|
|
54
65
|
};
|
|
55
66
|
if (result.command === "--help" || result.command === "-h") {
|
|
56
67
|
result.command = "";
|
|
@@ -65,6 +76,11 @@ function parseArgs(argv) {
|
|
|
65
76
|
if (arg === "--key" || arg === "-k") result.key = args[++i] ?? "";
|
|
66
77
|
else if (arg === "--file" || arg === "-f") result.file = args[++i] ?? "";
|
|
67
78
|
else if (arg === "--json") result.json = true;
|
|
79
|
+
else if (arg === "--markdown") result.markdown = true;
|
|
80
|
+
else if (arg === "--ci") result.ci = true;
|
|
81
|
+
else if (arg === "--allow-not-assessed") result.allowNotAssessed = true;
|
|
82
|
+
else if (arg === "--policy") result.policy = args[++i] ?? "";
|
|
83
|
+
else if (arg === "--summary-file") result.summaryFile = args[++i] ?? "";
|
|
68
84
|
else if (arg === "--help" || arg === "-h") result.help = true;
|
|
69
85
|
else if (arg === "--version" || arg === "-v") result.version = true;
|
|
70
86
|
else if (!arg.startsWith("-")) result.dir = arg;
|
|
@@ -88,6 +104,14 @@ ${c.bold}Options:${c.reset}
|
|
|
88
104
|
--key, -k API key (or set AGENTVERIFY_API_KEY env var)
|
|
89
105
|
--file, -f Scan a single file instead of a directory
|
|
90
106
|
--json Output results as JSON
|
|
107
|
+
--markdown Output a Markdown summary
|
|
108
|
+
--ci CI mode: concise output and non-zero exit for NOT VERIFIED
|
|
109
|
+
--allow-not-assessed
|
|
110
|
+
In CI mode, do not fail only because content is NOT_ASSESSED
|
|
111
|
+
--policy <id> Evaluate a built-in policy profile against this scan's evidence
|
|
112
|
+
in addition to the scanner's own verdict (e.g. financial-agent)
|
|
113
|
+
--summary-file <path>
|
|
114
|
+
Write results as JSON to this path (in addition to any other output)
|
|
91
115
|
--version, -v Print version
|
|
92
116
|
--help, -h Show this help message
|
|
93
117
|
|
|
@@ -104,17 +128,123 @@ ${c.bold}Examples:${c.reset}
|
|
|
104
128
|
${c.gray}# Output as JSON (useful for CI/CD)${c.reset}
|
|
105
129
|
agentverify scan . --key av_your_key --json
|
|
106
130
|
|
|
131
|
+
${c.gray}# Markdown summary for pull requests${c.reset}
|
|
132
|
+
agentverify scan . --key av_your_key --markdown
|
|
133
|
+
|
|
134
|
+
${c.bold}Exit codes (--ci):${c.reset}
|
|
135
|
+
0 All scanned files verified
|
|
136
|
+
1 NOT VERIFIED \u2014 a security/verification check failed
|
|
137
|
+
2 NOT ASSESSED \u2014 insufficient evidence for a verdict (see --allow-not-assessed)
|
|
138
|
+
3 Execution error \u2014 the scan itself could not run (bad key, network, missing file)
|
|
139
|
+
|
|
107
140
|
${c.bold}Get your API key:${c.reset}
|
|
108
141
|
https://aimodularity.com/agentverify/dashboard
|
|
109
142
|
|
|
110
143
|
${c.bold}Docs:${c.reset}
|
|
111
144
|
https://github.com/AI-Blockchain-Ventures/agentverify
|
|
145
|
+
|
|
146
|
+
${c.bold}A2SPA docs:${c.reset}
|
|
147
|
+
${A2SPA_DOCS_URL}
|
|
148
|
+
`);
|
|
149
|
+
}
|
|
150
|
+
function getTopBlocker(result) {
|
|
151
|
+
return result.reportInsights?.topBlocker ?? result.findings[0]?.title ?? "No blocking finding detected";
|
|
152
|
+
}
|
|
153
|
+
function getFixFirst(result) {
|
|
154
|
+
const fromInsights = result.reportInsights?.fixPriority?.filter((item) => item.priority === "fix_first").map((item) => item.title) ?? [];
|
|
155
|
+
const fallback = result.findings.slice(0, 3).map((item) => item.title);
|
|
156
|
+
return (fromInsights.length ? fromInsights : fallback).slice(0, 3);
|
|
157
|
+
}
|
|
158
|
+
function getRelevantThreatCategories(result) {
|
|
159
|
+
return (result.threatCategories ?? []).filter((item) => item.status !== "not_assessed").slice(0, 6).map((item) => `${item.label} (${item.status.replace("_", " ")})`);
|
|
160
|
+
}
|
|
161
|
+
function criticalCount(result) {
|
|
162
|
+
return result.findings.filter((f) => f.severity === "critical").length;
|
|
163
|
+
}
|
|
164
|
+
function highCount(result) {
|
|
165
|
+
return result.findings.filter((f) => f.severity === "high").length;
|
|
166
|
+
}
|
|
167
|
+
function fingerprintPreview(result) {
|
|
168
|
+
const hash = result.artifactFingerprint?.artifactHash;
|
|
169
|
+
return hash ? `${hash.slice(0, 12)}\u2026` : null;
|
|
170
|
+
}
|
|
171
|
+
function printDetailedSummary(result, fileName) {
|
|
172
|
+
const verified = result.verdict === "VERIFIED";
|
|
173
|
+
const verdictColor = verified ? c.green : result.verdict === "NOT_ASSESSED" ? c.yellow : c.red;
|
|
174
|
+
write(`
|
|
175
|
+
${c.bold}${c.cyan}Agent Verify${c.reset}`);
|
|
176
|
+
write(`${c.gray}${fileName}${c.reset}`);
|
|
177
|
+
write(`${c.bold}Score:${c.reset} ${result.riskScore}/100`);
|
|
178
|
+
write(`${c.bold}Verdict:${c.reset} ${verdictColor}${result.verdict === "NOT_VERIFIED" ? "NOT VERIFIED" : result.verdict.replace("_", " ")}${c.reset}`);
|
|
179
|
+
write(`${c.bold}Risk level:${c.reset} ${result.riskLevel}`);
|
|
180
|
+
write(`${c.bold}Confidence:${c.reset} ${result.confidence}/100`);
|
|
181
|
+
write(`${c.bold}Critical / High findings:${c.reset} ${criticalCount(result)} / ${highCount(result)}`);
|
|
182
|
+
if (result.metadata?.scannerVersion) write(`${c.bold}Ruleset version:${c.reset} ${result.metadata.scannerVersion}`);
|
|
183
|
+
if (fingerprintPreview(result)) write(`${c.bold}Artifact fingerprint:${c.reset} ${fingerprintPreview(result)}`);
|
|
184
|
+
if (result.policyProfile) write(`${c.bold}Policy (${result.policyProfile}):${c.reset} ${result.policyResult === "PASS" ? `${c.green}PASS${c.reset}` : `${c.red}FAIL${c.reset}`}`);
|
|
185
|
+
write(`${c.bold}Top blocker:${c.reset} ${getTopBlocker(result)}`);
|
|
186
|
+
write(`
|
|
187
|
+
${c.bold}Fix first:${c.reset}`);
|
|
188
|
+
const fixes = getFixFirst(result);
|
|
189
|
+
if (fixes.length) fixes.forEach((item, index) => write(` ${index + 1}. ${item}`));
|
|
190
|
+
else write(` No blocking fixes detected. Re-scan after any material change.`);
|
|
191
|
+
if (result.reportInsights?.nextAction) write(`
|
|
192
|
+
${c.bold}Next action:${c.reset} ${result.reportInsights.nextAction}`);
|
|
193
|
+
const threats = getRelevantThreatCategories(result);
|
|
194
|
+
if (threats.length) {
|
|
195
|
+
write(`
|
|
196
|
+
${c.bold}Threat categories:${c.reset}`);
|
|
197
|
+
threats.forEach((item) => write(` - ${item}`));
|
|
198
|
+
}
|
|
199
|
+
if (result.saved && result.reportId) write(`${c.bold}View report:${c.reset} https://aimodularity.com/agentverify/report/?id=${result.reportId}`);
|
|
200
|
+
write(`${c.bold}A2SPA docs:${c.reset} ${A2SPA_DOCS_URL}`);
|
|
201
|
+
write(`${c.gray}Re-scan:${c.reset} agentverify scan --file ${fileName} --key $AGENTVERIFY_API_KEY
|
|
202
|
+
`);
|
|
203
|
+
}
|
|
204
|
+
function markdownSummary(results, summary) {
|
|
205
|
+
const lines = ["# Agent Verify", "", `Files scanned: ${summary.total}`, `Verified: ${summary.verified}`, `Not verified: ${summary.notVerified}`, `Not assessed: ${summary.notAssessed}`, `Errors: ${summary.errors}`, ""];
|
|
206
|
+
for (const item of results) {
|
|
207
|
+
if (!item.result) {
|
|
208
|
+
lines.push(`## ${item.file}`, "", `Error: ${item.error ?? "Unknown error"}`, "");
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
lines.push(`## ${item.file}`, "", `Score: ${item.result.riskScore}/100`, `Verdict: ${item.result.verdict === "NOT_VERIFIED" ? "NOT VERIFIED" : item.result.verdict.replace("_", " ")}`, `Risk level: ${item.result.riskLevel}`, `Critical / High findings: ${criticalCount(item.result)} / ${highCount(item.result)}`);
|
|
212
|
+
if (item.result.metadata?.scannerVersion) lines.push(`Ruleset version: ${item.result.metadata.scannerVersion}`);
|
|
213
|
+
if (fingerprintPreview(item.result)) lines.push(`Artifact fingerprint: \`${fingerprintPreview(item.result)}\``);
|
|
214
|
+
if (item.result.policyProfile) lines.push(`Policy (${item.result.policyProfile}): ${item.result.policyResult}`);
|
|
215
|
+
lines.push(`Top blocker: ${getTopBlocker(item.result)}`, "");
|
|
216
|
+
const fixes = getFixFirst(item.result);
|
|
217
|
+
if (fixes.length) lines.push("Fix first:", ...fixes.map((fix, index) => `${index + 1}. ${fix}`), "");
|
|
218
|
+
const threats = getRelevantThreatCategories(item.result);
|
|
219
|
+
if (threats.length) lines.push("Threat categories:", ...threats.map((threat) => `- ${threat}`), "");
|
|
220
|
+
if (item.result.reportId) lines.push(`Report: https://aimodularity.com/agentverify/report/?id=${item.result.reportId}`, "");
|
|
221
|
+
}
|
|
222
|
+
lines.push(`A2SPA docs: ${A2SPA_DOCS_URL}`);
|
|
223
|
+
return lines.join("\n");
|
|
224
|
+
}
|
|
225
|
+
function writeGithubJobSummary(results, summary) {
|
|
226
|
+
const summaryPath = process.env.GITHUB_STEP_SUMMARY;
|
|
227
|
+
if (!summaryPath) return;
|
|
228
|
+
try {
|
|
229
|
+
appendFileSync(summaryPath, `${markdownSummary(results, summary)}
|
|
112
230
|
`);
|
|
231
|
+
} catch (e) {
|
|
232
|
+
console.error(`Warning: could not write GitHub job summary: ${e instanceof Error ? e.message : e}`);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
function writeSummaryFile(path, results, summary) {
|
|
236
|
+
if (!path) return;
|
|
237
|
+
try {
|
|
238
|
+
writeFileSync(path, JSON.stringify({ results, summary }, null, 2));
|
|
239
|
+
} catch (e) {
|
|
240
|
+
console.error(`Warning: could not write summary file: ${e instanceof Error ? e.message : e}`);
|
|
241
|
+
}
|
|
113
242
|
}
|
|
114
243
|
function printVerdict(result, fileName) {
|
|
115
244
|
const verified = result.verdict === "VERIFIED";
|
|
116
|
-
const
|
|
117
|
-
const
|
|
245
|
+
const notAssessed = result.verdict === "NOT_ASSESSED";
|
|
246
|
+
const icon = verified ? `${c.green}\u2713${c.reset}` : notAssessed ? `${c.yellow}\u26A0${c.reset}` : `${c.red}\u2717${c.reset}`;
|
|
247
|
+
const verdict = verified ? `${c.green}VERIFIED${c.reset} ` : notAssessed ? `${c.yellow}NOT ASSESSED${c.reset}` : `${c.red}NOT VERIFIED${c.reset}`;
|
|
118
248
|
const score = verified ? `${c.green}${result.riskScore}/100${c.reset}` : result.riskScore >= 50 ? `${c.yellow}${result.riskScore}/100${c.reset}` : `${c.red}${result.riskScore}/100${c.reset}`;
|
|
119
249
|
const findings = result.findings.length > 0 ? `${c.gray}\u2014 ${result.findings.length} finding${result.findings.length === 1 ? "" : "s"}${c.reset}` : "";
|
|
120
250
|
const name = fileName.padEnd(30);
|
|
@@ -128,20 +258,23 @@ async function runScan(args) {
|
|
|
128
258
|
console.error(`
|
|
129
259
|
${c.red}Error:${c.reset} API key required
|
|
130
260
|
`);
|
|
131
|
-
console.error(` Set with --key av_your_key or AGENTVERIFY_API_KEY env var
|
|
132
|
-
console.error(` Get your key at https://aimodularity.com/agentverify/dashboard
|
|
261
|
+
console.error(` Set with --key av_your_key or AGENTVERIFY_API_KEY env var.`);
|
|
262
|
+
console.error(` Get your key at https://aimodularity.com/agentverify/dashboard/`);
|
|
263
|
+
console.error(` Example: AGENTVERIFY_API_KEY=av_your_key agentverify scan .
|
|
133
264
|
`);
|
|
134
|
-
process.exit(
|
|
265
|
+
process.exit(EXIT_EXECUTION_ERROR);
|
|
135
266
|
}
|
|
136
267
|
let files = [];
|
|
137
268
|
if (args.file) {
|
|
138
269
|
files = [args.file];
|
|
139
270
|
} else {
|
|
140
271
|
const dir = args.dir;
|
|
141
|
-
|
|
272
|
+
if (!args.markdown && !args.json) {
|
|
273
|
+
write(`
|
|
142
274
|
${c.bold}Agent Verify${c.reset} ${c.gray}v${VERSION}${c.reset}`);
|
|
143
|
-
|
|
275
|
+
write(`${c.gray}Scanning ${dir}...${c.reset}
|
|
144
276
|
`);
|
|
277
|
+
}
|
|
145
278
|
files = collectFiles(dir);
|
|
146
279
|
if (files.length === 0) {
|
|
147
280
|
write(`${c.yellow}No agent files found in ${dir}${c.reset}`);
|
|
@@ -153,16 +286,21 @@ ${c.bold}Agent Verify${c.reset} ${c.gray}v${VERSION}${c.reset}`);
|
|
|
153
286
|
const jsonResults = [];
|
|
154
287
|
let verified = 0;
|
|
155
288
|
let notVerified = 0;
|
|
289
|
+
let notAssessed = 0;
|
|
156
290
|
let errors = 0;
|
|
157
291
|
const CONCURRENCY = 3;
|
|
158
292
|
for (let i = 0; i < files.length; i += CONCURRENCY) {
|
|
159
293
|
const batch = files.slice(i, i + CONCURRENCY);
|
|
160
294
|
const batchResults = await Promise.allSettled(
|
|
161
295
|
batch.map(async (filePath) => {
|
|
296
|
+
const fileStat = statSync(filePath);
|
|
297
|
+
if (fileStat.size > MAX_SCAN_FILE_BYTES) {
|
|
298
|
+
throw new Error(`File is ${(fileStat.size / 1024 / 1024).toFixed(1)}MB, over the ${MAX_SCAN_FILE_BYTES / 1024 / 1024}MB scan limit \u2014 skipped`);
|
|
299
|
+
}
|
|
162
300
|
const content = readFileSync(filePath, "utf-8");
|
|
163
301
|
const relPath = relative(args.dir === "." ? process.cwd() : args.dir, filePath);
|
|
164
302
|
const result = await scan(
|
|
165
|
-
{ content, fileName: relPath },
|
|
303
|
+
{ content, fileName: relPath, policyId: args.policy || void 0 },
|
|
166
304
|
{ apiKey: args.key, apiUrl: API_URL }
|
|
167
305
|
);
|
|
168
306
|
return { filePath, relPath, result };
|
|
@@ -172,9 +310,13 @@ ${c.bold}Agent Verify${c.reset} ${c.gray}v${VERSION}${c.reset}`);
|
|
|
172
310
|
const outcome = batchResults[j];
|
|
173
311
|
if (outcome.status === "fulfilled") {
|
|
174
312
|
const { relPath, result } = outcome.value;
|
|
175
|
-
if (!args.json
|
|
313
|
+
if (!args.json && !args.markdown) {
|
|
314
|
+
if (args.file && !args.ci) printDetailedSummary(result, relPath);
|
|
315
|
+
else printVerdict(result, relPath);
|
|
316
|
+
}
|
|
176
317
|
jsonResults.push({ file: relPath, result, error: null });
|
|
177
318
|
if (result.verdict === "VERIFIED") verified++;
|
|
319
|
+
else if (result.verdict === "NOT_ASSESSED") notAssessed++;
|
|
178
320
|
else notVerified++;
|
|
179
321
|
} else {
|
|
180
322
|
const filePath = batch[j];
|
|
@@ -188,32 +330,58 @@ ${c.bold}Agent Verify${c.reset} ${c.gray}v${VERSION}${c.reset}`);
|
|
|
188
330
|
}
|
|
189
331
|
}
|
|
190
332
|
}
|
|
333
|
+
const summary = { total: files.length, verified, notVerified, notAssessed, errors };
|
|
334
|
+
writeGithubJobSummary(jsonResults, summary);
|
|
335
|
+
writeSummaryFile(args.summaryFile, jsonResults, summary);
|
|
191
336
|
if (args.json) {
|
|
192
|
-
write(JSON.stringify({ results: jsonResults, summary
|
|
337
|
+
write(JSON.stringify({ results: jsonResults, summary }, null, 2));
|
|
338
|
+
if (errors > 0) process.exit(EXIT_EXECUTION_ERROR);
|
|
339
|
+
if (args.ci) {
|
|
340
|
+
if (notVerified > 0) process.exit(EXIT_NOT_VERIFIED);
|
|
341
|
+
if (notAssessed > 0 && !args.allowNotAssessed) process.exit(EXIT_NOT_ASSESSED);
|
|
342
|
+
}
|
|
193
343
|
return;
|
|
194
344
|
}
|
|
345
|
+
if (args.markdown) {
|
|
346
|
+
write(markdownSummary(jsonResults, summary));
|
|
347
|
+
if (errors > 0) process.exit(EXIT_EXECUTION_ERROR);
|
|
348
|
+
if (args.ci) {
|
|
349
|
+
if (notVerified > 0) process.exit(EXIT_NOT_VERIFIED);
|
|
350
|
+
if (notAssessed > 0 && !args.allowNotAssessed) process.exit(EXIT_NOT_ASSESSED);
|
|
351
|
+
}
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
const savedReports = jsonResults.filter((item) => item.result?.saved && item.result.reportId).length;
|
|
195
355
|
write(`
|
|
196
356
|
${c.gray}${"\u2500".repeat(60)}${c.reset}`);
|
|
197
357
|
write(`${c.bold}Agent Verify Scan Complete${c.reset}`);
|
|
198
358
|
write(`${c.gray}Files scanned:${c.reset} ${files.length}`);
|
|
199
359
|
write(`${c.green}Verified:${c.reset} ${verified}`);
|
|
200
360
|
if (notVerified > 0) write(`${c.red}Not Verified:${c.reset} ${notVerified}`);
|
|
361
|
+
if (notAssessed > 0) write(`${c.yellow}Not Assessed:${c.reset} ${notAssessed}`);
|
|
201
362
|
if (errors > 0) write(`${c.yellow}Errors:${c.reset} ${errors}`);
|
|
202
363
|
write(`${c.gray}${"\u2500".repeat(60)}${c.reset}`);
|
|
203
|
-
write(`
|
|
204
|
-
${c.gray}
|
|
364
|
+
if (savedReports > 0) write(`
|
|
365
|
+
${c.gray}Saved CLI reports appear in your dashboard: https://aimodularity.com/agentverify/dashboard/${c.reset}`);
|
|
366
|
+
else write(`
|
|
367
|
+
${c.gray}No dashboard report was saved. Check API key access and retry if you need dashboard sync.${c.reset}`);
|
|
368
|
+
write(`${c.gray}A2SPA docs: ${A2SPA_DOCS_URL}${c.reset}
|
|
205
369
|
`);
|
|
206
|
-
if (
|
|
370
|
+
if (errors > 0) process.exit(EXIT_EXECUTION_ERROR);
|
|
371
|
+
if (args.ci) {
|
|
372
|
+
if (notVerified > 0) process.exit(EXIT_NOT_VERIFIED);
|
|
373
|
+
if (notAssessed > 0 && !args.allowNotAssessed) process.exit(EXIT_NOT_ASSESSED);
|
|
374
|
+
}
|
|
207
375
|
}
|
|
208
376
|
async function main() {
|
|
209
377
|
const args = parseArgs(process.argv);
|
|
210
378
|
if (args.version) {
|
|
211
379
|
write(`agentverify v${VERSION}`);
|
|
212
|
-
process.exit(
|
|
380
|
+
process.exit(EXIT_OK);
|
|
213
381
|
}
|
|
214
382
|
if (args.help || !args.command) {
|
|
215
383
|
printHelp();
|
|
216
|
-
process.exit(
|
|
384
|
+
process.exit(EXIT_OK);
|
|
217
385
|
}
|
|
218
386
|
if (args.command === "scan") {
|
|
219
387
|
await runScan(args);
|
|
@@ -222,11 +390,11 @@ async function main() {
|
|
|
222
390
|
${c.red}Unknown command: ${args.command}${c.reset}`);
|
|
223
391
|
console.error(`Run ${c.cyan}agentverify --help${c.reset} for usage
|
|
224
392
|
`);
|
|
225
|
-
process.exit(
|
|
393
|
+
process.exit(EXIT_EXECUTION_ERROR);
|
|
226
394
|
}
|
|
227
395
|
}
|
|
228
396
|
main().catch((err) => {
|
|
229
397
|
console.error(`
|
|
230
398
|
${c.red}Fatal error:${c.reset}`, err instanceof Error ? err.message : err);
|
|
231
|
-
process.exit(
|
|
399
|
+
process.exit(EXIT_EXECUTION_ERROR);
|
|
232
400
|
});
|
package/dist/sdk.d.mts
CHANGED
|
@@ -2,25 +2,133 @@ interface ScanInput {
|
|
|
2
2
|
content: string;
|
|
3
3
|
fileName?: string;
|
|
4
4
|
platform?: string;
|
|
5
|
+
/** One of the built-in policy profile ids (e.g. "financial-agent") — evaluated server-side against this scan's evidence, in addition to (never instead of) the scanner's own verdict. */
|
|
6
|
+
policyId?: string;
|
|
7
|
+
}
|
|
8
|
+
interface ArtifactFingerprint {
|
|
9
|
+
artifactHash: string;
|
|
10
|
+
artifactHashAlgorithm: string;
|
|
11
|
+
artifactFingerprintVersion: string;
|
|
12
|
+
}
|
|
13
|
+
interface ReportIntegrity {
|
|
14
|
+
reportHash: string;
|
|
15
|
+
algorithm: string;
|
|
16
|
+
schemaVersion: string;
|
|
17
|
+
scannerVersion: string;
|
|
18
|
+
scanId: string;
|
|
19
|
+
timestamp: string;
|
|
20
|
+
}
|
|
21
|
+
interface SignedAttestation {
|
|
22
|
+
payload: Record<string, unknown>;
|
|
23
|
+
signature: string;
|
|
24
|
+
algorithm: string;
|
|
25
|
+
publicKey: Record<string, unknown>;
|
|
26
|
+
}
|
|
27
|
+
type Verdict = 'VERIFIED' | 'NOT_VERIFIED' | 'NOT_ASSESSED';
|
|
28
|
+
type RiskLevel = 'Low Risk' | 'Moderate Risk' | 'High Risk';
|
|
29
|
+
type Category = 'A' | 'B';
|
|
30
|
+
type Severity = 'critical' | 'high' | 'medium' | 'low';
|
|
31
|
+
type ThreatCategoryStatus = 'detected' | 'possible' | 'missing_evidence' | 'not_assessed';
|
|
32
|
+
interface ThreatCategoryAssessment {
|
|
33
|
+
id: string;
|
|
34
|
+
label: string;
|
|
35
|
+
status: ThreatCategoryStatus;
|
|
36
|
+
severity: Severity;
|
|
37
|
+
whatItMeans: string;
|
|
38
|
+
evidencePattern: string;
|
|
39
|
+
whyItMatters: string;
|
|
40
|
+
recommendedFix: string;
|
|
41
|
+
a2spaImpact: string;
|
|
5
42
|
}
|
|
6
43
|
interface Finding {
|
|
44
|
+
id: string;
|
|
45
|
+
code: string;
|
|
7
46
|
title: string;
|
|
8
|
-
category:
|
|
9
|
-
severity:
|
|
47
|
+
category: Category;
|
|
48
|
+
severity: Severity;
|
|
10
49
|
whatIsWrong: string;
|
|
11
50
|
whyItMatters: string;
|
|
12
|
-
recommendedFix: string;
|
|
13
51
|
evidence?: string;
|
|
52
|
+
recommendedFix: string;
|
|
53
|
+
fixCode?: string;
|
|
54
|
+
quickFix?: string;
|
|
55
|
+
compliance?: {
|
|
56
|
+
owasp?: string[];
|
|
57
|
+
nist?: string[];
|
|
58
|
+
soc2?: string[];
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
interface RuntimeBOM {
|
|
62
|
+
detectedLanguage: string;
|
|
63
|
+
detectedFramework: string | null;
|
|
64
|
+
detectedPlatform: string | null;
|
|
65
|
+
agentName: string | null;
|
|
66
|
+
toolAccessLevel: 'Restricted' | 'Unrestricted' | 'Unknown';
|
|
67
|
+
credentialExposure: 'Detected' | 'Not Detected';
|
|
68
|
+
memoryPersistence: 'Bounded' | 'Unbounded' | 'Unknown';
|
|
69
|
+
auditLogging: 'Present' | 'Absent' | 'Unknown';
|
|
70
|
+
humanGates: 'Present' | 'Absent' | 'Unknown';
|
|
71
|
+
rateLimiting: 'Present' | 'Absent' | 'Unknown';
|
|
72
|
+
promptInjectionSurface: 'Detected' | 'Not Detected' | 'Unknown';
|
|
73
|
+
delegationScope: 'Scoped' | 'Unscoped' | 'Unknown';
|
|
74
|
+
}
|
|
75
|
+
interface CategoryScore {
|
|
76
|
+
category: Category;
|
|
77
|
+
label: string;
|
|
78
|
+
score: number;
|
|
79
|
+
maxScore: number;
|
|
80
|
+
findingCount: number;
|
|
81
|
+
}
|
|
82
|
+
interface FixPriorityItem {
|
|
83
|
+
code: string;
|
|
84
|
+
title: string;
|
|
85
|
+
severity: Severity;
|
|
86
|
+
priority: 'fix_first' | 'fix_next' | 'nice_to_have';
|
|
87
|
+
reason: string;
|
|
88
|
+
}
|
|
89
|
+
interface ReportInsights {
|
|
90
|
+
executionReadinessScore: number;
|
|
91
|
+
a2spaReadinessScore: number;
|
|
92
|
+
remediationProgressScore: number;
|
|
93
|
+
topBlocker: string | null;
|
|
94
|
+
nextAction: string;
|
|
95
|
+
scoreExplanation: string[];
|
|
96
|
+
improvesScore: string[];
|
|
97
|
+
verificationBlockers: string[];
|
|
98
|
+
fixPriority: FixPriorityItem[];
|
|
14
99
|
}
|
|
15
100
|
interface ScanResult {
|
|
16
|
-
|
|
101
|
+
schemaVersion: '1.3.0';
|
|
102
|
+
reportId: string;
|
|
103
|
+
verdict: Verdict;
|
|
17
104
|
riskScore: number;
|
|
18
|
-
riskLevel:
|
|
105
|
+
riskLevel: RiskLevel;
|
|
106
|
+
confidence: number;
|
|
107
|
+
optimizationScore: number;
|
|
108
|
+
reportInsights?: ReportInsights;
|
|
109
|
+
threatCategories?: ThreatCategoryAssessment[];
|
|
19
110
|
findings: Finding[];
|
|
20
|
-
|
|
21
|
-
|
|
111
|
+
categoryScores: CategoryScore[];
|
|
112
|
+
bom: RuntimeBOM;
|
|
113
|
+
metadata: {
|
|
114
|
+
schemaVersion: '1.3.0';
|
|
115
|
+
scannerVersion: string;
|
|
116
|
+
fileName: string;
|
|
117
|
+
fileSize: number;
|
|
118
|
+
scannedAt: string;
|
|
119
|
+
detectedLanguage: string;
|
|
120
|
+
detectedFramework: string | null;
|
|
121
|
+
selectedPlatform: string | null;
|
|
122
|
+
agentName: string | null;
|
|
123
|
+
scanDuration: number;
|
|
124
|
+
};
|
|
22
125
|
saved: boolean;
|
|
23
|
-
reportUrl?: string;
|
|
126
|
+
reportUrl?: string | null;
|
|
127
|
+
artifactFingerprint?: ArtifactFingerprint;
|
|
128
|
+
reportIntegrity?: ReportIntegrity;
|
|
129
|
+
policyProfile?: string | null;
|
|
130
|
+
policyResult?: string | null;
|
|
131
|
+
attestation?: SignedAttestation | null;
|
|
24
132
|
}
|
|
25
133
|
interface ScanOptions {
|
|
26
134
|
apiKey: string;
|
|
@@ -39,7 +147,7 @@ interface ScanOptions {
|
|
|
39
147
|
* { apiKey: process.env.AGENTVERIFY_API_KEY }
|
|
40
148
|
* )
|
|
41
149
|
*
|
|
42
|
-
* result.verdict // 'VERIFIED' | '
|
|
150
|
+
* result.verdict // 'VERIFIED' | 'NOT_VERIFIED' | 'NOT_ASSESSED'
|
|
43
151
|
* result.riskScore // 0-100
|
|
44
152
|
* result.findings // Array of findings
|
|
45
153
|
*/
|
package/dist/sdk.d.ts
CHANGED
|
@@ -2,25 +2,133 @@ interface ScanInput {
|
|
|
2
2
|
content: string;
|
|
3
3
|
fileName?: string;
|
|
4
4
|
platform?: string;
|
|
5
|
+
/** One of the built-in policy profile ids (e.g. "financial-agent") — evaluated server-side against this scan's evidence, in addition to (never instead of) the scanner's own verdict. */
|
|
6
|
+
policyId?: string;
|
|
7
|
+
}
|
|
8
|
+
interface ArtifactFingerprint {
|
|
9
|
+
artifactHash: string;
|
|
10
|
+
artifactHashAlgorithm: string;
|
|
11
|
+
artifactFingerprintVersion: string;
|
|
12
|
+
}
|
|
13
|
+
interface ReportIntegrity {
|
|
14
|
+
reportHash: string;
|
|
15
|
+
algorithm: string;
|
|
16
|
+
schemaVersion: string;
|
|
17
|
+
scannerVersion: string;
|
|
18
|
+
scanId: string;
|
|
19
|
+
timestamp: string;
|
|
20
|
+
}
|
|
21
|
+
interface SignedAttestation {
|
|
22
|
+
payload: Record<string, unknown>;
|
|
23
|
+
signature: string;
|
|
24
|
+
algorithm: string;
|
|
25
|
+
publicKey: Record<string, unknown>;
|
|
26
|
+
}
|
|
27
|
+
type Verdict = 'VERIFIED' | 'NOT_VERIFIED' | 'NOT_ASSESSED';
|
|
28
|
+
type RiskLevel = 'Low Risk' | 'Moderate Risk' | 'High Risk';
|
|
29
|
+
type Category = 'A' | 'B';
|
|
30
|
+
type Severity = 'critical' | 'high' | 'medium' | 'low';
|
|
31
|
+
type ThreatCategoryStatus = 'detected' | 'possible' | 'missing_evidence' | 'not_assessed';
|
|
32
|
+
interface ThreatCategoryAssessment {
|
|
33
|
+
id: string;
|
|
34
|
+
label: string;
|
|
35
|
+
status: ThreatCategoryStatus;
|
|
36
|
+
severity: Severity;
|
|
37
|
+
whatItMeans: string;
|
|
38
|
+
evidencePattern: string;
|
|
39
|
+
whyItMatters: string;
|
|
40
|
+
recommendedFix: string;
|
|
41
|
+
a2spaImpact: string;
|
|
5
42
|
}
|
|
6
43
|
interface Finding {
|
|
44
|
+
id: string;
|
|
45
|
+
code: string;
|
|
7
46
|
title: string;
|
|
8
|
-
category:
|
|
9
|
-
severity:
|
|
47
|
+
category: Category;
|
|
48
|
+
severity: Severity;
|
|
10
49
|
whatIsWrong: string;
|
|
11
50
|
whyItMatters: string;
|
|
12
|
-
recommendedFix: string;
|
|
13
51
|
evidence?: string;
|
|
52
|
+
recommendedFix: string;
|
|
53
|
+
fixCode?: string;
|
|
54
|
+
quickFix?: string;
|
|
55
|
+
compliance?: {
|
|
56
|
+
owasp?: string[];
|
|
57
|
+
nist?: string[];
|
|
58
|
+
soc2?: string[];
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
interface RuntimeBOM {
|
|
62
|
+
detectedLanguage: string;
|
|
63
|
+
detectedFramework: string | null;
|
|
64
|
+
detectedPlatform: string | null;
|
|
65
|
+
agentName: string | null;
|
|
66
|
+
toolAccessLevel: 'Restricted' | 'Unrestricted' | 'Unknown';
|
|
67
|
+
credentialExposure: 'Detected' | 'Not Detected';
|
|
68
|
+
memoryPersistence: 'Bounded' | 'Unbounded' | 'Unknown';
|
|
69
|
+
auditLogging: 'Present' | 'Absent' | 'Unknown';
|
|
70
|
+
humanGates: 'Present' | 'Absent' | 'Unknown';
|
|
71
|
+
rateLimiting: 'Present' | 'Absent' | 'Unknown';
|
|
72
|
+
promptInjectionSurface: 'Detected' | 'Not Detected' | 'Unknown';
|
|
73
|
+
delegationScope: 'Scoped' | 'Unscoped' | 'Unknown';
|
|
74
|
+
}
|
|
75
|
+
interface CategoryScore {
|
|
76
|
+
category: Category;
|
|
77
|
+
label: string;
|
|
78
|
+
score: number;
|
|
79
|
+
maxScore: number;
|
|
80
|
+
findingCount: number;
|
|
81
|
+
}
|
|
82
|
+
interface FixPriorityItem {
|
|
83
|
+
code: string;
|
|
84
|
+
title: string;
|
|
85
|
+
severity: Severity;
|
|
86
|
+
priority: 'fix_first' | 'fix_next' | 'nice_to_have';
|
|
87
|
+
reason: string;
|
|
88
|
+
}
|
|
89
|
+
interface ReportInsights {
|
|
90
|
+
executionReadinessScore: number;
|
|
91
|
+
a2spaReadinessScore: number;
|
|
92
|
+
remediationProgressScore: number;
|
|
93
|
+
topBlocker: string | null;
|
|
94
|
+
nextAction: string;
|
|
95
|
+
scoreExplanation: string[];
|
|
96
|
+
improvesScore: string[];
|
|
97
|
+
verificationBlockers: string[];
|
|
98
|
+
fixPriority: FixPriorityItem[];
|
|
14
99
|
}
|
|
15
100
|
interface ScanResult {
|
|
16
|
-
|
|
101
|
+
schemaVersion: '1.3.0';
|
|
102
|
+
reportId: string;
|
|
103
|
+
verdict: Verdict;
|
|
17
104
|
riskScore: number;
|
|
18
|
-
riskLevel:
|
|
105
|
+
riskLevel: RiskLevel;
|
|
106
|
+
confidence: number;
|
|
107
|
+
optimizationScore: number;
|
|
108
|
+
reportInsights?: ReportInsights;
|
|
109
|
+
threatCategories?: ThreatCategoryAssessment[];
|
|
19
110
|
findings: Finding[];
|
|
20
|
-
|
|
21
|
-
|
|
111
|
+
categoryScores: CategoryScore[];
|
|
112
|
+
bom: RuntimeBOM;
|
|
113
|
+
metadata: {
|
|
114
|
+
schemaVersion: '1.3.0';
|
|
115
|
+
scannerVersion: string;
|
|
116
|
+
fileName: string;
|
|
117
|
+
fileSize: number;
|
|
118
|
+
scannedAt: string;
|
|
119
|
+
detectedLanguage: string;
|
|
120
|
+
detectedFramework: string | null;
|
|
121
|
+
selectedPlatform: string | null;
|
|
122
|
+
agentName: string | null;
|
|
123
|
+
scanDuration: number;
|
|
124
|
+
};
|
|
22
125
|
saved: boolean;
|
|
23
|
-
reportUrl?: string;
|
|
126
|
+
reportUrl?: string | null;
|
|
127
|
+
artifactFingerprint?: ArtifactFingerprint;
|
|
128
|
+
reportIntegrity?: ReportIntegrity;
|
|
129
|
+
policyProfile?: string | null;
|
|
130
|
+
policyResult?: string | null;
|
|
131
|
+
attestation?: SignedAttestation | null;
|
|
24
132
|
}
|
|
25
133
|
interface ScanOptions {
|
|
26
134
|
apiKey: string;
|
|
@@ -39,7 +147,7 @@ interface ScanOptions {
|
|
|
39
147
|
* { apiKey: process.env.AGENTVERIFY_API_KEY }
|
|
40
148
|
* )
|
|
41
149
|
*
|
|
42
|
-
* result.verdict // 'VERIFIED' | '
|
|
150
|
+
* result.verdict // 'VERIFIED' | 'NOT_VERIFIED' | 'NOT_ASSESSED'
|
|
43
151
|
* result.riskScore // 0-100
|
|
44
152
|
* result.findings // Array of findings
|
|
45
153
|
*/
|