agentcert 0.4.0 → 0.5.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/README.md
CHANGED
|
@@ -6,6 +6,10 @@ AgentCert checks what an agent may do, whether it passed pre-release evidence,
|
|
|
6
6
|
whether a high-risk runtime action may proceed, and who can verify the observed
|
|
7
7
|
outcome. It writes portable reports and accumulates a local failure corpus.
|
|
8
8
|
|
|
9
|
+
[Public assurance demo](https://agentcert-control-plane.onrender.com/demo) |
|
|
10
|
+
[Private workspace](https://agentcert-control-plane.onrender.com/app) |
|
|
11
|
+
[GitHub source](https://github.com/Kakarottoooo/agentcert)
|
|
12
|
+
|
|
9
13
|
## 5-minute local path
|
|
10
14
|
|
|
11
15
|
```bash
|
|
@@ -94,6 +98,23 @@ routes, with a 5-second timeout and 10-request-per-minute process-local cap.
|
|
|
94
98
|
The Stripe key is environment-only and is never written to output or evidence.
|
|
95
99
|
See the [Bounded Vendor Sandbox Egress guide](https://github.com/Kakarottoooo/agentcert/blob/main/docs/bounded-vendor-sandbox-egress.md).
|
|
96
100
|
|
|
101
|
+
Release maintainers can run the protected, manual
|
|
102
|
+
`Real Stripe sandbox acceptance` GitHub workflow. It performs the bounded read,
|
|
103
|
+
independently scans the generated report before upload, validates it again at
|
|
104
|
+
the CLI boundary, uploads the v0.4 evidence, and compares it with prior
|
|
105
|
+
protected runs. See the [real vendor acceptance guide](https://github.com/Kakarottoooo/agentcert/blob/main/docs/real-vendor-acceptance.md).
|
|
106
|
+
|
|
107
|
+
The workflow's upload step is also available as a narrow command for an
|
|
108
|
+
already-generated report:
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
npx agentcert sandbox upload-report --report .agentcert/vendor-sandbox/current-report.json --external-id vendor-acceptance:stripe:<run>:<attempt>
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
This command accepts only AgentCert sandbox conformance and vendor-egress
|
|
115
|
+
contracts and rejects reports containing credential-shaped values or forbidden
|
|
116
|
+
sensitive fields.
|
|
117
|
+
|
|
97
118
|
Review/export helpers:
|
|
98
119
|
|
|
99
120
|
```bash
|
package/dist/sandbox.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { mkdir, writeFile } from "node:fs/promises";
|
|
1
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
2
|
import { dirname, resolve } from "node:path";
|
|
3
3
|
import { pathToFileURL } from "node:url";
|
|
4
4
|
import { resolveConnection } from "./credentials.js";
|
|
@@ -19,6 +19,8 @@ export async function runSandboxCommand(args) {
|
|
|
19
19
|
return pushSandboxCertification(args.slice(1));
|
|
20
20
|
if (action === "stripe-readonly")
|
|
21
21
|
return runStripeSandboxReadOnly(args.slice(1));
|
|
22
|
+
if (action === "upload-report")
|
|
23
|
+
return uploadSandboxReport(args.slice(1));
|
|
22
24
|
throw new Error(`Unknown sandbox command ${JSON.stringify(action)}. Run \`npx agentcert sandbox --help\`.`);
|
|
23
25
|
}
|
|
24
26
|
export async function initializeSandboxAdapter(args) {
|
|
@@ -103,6 +105,59 @@ export async function runStripeSandboxReadOnly(args, runtimeOverride) {
|
|
|
103
105
|
}
|
|
104
106
|
return { exitCode: report.verdict.passed ? 0 : 1, reportPath, report };
|
|
105
107
|
}
|
|
108
|
+
export async function uploadSandboxReport(args, runtimeOverride) {
|
|
109
|
+
const requestedPath = flag(args, "--report");
|
|
110
|
+
if (!requestedPath)
|
|
111
|
+
throw new Error("Sandbox report upload requires --report <report.json>.");
|
|
112
|
+
const reportPath = resolve(requestedPath);
|
|
113
|
+
const report = parseUploadableSandboxReport(await readFile(reportPath, "utf8"));
|
|
114
|
+
const runtime = runtimeOverride ?? await loadSandboxRuntime();
|
|
115
|
+
const connection = await resolveConnection({
|
|
116
|
+
name: flag(args, "--connection"),
|
|
117
|
+
server: flag(args, "--server"),
|
|
118
|
+
projectId: flag(args, "--project"),
|
|
119
|
+
apiKey: flag(args, "--api-key"),
|
|
120
|
+
});
|
|
121
|
+
const uploaded = await runtime.uploadSandboxCertificationReport(report, {
|
|
122
|
+
baseUrl: connection.server,
|
|
123
|
+
projectId: connection.projectId,
|
|
124
|
+
apiKey: connection.apiKey,
|
|
125
|
+
externalId: flag(args, "--external-id"),
|
|
126
|
+
});
|
|
127
|
+
process.stdout.write(`Uploaded validated sandbox report: ${reportPath}\n`);
|
|
128
|
+
process.stdout.write(`Hosted sandbox run: ${String(uploaded.run.id ?? "created")}\n`);
|
|
129
|
+
process.stdout.write(`Hosted evidence: ${String(uploaded.evidence.id ?? "created")}\n`);
|
|
130
|
+
return { exitCode: report.verdict.passed ? 0 : 1, reportPath, report };
|
|
131
|
+
}
|
|
132
|
+
export function parseUploadableSandboxReport(rawText) {
|
|
133
|
+
let value;
|
|
134
|
+
try {
|
|
135
|
+
value = JSON.parse(rawText);
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
throw new Error("Sandbox report is not valid JSON.");
|
|
139
|
+
}
|
|
140
|
+
if (!isRecord(value))
|
|
141
|
+
throw new Error("Sandbox report must be a JSON object.");
|
|
142
|
+
const supported = (value.kind === "agentcert.sandbox_adapter_conformance"
|
|
143
|
+
&& value.schemaVersion === "agentcert.sandbox_adapter_conformance.v0.2")
|
|
144
|
+
|| (value.kind === "agentcert.sandbox_vendor_egress"
|
|
145
|
+
&& value.schemaVersion === "agentcert.sandbox_vendor_egress.v0.4");
|
|
146
|
+
if (!supported)
|
|
147
|
+
throw new Error("Sandbox report kind or schema version is not supported for upload.");
|
|
148
|
+
if (typeof value.implementation !== "string" || !value.implementation.trim()
|
|
149
|
+
|| typeof value.generatedAt !== "string" || !Number.isFinite(Date.parse(value.generatedAt))
|
|
150
|
+
|| !isRecord(value.verdict) || typeof value.verdict.passed !== "boolean"
|
|
151
|
+
|| typeof value.verdict.score !== "number" || value.verdict.score < 0 || value.verdict.score > 100
|
|
152
|
+
|| !Array.isArray(value.checks)) {
|
|
153
|
+
throw new Error("Sandbox report is missing required conformance fields.");
|
|
154
|
+
}
|
|
155
|
+
const findings = sensitiveReportFindings(value, rawText);
|
|
156
|
+
if (findings.length > 0) {
|
|
157
|
+
throw new Error(`Sandbox report contains forbidden credential material: ${findings.join(", ")}.`);
|
|
158
|
+
}
|
|
159
|
+
return value;
|
|
160
|
+
}
|
|
106
161
|
export async function loadSandboxAdapter(adapterPath) {
|
|
107
162
|
let module;
|
|
108
163
|
try {
|
|
@@ -146,18 +201,28 @@ Use \`agentcert connect\` first, or pass --server, --project, and --api-key.
|
|
|
146
201
|
Reads one Stripe sandbox PaymentIntent through a fixed GET/resource allowlist,
|
|
147
202
|
writes a redacted evidence report, and optionally uploads it to AgentCert Hosted.
|
|
148
203
|
The Stripe key is read only from STRIPE_RESTRICTED_TEST_KEY.
|
|
204
|
+
`;
|
|
205
|
+
if (action === "upload-report")
|
|
206
|
+
return `Usage:
|
|
207
|
+
agentcert sandbox upload-report --report .agentcert/vendor-sandbox/current-report.json --external-id <id>
|
|
208
|
+
|
|
209
|
+
Validates a recognized AgentCert sandbox report, rejects credential material,
|
|
210
|
+
and uploads it through the saved AgentCert connection. This command never
|
|
211
|
+
executes a vendor request.
|
|
149
212
|
`;
|
|
150
213
|
return `Usage:
|
|
151
214
|
agentcert sandbox init [--adapter agentcert.sandbox.mjs]
|
|
152
215
|
agentcert sandbox certify --adapter ./my-sandbox-adapter.js
|
|
153
216
|
agentcert sandbox push --adapter ./my-sandbox-adapter.js
|
|
154
217
|
agentcert sandbox stripe-readonly --payment-intent pi_... [--push]
|
|
218
|
+
agentcert sandbox upload-report --report <report.json>
|
|
155
219
|
|
|
156
220
|
Commands:
|
|
157
221
|
init Write one dependency-free adapter template
|
|
158
222
|
certify Run deterministic local conformance checks
|
|
159
223
|
push Certify and upload the report to AgentCert Hosted
|
|
160
224
|
stripe-readonly Run one bounded Stripe sandbox read and write evidence
|
|
225
|
+
upload-report Validate and upload an existing sandbox report
|
|
161
226
|
|
|
162
227
|
Common options:
|
|
163
228
|
--adapter <path> Adapter module (default: agentcert.sandbox.mjs)
|
|
@@ -199,6 +264,38 @@ function isSandboxSystem(value) {
|
|
|
199
264
|
&& ["createTenant", "deleteTenant", "resetTenant", "seedTenant", "hasTenant", "snapshotTenant", "adapterForTenant"]
|
|
200
265
|
.every((method) => typeof system[method] === "function");
|
|
201
266
|
}
|
|
267
|
+
function sensitiveReportFindings(value, rawText) {
|
|
268
|
+
const findings = new Set();
|
|
269
|
+
const forbiddenFields = new Set(["authorization", "headers", "apikey", "restrictedapikey", "secretkey", "clientsecret", "metadata", "rawresponse", "responsebody"]);
|
|
270
|
+
const inspect = (entry) => {
|
|
271
|
+
if (Array.isArray(entry)) {
|
|
272
|
+
entry.forEach(inspect);
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
if (!isRecord(entry))
|
|
276
|
+
return;
|
|
277
|
+
for (const [key, nested] of Object.entries(entry)) {
|
|
278
|
+
if (forbiddenFields.has(key.replace(/[^a-z]/gi, "").toLowerCase()))
|
|
279
|
+
findings.add(`field:${key}`);
|
|
280
|
+
inspect(nested);
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
inspect(value);
|
|
284
|
+
if (/\b(?:rk|sk)_(?:test|live)_[A-Za-z0-9_]{8,}\b/.test(rawText))
|
|
285
|
+
findings.add("stripe-key");
|
|
286
|
+
if (/\bac_live_[A-Za-z0-9_-]{8,}\b/.test(rawText))
|
|
287
|
+
findings.add("agentcert-key");
|
|
288
|
+
if (/\bBearer\s+\S+/i.test(rawText))
|
|
289
|
+
findings.add("authorization-value");
|
|
290
|
+
for (const secret of [process.env.STRIPE_RESTRICTED_TEST_KEY, process.env.AGENTCERT_API_KEY]) {
|
|
291
|
+
if (secret && secret.length >= 8 && rawText.includes(secret))
|
|
292
|
+
findings.add("exact-secret");
|
|
293
|
+
}
|
|
294
|
+
return [...findings].sort();
|
|
295
|
+
}
|
|
296
|
+
function isRecord(value) {
|
|
297
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
298
|
+
}
|
|
202
299
|
function flag(args, name) {
|
|
203
300
|
const index = args.indexOf(name);
|
|
204
301
|
return index >= 0 ? args[index + 1] : undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sandbox-hosted.d.ts","sourceRoot":"","sources":["../../../../onegent-runtime/src/sandbox-hosted.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"sandbox-hosted.d.ts","sourceRoot":"","sources":["../../../../onegent-runtime/src/sandbox-hosted.ts"],"names":[],"mappings":"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"}
|
|
@@ -68,6 +68,15 @@ export async function uploadSandboxCertificationReport(report, options) {
|
|
|
68
68
|
const digest = createHash("sha256").update(bytes).digest("hex");
|
|
69
69
|
const operationId = `sandbox-upload-${digest.slice(0, 32)}`;
|
|
70
70
|
const externalId = options.externalId?.trim() || `${report.kind}:${report.implementation}:${report.generatedAt}`;
|
|
71
|
+
const vendorMetadata = report.kind === "agentcert.sandbox_vendor_egress"
|
|
72
|
+
? {
|
|
73
|
+
vendor: report.vendor,
|
|
74
|
+
environment: report.environment,
|
|
75
|
+
policySha256: createHash("sha256").update(canonicalJson(report.policy)).digest("hex"),
|
|
76
|
+
requestDurationMs: report.audit.find((entry) => entry.outcome === "allowed")?.durationMs,
|
|
77
|
+
acceptanceType: externalId.startsWith("vendor-acceptance:stripe:") ? "real_vendor_sandbox" : "vendor_sandbox",
|
|
78
|
+
}
|
|
79
|
+
: {};
|
|
71
80
|
const run = await jsonRequest(requestFetch, `${projectUrl}/runs`, apiKey, {
|
|
72
81
|
method: "POST",
|
|
73
82
|
headers: { "idempotency-key": `${operationId}:run` },
|
|
@@ -81,6 +90,7 @@ export async function uploadSandboxCertificationReport(report, options) {
|
|
|
81
90
|
evidenceType: report.kind,
|
|
82
91
|
implementation: report.implementation,
|
|
83
92
|
sandboxOnly: true,
|
|
93
|
+
...vendorMetadata,
|
|
84
94
|
},
|
|
85
95
|
}),
|
|
86
96
|
});
|
|
@@ -131,3 +141,12 @@ function required(value, name) {
|
|
|
131
141
|
throw new Error(`Sandbox hosted upload ${name} is required.`);
|
|
132
142
|
return normalized;
|
|
133
143
|
}
|
|
144
|
+
function canonicalJson(value) {
|
|
145
|
+
if (Array.isArray(value))
|
|
146
|
+
return `[${value.map(canonicalJson).join(",")}]`;
|
|
147
|
+
if (value && typeof value === "object") {
|
|
148
|
+
const record = value;
|
|
149
|
+
return `{${Object.keys(record).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson(record[key])}`).join(",")}}`;
|
|
150
|
+
}
|
|
151
|
+
return JSON.stringify(value);
|
|
152
|
+
}
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentcert",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Assurance release gates, runtime evidence, and regression CI for AI agents.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
|
-
"homepage": "https://
|
|
7
|
+
"homepage": "https://agentcert-control-plane.onrender.com/demo",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
10
10
|
"url": "git+https://github.com/Kakarottoooo/agentcert.git",
|