witnora 0.8.1 → 0.9.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/README.md +275 -12
- package/dist/artifact-manifest.js +37 -0
- package/dist/artifact-validation.js +102 -0
- package/dist/badge.js +31 -0
- package/dist/browser-adapter.js +156 -0
- package/dist/bundle.js +100 -0
- package/dist/cli.js +971 -0
- package/dist/command-help.js +71 -0
- package/dist/companion-artifacts.js +170 -0
- package/dist/conformance.js +115 -0
- package/dist/control-plane.js +409 -0
- package/dist/corpus-governance.js +126 -0
- package/dist/corpus-store.js +239 -0
- package/dist/corpus.js +438 -0
- package/dist/credentials.js +127 -0
- package/dist/evidence-signing.js +85 -0
- package/dist/failure-review.js +188 -0
- package/dist/guided-setup.js +101 -0
- package/dist/index.js +16 -0
- package/dist/lab.js +323 -0
- package/dist/local-server.js +491 -0
- package/dist/monitor.js +100 -0
- package/dist/normalizers.js +265 -0
- package/dist/onboarding-templates.js +158 -0
- package/dist/release-gate.js +457 -0
- package/dist/report.js +165 -0
- package/dist/runner.js +349 -0
- package/dist/sandbox.js +424 -0
- package/dist/schema-validator.js +503 -0
- package/dist/try.js +135 -0
- package/dist/types.js +3 -0
- package/dist/vendor/onegent-runtime/browser-adapter-kit.d.ts +107 -0
- package/dist/vendor/onegent-runtime/browser-adapter-kit.d.ts.map +1 -0
- package/dist/vendor/onegent-runtime/browser-adapter-kit.js +187 -0
- package/dist/vendor/onegent-runtime/browser-enforcement-runtime.d.ts +183 -0
- package/dist/vendor/onegent-runtime/browser-enforcement-runtime.d.ts.map +1 -0
- package/dist/vendor/onegent-runtime/browser-enforcement-runtime.js +344 -0
- package/dist/vendor/onegent-runtime/controlled-adapter.d.ts +6 -0
- package/dist/vendor/onegent-runtime/controlled-adapter.d.ts.map +1 -0
- package/dist/vendor/onegent-runtime/controlled-adapter.js +40 -0
- package/dist/vendor/onegent-runtime/mock-procurement.d.ts +6 -0
- package/dist/vendor/onegent-runtime/mock-procurement.d.ts.map +1 -0
- package/dist/vendor/onegent-runtime/mock-procurement.js +33 -0
- package/dist/vendor/onegent-runtime/policies.d.ts +6 -0
- package/dist/vendor/onegent-runtime/policies.d.ts.map +1 -0
- package/dist/vendor/onegent-runtime/policies.js +109 -0
- package/dist/vendor/onegent-runtime/risk.d.ts +3 -0
- package/dist/vendor/onegent-runtime/risk.d.ts.map +1 -0
- package/dist/vendor/onegent-runtime/risk.js +54 -0
- package/dist/vendor/onegent-runtime/sandbox-adapter-kit.d.ts +45 -0
- package/dist/vendor/onegent-runtime/sandbox-adapter-kit.d.ts.map +1 -0
- package/dist/vendor/onegent-runtime/sandbox-adapter-kit.js +148 -0
- package/dist/vendor/onegent-runtime/sandbox-harness.d.ts +167 -0
- package/dist/vendor/onegent-runtime/sandbox-harness.d.ts.map +1 -0
- package/dist/vendor/onegent-runtime/sandbox-harness.js +729 -0
- package/dist/vendor/onegent-runtime/sandbox-hosted.d.ts +77 -0
- package/dist/vendor/onegent-runtime/sandbox-hosted.d.ts.map +1 -0
- package/dist/vendor/onegent-runtime/sandbox-hosted.js +152 -0
- package/dist/vendor/onegent-runtime/sdk.d.ts +29 -0
- package/dist/vendor/onegent-runtime/sdk.d.ts.map +1 -0
- package/dist/vendor/onegent-runtime/sdk.js +140 -0
- package/dist/vendor/onegent-runtime/service.d.ts +27 -0
- package/dist/vendor/onegent-runtime/service.d.ts.map +1 -0
- package/dist/vendor/onegent-runtime/service.js +423 -0
- package/dist/vendor/onegent-runtime/store.d.ts +16 -0
- package/dist/vendor/onegent-runtime/store.d.ts.map +1 -0
- package/dist/vendor/onegent-runtime/store.js +29 -0
- package/dist/vendor/onegent-runtime/stripe-test-readonly.d.ts +79 -0
- package/dist/vendor/onegent-runtime/stripe-test-readonly.d.ts.map +1 -0
- package/dist/vendor/onegent-runtime/stripe-test-readonly.js +163 -0
- package/dist/vendor/onegent-runtime/trust-crypto.d.ts +11 -0
- package/dist/vendor/onegent-runtime/trust-crypto.d.ts.map +1 -0
- package/dist/vendor/onegent-runtime/trust-crypto.js +70 -0
- package/dist/vendor/onegent-runtime/trust-types.d.ts +163 -0
- package/dist/vendor/onegent-runtime/trust-types.d.ts.map +1 -0
- package/dist/vendor/onegent-runtime/trust-types.js +7 -0
- package/dist/vendor/onegent-runtime/trusted-recorder.d.ts +49 -0
- package/dist/vendor/onegent-runtime/trusted-recorder.d.ts.map +1 -0
- package/dist/vendor/onegent-runtime/trusted-recorder.js +347 -0
- package/dist/vendor/onegent-runtime/types.d.ts +287 -0
- package/dist/vendor/onegent-runtime/types.d.ts.map +1 -0
- package/dist/vendor/onegent-runtime/types.js +1 -0
- package/dist/vendor/onegent-runtime/vendor-sandbox-egress.d.ts +49 -0
- package/dist/vendor/onegent-runtime/vendor-sandbox-egress.d.ts.map +1 -0
- package/dist/vendor/onegent-runtime/vendor-sandbox-egress.js +152 -0
- package/package.json +33 -13
- package/bin/witnora.js +0 -3
package/dist/sandbox.js
ADDED
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { dirname, resolve } from "node:path";
|
|
3
|
+
import { pathToFileURL } from "node:url";
|
|
4
|
+
import { resolveConnection } from "./credentials.js";
|
|
5
|
+
const DEFAULT_ADAPTER_PATH = "witnora.sandbox.mjs";
|
|
6
|
+
const DEFAULT_REPORT_PATH = ".witnora/sandbox/sandbox-adapter-conformance.json";
|
|
7
|
+
const DEFAULT_STRIPE_REPORT_PATH = ".witnora/sandbox/stripe-readonly-report.json";
|
|
8
|
+
export async function runSandboxCommand(args) {
|
|
9
|
+
const action = args[0] ?? "help";
|
|
10
|
+
if (action === "help" || args.includes("--help") || args.includes("-h")) {
|
|
11
|
+
process.stdout.write(renderSandboxHelp(action === "help" ? undefined : action));
|
|
12
|
+
return { exitCode: 0 };
|
|
13
|
+
}
|
|
14
|
+
if (action === "init")
|
|
15
|
+
return initializeSandboxAdapter(args.slice(1));
|
|
16
|
+
if (action === "certify")
|
|
17
|
+
return certifySandboxAdapter(args.slice(1));
|
|
18
|
+
if (action === "push")
|
|
19
|
+
return pushSandboxCertification(args.slice(1));
|
|
20
|
+
if (action === "stripe-readonly")
|
|
21
|
+
return runStripeSandboxReadOnly(args.slice(1));
|
|
22
|
+
if (action === "upload-report")
|
|
23
|
+
return uploadSandboxReport(args.slice(1));
|
|
24
|
+
throw new Error(`Unknown sandbox command ${JSON.stringify(action)}. Run \`npx witnora sandbox --help\`.`);
|
|
25
|
+
}
|
|
26
|
+
export async function initializeSandboxAdapter(args) {
|
|
27
|
+
const requestedPath = flag(args, "--adapter") ?? flag(args, "--out") ?? DEFAULT_ADAPTER_PATH;
|
|
28
|
+
const outPath = resolve(requestedPath);
|
|
29
|
+
await writeStarterFile(outPath, sandboxAdapterTemplate(), args.includes("--force"));
|
|
30
|
+
process.stdout.write(`Wrote ${outPath}\n\nNext:\n`);
|
|
31
|
+
process.stdout.write(` npx witnora sandbox certify --adapter ${JSON.stringify(requestedPath)}\n`);
|
|
32
|
+
process.stdout.write("\nReplace the in-memory handlers with your sandbox implementation. Keep production credentials and live systems out of this adapter.\n");
|
|
33
|
+
return { exitCode: 0 };
|
|
34
|
+
}
|
|
35
|
+
export async function certifySandboxAdapter(args, runtimeOverride) {
|
|
36
|
+
const runtime = runtimeOverride ?? await loadSandboxRuntime();
|
|
37
|
+
const adapterPath = resolve(flag(args, "--adapter") ?? DEFAULT_ADAPTER_PATH);
|
|
38
|
+
const reportPath = resolve(flag(args, "--out") ?? DEFAULT_REPORT_PATH);
|
|
39
|
+
const system = await loadSandboxAdapter(adapterPath);
|
|
40
|
+
const report = await runtime.runSandboxAdapterConformanceSuite({
|
|
41
|
+
system,
|
|
42
|
+
implementation: flag(args, "--implementation"),
|
|
43
|
+
targetSystem: flag(args, "--target-system"),
|
|
44
|
+
});
|
|
45
|
+
await runtime.writeSandboxAdapterConformanceReport(report, reportPath);
|
|
46
|
+
renderCertificationResult(report, reportPath);
|
|
47
|
+
return { exitCode: report.verdict.passed ? 0 : 1, reportPath, report };
|
|
48
|
+
}
|
|
49
|
+
export async function pushSandboxCertification(args, runtimeOverride) {
|
|
50
|
+
const runtime = runtimeOverride ?? await loadSandboxRuntime();
|
|
51
|
+
const certified = await certifySandboxAdapter(args, runtime);
|
|
52
|
+
if (!certified.report || !certified.reportPath)
|
|
53
|
+
throw new Error("Sandbox conformance did not produce a report.");
|
|
54
|
+
const connection = await resolveConnection({
|
|
55
|
+
name: flag(args, "--connection"),
|
|
56
|
+
server: flag(args, "--server"),
|
|
57
|
+
projectId: flag(args, "--project"),
|
|
58
|
+
apiKey: flag(args, "--api-key"),
|
|
59
|
+
});
|
|
60
|
+
const uploaded = await runtime.uploadSandboxCertificationReport(certified.report, {
|
|
61
|
+
baseUrl: connection.server,
|
|
62
|
+
projectId: connection.projectId,
|
|
63
|
+
apiKey: connection.apiKey,
|
|
64
|
+
externalId: flag(args, "--external-id"),
|
|
65
|
+
});
|
|
66
|
+
process.stdout.write(`Hosted sandbox run: ${String(uploaded.run.id ?? "created")}\n`);
|
|
67
|
+
process.stdout.write(`Hosted evidence: ${String(uploaded.evidence.id ?? "created")}\n`);
|
|
68
|
+
return certified;
|
|
69
|
+
}
|
|
70
|
+
export async function runStripeSandboxReadOnly(args, runtimeOverride) {
|
|
71
|
+
if (args.includes("--stripe-key") || args.includes("--restricted-key")) {
|
|
72
|
+
throw new Error("Stripe credentials are accepted only through STRIPE_RESTRICTED_TEST_KEY, never a CLI flag.");
|
|
73
|
+
}
|
|
74
|
+
const paymentIntentId = flag(args, "--payment-intent");
|
|
75
|
+
if (!paymentIntentId)
|
|
76
|
+
throw new Error("Stripe sandbox read requires --payment-intent <pi_...>.");
|
|
77
|
+
const restrictedApiKey = process.env.STRIPE_RESTRICTED_TEST_KEY?.trim();
|
|
78
|
+
if (!restrictedApiKey) {
|
|
79
|
+
throw new Error("Set STRIPE_RESTRICTED_TEST_KEY to a read-only Stripe rk_test_ restricted key.");
|
|
80
|
+
}
|
|
81
|
+
const runtime = runtimeOverride ?? await loadSandboxRuntime();
|
|
82
|
+
if (!runtime.runStripeSandboxReadOnlyCertification) {
|
|
83
|
+
throw new Error("Witnora Stripe sandbox runtime is missing. Reinstall the agentcert package and retry.");
|
|
84
|
+
}
|
|
85
|
+
const report = await runtime.runStripeSandboxReadOnlyCertification({ restrictedApiKey, paymentIntentId });
|
|
86
|
+
const reportPath = resolve(flag(args, "--out") ?? DEFAULT_STRIPE_REPORT_PATH);
|
|
87
|
+
await mkdir(dirname(reportPath), { recursive: true });
|
|
88
|
+
await writeFile(reportPath, `${JSON.stringify(report, null, 2)}\n`, "utf8");
|
|
89
|
+
renderVendorReadResult(report, reportPath);
|
|
90
|
+
if (args.includes("--push")) {
|
|
91
|
+
const connection = await resolveConnection({
|
|
92
|
+
name: flag(args, "--connection"),
|
|
93
|
+
server: flag(args, "--server"),
|
|
94
|
+
projectId: flag(args, "--project"),
|
|
95
|
+
apiKey: flag(args, "--api-key"),
|
|
96
|
+
});
|
|
97
|
+
const uploaded = await runtime.uploadSandboxCertificationReport(report, {
|
|
98
|
+
baseUrl: connection.server,
|
|
99
|
+
projectId: connection.projectId,
|
|
100
|
+
apiKey: connection.apiKey,
|
|
101
|
+
externalId: flag(args, "--external-id"),
|
|
102
|
+
});
|
|
103
|
+
process.stdout.write(`Hosted sandbox run: ${String(uploaded.run.id ?? "created")}\n`);
|
|
104
|
+
process.stdout.write(`Hosted evidence: ${String(uploaded.evidence.id ?? "created")}\n`);
|
|
105
|
+
}
|
|
106
|
+
return { exitCode: report.verdict.passed ? 0 : 1, reportPath, report };
|
|
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
|
+
}
|
|
161
|
+
export async function loadSandboxAdapter(adapterPath) {
|
|
162
|
+
let module;
|
|
163
|
+
try {
|
|
164
|
+
module = await import(pathToFileURL(adapterPath).href);
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
168
|
+
throw new Error(`Could not load sandbox adapter ${adapterPath}: ${message}`);
|
|
169
|
+
}
|
|
170
|
+
const candidate = module.sandboxSystem ?? module.default ?? module.system;
|
|
171
|
+
if (!isSandboxSystem(candidate)) {
|
|
172
|
+
throw new Error(`Sandbox adapter ${adapterPath} must export \`sandboxSystem\`, \`system\`, or a default SandboxSystem object.`);
|
|
173
|
+
}
|
|
174
|
+
return candidate;
|
|
175
|
+
}
|
|
176
|
+
export function renderSandboxHelp(action) {
|
|
177
|
+
if (action === "init")
|
|
178
|
+
return `Usage:
|
|
179
|
+
witnora sandbox init [--adapter witnora.sandbox.mjs] [--force]
|
|
180
|
+
|
|
181
|
+
Writes one dependency-free synthetic SandboxSystem adapter template.
|
|
182
|
+
`;
|
|
183
|
+
if (action === "certify")
|
|
184
|
+
return `Usage:
|
|
185
|
+
witnora sandbox certify --adapter ./my-sandbox-adapter.js [--out .witnora/sandbox/report.json]
|
|
186
|
+
|
|
187
|
+
Runs the deterministic sandbox adapter conformance suite locally.
|
|
188
|
+
`;
|
|
189
|
+
if (action === "push")
|
|
190
|
+
return `Usage:
|
|
191
|
+
witnora sandbox push --adapter ./my-sandbox-adapter.js
|
|
192
|
+
|
|
193
|
+
Runs conformance checks, writes the local report, and uploads it through the saved Witnora connection.
|
|
194
|
+
Use \`agentcert connect\` first, or pass --server, --project, and --api-key.
|
|
195
|
+
`;
|
|
196
|
+
if (action === "stripe-readonly")
|
|
197
|
+
return `Usage:
|
|
198
|
+
STRIPE_RESTRICTED_TEST_KEY=rk_test_... witnora sandbox stripe-readonly --payment-intent pi_...
|
|
199
|
+
witnora sandbox stripe-readonly --payment-intent pi_... --push
|
|
200
|
+
|
|
201
|
+
Reads one Stripe sandbox PaymentIntent through a fixed GET/resource allowlist,
|
|
202
|
+
writes a redacted evidence report, and optionally uploads it to Witnora Hosted.
|
|
203
|
+
The Stripe key is read only from STRIPE_RESTRICTED_TEST_KEY.
|
|
204
|
+
`;
|
|
205
|
+
if (action === "upload-report")
|
|
206
|
+
return `Usage:
|
|
207
|
+
witnora sandbox upload-report --report .witnora/vendor-sandbox/current-report.json --external-id <id>
|
|
208
|
+
|
|
209
|
+
Validates a recognized Witnora sandbox report, rejects credential material,
|
|
210
|
+
and uploads it through the saved Witnora connection. This command never
|
|
211
|
+
executes a vendor request.
|
|
212
|
+
`;
|
|
213
|
+
return `Usage:
|
|
214
|
+
witnora sandbox init [--adapter witnora.sandbox.mjs]
|
|
215
|
+
witnora sandbox certify --adapter ./my-sandbox-adapter.js
|
|
216
|
+
witnora sandbox push --adapter ./my-sandbox-adapter.js
|
|
217
|
+
witnora sandbox stripe-readonly --payment-intent pi_... [--push]
|
|
218
|
+
witnora sandbox upload-report --report <report.json>
|
|
219
|
+
|
|
220
|
+
Commands:
|
|
221
|
+
init Write one dependency-free adapter template
|
|
222
|
+
certify Run deterministic local conformance checks
|
|
223
|
+
push Check and upload the report to Witnora Hosted
|
|
224
|
+
stripe-readonly Run one bounded Stripe sandbox read and write evidence
|
|
225
|
+
upload-report Validate and upload an existing sandbox report
|
|
226
|
+
|
|
227
|
+
Common options:
|
|
228
|
+
--adapter <path> Adapter module (default: witnora.sandbox.mjs)
|
|
229
|
+
--out <path> Local report path
|
|
230
|
+
--implementation <id> Stable adapter implementation name
|
|
231
|
+
--target-system <name> Target system exercised by the suite
|
|
232
|
+
`;
|
|
233
|
+
}
|
|
234
|
+
async function loadSandboxRuntime() {
|
|
235
|
+
const adapterKitUrl = new URL("./vendor/onegent-runtime/sandbox-adapter-kit.js", import.meta.url);
|
|
236
|
+
const hostedUrl = new URL("./vendor/onegent-runtime/sandbox-hosted.js", import.meta.url);
|
|
237
|
+
const stripeUrl = new URL("./vendor/onegent-runtime/stripe-test-readonly.js", import.meta.url);
|
|
238
|
+
try {
|
|
239
|
+
const [adapterKit, hosted, stripe] = await Promise.all([
|
|
240
|
+
import(adapterKitUrl.href),
|
|
241
|
+
import(hostedUrl.href),
|
|
242
|
+
import(stripeUrl.href),
|
|
243
|
+
]);
|
|
244
|
+
return { ...adapterKit, ...hosted, ...stripe };
|
|
245
|
+
}
|
|
246
|
+
catch (error) {
|
|
247
|
+
const code = error && typeof error === "object" && "code" in error ? String(error.code) : undefined;
|
|
248
|
+
if (code === "ERR_MODULE_NOT_FOUND") {
|
|
249
|
+
throw new Error("Witnora sandbox runtime is missing. Reinstall the agentcert package and retry.");
|
|
250
|
+
}
|
|
251
|
+
throw error;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
function isSandboxSystem(value) {
|
|
255
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
256
|
+
return false;
|
|
257
|
+
const system = value;
|
|
258
|
+
const safety = system.safety;
|
|
259
|
+
return typeof system.name === "string"
|
|
260
|
+
&& safety?.mode === "sandbox"
|
|
261
|
+
&& safety.networkAccess === false
|
|
262
|
+
&& safety.syntheticDataOnly === true
|
|
263
|
+
&& Array.isArray(safety.allowedTargetSystems)
|
|
264
|
+
&& ["createTenant", "deleteTenant", "resetTenant", "seedTenant", "hasTenant", "snapshotTenant", "adapterForTenant"]
|
|
265
|
+
.every((method) => typeof system[method] === "function");
|
|
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 [
|
|
291
|
+
process.env.STRIPE_RESTRICTED_TEST_KEY,
|
|
292
|
+
process.env.WITNORA_API_KEY,
|
|
293
|
+
process.env.AGENTCERT_API_KEY,
|
|
294
|
+
]) {
|
|
295
|
+
if (secret && secret.length >= 8 && rawText.includes(secret))
|
|
296
|
+
findings.add("exact-secret");
|
|
297
|
+
}
|
|
298
|
+
return [...findings].sort();
|
|
299
|
+
}
|
|
300
|
+
function isRecord(value) {
|
|
301
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
302
|
+
}
|
|
303
|
+
function flag(args, name) {
|
|
304
|
+
const index = args.indexOf(name);
|
|
305
|
+
return index >= 0 ? args[index + 1] : undefined;
|
|
306
|
+
}
|
|
307
|
+
async function writeStarterFile(path, content, force) {
|
|
308
|
+
await mkdir(dirname(path), { recursive: true });
|
|
309
|
+
try {
|
|
310
|
+
await writeFile(path, content, { flag: force ? "w" : "wx" });
|
|
311
|
+
}
|
|
312
|
+
catch (error) {
|
|
313
|
+
if (error && typeof error === "object" && "code" in error && error.code === "EEXIST") {
|
|
314
|
+
throw new Error(`${path} already exists. Re-run with --force to overwrite it.`);
|
|
315
|
+
}
|
|
316
|
+
throw error;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
function renderCertificationResult(report, reportPath) {
|
|
320
|
+
process.stdout.write(`${report.verdict.passed ? "PASS" : "FAIL"} ${report.verdict.score}/100 sandbox adapter conformance\n`);
|
|
321
|
+
for (const check of report.checks) {
|
|
322
|
+
process.stdout.write(`- ${check.status === "passed" ? "PASS" : "FAIL"} ${check.id}: ${check.message}\n`);
|
|
323
|
+
}
|
|
324
|
+
process.stdout.write(`Report: ${reportPath}\n`);
|
|
325
|
+
}
|
|
326
|
+
function renderVendorReadResult(report, reportPath) {
|
|
327
|
+
process.stdout.write(`${report.verdict.passed ? "PASS" : "FAIL"} ${report.verdict.score}/100 Stripe sandbox bounded read\n`);
|
|
328
|
+
for (const check of report.checks) {
|
|
329
|
+
process.stdout.write(`- ${check.status === "passed" ? "PASS" : "FAIL"} ${check.id}: ${check.message}\n`);
|
|
330
|
+
}
|
|
331
|
+
process.stdout.write(`Requests audited: ${report.audit.length}\n`);
|
|
332
|
+
process.stdout.write(`Report: ${reportPath}\n`);
|
|
333
|
+
}
|
|
334
|
+
function sandboxAdapterTemplate() {
|
|
335
|
+
return `// Synthetic local state only. Do not import production credentials or call live systems here.
|
|
336
|
+
const tenants = new Map();
|
|
337
|
+
|
|
338
|
+
export const sandboxSystem = {
|
|
339
|
+
name: "my-sandbox-adapter",
|
|
340
|
+
safety: {
|
|
341
|
+
mode: "sandbox",
|
|
342
|
+
networkAccess: false,
|
|
343
|
+
syntheticDataOnly: true,
|
|
344
|
+
allowedTargetSystems: ["MySandboxSystem"],
|
|
345
|
+
},
|
|
346
|
+
createTenant(input) {
|
|
347
|
+
if (input.synthetic !== true) throw new Error("Synthetic tenants only.");
|
|
348
|
+
if (tenants.has(input.id)) throw new Error(\`Tenant \${input.id} already exists.\`);
|
|
349
|
+
const seed = syntheticSeed(input.seed ?? {});
|
|
350
|
+
tenants.set(input.id, { seed, state: structuredClone(seed) });
|
|
351
|
+
},
|
|
352
|
+
deleteTenant(tenantId) {
|
|
353
|
+
tenants.delete(tenantId);
|
|
354
|
+
},
|
|
355
|
+
resetTenant(tenantId) {
|
|
356
|
+
const tenant = requiredTenant(tenantId);
|
|
357
|
+
tenant.state = structuredClone(tenant.seed);
|
|
358
|
+
},
|
|
359
|
+
seedTenant(tenantId, seed) {
|
|
360
|
+
const tenant = requiredTenant(tenantId);
|
|
361
|
+
tenant.seed = syntheticSeed(seed);
|
|
362
|
+
tenant.state = structuredClone(seed);
|
|
363
|
+
},
|
|
364
|
+
hasTenant(tenantId) {
|
|
365
|
+
return tenants.has(tenantId);
|
|
366
|
+
},
|
|
367
|
+
snapshotTenant(tenantId) {
|
|
368
|
+
return structuredClone(requiredTenant(tenantId).state);
|
|
369
|
+
},
|
|
370
|
+
adapterForTenant(tenantId) {
|
|
371
|
+
requiredTenant(tenantId);
|
|
372
|
+
return {
|
|
373
|
+
name: \`my-sandbox-adapter:\${tenantId}\`,
|
|
374
|
+
safety: { mode: "sandbox", networkAccess: false, allowedTargetSystems: ["MySandboxSystem"] },
|
|
375
|
+
execute(action) {
|
|
376
|
+
const tenant = requiredTenant(tenantId);
|
|
377
|
+
const previousState = structuredClone(tenant.state[action.businessObjectId] ?? action.beforeState);
|
|
378
|
+
const observedState = structuredClone(action.proposedAfterState);
|
|
379
|
+
tenant.state[action.businessObjectId] = observedState;
|
|
380
|
+
return { method: "SYNTHETIC_SANDBOX", targetSystem: action.targetSystem, previousState, observedState };
|
|
381
|
+
},
|
|
382
|
+
rollback(action, execution) {
|
|
383
|
+
const restored = structuredClone(execution.previousState ?? action.beforeState);
|
|
384
|
+
requiredTenant(tenantId).state[action.businessObjectId] = restored;
|
|
385
|
+
return { success: true, observedState: restored };
|
|
386
|
+
},
|
|
387
|
+
};
|
|
388
|
+
},
|
|
389
|
+
};
|
|
390
|
+
|
|
391
|
+
export default sandboxSystem;
|
|
392
|
+
|
|
393
|
+
function requiredTenant(tenantId) {
|
|
394
|
+
const tenant = tenants.get(tenantId);
|
|
395
|
+
if (!tenant) throw new Error(\`Tenant \${tenantId} does not exist.\`);
|
|
396
|
+
return tenant;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
function syntheticSeed(value) {
|
|
400
|
+
assertSynthetic(value, "seed", new Set());
|
|
401
|
+
return structuredClone(value);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
function assertSynthetic(value, path, seen) {
|
|
405
|
+
if (value === null || ["boolean", "number"].includes(typeof value)) return;
|
|
406
|
+
if (typeof value === "string") {
|
|
407
|
+
if (/^(?:sk-(?:proj-)?|npm_|gh[pousr]_)[A-Za-z0-9_-]{16,}$/.test(value)) {
|
|
408
|
+
throw new Error(\`Credential-like value is not allowed at \${path}.\`);
|
|
409
|
+
}
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
if (typeof value !== "object") throw new Error(\`Synthetic data must be JSON-compatible at \${path}.\`);
|
|
413
|
+
if (seen.has(value)) throw new Error(\`Synthetic data cannot contain cycles at \${path}.\`);
|
|
414
|
+
seen.add(value);
|
|
415
|
+
for (const [key, item] of Object.entries(value)) {
|
|
416
|
+
if (/(?:password|passwd|secret|token|api[_-]?key|credential|authorization|cookie)/i.test(key)) {
|
|
417
|
+
throw new Error(\`Credential-like field \${path}.\${key} is not allowed.\`);
|
|
418
|
+
}
|
|
419
|
+
assertSynthetic(item, \`\${path}.\${key}\`, seen);
|
|
420
|
+
}
|
|
421
|
+
seen.delete(value);
|
|
422
|
+
}
|
|
423
|
+
`;
|
|
424
|
+
}
|