@blamejs/core 0.8.8 → 0.8.9
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 +2 -0
- package/index.js +1 -0
- package/lib/incident-report.js +314 -0
- package/package.json +1 -1
- package/sbom.cyclonedx.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,8 @@ upgrading across more than a few patches at a time.
|
|
|
8
8
|
|
|
9
9
|
## v0.8.x
|
|
10
10
|
|
|
11
|
+
- **0.8.9** (2026-05-07) — `b.incident.report` — generic 3-stage incident-reporting primitive. The three stages mirror the deadline pattern that recurs across regulatory regimes: initial / early-warning notification (within 24h of detection), intermediate / status update (within 72h), final report (within 30d or per-regime deadline). Built-in per-regime deadlines for `gdpr` (Article 33), `nis2` (Article 23), `dora` (Article 19), `cra` (Article 14), `hipaa` (Breach Notification Rule); operators select via `regime: "gdpr"` and `opts.deadlines` can override per-stage. Each stage records a tamper-evident audit event (`incident.report.stage_recorded`) with a `late: bool` + `lateBy: ms` flag — late filings are recorded with `outcome: "late"` so regulator audits can distinguish on-time from late-but-eventually filings. Operator-supplied `persist(record)` writes to a DB / SIEM / SOAR system; `onStage(event)` fires for synchronous routing. `status()` returns aggregate counts (open / closed / late-per-stage) for dashboards.
|
|
12
|
+
|
|
11
13
|
- **0.8.8** (2026-05-07) — `b.middleware.requireBoundKey` + `b.audit.rotateSigningKey` / `reSignAll` + `b.circuitBreaker` top-level surface + `b.htmlBalance.checkSafe` + permissions predicate-shape audit + FIPS 140-3 boundary docs + ESLint pin. **`b.middleware.requireBoundKey`** — Bearer-API-key middleware with three-axis binding: required scopes, bound-field equality (operator pulls values from headers / query / body via `getBoundField` getters; bound-fields registered on the key are checked with constant-time match), and peer-cert fingerprint allowlist (composes with v0.8.4's `b.crypto.hashCertFingerprint` / `isCertRevoked`). Operator-supplied async `resolver(apiKey)` returns the registered record `{ id, scopes, boundFields, peerCertFingerprints }` or `null` when revoked. Refusals carry structured reasons (`no-bearer-token`, `key-unknown-or-revoked`, `missing-scope`, `bound-field-missing`, `bound-field-mismatch`, `peer-cert-required`, `peer-cert-not-pinned`); audit chain captures the keyId + reason on every refusal. **`b.audit.rotateSigningKey`** — operator-callable rotation of the audit-signing keypair. Generates (or accepts BYO) a new keypair, copies the existing sealed file to a timestamped history path so historical signatures remain verifiable, re-seals with the operator's passphrase, and atomic-swaps the in-memory keys. Companion `reSignAll(iter)` walks an operator-supplied async iterable of `{ payload, signature, oldPublicKeyPem }` and re-signs each entry with the new key — the audit module's checkpoint store calls this to re-stamp historical checkpoints after a rotation. **`b.circuitBreaker`** — top-level re-export of `b.retry.CircuitBreaker` so operators discover it alongside `b.retry`; same state machine, same `wrap()` API, ergonomic `create(opts)` factory. **`b.htmlBalance.checkSafe(html, opts)`** — combines structural `balance()` check with a `b.guardHtml.gate` security pass under the same `{ profile, posture }` opt shape used by `b.fileUpload({ contentSafety })` / `b.staticServe({ contentSafety })`. **`b.permissions.policy(scope, predicate)`** — emits `permissions.policy_predicate_shape_warning` audit on register-time when the predicate's `.length < 2` (operators commonly forget the `context` argument and ship a predicate that's always-true on the actor parameter). **`SECURITY.md`** — new "FIPS 140-3 cryptographic boundary" section explaining the dual boundary (Node.js OpenSSL FIPS provider for classical primitives, vendored noble-* implementations for PQ algorithms — the latter implement FIPS-published algorithms but the *implementations themselves* are not CMVP-validated). Operator path for FIPS-mandated environments documented. **CI** — eslint pinned to `10.3.0` across `ci.yml` / `npm-publish.yml` / `release-container.yml`; `eslint@latest` was silently letting new rule additions break the publish gate on releases that had passed the day before. The pin moves on operator-confirmed bumps.
|
|
12
14
|
|
|
13
15
|
- **0.8.7** (2026-05-06) — `b.auth.accessLock` — three-mode access-lock primitive for stop-the-world / read-only / role-restricted operator interventions. `"open"` is normal operation; `"read-only"` refuses non-idempotent methods (POST/PUT/PATCH/DELETE) with 503 + Retry-After while letting GET/HEAD/OPTIONS pass; `"locked"` refuses everything except an operator-supplied `passthroughPaths` allowlist (status / health / unlock endpoint). Operators flip modes during incident response, schema-migration windows, or break-glass review via `lock.set("locked", { actor, reason })`; the transition emits `auth.access_lock.mode_changed` audit + metric. `unlockRoles: ["sre", ...]` lets a privileged role bypass all three modes via `getRole(req)` so a break-glass operator can always reach the unlock endpoint to flip back. The boot-time mode emits `auth.access_lock.boot` so the audit chain captures the deploy posture.
|
package/index.js
CHANGED
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* b.incident.report — generic 3-stage incident-reporting primitive.
|
|
4
|
+
*
|
|
5
|
+
* The three stages mirror the deadline pattern that recurs across
|
|
6
|
+
* regulatory regimes (GDPR Article 33 / NIS2 Article 23 / DORA
|
|
7
|
+
* Article 19 / CRA Article 14 / HIPAA Breach Notification Rule):
|
|
8
|
+
*
|
|
9
|
+
* 1. Initial / early-warning notification — within 24 hours of
|
|
10
|
+
* detection. Operators report what they know: scope, suspected
|
|
11
|
+
* cause, immediate-mitigation plan.
|
|
12
|
+
* 2. Intermediate / status update — within 72 hours of detection.
|
|
13
|
+
* Updated impact assessment, root-cause analysis progress,
|
|
14
|
+
* operator's response posture.
|
|
15
|
+
* 3. Final report — within 30 days of detection (or per-regime
|
|
16
|
+
* deadline). Full incident narrative, affected-data-subject
|
|
17
|
+
* count, remediation, lessons learned.
|
|
18
|
+
*
|
|
19
|
+
* var ir = b.incident.report.create({
|
|
20
|
+
* audit: b.audit,
|
|
21
|
+
* persist: async function (record) { await db.run("INSERT ..."); },
|
|
22
|
+
* onStage: function (event) {
|
|
23
|
+
* // event = { incidentId, stage: "initial"|"intermediate"|"final",
|
|
24
|
+
* // dueBy, regime, fields }
|
|
25
|
+
* },
|
|
26
|
+
* deadlines: { // operator-overridable per regime
|
|
27
|
+
* initial: C.TIME.hours(24),
|
|
28
|
+
* intermediate: C.TIME.hours(72),
|
|
29
|
+
* final: C.TIME.days(30),
|
|
30
|
+
* },
|
|
31
|
+
* });
|
|
32
|
+
* var incident = await ir.open({
|
|
33
|
+
* regime: "gdpr", // identifies the regulatory regime
|
|
34
|
+
* detectedAt: Date.now(),
|
|
35
|
+
* scope: "data-confidentiality-breach",
|
|
36
|
+
* summary: "...",
|
|
37
|
+
* impact: { dataSubjects: 1200, categories: ["pii", "phi"] },
|
|
38
|
+
* });
|
|
39
|
+
* await ir.recordInitial(incident.id, { ... });
|
|
40
|
+
* await ir.recordIntermediate(incident.id, { ... });
|
|
41
|
+
* await ir.recordFinal(incident.id, { ... });
|
|
42
|
+
*
|
|
43
|
+
* Each stage records a tamper-evident audit event with the regime,
|
|
44
|
+
* incident ID, stage, due-by timestamp, and operator-supplied
|
|
45
|
+
* payload. The `persist` hook writes to the operator's incident
|
|
46
|
+
* registry (DB / SIEM / SOAR system); audits give regulator-friendly
|
|
47
|
+
* proof-of-process when the primitive is queried later.
|
|
48
|
+
*
|
|
49
|
+
* Late-stage detection (filing past the due-by) is recorded with a
|
|
50
|
+
* `lateBy` field on the audit metadata and `outcome: "late"`, so
|
|
51
|
+
* regulator audits can distinguish "filed on time" from "filed late
|
|
52
|
+
* but eventually". Refusing to record at all would lose the data;
|
|
53
|
+
* the framework's posture is "always record, always flag late".
|
|
54
|
+
*/
|
|
55
|
+
|
|
56
|
+
var C = require("./constants");
|
|
57
|
+
var defineClass = require("./framework-error").defineClass;
|
|
58
|
+
var lazyRequire = require("./lazy-require");
|
|
59
|
+
var validateOpts = require("./validate-opts");
|
|
60
|
+
|
|
61
|
+
var audit = lazyRequire(function () { return require("./audit"); });
|
|
62
|
+
var observability = lazyRequire(function () { return require("./observability"); });
|
|
63
|
+
|
|
64
|
+
var IncidentReportError = defineClass("IncidentReportError", { alwaysPermanent: true });
|
|
65
|
+
|
|
66
|
+
var DEFAULT_DEADLINES = Object.freeze({
|
|
67
|
+
initial: C.TIME.hours(24),
|
|
68
|
+
intermediate: C.TIME.hours(72),
|
|
69
|
+
final: C.TIME.days(30),
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
var VALID_STAGES = Object.freeze({ initial: 1, intermediate: 1, final: 1 });
|
|
73
|
+
|
|
74
|
+
// Regime defaults — operators select these via opts.regime so the
|
|
75
|
+
// per-regime deadline shape is the framework default, not something
|
|
76
|
+
// the operator has to redefine each time. Operators with mixed
|
|
77
|
+
// regimes per incident pick the shortest deadline (the "first wall
|
|
78
|
+
// to hit" rule). Per-regime overrides go on opts.deadlines and
|
|
79
|
+
// override the regime defaults.
|
|
80
|
+
var REGIME_DEADLINES = Object.freeze({
|
|
81
|
+
// GDPR Article 33 §1: notify within 72 hours of awareness.
|
|
82
|
+
// No formal "initial" stage — the framework adds an internal
|
|
83
|
+
// 24-hour initial-warning checkpoint as a practical posture.
|
|
84
|
+
gdpr: Object.freeze({
|
|
85
|
+
initial: C.TIME.hours(24),
|
|
86
|
+
intermediate: C.TIME.hours(72),
|
|
87
|
+
final: C.TIME.days(30),
|
|
88
|
+
}),
|
|
89
|
+
// NIS2 Directive Article 23 §4: early warning within 24h, full
|
|
90
|
+
// notification within 72h, final report within 1 month.
|
|
91
|
+
nis2: Object.freeze({
|
|
92
|
+
initial: C.TIME.hours(24),
|
|
93
|
+
intermediate: C.TIME.hours(72),
|
|
94
|
+
final: C.TIME.days(30),
|
|
95
|
+
}),
|
|
96
|
+
// DORA (EU Digital Operational Resilience Act) Article 19: initial
|
|
97
|
+
// within 4h of classification, intermediate within 24h, final
|
|
98
|
+
// within 1 month.
|
|
99
|
+
dora: Object.freeze({
|
|
100
|
+
initial: C.TIME.hours(4),
|
|
101
|
+
intermediate: C.TIME.hours(24),
|
|
102
|
+
final: C.TIME.days(30),
|
|
103
|
+
}),
|
|
104
|
+
// CRA (EU Cyber Resilience Act) Article 14: early warning within
|
|
105
|
+
// 24h, vulnerability/incident notification within 72h, final
|
|
106
|
+
// report within 14 days.
|
|
107
|
+
cra: Object.freeze({
|
|
108
|
+
initial: C.TIME.hours(24),
|
|
109
|
+
intermediate: C.TIME.hours(72),
|
|
110
|
+
final: C.TIME.days(14),
|
|
111
|
+
}),
|
|
112
|
+
// HIPAA Breach Notification Rule (45 CFR §164.404 etc.): notify
|
|
113
|
+
// affected individuals within 60 days; HHS within 60 days for
|
|
114
|
+
// breaches affecting 500+ individuals (annually otherwise). The
|
|
115
|
+
// initial / intermediate stages have no statutory deadline; we
|
|
116
|
+
// adopt the EU 24/72-hour internal checkpoints as good operator
|
|
117
|
+
// practice.
|
|
118
|
+
hipaa: Object.freeze({
|
|
119
|
+
initial: C.TIME.hours(24),
|
|
120
|
+
intermediate: C.TIME.hours(72),
|
|
121
|
+
final: C.TIME.days(60),
|
|
122
|
+
}),
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
function _resolveDeadlines(regime, override) {
|
|
126
|
+
var base = (regime && REGIME_DEADLINES[regime]) || DEFAULT_DEADLINES;
|
|
127
|
+
if (!override || typeof override !== "object") return base;
|
|
128
|
+
return Object.freeze({
|
|
129
|
+
initial: typeof override.initial === "number" ? override.initial : base.initial,
|
|
130
|
+
intermediate: typeof override.intermediate === "number" ? override.intermediate : base.intermediate,
|
|
131
|
+
final: typeof override.final === "number" ? override.final : base.final,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function create(opts) {
|
|
136
|
+
opts = opts || {};
|
|
137
|
+
validateOpts(opts, [
|
|
138
|
+
"audit", "persist", "onStage", "deadlines", "now",
|
|
139
|
+
], "incident.report");
|
|
140
|
+
|
|
141
|
+
var auditOn = opts.audit !== false;
|
|
142
|
+
var persist = typeof opts.persist === "function" ? opts.persist : null;
|
|
143
|
+
var onStage = typeof opts.onStage === "function" ? opts.onStage : null;
|
|
144
|
+
var deadlinesOverride = opts.deadlines || null;
|
|
145
|
+
var now = typeof opts.now === "function" ? opts.now : function () { return Date.now(); };
|
|
146
|
+
|
|
147
|
+
// In-memory registry — operators wire `persist` for durable
|
|
148
|
+
// storage. The in-memory map is for unit tests + small operator
|
|
149
|
+
// deployments that don't yet wire a DB-backed registry.
|
|
150
|
+
var incidents = new Map();
|
|
151
|
+
var seq = 0;
|
|
152
|
+
|
|
153
|
+
function _emitAudit(action, outcome, metadata) {
|
|
154
|
+
if (!auditOn) return;
|
|
155
|
+
try {
|
|
156
|
+
audit().safeEmit({
|
|
157
|
+
action: "incident.report." + action,
|
|
158
|
+
outcome: outcome,
|
|
159
|
+
metadata: metadata || {},
|
|
160
|
+
});
|
|
161
|
+
} catch (_e) { /* drop-silent */ }
|
|
162
|
+
}
|
|
163
|
+
function _emitMetric(verb, n, labels) {
|
|
164
|
+
try { observability().safeEvent("incident.report." + verb, n || 1, labels || {}); }
|
|
165
|
+
catch (_e) { /* drop-silent */ }
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function _genIncidentId(regime, detectedAt) {
|
|
169
|
+
seq += 1;
|
|
170
|
+
var ts = new Date(detectedAt).toISOString().replace(/[:.]/g, "-");
|
|
171
|
+
return "incident-" + (regime || "generic") + "-" + ts + "-" + seq;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async function open(spec) {
|
|
175
|
+
if (!spec || typeof spec !== "object") {
|
|
176
|
+
throw new IncidentReportError("incident-report/bad-spec",
|
|
177
|
+
"incident.report.open: spec must be an object with { regime, detectedAt, scope, summary, impact }");
|
|
178
|
+
}
|
|
179
|
+
if (typeof spec.regime !== "string" || spec.regime.length === 0) {
|
|
180
|
+
throw new IncidentReportError("incident-report/bad-regime",
|
|
181
|
+
"incident.report.open: spec.regime must be a non-empty string (gdpr / nis2 / dora / cra / hipaa or operator-defined)");
|
|
182
|
+
}
|
|
183
|
+
if (typeof spec.detectedAt !== "number" || !isFinite(spec.detectedAt)) {
|
|
184
|
+
throw new IncidentReportError("incident-report/bad-detected-at",
|
|
185
|
+
"incident.report.open: spec.detectedAt must be a finite Unix-ms timestamp");
|
|
186
|
+
}
|
|
187
|
+
var deadlines = _resolveDeadlines(spec.regime, deadlinesOverride);
|
|
188
|
+
var id = _genIncidentId(spec.regime, spec.detectedAt);
|
|
189
|
+
var record = {
|
|
190
|
+
id: id,
|
|
191
|
+
regime: spec.regime,
|
|
192
|
+
detectedAt: spec.detectedAt,
|
|
193
|
+
scope: spec.scope || null,
|
|
194
|
+
summary: spec.summary || null,
|
|
195
|
+
impact: spec.impact || null,
|
|
196
|
+
deadlines: deadlines,
|
|
197
|
+
dueBy: {
|
|
198
|
+
initial: spec.detectedAt + deadlines.initial,
|
|
199
|
+
intermediate: spec.detectedAt + deadlines.intermediate,
|
|
200
|
+
final: spec.detectedAt + deadlines.final,
|
|
201
|
+
},
|
|
202
|
+
stages: {}, // populated by recordInitial / recordIntermediate / recordFinal
|
|
203
|
+
openedAt: now(),
|
|
204
|
+
closedAt: null,
|
|
205
|
+
};
|
|
206
|
+
incidents.set(id, record);
|
|
207
|
+
_emitAudit("opened", "success", {
|
|
208
|
+
incidentId: id, regime: spec.regime, detectedAt: spec.detectedAt,
|
|
209
|
+
dueByInitial: record.dueBy.initial,
|
|
210
|
+
dueByIntermediate: record.dueBy.intermediate,
|
|
211
|
+
dueByFinal: record.dueBy.final,
|
|
212
|
+
});
|
|
213
|
+
_emitMetric("opened", 1, { regime: spec.regime });
|
|
214
|
+
if (persist) {
|
|
215
|
+
try { await persist(record); }
|
|
216
|
+
catch (e) { _emitAudit("persist_failed", "failure", { incidentId: id, error: (e && e.message) || String(e) }); }
|
|
217
|
+
}
|
|
218
|
+
return record;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
async function _recordStage(incidentId, stage, payload) {
|
|
222
|
+
if (!VALID_STAGES[stage]) {
|
|
223
|
+
throw new IncidentReportError("incident-report/bad-stage",
|
|
224
|
+
"incident.report._recordStage: stage must be one of " + Object.keys(VALID_STAGES).join(", "));
|
|
225
|
+
}
|
|
226
|
+
var rec = incidents.get(incidentId);
|
|
227
|
+
if (!rec) {
|
|
228
|
+
throw new IncidentReportError("incident-report/unknown-incident",
|
|
229
|
+
"incident.report: no incident with id '" + incidentId + "'");
|
|
230
|
+
}
|
|
231
|
+
if (rec.stages[stage]) {
|
|
232
|
+
throw new IncidentReportError("incident-report/stage-already-filed",
|
|
233
|
+
"incident.report: incident '" + incidentId + "' already has a '" + stage + "' stage filing");
|
|
234
|
+
}
|
|
235
|
+
var nowMs = now();
|
|
236
|
+
var dueBy = rec.dueBy[stage];
|
|
237
|
+
var late = nowMs > dueBy;
|
|
238
|
+
var lateBy = late ? (nowMs - dueBy) : 0;
|
|
239
|
+
rec.stages[stage] = {
|
|
240
|
+
filedAt: nowMs,
|
|
241
|
+
dueBy: dueBy,
|
|
242
|
+
late: late,
|
|
243
|
+
lateBy: lateBy,
|
|
244
|
+
payload: payload || {},
|
|
245
|
+
};
|
|
246
|
+
if (stage === "final") rec.closedAt = nowMs;
|
|
247
|
+
|
|
248
|
+
_emitAudit("stage_recorded", late ? "late" : "success", {
|
|
249
|
+
incidentId: incidentId, regime: rec.regime, stage: stage,
|
|
250
|
+
dueBy: dueBy, filedAt: nowMs, late: late, lateBy: lateBy,
|
|
251
|
+
});
|
|
252
|
+
_emitMetric("stage_recorded", 1, { regime: rec.regime, stage: stage, late: String(late) });
|
|
253
|
+
if (onStage) {
|
|
254
|
+
try { onStage({ incidentId: incidentId, stage: stage, dueBy: dueBy, late: late, regime: rec.regime, fields: payload }); }
|
|
255
|
+
catch (_e) { /* drop-silent — operator hook */ }
|
|
256
|
+
}
|
|
257
|
+
if (persist) {
|
|
258
|
+
try { await persist(rec); }
|
|
259
|
+
catch (e) { _emitAudit("persist_failed", "failure", { incidentId: incidentId, stage: stage, error: (e && e.message) || String(e) }); }
|
|
260
|
+
}
|
|
261
|
+
return rec;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function recordInitial(incidentId, payload) { return _recordStage(incidentId, "initial", payload); }
|
|
265
|
+
function recordIntermediate(incidentId, payload) { return _recordStage(incidentId, "intermediate", payload); }
|
|
266
|
+
function recordFinal(incidentId, payload) { return _recordStage(incidentId, "final", payload); }
|
|
267
|
+
|
|
268
|
+
function get(incidentId) { return incidents.get(incidentId) || null; }
|
|
269
|
+
function list() {
|
|
270
|
+
var out = [];
|
|
271
|
+
incidents.forEach(function (rec) { out.push(rec); });
|
|
272
|
+
return out;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Operator-facing summary for dashboards / regulator-prep — counts
|
|
276
|
+
// open / late / closed across all tracked regimes.
|
|
277
|
+
function status() {
|
|
278
|
+
var nowMs = now();
|
|
279
|
+
var summary = {
|
|
280
|
+
total: incidents.size,
|
|
281
|
+
open: 0,
|
|
282
|
+
closed: 0,
|
|
283
|
+
late: { initial: 0, intermediate: 0, final: 0 },
|
|
284
|
+
};
|
|
285
|
+
incidents.forEach(function (rec) {
|
|
286
|
+
if (rec.closedAt) summary.closed += 1; else summary.open += 1;
|
|
287
|
+
["initial", "intermediate", "final"].forEach(function (s) {
|
|
288
|
+
if (!rec.stages[s] && nowMs > rec.dueBy[s]) summary.late[s] += 1;
|
|
289
|
+
else if (rec.stages[s] && rec.stages[s].late) summary.late[s] += 1;
|
|
290
|
+
});
|
|
291
|
+
});
|
|
292
|
+
return summary;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
return {
|
|
296
|
+
open: open,
|
|
297
|
+
recordInitial: recordInitial,
|
|
298
|
+
recordIntermediate: recordIntermediate,
|
|
299
|
+
recordFinal: recordFinal,
|
|
300
|
+
get: get,
|
|
301
|
+
list: list,
|
|
302
|
+
status: status,
|
|
303
|
+
REGIME_DEADLINES: REGIME_DEADLINES,
|
|
304
|
+
DEFAULT_DEADLINES: DEFAULT_DEADLINES,
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
module.exports = {
|
|
309
|
+
create: create,
|
|
310
|
+
IncidentReportError: IncidentReportError,
|
|
311
|
+
REGIME_DEADLINES: REGIME_DEADLINES,
|
|
312
|
+
DEFAULT_DEADLINES: DEFAULT_DEADLINES,
|
|
313
|
+
VALID_STAGES: Object.keys(VALID_STAGES),
|
|
314
|
+
};
|
package/package.json
CHANGED
package/sbom.cyclonedx.json
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
"$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json",
|
|
3
3
|
"bomFormat": "CycloneDX",
|
|
4
4
|
"specVersion": "1.5",
|
|
5
|
-
"serialNumber": "urn:uuid:
|
|
5
|
+
"serialNumber": "urn:uuid:b2addb57-b40a-4a44-9c34-7f7bd7d741c3",
|
|
6
6
|
"version": 1,
|
|
7
7
|
"metadata": {
|
|
8
|
-
"timestamp": "2026-05-
|
|
8
|
+
"timestamp": "2026-05-07T02:18:24.599Z",
|
|
9
9
|
"lifecycles": [
|
|
10
10
|
{
|
|
11
11
|
"phase": "build"
|
|
@@ -19,14 +19,14 @@
|
|
|
19
19
|
}
|
|
20
20
|
],
|
|
21
21
|
"component": {
|
|
22
|
-
"bom-ref": "@blamejs/core@0.8.
|
|
22
|
+
"bom-ref": "@blamejs/core@0.8.9",
|
|
23
23
|
"type": "library",
|
|
24
24
|
"name": "blamejs",
|
|
25
|
-
"version": "0.8.
|
|
25
|
+
"version": "0.8.9",
|
|
26
26
|
"scope": "required",
|
|
27
27
|
"author": "blamejs contributors",
|
|
28
28
|
"description": "The Node framework that owns its stack.",
|
|
29
|
-
"purl": "pkg:npm/%40blamejs/core@0.8.
|
|
29
|
+
"purl": "pkg:npm/%40blamejs/core@0.8.9",
|
|
30
30
|
"properties": [],
|
|
31
31
|
"externalReferences": [
|
|
32
32
|
{
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"components": [],
|
|
55
55
|
"dependencies": [
|
|
56
56
|
{
|
|
57
|
-
"ref": "@blamejs/core@0.8.
|
|
57
|
+
"ref": "@blamejs/core@0.8.9",
|
|
58
58
|
"dependsOn": []
|
|
59
59
|
}
|
|
60
60
|
]
|