@voltro/plan 0.83.1 → 0.85.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 +1191 -0
- package/dist/index.d.ts +125 -125
- package/dist/index.js +304 -222
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Cause as e, Exit as t, Schema as n, SchemaIssue as r } from "effect";
|
|
2
2
|
//#region src/ids.ts
|
|
3
|
-
var
|
|
4
|
-
if (!
|
|
3
|
+
var i = /^[A-Z][A-Z0-9]{1,9}$/, a = (e) => {
|
|
4
|
+
if (!i.test(e)) throw Error(`plan id prefix "${e}" is not 2 to 10 capital letters or digits, starting with a letter`);
|
|
5
5
|
return {
|
|
6
6
|
prefix: e,
|
|
7
7
|
plan: RegExp(`^${e}-PLAN-[0-9]{2}$`),
|
|
@@ -15,24 +15,24 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
|
|
|
15
15
|
closureRun: RegExp(`^CLR-${e}-[0-9]{2}-[0-9]{3}$`),
|
|
16
16
|
certificate: RegExp(`^CRT-${e}-[0-9]{2}-[0-9]{3}$`)
|
|
17
17
|
};
|
|
18
|
-
},
|
|
18
|
+
}, o = (e) => {
|
|
19
19
|
let t = /-(?:PLAN-)?([0-9]{2})(?:-|$)/.exec(e);
|
|
20
20
|
return t === null ? null : t[1];
|
|
21
|
-
},
|
|
21
|
+
}, s = (e, t) => {
|
|
22
22
|
let n = RegExp(`(?:^|-)(${e.prefix}-[0-9]{2}-[0-9]{3})(?:-|$)`).exec(t);
|
|
23
23
|
return n === null ? null : n[1];
|
|
24
|
-
},
|
|
25
|
-
if (Array.isArray(e)) return e.map(
|
|
24
|
+
}, c = (e) => JSON.stringify(l(e)), l = (e) => {
|
|
25
|
+
if (Array.isArray(e)) return e.map(l);
|
|
26
26
|
if (typeof e == "object" && e) {
|
|
27
27
|
let t = {};
|
|
28
28
|
for (let n of Object.keys(e).sort()) {
|
|
29
29
|
let r = e[n];
|
|
30
|
-
r !== void 0 && (t[n] =
|
|
30
|
+
r !== void 0 && (t[n] = l(r));
|
|
31
31
|
}
|
|
32
32
|
return t;
|
|
33
33
|
}
|
|
34
34
|
return e;
|
|
35
|
-
},
|
|
35
|
+
}, u = new Uint32Array([
|
|
36
36
|
1116352408,
|
|
37
37
|
1899447441,
|
|
38
38
|
3049323471,
|
|
@@ -97,7 +97,7 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
|
|
|
97
97
|
2756734187,
|
|
98
98
|
3204031479,
|
|
99
99
|
3329325298
|
|
100
|
-
]),
|
|
100
|
+
]), d = (e) => {
|
|
101
101
|
let t = new TextEncoder().encode(e), n = t.length * 8, r = new Uint8Array(t.length + 9 + 63 >> 6 << 6);
|
|
102
102
|
r.set(t), r[t.length] = 128;
|
|
103
103
|
let i = new DataView(r.buffer);
|
|
@@ -118,7 +118,7 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
|
|
|
118
118
|
let t = s(o[e - 15], 7) ^ s(o[e - 15], 18) ^ o[e - 15] >>> 3, n = s(o[e - 2], 17) ^ s(o[e - 2], 19) ^ o[e - 2] >>> 10;
|
|
119
119
|
o[e] = o[e - 16] + t + o[e - 7] + n >>> 0;
|
|
120
120
|
}
|
|
121
|
-
let [t, n, r, c,
|
|
121
|
+
let [t, n, r, c, l, d, f, p] = [
|
|
122
122
|
a[0],
|
|
123
123
|
a[1],
|
|
124
124
|
a[2],
|
|
@@ -129,20 +129,20 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
|
|
|
129
129
|
a[7]
|
|
130
130
|
];
|
|
131
131
|
for (let e = 0; e < 64; e++) {
|
|
132
|
-
let i = p + (s(
|
|
133
|
-
p = f, f = d, d =
|
|
132
|
+
let i = p + (s(l, 6) ^ s(l, 11) ^ s(l, 25)) + (l & d ^ ~l & f) + u[e] + o[e] >>> 0, a = (s(t, 2) ^ s(t, 13) ^ s(t, 22)) + (t & n ^ t & r ^ n & r) >>> 0;
|
|
133
|
+
p = f, f = d, d = l, l = c + i >>> 0, c = r, r = n, n = t, t = i + a >>> 0;
|
|
134
134
|
}
|
|
135
|
-
a[0] = a[0] + t >>> 0, a[1] = a[1] + n >>> 0, a[2] = a[2] + r >>> 0, a[3] = a[3] + c >>> 0, a[4] = a[4] +
|
|
135
|
+
a[0] = a[0] + t >>> 0, a[1] = a[1] + n >>> 0, a[2] = a[2] + r >>> 0, a[3] = a[3] + c >>> 0, a[4] = a[4] + l >>> 0, a[5] = a[5] + d >>> 0, a[6] = a[6] + f >>> 0, a[7] = a[7] + p >>> 0;
|
|
136
136
|
}
|
|
137
137
|
return [...a].map((e) => e.toString(16).padStart(8, "0")).join("");
|
|
138
|
-
},
|
|
138
|
+
}, f = (e) => `sha256:${d(c(e))}`, p = n.String.pipe(n.check(n.isMinLength(1))), m = n.String.pipe(n.check(n.isPattern(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2}(\.\d+)?)?(Z|[+-]\d{2}:\d{2})$/))), h = n.String.pipe(n.check(n.isPattern(/^sha256:[a-f0-9]{64}$/))), g = (e) => n.Array(e).pipe(n.check(n.makeFilter((e) => new Set(e).size === e.length || "items are listed twice"))), _ = (e) => n.Array(e).pipe(n.check(n.isMinLength(1))), v = n.String.pipe(n.check(n.isPattern(/^[a-z0-9]+(?:[.:/-][a-z0-9]+)*$/))), y = [
|
|
139
139
|
"proposed",
|
|
140
140
|
"ready",
|
|
141
141
|
"active",
|
|
142
142
|
"blocked",
|
|
143
143
|
"verified",
|
|
144
144
|
"closed"
|
|
145
|
-
],
|
|
145
|
+
], b = [
|
|
146
146
|
"proposed",
|
|
147
147
|
"decomposing",
|
|
148
148
|
"ready",
|
|
@@ -151,7 +151,7 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
|
|
|
151
151
|
"closure-1",
|
|
152
152
|
"closure-2",
|
|
153
153
|
"complete"
|
|
154
|
-
],
|
|
154
|
+
], x = [
|
|
155
155
|
"commit",
|
|
156
156
|
"typecheck",
|
|
157
157
|
"unit-test",
|
|
@@ -173,7 +173,7 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
|
|
|
173
173
|
"research",
|
|
174
174
|
"review",
|
|
175
175
|
"artifact"
|
|
176
|
-
],
|
|
176
|
+
], ee = [
|
|
177
177
|
"P0",
|
|
178
178
|
"P1",
|
|
179
179
|
"P2",
|
|
@@ -189,134 +189,168 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
|
|
|
189
189
|
"documentation",
|
|
190
190
|
"research",
|
|
191
191
|
"tooling"
|
|
192
|
-
], C = (e) => n.String.pipe(n.
|
|
193
|
-
let t = e.grammar, r =
|
|
192
|
+
], C = (e) => n.String.pipe(n.check(n.isPattern(e))), w = (e) => {
|
|
193
|
+
let t = e.grammar, r = g(C(t.evidence)), i = n.Struct({
|
|
194
194
|
id: C(t.source),
|
|
195
|
-
uri:
|
|
196
|
-
locator:
|
|
197
|
-
excerpt:
|
|
198
|
-
lineHint: n.optional(n.Int.pipe(n.
|
|
199
|
-
contentHash:
|
|
200
|
-
disposition: n.
|
|
201
|
-
|
|
195
|
+
uri: p,
|
|
196
|
+
locator: p,
|
|
197
|
+
excerpt: p,
|
|
198
|
+
lineHint: n.optional(n.Int.pipe(n.check(n.isGreaterThanOrEqualTo(1)))),
|
|
199
|
+
contentHash: h,
|
|
200
|
+
disposition: n.Literals([
|
|
201
|
+
"owned-by",
|
|
202
|
+
"referenced-by",
|
|
203
|
+
"decision",
|
|
204
|
+
"non-goal"
|
|
205
|
+
]),
|
|
206
|
+
targetId: n.optional(n.Union([C(t.workItem), C(t.adr)]))
|
|
202
207
|
}), a = n.Struct({
|
|
203
208
|
id: C(t.acceptance),
|
|
204
|
-
statement:
|
|
205
|
-
status: n.
|
|
209
|
+
statement: p,
|
|
210
|
+
status: n.Literals([
|
|
211
|
+
"pending",
|
|
212
|
+
"passed",
|
|
213
|
+
"failed"
|
|
214
|
+
]),
|
|
206
215
|
evidenceRefs: r
|
|
207
216
|
}), o = n.Struct({
|
|
208
|
-
rationale:
|
|
209
|
-
reviewer:
|
|
210
|
-
expiresAt:
|
|
211
|
-
compensatingControl:
|
|
217
|
+
rationale: p,
|
|
218
|
+
reviewer: p,
|
|
219
|
+
expiresAt: m,
|
|
220
|
+
compensatingControl: p
|
|
212
221
|
}), s = n.Struct({
|
|
213
222
|
id: C(t.requirement),
|
|
214
|
-
statement:
|
|
215
|
-
kinds:
|
|
216
|
-
status: n.
|
|
223
|
+
statement: p,
|
|
224
|
+
kinds: _(n.Literals(x)),
|
|
225
|
+
status: n.Literals([
|
|
226
|
+
"open",
|
|
227
|
+
"satisfied",
|
|
228
|
+
"waived"
|
|
229
|
+
]),
|
|
217
230
|
evidenceRefs: r,
|
|
218
231
|
waiver: n.optional(o)
|
|
219
232
|
}), c = n.Struct({
|
|
220
|
-
capabilities: n.Array(
|
|
221
|
-
journeys: n.Array(
|
|
222
|
-
adrs:
|
|
223
|
-
risks: n.Array(
|
|
224
|
-
relatedTo:
|
|
233
|
+
capabilities: n.Array(p),
|
|
234
|
+
journeys: n.Array(p),
|
|
235
|
+
adrs: g(C(t.adr)),
|
|
236
|
+
risks: n.Array(p),
|
|
237
|
+
relatedTo: g(C(t.workItem))
|
|
225
238
|
}), l = n.Struct({
|
|
226
|
-
reason:
|
|
227
|
-
owner:
|
|
228
|
-
nextAction:
|
|
229
|
-
recheckTrigger:
|
|
230
|
-
since:
|
|
239
|
+
reason: p,
|
|
240
|
+
owner: p,
|
|
241
|
+
nextAction: p,
|
|
242
|
+
recheckTrigger: p,
|
|
243
|
+
since: m
|
|
231
244
|
}), u = n.Struct({
|
|
232
|
-
from: n.NullOr(n.
|
|
233
|
-
to: n.
|
|
234
|
-
reason:
|
|
235
|
-
actor:
|
|
236
|
-
at:
|
|
245
|
+
from: n.NullOr(n.Literals(y)),
|
|
246
|
+
to: n.Literals(y),
|
|
247
|
+
reason: p,
|
|
248
|
+
actor: p,
|
|
249
|
+
at: m,
|
|
237
250
|
evidenceRefs: r
|
|
238
251
|
}), d = n.Struct({
|
|
239
252
|
id: C(t.workItem),
|
|
240
|
-
title:
|
|
253
|
+
title: p,
|
|
241
254
|
ownerPlan: C(t.plan),
|
|
242
|
-
slice:
|
|
243
|
-
kind: n.
|
|
244
|
-
priority: n.
|
|
255
|
+
slice: p,
|
|
256
|
+
kind: n.Literals(S),
|
|
257
|
+
priority: n.Literals(ee),
|
|
245
258
|
mandatory: n.Boolean,
|
|
246
|
-
status: n.
|
|
247
|
-
owners: n.Array(
|
|
248
|
-
repos:
|
|
249
|
-
sourceRefs:
|
|
250
|
-
dependsOn:
|
|
259
|
+
status: n.Literals(y),
|
|
260
|
+
owners: n.Array(p),
|
|
261
|
+
repos: _(n.Literals(e.repos)),
|
|
262
|
+
sourceRefs: _(i),
|
|
263
|
+
dependsOn: g(C(t.workItem)),
|
|
251
264
|
links: c,
|
|
252
|
-
acceptance:
|
|
253
|
-
evidenceRequirements:
|
|
265
|
+
acceptance: _(a),
|
|
266
|
+
evidenceRequirements: _(s),
|
|
254
267
|
evidenceRefs: r,
|
|
255
|
-
impactTags:
|
|
256
|
-
statusHistory:
|
|
268
|
+
impactTags: _(v),
|
|
269
|
+
statusHistory: _(u),
|
|
257
270
|
blocker: n.optional(l),
|
|
258
271
|
supersededBy: n.optional(C(t.workItem)),
|
|
259
|
-
createdAt:
|
|
260
|
-
updatedAt:
|
|
261
|
-
}),
|
|
272
|
+
createdAt: m,
|
|
273
|
+
updatedAt: m
|
|
274
|
+
}), f = n.Struct({
|
|
262
275
|
id: C(t.plan),
|
|
263
|
-
number: n.String.pipe(n.
|
|
264
|
-
title:
|
|
265
|
-
source:
|
|
266
|
-
sourceRevision:
|
|
267
|
-
status: n.
|
|
276
|
+
number: n.String.pipe(n.check(n.isPattern(/^[0-9]{2}$/))),
|
|
277
|
+
title: p,
|
|
278
|
+
source: p,
|
|
279
|
+
sourceRevision: h,
|
|
280
|
+
status: n.Literals(b),
|
|
268
281
|
decompositionComplete: n.Boolean,
|
|
269
|
-
updatedAt:
|
|
282
|
+
updatedAt: m
|
|
283
|
+
}), w = n.Struct({
|
|
284
|
+
rulesVersion: p,
|
|
285
|
+
sourceRevision: h,
|
|
286
|
+
blockKinds: n.Array(p),
|
|
287
|
+
extractedAt: m
|
|
270
288
|
}), T = n.Struct({
|
|
271
|
-
rulesVersion: f,
|
|
272
|
-
sourceRevision: m,
|
|
273
|
-
blockKinds: n.Array(f),
|
|
274
|
-
extractedAt: p
|
|
275
|
-
}), E = n.Struct({
|
|
276
289
|
extractedSources: n.Int,
|
|
277
290
|
disposedSources: n.Int,
|
|
278
291
|
unmatchedSources: n.Int,
|
|
279
292
|
duplicateSources: n.Int,
|
|
280
|
-
sourceIdsHash:
|
|
281
|
-
result: n.
|
|
293
|
+
sourceIdsHash: h,
|
|
294
|
+
result: n.Literals(["complete", "incomplete"])
|
|
282
295
|
});
|
|
283
296
|
return n.Struct({
|
|
284
297
|
schemaVersion: n.Literal(e.instanceVersion),
|
|
285
|
-
plan:
|
|
286
|
-
extraction:
|
|
298
|
+
plan: f,
|
|
299
|
+
extraction: w,
|
|
287
300
|
workItems: n.Array(d),
|
|
288
|
-
coverage:
|
|
301
|
+
coverage: T
|
|
289
302
|
});
|
|
290
303
|
}, T = (e) => n.Struct({
|
|
291
304
|
id: C(e.grammar.evidence),
|
|
292
|
-
kind: n.
|
|
293
|
-
title:
|
|
294
|
-
uri:
|
|
295
|
-
fingerprint: n.optional(
|
|
296
|
-
result: n.
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
305
|
+
kind: n.Literals(x),
|
|
306
|
+
title: p,
|
|
307
|
+
uri: p,
|
|
308
|
+
fingerprint: n.optional(h),
|
|
309
|
+
result: n.Literals([
|
|
310
|
+
"passed",
|
|
311
|
+
"failed",
|
|
312
|
+
"informational"
|
|
313
|
+
]),
|
|
314
|
+
subjectRefs: n.Array(p),
|
|
315
|
+
command: n.optional(p),
|
|
316
|
+
environment: n.optional(p),
|
|
317
|
+
producedBy: p,
|
|
318
|
+
reviewedBy: n.optional(p),
|
|
319
|
+
observedAt: m,
|
|
320
|
+
idempotencyKey: n.optional(p)
|
|
304
321
|
}), E = (e) => n.Struct({
|
|
305
322
|
id: C(e.grammar.finding),
|
|
306
|
-
title:
|
|
307
|
-
severity: n.
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
323
|
+
title: p,
|
|
324
|
+
severity: n.Literals([
|
|
325
|
+
"critical",
|
|
326
|
+
"high",
|
|
327
|
+
"medium",
|
|
328
|
+
"low"
|
|
329
|
+
]),
|
|
330
|
+
status: n.Literals([
|
|
331
|
+
"open",
|
|
332
|
+
"resolved",
|
|
333
|
+
"accepted-risk"
|
|
334
|
+
]),
|
|
335
|
+
source: n.Literals([
|
|
336
|
+
"contract-reconciliation",
|
|
337
|
+
"adversarial-audit",
|
|
338
|
+
"test",
|
|
339
|
+
"incident",
|
|
340
|
+
"research",
|
|
341
|
+
"review",
|
|
342
|
+
"production"
|
|
343
|
+
]),
|
|
344
|
+
targetWorkItems: _(C(e.grammar.workItem)),
|
|
345
|
+
impactTags: n.Array(p),
|
|
346
|
+
evidenceRefs: n.optional(g(C(e.grammar.evidence))),
|
|
347
|
+
openedAt: m,
|
|
348
|
+
resolvedAt: n.optional(m)
|
|
315
349
|
}), D = {
|
|
316
350
|
security: 0,
|
|
317
351
|
error: 1,
|
|
318
352
|
warning: 2
|
|
319
|
-
}, O = (e, t) => (e ?? "") < (t ?? "") ? -1 : +((e ?? "") > (t ?? "")),
|
|
353
|
+
}, O = (e, t) => (e ?? "") < (t ?? "") ? -1 : +((e ?? "") > (t ?? "")), te = (e) => [...e].sort((e, t) => D[e.severity] - D[t.severity] || O(e.locator.plan, t.locator.plan) || O(e.locator.workItem, t.locator.workItem) || O(e.ruleId, t.ruleId) || O(e.locator.file, t.locator.file) || O(e.locator.path, t.locator.path) || O(e.message, t.message)), ne = (e, t = !1) => e.some((e) => e.severity !== "warning" || t), k = {
|
|
320
354
|
proposed: ["ready", "blocked"],
|
|
321
355
|
ready: [
|
|
322
356
|
"active",
|
|
@@ -335,9 +369,12 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
|
|
|
335
369
|
],
|
|
336
370
|
verified: ["closed", "active"],
|
|
337
371
|
closed: ["active"]
|
|
338
|
-
},
|
|
372
|
+
}, A = /* @__PURE__ */ new Set(["verified", "closed"]), j = (e, t) => {
|
|
339
373
|
let n = /* @__PURE__ */ new Map();
|
|
340
|
-
for (let e of t) if (e.status === "open") for (let t of e.targetWorkItems)
|
|
374
|
+
for (let e of t) if (e.status === "open") for (let t of e.targetWorkItems) {
|
|
375
|
+
let r = n.get(t);
|
|
376
|
+
r === void 0 ? n.set(t, [e]) : r.push(e);
|
|
377
|
+
}
|
|
341
378
|
let r = /* @__PURE__ */ new Map();
|
|
342
379
|
for (let t of e) for (let e of new Set(t.subjectRefs)) {
|
|
343
380
|
let n = r.get(e);
|
|
@@ -348,50 +385,65 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
|
|
|
348
385
|
bySubject: r,
|
|
349
386
|
openFindings: n
|
|
350
387
|
};
|
|
351
|
-
},
|
|
352
|
-
let i = [], a = t.bySubject.get(e.id) ?? [], o
|
|
388
|
+
}, M = (e, t, n, r) => e.result === "passed" && e.subjectRefs.includes(n) && (r === void 0 || r.includes(e.kind)) && Date.parse(e.observedAt) >= Date.parse(t.updatedAt), N = (e, t, n, r) => {
|
|
389
|
+
let i = [], a = t.bySubject.get(e.id) ?? [], o;
|
|
390
|
+
if (e.acceptance.length > 4 || e.acceptance.length + e.evidenceRequirements.length > 4 && e.acceptance.length + e.evidenceRequirements.filter((e) => e.status !== "waived").length > 4) {
|
|
391
|
+
let t = /* @__PURE__ */ new Set([...e.acceptance.map((e) => e.id), ...e.evidenceRequirements.filter((e) => e.status !== "waived").map((e) => e.id)]);
|
|
392
|
+
o = /* @__PURE__ */ new Map();
|
|
393
|
+
let n = Date.parse(e.updatedAt);
|
|
394
|
+
for (let e of a) if (e.result === "passed" && Date.parse(e.observedAt) >= n) for (let n of e.subjectRefs) {
|
|
395
|
+
if (!t.has(n)) continue;
|
|
396
|
+
let r = o.get(n);
|
|
397
|
+
r === void 0 ? o.set(n, /* @__PURE__ */ new Set([e.kind])) : r.add(e.kind);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
let s = (t, n) => {
|
|
401
|
+
if (o === void 0) return a.some((r) => M(r, e, t, n));
|
|
402
|
+
let r = o.get(t);
|
|
403
|
+
return r !== void 0 && (n === void 0 || n.some((e) => r.has(e)));
|
|
404
|
+
}, c = (n, r) => n.some((n) => {
|
|
353
405
|
let i = t.evidence.get(n);
|
|
354
|
-
return i !== void 0 &&
|
|
406
|
+
return i !== void 0 && M(i, e, e.id, r);
|
|
355
407
|
});
|
|
356
|
-
for (let t of e.acceptance) !
|
|
357
|
-
for (let t of e.evidenceRequirements) t.status === "waived" ? (t.waiver === void 0 || Date.parse(t.waiver.expiresAt) <= r.now().getTime()) && i.push(`the waiver of ${t.id} has expired`) : !
|
|
408
|
+
for (let t of e.acceptance) !s(t.id) && !(t.status === "passed" && c(t.evidenceRefs)) && i.push(`acceptance ${t.id} has no passing evidence`);
|
|
409
|
+
for (let t of e.evidenceRequirements) t.status === "waived" ? (t.waiver === void 0 || Date.parse(t.waiver.expiresAt) <= r.now().getTime()) && i.push(`the waiver of ${t.id} has expired`) : !s(t.id, t.kinds) && !c(t.evidenceRefs, t.kinds) && i.push(`requirement ${t.id} has no passing ${t.kinds.join("/")} evidence`);
|
|
358
410
|
for (let n of t.openFindings.get(e.id) ?? []) i.push(`finding ${n.id} is open`);
|
|
359
411
|
for (let t of e.dependsOn) {
|
|
360
412
|
let e = n(t);
|
|
361
413
|
e !== "verified" && e !== "closed" && i.push(`dependency ${t} is ${e ?? "missing"}`);
|
|
362
414
|
}
|
|
363
415
|
return i;
|
|
364
|
-
},
|
|
365
|
-
let t = e.grammar, r = (e) => n.String.pipe(n.
|
|
416
|
+
}, P = (e, t) => t.length === 0 ? "verified" : A.has(e.status) ? "active" : e.status, F = "voltro-plan-closure/v1", I = n.String.pipe(n.check(n.isMinLength(1))), L = n.String.pipe(n.check(n.isPattern(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2}(\.\d+)?)?(Z|[+-]\d{2}:\d{2})$/))), R = n.String.pipe(n.check(n.isPattern(/^sha256:[a-f0-9]{64}$/))), z = (e) => {
|
|
417
|
+
let t = e.grammar, r = (e) => n.String.pipe(n.check(n.isPattern(e))), i = n.Struct({
|
|
366
418
|
id: r(t.closureRun),
|
|
367
|
-
pass: n.
|
|
368
|
-
reviewer:
|
|
369
|
-
reviewContext:
|
|
370
|
-
planRevision:
|
|
371
|
-
completedAt:
|
|
419
|
+
pass: n.Literals(["contract", "adversarial"]),
|
|
420
|
+
reviewer: I,
|
|
421
|
+
reviewContext: I,
|
|
422
|
+
planRevision: R,
|
|
423
|
+
completedAt: L,
|
|
372
424
|
findingRefs: n.Array(r(t.finding)),
|
|
373
|
-
result: n.
|
|
374
|
-
toolVersion:
|
|
425
|
+
result: n.Literals(["passed", "findings"]),
|
|
426
|
+
toolVersion: I
|
|
375
427
|
}), a = n.Struct({
|
|
376
428
|
id: r(t.certificate),
|
|
377
429
|
plan: r(t.plan),
|
|
378
|
-
planRevision:
|
|
430
|
+
planRevision: R,
|
|
379
431
|
workItems: n.Array(r(t.workItem)),
|
|
380
432
|
evidenceRefs: n.Array(r(t.evidence)),
|
|
381
|
-
closureRuns: n.Array(r(t.closureRun)).pipe(n.
|
|
382
|
-
confirmedBy:
|
|
383
|
-
issuedAt:
|
|
384
|
-
toolVersion:
|
|
433
|
+
closureRuns: n.Array(r(t.closureRun)).pipe(n.check(n.isBetweenLength(2, 2))),
|
|
434
|
+
confirmedBy: I,
|
|
435
|
+
issuedAt: L,
|
|
436
|
+
toolVersion: I
|
|
385
437
|
});
|
|
386
438
|
return n.Struct({
|
|
387
|
-
schemaVersion: n.Literal(
|
|
439
|
+
schemaVersion: n.Literal(F),
|
|
388
440
|
plan: r(t.plan),
|
|
389
441
|
runs: n.Array(i),
|
|
390
442
|
certificates: n.Array(a)
|
|
391
443
|
});
|
|
392
444
|
}, B = (e, t, n, r = () => void 0, i = /* @__PURE__ */ new Map()) => {
|
|
393
445
|
let a = new Set(e.workItems.map((e) => e.id));
|
|
394
|
-
return
|
|
446
|
+
return f({
|
|
395
447
|
instance: e,
|
|
396
448
|
dependencies: [...new Set(e.workItems.flatMap((e) => e.dependsOn).filter((e) => !a.has(e)))].sort().map((e) => {
|
|
397
449
|
let t = r(e);
|
|
@@ -423,7 +475,7 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
|
|
|
423
475
|
related: [],
|
|
424
476
|
nextAction: n
|
|
425
477
|
}
|
|
426
|
-
}), U = (e, t) => `${e}${String(Math.max(0, ...t.filter((t) => t.startsWith(e)).map((t) => Number(t.slice(e.length)))) + 1).padStart(3, "0")}`,
|
|
478
|
+
}), U = (e, t) => `${e}${String(Math.max(0, ...t.filter((t) => t.startsWith(e)).map((t) => Number(t.slice(e.length)))) + 1).padStart(3, "0")}`, re = (e, t, n, r, i, a, o, s) => {
|
|
427
479
|
let c = t.plan.id, l = t.workItems.filter((e) => e.mandatory && e.supersededBy === void 0), u = l.filter((e) => i(e.id) !== "verified" && i(e.id) !== "closed");
|
|
428
480
|
if (s.findings.length === 0 && u.length > 0) return H("closure/unverified-items", `${u.length} of ${l.length} mandatory items of ${c} are not verified (${u.slice(0, 3).map((e) => e.id).join(", ")}${u.length > 3 ? ", …" : ""})`, "Verify every mandatory item first: voltro plan status, voltro plan evidence add.", c);
|
|
429
481
|
let d = new Set(t.workItems.map((e) => e.id)), f = o.filter((e) => e.status === "open" && e.targetWorkItems.some((e) => d.has(e)));
|
|
@@ -463,7 +515,7 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
|
|
|
463
515
|
return {
|
|
464
516
|
outcome: "recorded",
|
|
465
517
|
file: {
|
|
466
|
-
schemaVersion:
|
|
518
|
+
schemaVersion: F,
|
|
467
519
|
plan: c,
|
|
468
520
|
runs: [...p, h],
|
|
469
521
|
certificates: [...n?.certificates ?? [], ...g === null ? [] : [g]]
|
|
@@ -478,27 +530,34 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
|
|
|
478
530
|
locator: r,
|
|
479
531
|
related: [...a].sort(),
|
|
480
532
|
nextAction: i
|
|
481
|
-
}), G = (r, i, a, o) => {
|
|
482
|
-
let s = n.
|
|
533
|
+
}), ie = r.makeFormatterStandardSchemaV1(), G = (r, i, a, o) => {
|
|
534
|
+
let s = n.decodeUnknownExit(i, {
|
|
483
535
|
onExcessProperty: "error",
|
|
484
536
|
errors: "all"
|
|
485
537
|
}), c = [];
|
|
486
538
|
for (let n of r) {
|
|
487
539
|
let r = s(n.content);
|
|
488
|
-
if (
|
|
540
|
+
if (t.isSuccess(r)) {
|
|
489
541
|
c.push({
|
|
490
542
|
file: n.file,
|
|
491
|
-
value: r.
|
|
543
|
+
value: r.value
|
|
492
544
|
});
|
|
493
545
|
continue;
|
|
494
546
|
}
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
path:
|
|
498
|
-
}
|
|
547
|
+
let i = e.findErrorOption(r.cause), l = i._tag === "Some" ? ie(i.value.issue).issues : [{
|
|
548
|
+
message: e.pretty(r.cause),
|
|
549
|
+
path: []
|
|
550
|
+
}];
|
|
551
|
+
for (let e of l) {
|
|
552
|
+
let t = (e.path ?? []).map((e) => String(typeof e == "object" ? e.key : e));
|
|
553
|
+
o.push(W("schema/invalid", "error", `${a}: ${e.message}`, {
|
|
554
|
+
file: n.file,
|
|
555
|
+
path: t.join(".") || "(root)"
|
|
556
|
+
}, "Correct the field to the v1 contract; unknown fields are not allowed."));
|
|
557
|
+
}
|
|
499
558
|
}
|
|
500
559
|
return c;
|
|
501
|
-
},
|
|
560
|
+
}, ae = [
|
|
502
561
|
["private key", /-----BEGIN [A-Z ]*PRIVATE KEY-----/],
|
|
503
562
|
["Stripe key", /\b[sr]k_(live|test)_[A-Za-z0-9]{16,}/],
|
|
504
563
|
["GitHub token", /\bgh[pousr]_[A-Za-z0-9]{30,}/],
|
|
@@ -506,7 +565,7 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
|
|
|
506
565
|
["Slack token", /\bxox[abprs]-[A-Za-z0-9-]{10,}/],
|
|
507
566
|
["bearer token", /\bBearer\s+[A-Za-z0-9._~+/-]{20,}/]
|
|
508
567
|
], K = (e, t, n, r) => {
|
|
509
|
-
if (typeof e == "string") for (let [i, a] of
|
|
568
|
+
if (typeof e == "string") for (let [i, a] of ae) a.test(e) && r.push(W("security/secret", "security", `a ${i} is in the plan artefact`, {
|
|
510
569
|
file: t,
|
|
511
570
|
path: n
|
|
512
571
|
}, "Remove the secret, rotate it, and reference where it is kept instead."));
|
|
@@ -528,7 +587,7 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
|
|
|
528
587
|
};
|
|
529
588
|
for (let n of [...e.keys()].sort()) t.has(n) || r(n);
|
|
530
589
|
return [...n.values()].sort((e, t) => e.join(">") < t.join(">") ? -1 : 1);
|
|
531
|
-
},
|
|
590
|
+
}, oe = (e) => {
|
|
532
591
|
let { shape: t, clock: n } = e, r = t.grammar, i = [];
|
|
533
592
|
for (let t of [
|
|
534
593
|
...e.instances,
|
|
@@ -536,7 +595,7 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
|
|
|
536
595
|
...e.findings ?? [],
|
|
537
596
|
...e.closures ?? []
|
|
538
597
|
]) K(t.content, t.file, "", i);
|
|
539
|
-
let
|
|
598
|
+
let a = G(e.instances, w(t), "plan instance", i).map(({ file: e, value: t }) => ({
|
|
540
599
|
file: e,
|
|
541
600
|
instance: t
|
|
542
601
|
})), c = (e.evidence ?? []).flatMap((e) => G(Array.isArray(e.content) ? e.content.map((t, n) => ({
|
|
@@ -546,53 +605,53 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
|
|
|
546
605
|
file: `${e.file}#${n}`,
|
|
547
606
|
content: t
|
|
548
607
|
})) : [e], E(t), "finding", i).map((e) => e.value)), u = /* @__PURE__ */ new Map(), d = /* @__PURE__ */ new Map(), f = /* @__PURE__ */ new Map();
|
|
549
|
-
for (let { file: t, instance: n } of
|
|
550
|
-
let
|
|
551
|
-
u.has(
|
|
608
|
+
for (let { file: t, instance: n } of a) {
|
|
609
|
+
let a = n.plan;
|
|
610
|
+
u.has(a.id) && i.push(W("identity/duplicate-plan", "error", `${a.id} is declared in two files`, {
|
|
552
611
|
file: t,
|
|
553
|
-
plan:
|
|
554
|
-
}, "Keep one instance file per plan.", [u.get(
|
|
612
|
+
plan: a.id
|
|
613
|
+
}, "Keep one instance file per plan.", [u.get(a.id)])), u.set(a.id, t), o(a.id) !== a.number && i.push(W("identity/plan-number", "error", `${a.id} has number ${a.number}`, {
|
|
555
614
|
file: t,
|
|
556
|
-
plan:
|
|
615
|
+
plan: a.id,
|
|
557
616
|
path: "plan.number"
|
|
558
617
|
}, "Make the number match the plan id."));
|
|
559
618
|
for (let e of n.workItems) {
|
|
560
619
|
let n = {
|
|
561
620
|
file: t,
|
|
562
|
-
plan:
|
|
621
|
+
plan: a.id,
|
|
563
622
|
workItem: e.id
|
|
564
623
|
};
|
|
565
624
|
d.has(e.id) ? i.push(W("identity/duplicate-work-item", "error", `${e.id} is declared twice`, n, "Give the second item its own id; ids are never reused.", [d.get(e.id).file])) : d.set(e.id, {
|
|
566
625
|
file: t,
|
|
567
626
|
item: e
|
|
568
|
-
}), (e.ownerPlan !==
|
|
627
|
+
}), (e.ownerPlan !== a.id || o(e.id) !== a.number) && i.push(W("identity/owner-mismatch", "error", `${e.id} is owned by ${e.ownerPlan} but lives in ${a.id}`, n, "Move the item to its plan’s file or correct ownerPlan."));
|
|
569
628
|
let c = [
|
|
570
629
|
...e.acceptance.map((e) => e.id),
|
|
571
630
|
...e.evidenceRequirements.map((e) => e.id),
|
|
572
631
|
...e.sourceRefs.map((e) => e.id)
|
|
573
632
|
];
|
|
574
|
-
for (let t of c)
|
|
633
|
+
for (let t of c) s(r, t) !== e.id && i.push(W("identity/child-id", "error", `${t} does not belong to ${e.id}`, n, `Number it under ${e.id}.`));
|
|
575
634
|
for (let t of e.sourceRefs) f.has(t.id) && i.push(W("source/duplicate", "error", `source ${t.id} is disposed twice`, n, "Dispose each source block exactly once.", [f.get(t.id)])), f.set(t.id, e.id), t.disposition !== "owned-by" && t.targetId === void 0 && i.push(W("source/missing-target", "error", `source ${t.id} is ${t.disposition} without a target`, {
|
|
576
635
|
...n,
|
|
577
636
|
path: `sourceRefs.${t.id}`
|
|
578
637
|
}, "Name the work item or decision it points to."));
|
|
579
638
|
}
|
|
580
639
|
if (e.sources !== void 0) {
|
|
581
|
-
let r = e.sources.get(
|
|
582
|
-
r == null ? i.push(W("source/missing", "error", `${
|
|
640
|
+
let r = e.sources.get(a.source);
|
|
641
|
+
r == null ? i.push(W("source/missing", "error", `${a.id}'s source ${a.source} cannot be read`, {
|
|
583
642
|
file: t,
|
|
584
|
-
plan:
|
|
643
|
+
plan: a.id,
|
|
585
644
|
path: "plan.source"
|
|
586
|
-
}, "Restore the source or point the plan at it.")) : (r !==
|
|
645
|
+
}, "Restore the source or point the plan at it.")) : (r !== a.sourceRevision || r !== n.extraction.sourceRevision) && i.push(W("source/stale", "error", `${a.source} changed since ${a.id} was decomposed (${a.sourceRevision.slice(0, 19)}… → ${r.slice(0, 19)}…)`, {
|
|
587
646
|
file: t,
|
|
588
|
-
plan:
|
|
647
|
+
plan: a.id,
|
|
589
648
|
path: "plan.sourceRevision"
|
|
590
649
|
}, "Regenerate the plan’s decomposition from its source; closure waits until then."));
|
|
591
650
|
}
|
|
592
651
|
let c = n.workItems.reduce((e, t) => e + t.sourceRefs.length, 0);
|
|
593
|
-
(n.coverage.disposedSources !== c || n.coverage.unmatchedSources > 0 || n.coverage.duplicateSources > 0 ||
|
|
652
|
+
(n.coverage.disposedSources !== c || n.coverage.unmatchedSources > 0 || n.coverage.duplicateSources > 0 || a.decompositionComplete && n.coverage.result !== "complete") && i.push(W("source/coverage", "error", `${a.id} claims ${n.coverage.disposedSources} disposed sources (${n.coverage.unmatchedSources} unmatched, ${n.coverage.duplicateSources} duplicate, ${n.coverage.result}) and holds ${c}`, {
|
|
594
653
|
file: t,
|
|
595
|
-
plan:
|
|
654
|
+
plan: a.id,
|
|
596
655
|
path: "coverage"
|
|
597
656
|
}, "Regenerate the plan’s decomposition from its source."));
|
|
598
657
|
}
|
|
@@ -643,15 +702,15 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
|
|
|
643
702
|
path: e.id
|
|
644
703
|
}, "Write the waiver: rationale, reviewer, expiry and compensating control."));
|
|
645
704
|
}
|
|
646
|
-
let m =
|
|
705
|
+
let m = j(c, l), h = /* @__PURE__ */ new Map(), g = /* @__PURE__ */ new Map(), _ = /* @__PURE__ */ new Set(), v = (e) => {
|
|
647
706
|
let t = h.get(e);
|
|
648
707
|
if (t !== void 0) return t;
|
|
649
708
|
let r = d.get(e);
|
|
650
709
|
if (r === void 0 || _.has(e)) return;
|
|
651
710
|
_.add(e);
|
|
652
|
-
let i =
|
|
711
|
+
let i = N(r.item, m, v, n);
|
|
653
712
|
_.delete(e);
|
|
654
|
-
let a =
|
|
713
|
+
let a = P(r.item, i);
|
|
655
714
|
return h.set(e, a), g.set(e, i), a;
|
|
656
715
|
};
|
|
657
716
|
for (let { file: e, item: t } of [...d.values()].sort((e, t) => e.item.id < t.item.id ? -1 : 1)) {
|
|
@@ -661,10 +720,10 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
|
|
|
661
720
|
workItem: t.id
|
|
662
721
|
}, r = null;
|
|
663
722
|
t.statusHistory.forEach((e, a) => {
|
|
664
|
-
e.from === r ? e.from !== null && !
|
|
723
|
+
e.from === r ? e.from !== null && !k[e.from].includes(e.to) && i.push(W("state/forbidden-transition", "error", `${t.id} went ${e.from} → ${e.to}`, {
|
|
665
724
|
...n,
|
|
666
725
|
path: `statusHistory[${a}]`
|
|
667
|
-
}, `From ${e.from} an item goes to ${
|
|
726
|
+
}, `From ${e.from} an item goes to ${k[e.from].join(", ")}.`)) : i.push(W("state/history-break", "error", `${t.id} history step ${a + 1} starts at ${e.from ?? "nothing"}, after ${r ?? "nothing"}`, {
|
|
668
727
|
...n,
|
|
669
728
|
path: `statusHistory[${a}]`
|
|
670
729
|
}, "Record every transition in order.")), r = e.to;
|
|
@@ -691,7 +750,7 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
|
|
|
691
750
|
plan: r.plan
|
|
692
751
|
}, "Remove it or correct the plan id."));
|
|
693
752
|
let b = /* @__PURE__ */ new Map(), x = /* @__PURE__ */ new Map();
|
|
694
|
-
for (let { instance: t } of
|
|
753
|
+
for (let { instance: t } of a) {
|
|
695
754
|
let n = B(t, c, l, (e) => h.get(e), e.decisions);
|
|
696
755
|
b.set(t.plan.id, n);
|
|
697
756
|
let r = y.get(t.plan.id), a = V(r?.record, n);
|
|
@@ -706,7 +765,7 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
|
|
|
706
765
|
}
|
|
707
766
|
for (let { file: e, item: t } of [...d.values()].sort((e, t) => e.item.id < t.item.id ? -1 : 1)) {
|
|
708
767
|
let n = h.get(t.id);
|
|
709
|
-
|
|
768
|
+
A.has(t.status) && n !== t.status && (t.status !== "verified" || n !== "closed") && i.push(W("state/manual-derived", "error", `${t.id} says ${t.status}; the evidence says ${n}: ${(g.get(t.id) ?? []).slice(0, 3).join("; ") || "its plan holds no certificate on its current revision"}`, {
|
|
710
769
|
file: e,
|
|
711
770
|
plan: t.ownerPlan,
|
|
712
771
|
workItem: t.id,
|
|
@@ -715,7 +774,7 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
|
|
|
715
774
|
}
|
|
716
775
|
return {
|
|
717
776
|
program: {
|
|
718
|
-
instances:
|
|
777
|
+
instances: a,
|
|
719
778
|
items: d,
|
|
720
779
|
evidence: c,
|
|
721
780
|
findings: l,
|
|
@@ -725,9 +784,9 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
|
|
|
725
784
|
revisions: b,
|
|
726
785
|
closure: x
|
|
727
786
|
},
|
|
728
|
-
diagnostics:
|
|
787
|
+
diagnostics: te(i)
|
|
729
788
|
};
|
|
730
|
-
}, J = () => Object.fromEntries(
|
|
789
|
+
}, J = () => Object.fromEntries(y.map((e) => [e, 0])), se = (e) => {
|
|
731
790
|
let t = J();
|
|
732
791
|
return {
|
|
733
792
|
plans: e.instances.map(({ file: n, instance: r }) => {
|
|
@@ -757,12 +816,12 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
|
|
|
757
816
|
mandatory: t,
|
|
758
817
|
items: e.items.size
|
|
759
818
|
};
|
|
760
|
-
},
|
|
819
|
+
}, ce = {
|
|
761
820
|
P0: 0,
|
|
762
821
|
P1: 1,
|
|
763
822
|
P2: 2,
|
|
764
823
|
P3: 3
|
|
765
|
-
},
|
|
824
|
+
}, le = (e, t, n) => {
|
|
766
825
|
if (n) return {
|
|
767
826
|
outcome: "invalid",
|
|
768
827
|
reason: "lint reports blocking diagnostics; fix them first (`voltro plan lint`)"
|
|
@@ -775,39 +834,49 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
|
|
|
775
834
|
if (a.every(({ item: e }) => ["verified", "closed"].includes(r(e.id) ?? ""))) return { outcome: "complete" };
|
|
776
835
|
let o = (e) => e.dependsOn.every((e) => ["verified", "closed"].includes(r(e) ?? "")), s = (e) => [
|
|
777
836
|
r(e.id) === "active" ? 0 : 1,
|
|
778
|
-
|
|
837
|
+
ce[e.priority],
|
|
779
838
|
e.kind === "security" ? 0 : 1,
|
|
780
839
|
+!i.has(e.ownerPlan),
|
|
781
840
|
+!o(e),
|
|
782
841
|
e.id
|
|
783
842
|
], c = (e, t) => {
|
|
784
|
-
let
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
843
|
+
for (let n = 0; n < e.length; n++) if (e[n] !== t[n]) return e[n] < t[n];
|
|
844
|
+
return !1;
|
|
845
|
+
}, l = (e) => {
|
|
846
|
+
let t;
|
|
847
|
+
for (let n of a) {
|
|
848
|
+
if (!e.includes(r(n.item.id) ?? "")) continue;
|
|
849
|
+
let i = s(n.item);
|
|
850
|
+
(t === void 0 || c(i, t.rank)) && (t = {
|
|
851
|
+
entry: n,
|
|
852
|
+
rank: i
|
|
853
|
+
});
|
|
854
|
+
}
|
|
855
|
+
return t;
|
|
856
|
+
}, u = l(["active", "ready"]);
|
|
857
|
+
if (u !== void 0) {
|
|
858
|
+
let e = u.entry, t = r(e.item.id);
|
|
790
859
|
return {
|
|
791
860
|
outcome: "item",
|
|
792
|
-
workItem:
|
|
793
|
-
file:
|
|
861
|
+
workItem: e.item,
|
|
862
|
+
file: e.file,
|
|
794
863
|
why: [
|
|
795
|
-
|
|
796
|
-
`priority ${
|
|
797
|
-
...
|
|
798
|
-
i.has(
|
|
799
|
-
|
|
864
|
+
t === "active" ? "it is already active" : "it is ready",
|
|
865
|
+
`priority ${e.item.priority}`,
|
|
866
|
+
...e.item.kind === "security" ? ["security work comes first"] : [],
|
|
867
|
+
i.has(e.item.ownerPlan) ? `${e.item.ownerPlan} is already under way` : "no started plan has workable items",
|
|
868
|
+
u.rank[4] === 0 ? "its dependencies are verified" : "no dependency-ready item is left, so it waits on its dependencies"
|
|
800
869
|
]
|
|
801
870
|
};
|
|
802
871
|
}
|
|
803
|
-
let
|
|
872
|
+
let d = l(["proposed"]), f = a.filter(({ item: e }) => r(e.id) === "proposed").length, p = a.filter(({ item: e }) => r(e.id) === "blocked").length;
|
|
804
873
|
return {
|
|
805
874
|
outcome: "blocked",
|
|
806
|
-
reason: `no mandatory item is active or ready: ${
|
|
807
|
-
prepare:
|
|
808
|
-
file:
|
|
875
|
+
reason: `no mandatory item is active or ready: ${f} proposed await readiness${p > 0 ? `, ${p} blocked` : ""}`,
|
|
876
|
+
prepare: d?.entry.item ?? null,
|
|
877
|
+
file: d?.entry.file ?? null
|
|
809
878
|
};
|
|
810
|
-
}, Y = "voltro-plan-command/v1",
|
|
879
|
+
}, Y = "voltro-plan-command/v1", ue = (e) => ({
|
|
811
880
|
schemaVersion: Y,
|
|
812
881
|
...e
|
|
813
882
|
}), X = {
|
|
@@ -819,12 +888,12 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
|
|
|
819
888
|
provider: 5,
|
|
820
889
|
security: 6,
|
|
821
890
|
internal: 70
|
|
822
|
-
}, Z = "voltro-plan-manifest/v1", Q = n.String.pipe(n.
|
|
891
|
+
}, Z = "voltro-plan-manifest/v1", Q = n.String.pipe(n.check(n.isMinLength(1)), n.check(n.makeFilter((e) => !e.startsWith("/") && !/^[A-Za-z]:/.test(e) && !e.split("/").includes("..") || "a path stays inside the project root"))), de = n.Struct({
|
|
823
892
|
schemaVersion: n.Literal(Z),
|
|
824
|
-
prefix: n.String.pipe(n.
|
|
825
|
-
instanceVersion: n.String.pipe(n.
|
|
826
|
-
repos: n.Array(n.String.pipe(n.
|
|
827
|
-
instances: n.Array(Q).pipe(n.
|
|
893
|
+
prefix: n.String.pipe(n.check(n.isPattern(/^[A-Z][A-Z0-9]{1,9}$/))),
|
|
894
|
+
instanceVersion: n.String.pipe(n.check(n.isMinLength(1))),
|
|
895
|
+
repos: n.Array(n.String.pipe(n.check(n.isMinLength(1)))).pipe(n.check(n.isMinLength(1))),
|
|
896
|
+
instances: n.Array(Q).pipe(n.check(n.isMinLength(1))),
|
|
828
897
|
evidence: n.optional(n.Array(Q)),
|
|
829
898
|
evidenceDir: n.optional(Q),
|
|
830
899
|
closureDir: n.optional(Q),
|
|
@@ -841,14 +910,14 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
|
|
|
841
910
|
related: [],
|
|
842
911
|
nextAction: r
|
|
843
912
|
}
|
|
844
|
-
}),
|
|
913
|
+
}), fe = [
|
|
845
914
|
/-----BEGIN [A-Z ]*PRIVATE KEY-----/,
|
|
846
915
|
/\b[sr]k_(live|test)_[A-Za-z0-9]{16,}/,
|
|
847
916
|
/\bgh[pousr]_[A-Za-z0-9]{30,}/,
|
|
848
917
|
/\bAKIA[0-9A-Z]{16}\b/,
|
|
849
918
|
/\bxox[abprs]-[A-Za-z0-9-]{10,}/,
|
|
850
919
|
/\bBearer\s+[A-Za-z0-9._~+/-]{20,}/
|
|
851
|
-
],
|
|
920
|
+
], pe = (e, t, n) => {
|
|
852
921
|
let r = e.items.get(n.workItem)?.item;
|
|
853
922
|
if (r === void 0) return $("evidence/unknown-target", "error", `${n.workItem} is not a work item of this program`, "Name an existing work item.", n.workItem, X.failed);
|
|
854
923
|
if ([
|
|
@@ -857,8 +926,8 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
|
|
|
857
926
|
n.producedBy,
|
|
858
927
|
n.command ?? "",
|
|
859
928
|
n.environment ?? ""
|
|
860
|
-
].some((e) =>
|
|
861
|
-
if (!
|
|
929
|
+
].some((e) => fe.some((t) => t.test(e))) || /^[a-z][a-z0-9+.-]*:\/\/[^/@\s]*:[^/@\s]*@/i.test(n.uri)) return $("security/secret", "security", "the evidence carries a secret or a credential", "Remove it; reference where the result is kept, never a key.", r.id, X.security);
|
|
930
|
+
if (!x.includes(n.kind)) return $("evidence/kind", "error", `"${n.kind}" is not an evidence kind`, `Use one of: ${x.join(", ")}.`, r.id, X.usage);
|
|
862
931
|
if (/^http:\/\//i.test(n.uri)) return $("evidence/uri", "error", `${n.uri} is not https`, "Reference evidence over https, git: or a project-relative path.", r.id, X.failed);
|
|
863
932
|
if (n.uri.startsWith("/") || /^[A-Za-z]:[\\/]/.test(n.uri) || n.uri.split("/").includes("..")) return $("evidence/uri", "error", `${n.uri} is not portable`, "Use a path relative to the project root, or a URL.", r.id, X.failed);
|
|
864
933
|
let i = [r.id];
|
|
@@ -882,14 +951,14 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
|
|
|
882
951
|
if (n.idempotencyKey !== void 0) {
|
|
883
952
|
let t = e.evidence.find((e) => e.idempotencyKey === n.idempotencyKey);
|
|
884
953
|
if (t !== void 0) {
|
|
885
|
-
let { id: e, observedAt: i, idempotencyKey: o, ...
|
|
886
|
-
return s
|
|
954
|
+
let { id: e, observedAt: i, idempotencyKey: o, ...s } = t;
|
|
955
|
+
return c(s) === c(a) ? {
|
|
887
956
|
outcome: "same",
|
|
888
957
|
evidence: t
|
|
889
958
|
} : $("evidence/idempotency-conflict", "error", `the key "${n.idempotencyKey}" was used for other evidence (${t.id})`, "Use a new key for new evidence.", r.id, X.conflict);
|
|
890
959
|
}
|
|
891
960
|
}
|
|
892
|
-
let o = `EVD-${r.id}-`,
|
|
961
|
+
let o = `EVD-${r.id}-`, s = e.evidence.filter((e) => e.id.startsWith(o)).map((e) => Number(e.id.slice(o.length))), l = String(Math.max(0, ...s) + 1).padStart(3, "0");
|
|
893
962
|
return t.evidence.test(`${o}${l}`) ? {
|
|
894
963
|
outcome: "add",
|
|
895
964
|
evidence: {
|
|
@@ -899,35 +968,48 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
|
|
|
899
968
|
...n.idempotencyKey === void 0 ? {} : { idempotencyKey: n.idempotencyKey }
|
|
900
969
|
}
|
|
901
970
|
} : $("evidence/exhausted", "error", `${r.id} has no evidence number left`, "Supersede the item.", r.id, X.failed);
|
|
902
|
-
},
|
|
971
|
+
}, me = [
|
|
903
972
|
".",
|
|
904
973
|
":",
|
|
905
974
|
"/",
|
|
906
975
|
"-"
|
|
907
|
-
],
|
|
976
|
+
], he = (e, t) => e === t || me.some((n) => t.startsWith(e + n) || e.startsWith(t + n)), ge = (e, t) => {
|
|
908
977
|
let n = /* @__PURE__ */ new Map(), r = (e, t) => {
|
|
909
978
|
let r = n.get(e) ?? /* @__PURE__ */ new Set();
|
|
910
979
|
r.add(t), n.set(e, r);
|
|
911
980
|
};
|
|
912
|
-
for (let { item: n } of e.items.values()) for (let e of t.tags) for (let t of n.impactTags)
|
|
913
|
-
let i = [], a = new Map(e.instances.map(({ file: e, instance: t }) => [e, t]));
|
|
914
|
-
for (let
|
|
915
|
-
let
|
|
916
|
-
|
|
981
|
+
for (let { item: n } of e.items.values()) for (let e of t.tags) for (let t of n.impactTags) he(e, t) && r(n.id, `tag ${t}`);
|
|
982
|
+
let i = [], a = new Map(e.instances.map(({ file: e, instance: t }) => [e, t])), o = /* @__PURE__ */ new Map();
|
|
983
|
+
for (let { item: t } of e.items.values()) for (let e of t.sourceRefs) {
|
|
984
|
+
let n = o.get(e.uri);
|
|
985
|
+
n === void 0 && (n = /* @__PURE__ */ new Set(), o.set(e.uri, n)), n.add(t.id);
|
|
986
|
+
}
|
|
987
|
+
for (let e of [...new Set(t.files)].sort()) {
|
|
988
|
+
let t = !1, n = a.get(e);
|
|
989
|
+
if (n !== void 0) {
|
|
917
990
|
t = !0;
|
|
918
|
-
for (let
|
|
991
|
+
for (let t of n.workItems) r(t.id, `its plan file ${e} changed`);
|
|
919
992
|
}
|
|
920
|
-
for (let
|
|
921
|
-
t || i.push(
|
|
993
|
+
for (let n of o.get(e) ?? []) t = !0, r(n, `its source ${e} changed`);
|
|
994
|
+
t || i.push(e);
|
|
922
995
|
}
|
|
923
|
-
let
|
|
924
|
-
for (let { item: t } of e.items.values()) for (let e of t.dependsOn)
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
996
|
+
let s = /* @__PURE__ */ new Map();
|
|
997
|
+
for (let { item: t } of e.items.values()) for (let e of t.dependsOn) {
|
|
998
|
+
let n = s.get(e);
|
|
999
|
+
n === void 0 && (n = [], s.set(e, n)), n.push(t.id);
|
|
1000
|
+
}
|
|
1001
|
+
for (let e of s.values()) e.sort();
|
|
1002
|
+
let c = /* @__PURE__ */ new Map(), l = [...n.keys()].sort().map((e) => [e]), u = new Set(n.keys());
|
|
1003
|
+
for (let e = 0; e < l.length; e++) {
|
|
1004
|
+
let t = l[e];
|
|
1005
|
+
for (let e of s.get(t.at(-1)) ?? []) {
|
|
1006
|
+
if (u.has(e)) continue;
|
|
1007
|
+
u.add(e);
|
|
1008
|
+
let n = [...t, e];
|
|
1009
|
+
c.set(e, n), l.push(n);
|
|
1010
|
+
}
|
|
929
1011
|
}
|
|
930
|
-
let
|
|
1012
|
+
let d = [...u], f = [...new Set(d.map((t) => e.items.get(t).item.ownerPlan))].sort(), p = f.flatMap((t) => {
|
|
931
1013
|
let n = e.closure.get(t);
|
|
932
1014
|
return n?.state === "certified" ? [{
|
|
933
1015
|
plan: t,
|
|
@@ -939,14 +1021,14 @@ var r = /^[A-Z][A-Z0-9]{1,9}$/, i = (e) => {
|
|
|
939
1021
|
workItem: e,
|
|
940
1022
|
because: [...t].sort()
|
|
941
1023
|
})).sort((e, t) => e.workItem < t.workItem ? -1 : 1),
|
|
942
|
-
transitive: [...
|
|
1024
|
+
transitive: [...c.entries()].map(([e, t]) => ({
|
|
943
1025
|
workItem: e,
|
|
944
1026
|
path: t
|
|
945
1027
|
})).sort((e, t) => e.workItem < t.workItem ? -1 : 1),
|
|
946
|
-
plans:
|
|
947
|
-
certificates:
|
|
1028
|
+
plans: f,
|
|
1029
|
+
certificates: p,
|
|
948
1030
|
unknown: i
|
|
949
1031
|
};
|
|
950
1032
|
};
|
|
951
1033
|
//#endregion
|
|
952
|
-
export {
|
|
1034
|
+
export { F as CLOSURE_SCHEMA_VERSION, Y as COMMAND_SCHEMA_VERSION, A as DERIVED, x as EVIDENCE_KINDS, X as EXIT, S as ITEM_KINDS, Z as MANIFEST_SCHEMA_VERSION, de as Manifest, b as PLAN_STATUSES, ee as PRIORITIES, k as TRANSITIONS, y as WORK_ITEM_STATUSES, ne as blocks, c as canonicalJson, z as closureFileSchema, V as closureState, re as decideClosure, pe as decideEvidence, q as dependencyCycles, P as derivedStatus, ue as envelope, T as evidenceSchema, E as findingSchema, f as fingerprint, a as idGrammar, ge as impactOf, j as indexEvidence, oe as lintProgram, le as nextItem, s as ownerItemOf, w as planInstanceSchema, o as planNumberOf, B as planRevision, se as programStatus, d as sha256Hex, te as sortDiagnostics, N as verificationGaps };
|