@blamejs/exceptd-skills 0.13.11 → 0.13.12
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 +12 -0
- package/data/_indexes/_meta.json +2 -2
- package/manifest.json +44 -44
- package/package.json +1 -1
- package/sbom.cdx.json +720 -12
- package/scripts/check-sbom-currency.js +35 -6
- package/scripts/refresh-sbom.js +27 -1
|
@@ -162,21 +162,50 @@ function checkSbomCurrency(root) {
|
|
|
162
162
|
);
|
|
163
163
|
continue;
|
|
164
164
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
)
|
|
165
|
+
// v0.13.12: verify SHA-256 AND SHA3-512 when present. SHA-256 is
|
|
166
|
+
// the universal-tool contract (CycloneDX 1.6 default, Anchore /
|
|
167
|
+
// Trivy / Dependency-Track / GitHub Dependency Graph). SHA3-512
|
|
168
|
+
// is the SHA-3 family hedge, matching the existing key-fingerprint
|
|
169
|
+
// pattern (lib/verify.js). Both must agree with the live bytes;
|
|
170
|
+
// a mismatch on either fires the same drift error. A missing
|
|
171
|
+
// SHA-256 is a hard error (the universal contract is the floor);
|
|
172
|
+
// a missing SHA3-512 surfaces as a downgrade-attack warning so an
|
|
173
|
+
// operator who intentionally strips the second hash from an
|
|
174
|
+
// SBOM (post-quantum posture relaxation) sees it in the gate
|
|
175
|
+
// output, not in the JSON downstream.
|
|
176
|
+
const sha256Entry = (comp.hashes || []).find((h) => h && h.alg === "SHA-256");
|
|
177
|
+
const sha3Entry = (comp.hashes || []).find((h) => h && h.alg === "SHA3-512");
|
|
168
178
|
if (!sha256Entry || typeof sha256Entry.content !== "string") {
|
|
169
179
|
errors.push(
|
|
170
180
|
`SBOM file component "${relPath}" lacks a SHA-256 hash entry`
|
|
171
181
|
);
|
|
172
182
|
continue;
|
|
173
183
|
}
|
|
174
|
-
const
|
|
175
|
-
|
|
184
|
+
const fileBytes = fs.readFileSync(absPath);
|
|
185
|
+
const liveSha256 = crypto.createHash("sha256").update(fileBytes).digest("hex");
|
|
186
|
+
if (liveSha256 !== sha256Entry.content) {
|
|
176
187
|
errors.push(
|
|
177
|
-
`SBOM file component "${relPath}"
|
|
188
|
+
`SBOM file component "${relPath}" SHA-256 drift: recorded ${sha256Entry.content.slice(0, 12)}…, live ${liveSha256.slice(0, 12)}… — re-sign skills (\`node $(exceptd path)/lib/sign.js sign-all\` from a contributor checkout) and then \`npm run refresh-sbom\`, in that order (sbom must regenerate AFTER the final sign).`,
|
|
178
189
|
);
|
|
179
190
|
}
|
|
191
|
+
// Codex P1 on PR #52: the dual-hash contract requires SHA3-512 to be
|
|
192
|
+
// PRESENT, not just verified when present. An attacker (or a careless
|
|
193
|
+
// sbom-generator regression) that strips the SHA3-512 column would
|
|
194
|
+
// silently pass the gate under an `if (sha3Entry)` guard, defeating
|
|
195
|
+
// the downgrade defense the dual-hash design is supposed to provide.
|
|
196
|
+
// Refuse absence as a hard error.
|
|
197
|
+
if (!sha3Entry || typeof sha3Entry.content !== "string") {
|
|
198
|
+
errors.push(
|
|
199
|
+
`SBOM file component "${relPath}" lacks a SHA3-512 hash entry — the dual-hash contract (SHA-256 + SHA3-512) requires both algorithms on every file: component. Regenerate via \`npm run refresh-sbom\` (v0.13.12+).`
|
|
200
|
+
);
|
|
201
|
+
} else {
|
|
202
|
+
const liveSha3 = crypto.createHash("sha3-512").update(fileBytes).digest("hex");
|
|
203
|
+
if (liveSha3 !== sha3Entry.content) {
|
|
204
|
+
errors.push(
|
|
205
|
+
`SBOM file component "${relPath}" SHA3-512 drift: recorded ${sha3Entry.content.slice(0, 12)}…, live ${liveSha3.slice(0, 12)}… — same remediation as SHA-256 drift (re-sign then refresh-sbom).`,
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
180
209
|
fileComponentsChecked++;
|
|
181
210
|
}
|
|
182
211
|
|
package/scripts/refresh-sbom.js
CHANGED
|
@@ -185,6 +185,29 @@ function sha256File(absPath) {
|
|
|
185
185
|
.digest('hex');
|
|
186
186
|
}
|
|
187
187
|
|
|
188
|
+
// v0.13.12 — emit SHA3-512 alongside SHA-256 for every file: component.
|
|
189
|
+
// CycloneDX 1.6 supports multiple hash entries per component. Rationale
|
|
190
|
+
// mirrors the existing key-fingerprint emission in lib/verify.js:
|
|
191
|
+
//
|
|
192
|
+
// - SHA-256 stays as the universal-tool contract (Anchore / Trivy /
|
|
193
|
+
// Dependency-Track / GitHub Dependency Graph all parse it).
|
|
194
|
+
// - SHA3-512 is the SHA-3 family (Keccak / sponge), different
|
|
195
|
+
// mathematical foundation. Hedges against future SHA-2 weaknesses
|
|
196
|
+
// and aligns with the project's PQ posture (ML-KEM / ML-DSA both
|
|
197
|
+
// internally hash with SHA-3).
|
|
198
|
+
//
|
|
199
|
+
// check-sbom-currency.js verifies BOTH when present and refuses if a
|
|
200
|
+
// SHA3-512 entry is recorded but its content drifts from the live
|
|
201
|
+
// bytes — so a downgrade attack that drops SHA3-512 from the recorded
|
|
202
|
+
// SBOM (leaving only SHA-256) is observable as a missing-hash error,
|
|
203
|
+
// not a silent acceptance.
|
|
204
|
+
function sha3_512File(absPath) {
|
|
205
|
+
return crypto
|
|
206
|
+
.createHash('sha3-512')
|
|
207
|
+
.update(fs.readFileSync(absPath))
|
|
208
|
+
.digest('hex');
|
|
209
|
+
}
|
|
210
|
+
|
|
188
211
|
function fileComponents(allowlist) {
|
|
189
212
|
const rels = expandAllowlist(allowlist);
|
|
190
213
|
const out = [];
|
|
@@ -194,7 +217,10 @@ function fileComponents(allowlist) {
|
|
|
194
217
|
'bom-ref': `file:${rel}`,
|
|
195
218
|
type: 'file',
|
|
196
219
|
name: rel,
|
|
197
|
-
hashes: [
|
|
220
|
+
hashes: [
|
|
221
|
+
{ alg: 'SHA-256', content: sha256File(abs) },
|
|
222
|
+
{ alg: 'SHA3-512', content: sha3_512File(abs) },
|
|
223
|
+
],
|
|
198
224
|
});
|
|
199
225
|
}
|
|
200
226
|
return out;
|