@peac/cli 0.14.1 → 0.14.3
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/dist/commands/bridge/start.d.ts.map +1 -1
- package/dist/commands/bridge/status.d.ts.map +1 -1
- package/dist/commands/bridge/stop.d.ts.map +1 -1
- package/dist/commands/bundle.d.ts.map +1 -1
- package/dist/commands/policy.d.ts.map +1 -1
- package/dist/commands/reconcile.d.ts.map +1 -1
- package/dist/commands/verify.d.ts.map +1 -1
- package/dist/index.cjs +96 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +97 -23
- package/dist/index.mjs.map +1 -1
- package/dist/lib/bridge-url.d.ts +25 -0
- package/dist/lib/bridge-url.d.ts.map +1 -0
- package/dist/lib/safe-file.d.ts +88 -0
- package/dist/lib/safe-file.d.ts.map +1 -0
- package/package.json +8 -8
package/dist/index.mjs
CHANGED
|
@@ -3,12 +3,12 @@ import { Command, CommanderError, Option } from 'commander';
|
|
|
3
3
|
import { verifyReceipt, fetchIssuerConfig, parseIssuerConfig, issue, IssueError } from '@peac/protocol';
|
|
4
4
|
import { generateKeypair, base64urlEncode, sign, decode, canonicalize, sha256Hex as sha256Hex$1, validateKeypair, base64urlDecode, derivePublicKey } from '@peac/crypto';
|
|
5
5
|
import * as fs6 from 'fs';
|
|
6
|
-
import { existsSync, readFileSync,
|
|
6
|
+
import { writeFileSync, existsSync, readFileSync, openSync, constants, fstatSync, closeSync, statSync, unlinkSync, createReadStream } from 'fs';
|
|
7
7
|
import * as path6 from 'path';
|
|
8
8
|
import { join, resolve, dirname, delimiter } from 'path';
|
|
9
9
|
import { loadProfile, serializePolicyJson, serializePolicyYaml, ProfileError, createExamplePolicy, loadPolicy, PolicyValidationError, PolicyLoadError, compilePeacTxt, compileRobotsSnippet, compileAiprefTemplates, renderPolicyMarkdown, explainMatches, evaluate, listProfiles, getProfileSummary, parsePolicyDocument } from '@peac/policy-kit';
|
|
10
|
-
import { execSync, spawn } from 'child_process';
|
|
11
10
|
import { createHash, randomBytes } from 'crypto';
|
|
11
|
+
import { execSync, spawn } from 'child_process';
|
|
12
12
|
import { LIFECYCLE_OBSERVATION_TYPE_URIS, computeReceiptRef, CLI_EXECUTION_EXTENSION_KEY, CLI_COMMAND_EXECUTION_TYPE, validateLifecycleObservation, LIFECYCLE_OBSERVATION_EXTENSION_KEY, validateWorkflowContextOrdered, isValidPurposeToken, isCanonicalPurpose, validateObligationsExtension, validateInteractionOrdered, validateDisputeAttestation, validateAttributionAttestation, validateAgentIdentityAttestation, ReceiptClaimsSchema, CliExecutionSchema, isCanonicalIss, parseReceiptClaims, OpaqueRefSchema } from '@peac/schema';
|
|
13
13
|
import { POLICY } from '@peac/kernel';
|
|
14
14
|
import { readDisputeBundle } from '@peac/audit';
|
|
@@ -16,6 +16,55 @@ import { createRequire } from 'module';
|
|
|
16
16
|
import chalk from 'chalk';
|
|
17
17
|
import { readFile } from 'fs/promises';
|
|
18
18
|
|
|
19
|
+
function readFileBufferSnapshot(path9, options) {
|
|
20
|
+
let fd;
|
|
21
|
+
try {
|
|
22
|
+
fd = openSync(path9, constants.O_RDONLY);
|
|
23
|
+
const stat = fstatSync(fd);
|
|
24
|
+
if (stat.isDirectory()) {
|
|
25
|
+
throw makeErrnoError("EISDIR", `is a directory: ${path9}`);
|
|
26
|
+
}
|
|
27
|
+
if (options?.maxBytes !== void 0 && stat.size > options.maxBytes) {
|
|
28
|
+
throw makeFileTooLargeError(path9, stat.size, options.maxBytes);
|
|
29
|
+
}
|
|
30
|
+
const data = readFileSync(fd);
|
|
31
|
+
if (options?.maxBytes !== void 0 && data.length > options.maxBytes) {
|
|
32
|
+
throw makeFileTooLargeError(path9, data.length, options.maxBytes);
|
|
33
|
+
}
|
|
34
|
+
return data;
|
|
35
|
+
} finally {
|
|
36
|
+
if (fd !== void 0) {
|
|
37
|
+
try {
|
|
38
|
+
closeSync(fd);
|
|
39
|
+
} catch {
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function makeFileTooLargeError(path9, actualSize, maxBytes) {
|
|
45
|
+
const err = new Error(
|
|
46
|
+
`file size ${actualSize} bytes exceeds limit ${maxBytes} bytes: ${path9}`
|
|
47
|
+
);
|
|
48
|
+
err.code = "E_PEAC_FILE_TOO_LARGE";
|
|
49
|
+
err.actualSize = actualSize;
|
|
50
|
+
err.maxBytes = maxBytes;
|
|
51
|
+
return err;
|
|
52
|
+
}
|
|
53
|
+
function writeFileNoOverwrite(path9, data, options) {
|
|
54
|
+
const encoding = typeof data === "string" ? options?.encoding : null;
|
|
55
|
+
writeFileSync(path9, data, {
|
|
56
|
+
encoding,
|
|
57
|
+
flag: "wx",
|
|
58
|
+
mode: options?.mode
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
function makeErrnoError(code, message) {
|
|
62
|
+
const err = new Error(message);
|
|
63
|
+
err.code = code;
|
|
64
|
+
return err;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// src/commands/policy.ts
|
|
19
68
|
function getGlobalOptions(cmd) {
|
|
20
69
|
const parent = cmd.parent;
|
|
21
70
|
if (!parent) return {};
|
|
@@ -40,14 +89,6 @@ policy.command("init").description("Create a new PEAC policy file").option("-f,
|
|
|
40
89
|
try {
|
|
41
90
|
const format = options.format?.toLowerCase() || "yaml";
|
|
42
91
|
const outputPath = options.output || (format === "json" ? "peac-policy.json" : "peac-policy.yaml");
|
|
43
|
-
if (fs6.existsSync(outputPath) && !options.force && !globalOpts.yes) {
|
|
44
|
-
outputError(
|
|
45
|
-
`File already exists: ${outputPath}`,
|
|
46
|
-
{ path: outputPath, hint: "Use --force or --yes to overwrite" },
|
|
47
|
-
globalOpts
|
|
48
|
-
);
|
|
49
|
-
process.exit(1);
|
|
50
|
-
}
|
|
51
92
|
let content;
|
|
52
93
|
let policyName;
|
|
53
94
|
if (options.profile) {
|
|
@@ -68,7 +109,24 @@ policy.command("init").description("Create a new PEAC policy file").option("-f,
|
|
|
68
109
|
content = format === "json" ? serializePolicyJson(example) : serializePolicyYaml(example);
|
|
69
110
|
policyName = "Example Policy";
|
|
70
111
|
}
|
|
71
|
-
|
|
112
|
+
const allowOverwrite = options.force === true || globalOpts.yes === true;
|
|
113
|
+
try {
|
|
114
|
+
if (allowOverwrite) {
|
|
115
|
+
fs6.writeFileSync(outputPath, content, { encoding: "utf-8", flag: "w" });
|
|
116
|
+
} else {
|
|
117
|
+
writeFileNoOverwrite(outputPath, content, { encoding: "utf-8" });
|
|
118
|
+
}
|
|
119
|
+
} catch (err) {
|
|
120
|
+
if (err.code === "EEXIST") {
|
|
121
|
+
outputError(
|
|
122
|
+
`File already exists: ${outputPath}`,
|
|
123
|
+
{ path: outputPath, hint: "Use --force or --yes to overwrite" },
|
|
124
|
+
globalOpts
|
|
125
|
+
);
|
|
126
|
+
process.exit(1);
|
|
127
|
+
}
|
|
128
|
+
throw err;
|
|
129
|
+
}
|
|
72
130
|
if (globalOpts.json) {
|
|
73
131
|
output(
|
|
74
132
|
{
|
|
@@ -484,7 +542,6 @@ policy.command("show-profile").description("Show details of a specific policy pr
|
|
|
484
542
|
const globalOpts = getGlobalOptions(cmd);
|
|
485
543
|
try {
|
|
486
544
|
const profile = loadProfile(id);
|
|
487
|
-
const summary = getProfileSummary(id);
|
|
488
545
|
if (globalOpts.json) {
|
|
489
546
|
output(
|
|
490
547
|
{
|
|
@@ -2359,20 +2416,37 @@ function findDiffFields(jwsA, jwsB) {
|
|
|
2359
2416
|
return diffs.sort();
|
|
2360
2417
|
}
|
|
2361
2418
|
async function readBundle(bundlePath, label) {
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2419
|
+
let resolved;
|
|
2420
|
+
try {
|
|
2421
|
+
resolved = fs6.realpathSync(bundlePath);
|
|
2422
|
+
} catch (err) {
|
|
2423
|
+
if (err.code === "ENOENT") {
|
|
2424
|
+
return { ok: false, error: `Bundle file not found: ${bundlePath}` };
|
|
2425
|
+
}
|
|
2426
|
+
return { ok: false, error: `Failed to read ${label}: ${err.message}` };
|
|
2368
2427
|
}
|
|
2369
|
-
const resolved = fs6.realpathSync(bundlePath);
|
|
2370
2428
|
if (resolved !== bundlePath && !resolved.startsWith(process.cwd())) {
|
|
2371
|
-
|
|
2372
|
-
|
|
2429
|
+
return { ok: false, error: `Path traversal detected for ${label}` };
|
|
2430
|
+
}
|
|
2431
|
+
let zipBuffer;
|
|
2432
|
+
try {
|
|
2433
|
+
zipBuffer = readFileBufferSnapshot(bundlePath, { maxBytes: MAX_BUNDLE_SIZE });
|
|
2434
|
+
} catch (err) {
|
|
2435
|
+
const e = err;
|
|
2436
|
+
if (e.code === "ENOENT") {
|
|
2437
|
+
return { ok: false, error: `Bundle file not found: ${bundlePath}` };
|
|
2438
|
+
}
|
|
2439
|
+
if (e.code === "EISDIR") {
|
|
2440
|
+
return { ok: false, error: `Bundle ${label} is not a regular file: ${bundlePath}` };
|
|
2373
2441
|
}
|
|
2442
|
+
if (e.code === "E_PEAC_FILE_TOO_LARGE") {
|
|
2443
|
+
return {
|
|
2444
|
+
ok: false,
|
|
2445
|
+
error: `Bundle ${label} exceeds 16 MB size limit (${e.actualSize ?? "unknown"} bytes)`
|
|
2446
|
+
};
|
|
2447
|
+
}
|
|
2448
|
+
return { ok: false, error: `Failed to read ${label}: ${err.message}` };
|
|
2374
2449
|
}
|
|
2375
|
-
const zipBuffer = fs6.readFileSync(bundlePath);
|
|
2376
2450
|
const result = await readDisputeBundle(zipBuffer);
|
|
2377
2451
|
if (!result.ok) {
|
|
2378
2452
|
return { ok: false, error: `Failed to read ${label}: ${result.error.message}` };
|
|
@@ -4731,7 +4805,7 @@ program.command("verify").description("Verify a PEAC receipt JWS").argument("<jw
|
|
|
4731
4805
|
jws = fs6.readFileSync(jwsInput, "utf-8").trim();
|
|
4732
4806
|
}
|
|
4733
4807
|
console.log("Verifying PEAC receipt...\n");
|
|
4734
|
-
const {
|
|
4808
|
+
const { payload } = decode(jws);
|
|
4735
4809
|
console.log("Receipt Information:");
|
|
4736
4810
|
console.log(` Receipt ID: ${payload.rid}`);
|
|
4737
4811
|
console.log(` Issuer: ${payload.iss}`);
|