prae-cli 0.9.1 → 0.10.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/lib/bin.js +2 -6
- package/lib/{checkpoint-BWSuhZ5h.js → checkpoint-BqJ8xwky.js} +1 -1
- package/lib/cli-BcAJlB9B.js +8 -0
- package/lib/{cli-7yLG89gV.js → cli-DPldZ2ND.js} +1122 -511
- package/lib/docs/pilot-claude-code.md +191 -0
- package/lib/{evaluate-wMPkjcIC.js → evaluate-DhoGQw0Z.js} +32 -1
- package/lib/frameworks-BgZHGKiH.js +118 -0
- package/lib/hook-Gll2spTP.js +695 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.js +6 -4
- package/lib/init-DRd-IRR6.js +266 -0
- package/lib/{instructions-Bg5puOen.js → instructions-DihPiiC2.js} +2 -1
- package/lib/{mcp-doNYU-1C.js → mcp-D521EoP6.js} +2 -2
- package/lib/pack-h5n43S0F.js +448 -0
- package/lib/playground.js +27 -3
- package/lib/{receipt-check-CD-W1wXs.js → receipt-check-CUuEjGHL.js} +1 -1
- package/lib/{src-D7g4lvaY.js → src-Cy-Acrgj.js} +111 -25
- package/lib/{src-CwBEFhww.js → src-DMoYsdfo.js} +1 -1
- package/lib/src-wG36kT6K.js +3 -0
- package/lib/{test-run-XNIRHKOu.js → test-run-C8dHjIE2.js} +1 -1
- package/lib/views-CUcixte0.js +1396 -0
- package/package.json +5 -4
- package/lib/src-d82lSKVQ.js +0 -3
- package/lib/views-CIg2ZNG0.js +0 -1401
- /package/lib/{ocsf-DhVGbBUw.js → ocsf-DF9cMiQJ.js} +0 -0
- /package/lib/{src-CBGZVvt7.js → src-BlmsTGdY.js} +0 -0
- /package/lib/{src-B25SHWcz.js → src-BuaOiL2v.js} +0 -0
- /package/lib/{src-d4c_n6wd.js → src-CDYyZyeU.js} +0 -0
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
import { t as FRAMEWORKS } from "./frameworks-BgZHGKiH.js";
|
|
2
|
+
|
|
3
|
+
//#region ../kernel/src/pack.ts
|
|
4
|
+
function isObject(value) {
|
|
5
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6
|
+
}
|
|
7
|
+
var Check = class {
|
|
8
|
+
errors = [];
|
|
9
|
+
fail(path, message) {
|
|
10
|
+
this.errors.push({
|
|
11
|
+
path,
|
|
12
|
+
message
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
/** Reject any key the shape does not define. */
|
|
16
|
+
keys(path, value, allowed) {
|
|
17
|
+
for (const key of Object.keys(value)) if (!allowed.includes(key)) this.fail(`${path}.${key}`, `unknown field \`${key}\`; expected one of ${allowed.map((k) => `\`${k}\``).join(", ")}`);
|
|
18
|
+
}
|
|
19
|
+
text(path, value) {
|
|
20
|
+
if (typeof value !== "string" || value.trim() === "") {
|
|
21
|
+
this.fail(path, "expected a non-empty string");
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
return value;
|
|
25
|
+
}
|
|
26
|
+
/** A field that accepts one name or a list of them. */
|
|
27
|
+
names(path, value) {
|
|
28
|
+
if (typeof value === "string") return value;
|
|
29
|
+
if (Array.isArray(value)) {
|
|
30
|
+
const out = [];
|
|
31
|
+
value.forEach((entry, index) => {
|
|
32
|
+
const one = this.text(`${path}[${index}]`, entry);
|
|
33
|
+
if (one !== void 0) out.push(one);
|
|
34
|
+
});
|
|
35
|
+
return out;
|
|
36
|
+
}
|
|
37
|
+
this.fail(path, "expected a string or an array of strings");
|
|
38
|
+
}
|
|
39
|
+
globs(path, value) {
|
|
40
|
+
if (!Array.isArray(value)) {
|
|
41
|
+
this.fail(path, "expected an array of path globs");
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const out = [];
|
|
45
|
+
value.forEach((entry, index) => {
|
|
46
|
+
const one = this.text(`${path}[${index}]`, entry);
|
|
47
|
+
if (one !== void 0) out.push(one);
|
|
48
|
+
});
|
|
49
|
+
return out;
|
|
50
|
+
}
|
|
51
|
+
number(path, value) {
|
|
52
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
53
|
+
this.fail(path, "expected a finite number");
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
return value;
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
function readCommandMatch(check, path, value) {
|
|
60
|
+
if (!isObject(value)) {
|
|
61
|
+
check.fail(path, "expected an object");
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
check.keys(path, value, [
|
|
65
|
+
"name",
|
|
66
|
+
"token",
|
|
67
|
+
"anyToken",
|
|
68
|
+
"pipedTo",
|
|
69
|
+
"pipeline"
|
|
70
|
+
]);
|
|
71
|
+
if (value.name === void 0 && value.token === void 0 && value.anyToken === void 0 && value.pipedTo === void 0 && value.pipeline === void 0) check.fail(path, "expected `name`, `token`, `anyToken`, `pipedTo` or `pipeline`; a command matcher with none matches nothing");
|
|
72
|
+
const out = {};
|
|
73
|
+
if (value.token !== void 0) {
|
|
74
|
+
const tokens = check.names(`${path}.token`, value.token);
|
|
75
|
+
if (tokens !== void 0) out.token = tokens;
|
|
76
|
+
}
|
|
77
|
+
if (value.name !== void 0) {
|
|
78
|
+
const names = check.names(`${path}.name`, value.name);
|
|
79
|
+
if (names !== void 0) out.name = names;
|
|
80
|
+
}
|
|
81
|
+
if (value.anyToken !== void 0) {
|
|
82
|
+
const tokens = check.names(`${path}.anyToken`, value.anyToken);
|
|
83
|
+
if (tokens !== void 0) out.anyToken = tokens;
|
|
84
|
+
}
|
|
85
|
+
if (value.pipedTo !== void 0) {
|
|
86
|
+
const pipes = check.names(`${path}.pipedTo`, value.pipedTo);
|
|
87
|
+
if (pipes !== void 0) out.pipedTo = pipes;
|
|
88
|
+
}
|
|
89
|
+
if (value.pipeline !== void 0) {
|
|
90
|
+
const shapes = check.names(`${path}.pipeline`, value.pipeline);
|
|
91
|
+
if (shapes !== void 0) out.pipeline = shapes;
|
|
92
|
+
}
|
|
93
|
+
return out;
|
|
94
|
+
}
|
|
95
|
+
function readPathMatch(check, path, value) {
|
|
96
|
+
if (!isObject(value)) {
|
|
97
|
+
check.fail(path, "expected an object");
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
check.keys(path, value, [
|
|
101
|
+
"arg",
|
|
102
|
+
"outside",
|
|
103
|
+
"inside"
|
|
104
|
+
]);
|
|
105
|
+
if (value.outside === void 0 && value.inside === void 0) check.fail(path, "expected `inside` or `outside`; a path matcher with neither matches nothing");
|
|
106
|
+
const out = {};
|
|
107
|
+
if (value.arg !== void 0) {
|
|
108
|
+
const arg = check.text(`${path}.arg`, value.arg);
|
|
109
|
+
if (arg !== void 0) out.arg = arg;
|
|
110
|
+
}
|
|
111
|
+
if (value.outside !== void 0) {
|
|
112
|
+
const globs = check.globs(`${path}.outside`, value.outside);
|
|
113
|
+
if (globs !== void 0) out.outside = globs;
|
|
114
|
+
}
|
|
115
|
+
if (value.inside !== void 0) {
|
|
116
|
+
const globs = check.globs(`${path}.inside`, value.inside);
|
|
117
|
+
if (globs !== void 0) out.inside = globs;
|
|
118
|
+
}
|
|
119
|
+
return out;
|
|
120
|
+
}
|
|
121
|
+
function readHostMatch(check, path, value) {
|
|
122
|
+
if (!isObject(value)) {
|
|
123
|
+
check.fail(path, "expected an object");
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
check.keys(path, value, [
|
|
127
|
+
"arg",
|
|
128
|
+
"in",
|
|
129
|
+
"notIn"
|
|
130
|
+
]);
|
|
131
|
+
if (value.in === void 0 && value.notIn === void 0) check.fail(path, "expected `in` or `notIn`; a host matcher with neither matches nothing");
|
|
132
|
+
const out = {};
|
|
133
|
+
if (value.arg !== void 0) {
|
|
134
|
+
const arg = check.text(`${path}.arg`, value.arg);
|
|
135
|
+
if (arg !== void 0) out.arg = arg;
|
|
136
|
+
}
|
|
137
|
+
if (value.in !== void 0) {
|
|
138
|
+
const list = check.globs(`${path}.in`, value.in);
|
|
139
|
+
if (list !== void 0) out.in = list;
|
|
140
|
+
}
|
|
141
|
+
if (value.notIn !== void 0) {
|
|
142
|
+
const list = check.globs(`${path}.notIn`, value.notIn);
|
|
143
|
+
if (list !== void 0) out.notIn = list;
|
|
144
|
+
}
|
|
145
|
+
return out;
|
|
146
|
+
}
|
|
147
|
+
function readArgMatch(check, path, value) {
|
|
148
|
+
if (!isObject(value)) {
|
|
149
|
+
check.fail(path, "expected an object");
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
check.keys(path, value, [
|
|
153
|
+
"name",
|
|
154
|
+
"above",
|
|
155
|
+
"below",
|
|
156
|
+
"equals",
|
|
157
|
+
"oneOf",
|
|
158
|
+
"present"
|
|
159
|
+
]);
|
|
160
|
+
const name = check.text(`${path}.name`, value.name);
|
|
161
|
+
if (name === void 0) return void 0;
|
|
162
|
+
const out = { name };
|
|
163
|
+
if (value.above !== void 0) {
|
|
164
|
+
const n = check.number(`${path}.above`, value.above);
|
|
165
|
+
if (n !== void 0) out.above = n;
|
|
166
|
+
}
|
|
167
|
+
if (value.below !== void 0) {
|
|
168
|
+
const n = check.number(`${path}.below`, value.below);
|
|
169
|
+
if (n !== void 0) out.below = n;
|
|
170
|
+
}
|
|
171
|
+
if (value.equals !== void 0) out.equals = value.equals;
|
|
172
|
+
if (value.oneOf !== void 0) if (!Array.isArray(value.oneOf)) check.fail(`${path}.oneOf`, "expected an array");
|
|
173
|
+
else out.oneOf = value.oneOf;
|
|
174
|
+
if (value.present !== void 0) if (typeof value.present !== "boolean") check.fail(`${path}.present`, "expected a boolean");
|
|
175
|
+
else out.present = value.present;
|
|
176
|
+
return out;
|
|
177
|
+
}
|
|
178
|
+
function readSecretsMatch(check, path, value) {
|
|
179
|
+
if (!isObject(value)) {
|
|
180
|
+
check.fail(path, "expected an object");
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
check.keys(path, value, [
|
|
184
|
+
"types",
|
|
185
|
+
"minConfidence",
|
|
186
|
+
"atLeast"
|
|
187
|
+
]);
|
|
188
|
+
const out = {};
|
|
189
|
+
if (value.types !== void 0) {
|
|
190
|
+
const types = check.globs(`${path}.types`, value.types);
|
|
191
|
+
if (types !== void 0) out.types = types;
|
|
192
|
+
}
|
|
193
|
+
if (value.minConfidence !== void 0) {
|
|
194
|
+
const n = check.number(`${path}.minConfidence`, value.minConfidence);
|
|
195
|
+
if (n !== void 0) if (n < 0 || n > 1) check.fail(`${path}.minConfidence`, "expected a number between 0 and 1");
|
|
196
|
+
else out.minConfidence = n;
|
|
197
|
+
}
|
|
198
|
+
if (value.atLeast !== void 0) {
|
|
199
|
+
const n = check.number(`${path}.atLeast`, value.atLeast);
|
|
200
|
+
if (n !== void 0) if (!Number.isInteger(n) || n < 1) check.fail(`${path}.atLeast`, "expected an integer of at least 1");
|
|
201
|
+
else out.atLeast = n;
|
|
202
|
+
}
|
|
203
|
+
return out;
|
|
204
|
+
}
|
|
205
|
+
function readMatch(check, path, value) {
|
|
206
|
+
if (!isObject(value)) {
|
|
207
|
+
check.fail(path, "expected an object");
|
|
208
|
+
return {};
|
|
209
|
+
}
|
|
210
|
+
check.keys(path, value, [
|
|
211
|
+
"tool",
|
|
212
|
+
"capability",
|
|
213
|
+
"plugin",
|
|
214
|
+
"agent",
|
|
215
|
+
"command",
|
|
216
|
+
"path",
|
|
217
|
+
"host",
|
|
218
|
+
"arg",
|
|
219
|
+
"count",
|
|
220
|
+
"secrets"
|
|
221
|
+
]);
|
|
222
|
+
const out = {};
|
|
223
|
+
for (const key of [
|
|
224
|
+
"tool",
|
|
225
|
+
"capability",
|
|
226
|
+
"plugin",
|
|
227
|
+
"agent"
|
|
228
|
+
]) if (value[key] !== void 0) {
|
|
229
|
+
const names = check.names(`${path}.${key}`, value[key]);
|
|
230
|
+
if (names !== void 0) out[key] = names;
|
|
231
|
+
}
|
|
232
|
+
if (value.command !== void 0) {
|
|
233
|
+
const match = readCommandMatch(check, `${path}.command`, value.command);
|
|
234
|
+
if (match !== void 0) out.command = match;
|
|
235
|
+
}
|
|
236
|
+
if (value.path !== void 0) {
|
|
237
|
+
const match = readPathMatch(check, `${path}.path`, value.path);
|
|
238
|
+
if (match !== void 0) out.path = match;
|
|
239
|
+
}
|
|
240
|
+
if (value.host !== void 0) {
|
|
241
|
+
const match = readHostMatch(check, `${path}.host`, value.host);
|
|
242
|
+
if (match !== void 0) out.host = match;
|
|
243
|
+
}
|
|
244
|
+
if (value.arg !== void 0) if (!Array.isArray(value.arg)) check.fail(`${path}.arg`, "expected an array of argument matchers");
|
|
245
|
+
else {
|
|
246
|
+
const list = [];
|
|
247
|
+
value.arg.forEach((entry, index) => {
|
|
248
|
+
const one = readArgMatch(check, `${path}.arg[${index}]`, entry);
|
|
249
|
+
if (one !== void 0) list.push(one);
|
|
250
|
+
});
|
|
251
|
+
out.arg = list;
|
|
252
|
+
}
|
|
253
|
+
if (value.count !== void 0) if (!isObject(value.count)) check.fail(`${path}.count`, "expected an object");
|
|
254
|
+
else {
|
|
255
|
+
check.keys(`${path}.count`, value.count, ["above"]);
|
|
256
|
+
const above = check.number(`${path}.count.above`, value.count.above);
|
|
257
|
+
if (above !== void 0) out.count = { above };
|
|
258
|
+
}
|
|
259
|
+
if (value.secrets !== void 0) {
|
|
260
|
+
const match = readSecretsMatch(check, `${path}.secrets`, value.secrets);
|
|
261
|
+
if (match !== void 0) out.secrets = match;
|
|
262
|
+
}
|
|
263
|
+
return out;
|
|
264
|
+
}
|
|
265
|
+
function readEffect(check, path, value) {
|
|
266
|
+
if (!isObject(value)) {
|
|
267
|
+
check.fail(path, "expected an object");
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
const kind = value.kind === "hold" ? "ask" : value.kind;
|
|
271
|
+
if (kind !== "deny" && kind !== "ask" && kind !== "correct" && kind !== "warn" && kind !== "allow") {
|
|
272
|
+
check.fail(`${path}.kind`, "expected `allow`, `deny`, `hold` (or `ask`), `correct`, or `warn`");
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
check.keys(path, value, kind === "correct" ? [
|
|
276
|
+
"kind",
|
|
277
|
+
"set",
|
|
278
|
+
"because"
|
|
279
|
+
] : ["kind", "because"]);
|
|
280
|
+
const because = check.text(`${path}.because`, value.because);
|
|
281
|
+
if (because === void 0) return void 0;
|
|
282
|
+
if (kind === "correct") {
|
|
283
|
+
if (!isObject(value.set) || Object.keys(value.set).length === 0) {
|
|
284
|
+
check.fail(`${path}.set`, "expected a non-empty object of arguments to set");
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
return {
|
|
288
|
+
kind,
|
|
289
|
+
set: value.set,
|
|
290
|
+
because
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
return {
|
|
294
|
+
kind,
|
|
295
|
+
because
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
function readRule(check, path, value) {
|
|
299
|
+
if (!isObject(value)) {
|
|
300
|
+
check.fail(path, "expected an object");
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
check.keys(path, value, [
|
|
304
|
+
"id",
|
|
305
|
+
"title",
|
|
306
|
+
"description",
|
|
307
|
+
"when",
|
|
308
|
+
"effect",
|
|
309
|
+
"frameworks"
|
|
310
|
+
]);
|
|
311
|
+
const id = check.text(`${path}.id`, value.id);
|
|
312
|
+
const effect = readEffect(check, `${path}.effect`, value.effect);
|
|
313
|
+
if (value.when === void 0) check.fail(`${path}.when`, "expected an object");
|
|
314
|
+
const when = readMatch(check, `${path}.when`, value.when ?? {});
|
|
315
|
+
if (id === void 0 || effect === void 0) return void 0;
|
|
316
|
+
const rule = {
|
|
317
|
+
id,
|
|
318
|
+
when,
|
|
319
|
+
effect
|
|
320
|
+
};
|
|
321
|
+
if (value.title !== void 0) {
|
|
322
|
+
const title = check.text(`${path}.title`, value.title);
|
|
323
|
+
if (title !== void 0) rule.title = title;
|
|
324
|
+
}
|
|
325
|
+
if (value.description !== void 0) {
|
|
326
|
+
const description = check.text(`${path}.description`, value.description);
|
|
327
|
+
if (description !== void 0) rule.description = description;
|
|
328
|
+
}
|
|
329
|
+
if (value.frameworks !== void 0) {
|
|
330
|
+
const frameworks = readFrameworks(check, `${path}.frameworks`, value.frameworks);
|
|
331
|
+
if (frameworks !== void 0) rule.frameworks = frameworks;
|
|
332
|
+
}
|
|
333
|
+
return rule;
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* A citation names a framework edition and controls the registry knows. A
|
|
337
|
+
* typo here would be a rule that claims a mapping no view can render, so it
|
|
338
|
+
* is a pack error rather than an ignored key — the same line the rest of the
|
|
339
|
+
* parser takes.
|
|
340
|
+
*/
|
|
341
|
+
function readFrameworks(check, path, value) {
|
|
342
|
+
if (!isObject(value)) {
|
|
343
|
+
check.fail(path, "expected an object keyed by framework id");
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
const out = {};
|
|
347
|
+
for (const [framework, controls] of Object.entries(value)) {
|
|
348
|
+
const known = FRAMEWORKS[framework];
|
|
349
|
+
if (known === void 0) {
|
|
350
|
+
check.fail(`${path}.${framework}`, `unknown framework; expected one of ${Object.keys(FRAMEWORKS).map((k) => `\`${k}\``).join(", ")}`);
|
|
351
|
+
continue;
|
|
352
|
+
}
|
|
353
|
+
if (!Array.isArray(controls) || !controls.every((c) => typeof c === "string")) {
|
|
354
|
+
check.fail(`${path}.${framework}`, "expected an array of control ids");
|
|
355
|
+
continue;
|
|
356
|
+
}
|
|
357
|
+
const ids = controls;
|
|
358
|
+
for (const control of ids) if (known.controls[control] === void 0) check.fail(`${path}.${framework}`, `unknown control \`${control}\` in ${known.name}`);
|
|
359
|
+
if (ids.length > 0) out[framework] = [...new Set(ids)];
|
|
360
|
+
}
|
|
361
|
+
return out;
|
|
362
|
+
}
|
|
363
|
+
/** Parse and validate a pack. Never throws; every problem lands in `errors`. */
|
|
364
|
+
function parsePack(source) {
|
|
365
|
+
let raw;
|
|
366
|
+
try {
|
|
367
|
+
raw = JSON.parse(source);
|
|
368
|
+
} catch (error) {
|
|
369
|
+
return {
|
|
370
|
+
ok: false,
|
|
371
|
+
errors: [{
|
|
372
|
+
path: "",
|
|
373
|
+
message: `invalid JSON: ${error instanceof Error ? error.message : String(error)}`
|
|
374
|
+
}]
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
const check = new Check();
|
|
378
|
+
if (!isObject(raw)) return {
|
|
379
|
+
ok: false,
|
|
380
|
+
errors: [{
|
|
381
|
+
path: "",
|
|
382
|
+
message: "expected an object"
|
|
383
|
+
}]
|
|
384
|
+
};
|
|
385
|
+
check.keys("", raw, [
|
|
386
|
+
"prae",
|
|
387
|
+
"pack",
|
|
388
|
+
"version",
|
|
389
|
+
"description",
|
|
390
|
+
"owner",
|
|
391
|
+
"expires_at",
|
|
392
|
+
"baseline",
|
|
393
|
+
"rules"
|
|
394
|
+
]);
|
|
395
|
+
if (raw.prae !== "0.1") check.fail(".prae", "expected `\"0.1\"`; this is the only pack version this build reads");
|
|
396
|
+
const name = check.text(".pack", raw.pack);
|
|
397
|
+
const version = check.text(".version", raw.version);
|
|
398
|
+
const description = raw.description === void 0 ? void 0 : check.text(".description", raw.description);
|
|
399
|
+
const owner = raw.owner === void 0 ? void 0 : check.text(".owner", raw.owner);
|
|
400
|
+
const expires = raw.expires_at === void 0 ? void 0 : check.text(".expires_at", raw.expires_at);
|
|
401
|
+
if (expires !== void 0 && (!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2}(\.\d+)?)?(Z|[+-]\d{2}:\d{2})$/.test(expires) || !Number.isFinite(Date.parse(expires)))) check.fail(".expires_at", "expected an ISO 8601 time with a zone, e.g. 2026-10-05T12:00:00Z");
|
|
402
|
+
let baseline;
|
|
403
|
+
if (raw.baseline !== void 0) if (!isObject(raw.baseline)) check.fail(".baseline", "expected an object");
|
|
404
|
+
else {
|
|
405
|
+
check.keys(".baseline", raw.baseline, ["capabilities"]);
|
|
406
|
+
const caps = check.globs(".baseline.capabilities", raw.baseline.capabilities);
|
|
407
|
+
if (caps !== void 0) baseline = { capabilities: caps };
|
|
408
|
+
}
|
|
409
|
+
const rules = [];
|
|
410
|
+
if (!Array.isArray(raw.rules)) check.fail(".rules", "expected an array");
|
|
411
|
+
else {
|
|
412
|
+
const seen = /* @__PURE__ */ new Set();
|
|
413
|
+
raw.rules.forEach((entry, index) => {
|
|
414
|
+
const rule = readRule(check, `rules[${index}]`, entry);
|
|
415
|
+
if (rule === void 0) return;
|
|
416
|
+
if (seen.has(rule.id)) {
|
|
417
|
+
check.fail(`rules[${index}].id`, `duplicate rule id \`${rule.id}\``);
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
seen.add(rule.id);
|
|
421
|
+
rules.push(rule);
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
if (check.errors.length > 0) return {
|
|
425
|
+
ok: false,
|
|
426
|
+
errors: check.errors
|
|
427
|
+
};
|
|
428
|
+
if (name === void 0 || version === void 0) return {
|
|
429
|
+
ok: false,
|
|
430
|
+
errors: check.errors
|
|
431
|
+
};
|
|
432
|
+
return {
|
|
433
|
+
ok: true,
|
|
434
|
+
pack: {
|
|
435
|
+
prae: "0.1",
|
|
436
|
+
pack: name,
|
|
437
|
+
version,
|
|
438
|
+
...description === void 0 ? {} : { description },
|
|
439
|
+
...owner === void 0 ? {} : { owner },
|
|
440
|
+
...expires === void 0 ? {} : { expires_at: expires },
|
|
441
|
+
...baseline === void 0 ? {} : { baseline },
|
|
442
|
+
rules
|
|
443
|
+
}
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
//#endregion
|
|
448
|
+
export { parsePack as t };
|
package/lib/playground.js
CHANGED
|
@@ -351,6 +351,7 @@
|
|
|
351
351
|
* appending to a pack can never accidentally widen what an agent may do.
|
|
352
352
|
*/
|
|
353
353
|
const STRENGTH = {
|
|
354
|
+
allow: 0,
|
|
354
355
|
warn: 1,
|
|
355
356
|
correct: 2,
|
|
356
357
|
ask: 3,
|
|
@@ -482,6 +483,14 @@
|
|
|
482
483
|
evaluated
|
|
483
484
|
};
|
|
484
485
|
}
|
|
486
|
+
const allowed = hits.find((hit) => hit.effect.kind === "allow");
|
|
487
|
+
if (allowed !== void 0) return {
|
|
488
|
+
kind: "allow",
|
|
489
|
+
rule: allowed.rule.id,
|
|
490
|
+
reason: allowed.effect.because,
|
|
491
|
+
trace,
|
|
492
|
+
evaluated
|
|
493
|
+
};
|
|
485
494
|
return {
|
|
486
495
|
kind: "allow",
|
|
487
496
|
trace,
|
|
@@ -854,9 +863,9 @@
|
|
|
854
863
|
check.fail(path, "expected an object");
|
|
855
864
|
return;
|
|
856
865
|
}
|
|
857
|
-
const kind = value.kind;
|
|
858
|
-
if (kind !== "deny" && kind !== "ask" && kind !== "correct" && kind !== "warn") {
|
|
859
|
-
check.fail(`${path}.kind`, "expected `deny`, `ask
|
|
866
|
+
const kind = value.kind === "hold" ? "ask" : value.kind;
|
|
867
|
+
if (kind !== "deny" && kind !== "ask" && kind !== "correct" && kind !== "warn" && kind !== "allow") {
|
|
868
|
+
check.fail(`${path}.kind`, "expected `allow`, `deny`, `hold` (or `ask`), `correct`, or `warn`");
|
|
860
869
|
return;
|
|
861
870
|
}
|
|
862
871
|
check.keys(path, value, kind === "correct" ? [
|
|
@@ -890,6 +899,7 @@
|
|
|
890
899
|
check.keys(path, value, [
|
|
891
900
|
"id",
|
|
892
901
|
"title",
|
|
902
|
+
"description",
|
|
893
903
|
"when",
|
|
894
904
|
"effect",
|
|
895
905
|
"frameworks"
|
|
@@ -908,6 +918,10 @@
|
|
|
908
918
|
const title = check.text(`${path}.title`, value.title);
|
|
909
919
|
if (title !== void 0) rule.title = title;
|
|
910
920
|
}
|
|
921
|
+
if (value.description !== void 0) {
|
|
922
|
+
const description = check.text(`${path}.description`, value.description);
|
|
923
|
+
if (description !== void 0) rule.description = description;
|
|
924
|
+
}
|
|
911
925
|
if (value.frameworks !== void 0) {
|
|
912
926
|
const frameworks = readFrameworks(check, `${path}.frameworks`, value.frameworks);
|
|
913
927
|
if (frameworks !== void 0) rule.frameworks = frameworks;
|
|
@@ -968,12 +982,19 @@
|
|
|
968
982
|
"prae",
|
|
969
983
|
"pack",
|
|
970
984
|
"version",
|
|
985
|
+
"description",
|
|
986
|
+
"owner",
|
|
987
|
+
"expires_at",
|
|
971
988
|
"baseline",
|
|
972
989
|
"rules"
|
|
973
990
|
]);
|
|
974
991
|
if (raw.prae !== "0.1") check.fail(".prae", "expected `\"0.1\"`; this is the only pack version this build reads");
|
|
975
992
|
const name = check.text(".pack", raw.pack);
|
|
976
993
|
const version = check.text(".version", raw.version);
|
|
994
|
+
const description = raw.description === void 0 ? void 0 : check.text(".description", raw.description);
|
|
995
|
+
const owner = raw.owner === void 0 ? void 0 : check.text(".owner", raw.owner);
|
|
996
|
+
const expires = raw.expires_at === void 0 ? void 0 : check.text(".expires_at", raw.expires_at);
|
|
997
|
+
if (expires !== void 0 && (!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2}(\.\d+)?)?(Z|[+-]\d{2}:\d{2})$/.test(expires) || !Number.isFinite(Date.parse(expires)))) check.fail(".expires_at", "expected an ISO 8601 time with a zone, e.g. 2026-10-05T12:00:00Z");
|
|
977
998
|
let baseline;
|
|
978
999
|
if (raw.baseline !== void 0) if (!isObject(raw.baseline)) check.fail(".baseline", "expected an object");
|
|
979
1000
|
else {
|
|
@@ -1010,6 +1031,9 @@
|
|
|
1010
1031
|
prae: "0.1",
|
|
1011
1032
|
pack: name,
|
|
1012
1033
|
version,
|
|
1034
|
+
...description === void 0 ? {} : { description },
|
|
1035
|
+
...owner === void 0 ? {} : { owner },
|
|
1036
|
+
...expires === void 0 ? {} : { expires_at: expires },
|
|
1013
1037
|
...baseline === void 0 ? {} : { baseline },
|
|
1014
1038
|
rules
|
|
1015
1039
|
}
|