diff --git a/.github/workflows/publish-npm.yml b/.github/workflows/publish-npm.yml index 8ea02da..c6eb729 100644 --- a/.github/workflows/publish-npm.yml +++ b/.github/workflows/publish-npm.yml @@ -6,7 +6,7 @@ on: workflow_dispatch: inputs: tag: - description: Existing attested GitHub release tag, for example v0.2.13 + description: Existing attested GitHub release tag, for example v0.2.14 required: true type: string confirm_publish: diff --git a/CHANGELOG.md b/CHANGELOG.md index b1a8623..7f84dca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,17 @@ CodeTruss CLI follows semantic versioning. Release artifacts and their SHA-256 checksums are published at . -## 0.2.13 — 2026-07-14 +## 0.2.14 — 2026-07-14 + +- Give the deterministic CycloneDX SBOM a canonical UUIDv5 serial number and + enforce that identity in release verification, making the SBOM directly + compatible with GitHub artifact attestations without weakening reproducible + package bytes. +- Retain the complete v0.2.13 Windows long-path and installer hardening in a new + immutable candidate after GitHub rejected the prior candidate's otherwise + valid SBOM because it did not carry a top-level serial number. + +## 0.2.13 — 2026-07-14 (unpublished) - Enable Git for Windows long-path support command-locally for every CodeTruss-owned Git process and generated hook entry point. Exact private @@ -12,6 +22,9 @@ checksums are published at . -## 0.2.13 — 2026-07-14 +## 0.2.14 — 2026-07-14 + +- Give the deterministic CycloneDX SBOM a canonical UUIDv5 serial number and + enforce that identity in release verification, making the SBOM directly + compatible with GitHub artifact attestations without weakening reproducible + package bytes. +- Retain the complete v0.2.13 Windows long-path and installer hardening in a new + immutable candidate after GitHub rejected the prior candidate's otherwise + valid SBOM because it did not carry a top-level serial number. + +## 0.2.13 — 2026-07-14 (unpublished) - Enable Git for Windows long-path support command-locally for every CodeTruss-owned Git process and generated hook entry point. Exact private @@ -12,6 +22,9 @@ checksums are published at verify(), /not the canonical checksum/) + + for (const file of PACKAGE_ARCHIVE_FILES) { + await copyFile(join(packageDir, file.source), join(tamperedPackageDir, file.source)) + } + const noncanonicalSbom = JSON.parse(await readFile(join(tamperedPackageDir, 'SBOM.cdx.json'), 'utf8')) + delete noncanonicalSbom.serialNumber + await writeFile(join(tamperedPackageDir, 'SBOM.cdx.json'), `${JSON.stringify(noncanonicalSbom, null, 2)}\n`) + await writeRelease(tamperedPackageDir) + await assert.rejects( + () => verify(tamperedPackageDir), + /does not have the canonical CycloneDX identity/, + ) } finally { await rm(scratch, { recursive: true, force: true }) } diff --git a/packages/cli/scripts/verify-release.mjs b/packages/cli/scripts/verify-release.mjs index d7a4bad..31d99fb 100644 --- a/packages/cli/scripts/verify-release.mjs +++ b/packages/cli/scripts/verify-release.mjs @@ -3,6 +3,7 @@ import { readFile } from 'node:fs/promises' import { dirname, join, resolve } from 'node:path' import { fileURLToPath, pathToFileURL } from 'node:url' import { assertReleasePackagePolicy } from './release-package-policy.mjs' +import { cycloneDxSerialNumber } from './generate-sbom.mjs' import { verifyDeterministicPackageArchive } from './verify-deterministic-package.mjs' const scriptPath = fileURLToPath(import.meta.url) @@ -94,6 +95,12 @@ export async function verifyRelease({ if (packagedSbom.metadata?.component?.name !== pkg.name || packagedSbom.metadata?.component?.version !== pkg.version) { throw new Error(`${sbomName} does not identify ${pkg.name}@${pkg.version}`) } + const expectedSerialNumber = cycloneDxSerialNumber(pkg.name, pkg.version) + if (packagedSbom.bomFormat !== 'CycloneDX' + || packagedSbom.specVersion !== '1.6' + || packagedSbom.serialNumber !== expectedSerialNumber) { + throw new Error(`${sbomName} does not have the canonical CycloneDX identity ${expectedSerialNumber}`) + } return { version: pkg.version, sha256: archiveSha256 } } diff --git a/public/downloads/codetruss-cli-0.2.14.sbom.cdx.json b/public/downloads/codetruss-cli-0.2.14.sbom.cdx.json new file mode 100644 index 0000000..c2df957 --- /dev/null +++ b/public/downloads/codetruss-cli-0.2.14.sbom.cdx.json @@ -0,0 +1,170 @@ +{ + "$schema": "https://cyclonedx.org/schema/bom-1.6.schema.json", + "bomFormat": "CycloneDX", + "serialNumber": "urn:uuid:ba46f3f9-7de6-5197-8fe5-af77d139712c", + "specVersion": "1.6", + "version": 1, + "metadata": { + "component": { + "type": "application", + "bom-ref": "pkg:npm/%40codetruss/cli@0.2.14", + "name": "@codetruss/cli", + "version": "0.2.14", + "description": "Local-first scope, quality, and verification receipts for coding agents", + "licenses": [ + { + "license": { + "name": "CodeTruss CLI Proprietary License" + } + } + ], + "purl": "pkg:npm/%40codetruss/cli@0.2.14" + }, + "properties": [ + { + "name": "codetruss:distribution", + "value": "single-file JavaScript bundle" + }, + { + "name": "codetruss:runtimeDependencies", + "value": "0" + } + ] + }, + "components": [ + { + "type": "library", + "bom-ref": "pkg:npm/%40codetruss/analyzer-engine@0.1.0", + "name": "@codetruss/analyzer-engine", + "version": "0.1.0", + "licenses": [ + { + "license": { + "name": "CodeTruss CLI Proprietary License" + } + } + ], + "purl": "pkg:npm/%40codetruss/analyzer-engine@0.1.0", + "properties": [ + { + "name": "codetruss:bundled", + "value": "true" + } + ] + }, + { + "type": "library", + "bom-ref": "pkg:npm/balanced-match@4.0.4", + "name": "balanced-match", + "version": "4.0.4", + "licenses": [ + { + "license": { + "id": "MIT" + } + } + ], + "purl": "pkg:npm/balanced-match@4.0.4", + "properties": [ + { + "name": "codetruss:bundled", + "value": "true" + } + ] + }, + { + "type": "library", + "bom-ref": "pkg:npm/brace-expansion@5.0.7", + "name": "brace-expansion", + "version": "5.0.7", + "licenses": [ + { + "license": { + "id": "MIT" + } + } + ], + "purl": "pkg:npm/brace-expansion@5.0.7", + "properties": [ + { + "name": "codetruss:bundled", + "value": "true" + } + ] + }, + { + "type": "library", + "bom-ref": "pkg:npm/minimatch@10.2.5", + "name": "minimatch", + "version": "10.2.5", + "licenses": [ + { + "license": { + "id": "BlueOak-1.0.0" + } + } + ], + "purl": "pkg:npm/minimatch@10.2.5", + "properties": [ + { + "name": "codetruss:bundled", + "value": "true" + } + ] + }, + { + "type": "library", + "bom-ref": "pkg:npm/yaml@2.9.0", + "name": "yaml", + "version": "2.9.0", + "licenses": [ + { + "license": { + "id": "ISC" + } + } + ], + "purl": "pkg:npm/yaml@2.9.0", + "properties": [ + { + "name": "codetruss:bundled", + "value": "true" + } + ] + } + ], + "dependencies": [ + { + "ref": "pkg:npm/%40codetruss/analyzer-engine@0.1.0", + "dependsOn": [] + }, + { + "ref": "pkg:npm/%40codetruss/cli@0.2.14", + "dependsOn": [ + "pkg:npm/%40codetruss/analyzer-engine@0.1.0", + "pkg:npm/minimatch@10.2.5", + "pkg:npm/yaml@2.9.0" + ] + }, + { + "ref": "pkg:npm/balanced-match@4.0.4", + "dependsOn": [] + }, + { + "ref": "pkg:npm/brace-expansion@5.0.7", + "dependsOn": [ + "pkg:npm/balanced-match@4.0.4" + ] + }, + { + "ref": "pkg:npm/minimatch@10.2.5", + "dependsOn": [ + "pkg:npm/brace-expansion@5.0.7" + ] + }, + { + "ref": "pkg:npm/yaml@2.9.0", + "dependsOn": [] + } + ] +} diff --git a/public/downloads/codetruss-cli-0.2.14.tgz b/public/downloads/codetruss-cli-0.2.14.tgz new file mode 100644 index 0000000..0fdafff Binary files /dev/null and b/public/downloads/codetruss-cli-0.2.14.tgz differ diff --git a/public/downloads/codetruss-cli-0.2.14.tgz.sha256 b/public/downloads/codetruss-cli-0.2.14.tgz.sha256 new file mode 100644 index 0000000..04f215f --- /dev/null +++ b/public/downloads/codetruss-cli-0.2.14.tgz.sha256 @@ -0,0 +1 @@ +7fd85c939a3224c0678f6541b52ba4dd7e40b68a8dfaffd5bd6c8e0c12425da1 codetruss-cli-0.2.14.tgz diff --git a/public/downloads/codetruss-cli-latest.json b/public/downloads/codetruss-cli-latest.json index 80c906c..0a39282 100644 --- a/public/downloads/codetruss-cli-latest.json +++ b/public/downloads/codetruss-cli-latest.json @@ -1,13 +1,13 @@ { "name": "@codetruss/cli", - "version": "0.2.13", - "url": "/downloads/codetruss-cli-0.2.13.tgz", + "version": "0.2.14", + "url": "/downloads/codetruss-cli-0.2.14.tgz", "latestUrl": "/downloads/codetruss-cli-latest.tgz", - "sha256": "2d13b2912c6e4ca0ab59880f39864f9a019487c191c1a6a41bbcbaf6d694b81c", - "sbomUrl": "/downloads/codetruss-cli-0.2.13.sbom.cdx.json", - "sbomSha256": "60d5126f46b295de8481d7a5265fba6b22852b8abb35940e69a250829ed459ed", + "sha256": "7fd85c939a3224c0678f6541b52ba4dd7e40b68a8dfaffd5bd6c8e0c12425da1", + "sbomUrl": "/downloads/codetruss-cli-0.2.14.sbom.cdx.json", + "sbomSha256": "3b42ec612bcf2340fd815127a39ab19e923794869cc912ad8358802ba6ee9438", "node": ">=20.9.0", "repository": "https://github.com/DeliriumPulse/codetruss-cli", - "releaseUrl": "https://github.com/DeliriumPulse/codetruss-cli/releases/tag/v0.2.13", - "attestationCommand": "gh attestation verify codetruss-cli-0.2.13.tgz --repo DeliriumPulse/codetruss-cli" + "releaseUrl": "https://github.com/DeliriumPulse/codetruss-cli/releases/tag/v0.2.14", + "attestationCommand": "gh attestation verify codetruss-cli-0.2.14.tgz --repo DeliriumPulse/codetruss-cli" } diff --git a/public/downloads/codetruss-cli-latest.sbom.cdx.json b/public/downloads/codetruss-cli-latest.sbom.cdx.json index 9c2123f..c2df957 100644 --- a/public/downloads/codetruss-cli-latest.sbom.cdx.json +++ b/public/downloads/codetruss-cli-latest.sbom.cdx.json @@ -1,14 +1,15 @@ { "$schema": "https://cyclonedx.org/schema/bom-1.6.schema.json", "bomFormat": "CycloneDX", + "serialNumber": "urn:uuid:ba46f3f9-7de6-5197-8fe5-af77d139712c", "specVersion": "1.6", "version": 1, "metadata": { "component": { "type": "application", - "bom-ref": "pkg:npm/%40codetruss/cli@0.2.13", + "bom-ref": "pkg:npm/%40codetruss/cli@0.2.14", "name": "@codetruss/cli", - "version": "0.2.13", + "version": "0.2.14", "description": "Local-first scope, quality, and verification receipts for coding agents", "licenses": [ { @@ -17,7 +18,7 @@ } } ], - "purl": "pkg:npm/%40codetruss/cli@0.2.13" + "purl": "pkg:npm/%40codetruss/cli@0.2.14" }, "properties": [ { @@ -138,7 +139,7 @@ "dependsOn": [] }, { - "ref": "pkg:npm/%40codetruss/cli@0.2.13", + "ref": "pkg:npm/%40codetruss/cli@0.2.14", "dependsOn": [ "pkg:npm/%40codetruss/analyzer-engine@0.1.0", "pkg:npm/minimatch@10.2.5", diff --git a/public/downloads/codetruss-cli-latest.tgz b/public/downloads/codetruss-cli-latest.tgz index d23a36d..0fdafff 100644 Binary files a/public/downloads/codetruss-cli-latest.tgz and b/public/downloads/codetruss-cli-latest.tgz differ diff --git a/public/downloads/codetruss-cli-latest.tgz.sha256 b/public/downloads/codetruss-cli-latest.tgz.sha256 index ff597d4..0318ea0 100644 --- a/public/downloads/codetruss-cli-latest.tgz.sha256 +++ b/public/downloads/codetruss-cli-latest.tgz.sha256 @@ -1 +1 @@ -2d13b2912c6e4ca0ab59880f39864f9a019487c191c1a6a41bbcbaf6d694b81c codetruss-cli-latest.tgz +7fd85c939a3224c0678f6541b52ba4dd7e40b68a8dfaffd5bd6c8e0c12425da1 codetruss-cli-latest.tgz diff --git a/release-reference.json b/release-reference.json index 37949ff..5c65006 100644 --- a/release-reference.json +++ b/release-reference.json @@ -1,8 +1,8 @@ { "schemaVersion": 1, - "version": "0.2.13", - "websiteArchive": "https://codetruss.com/downloads/codetruss-cli-0.2.13.tgz", - "archiveSha256": "2d13b2912c6e4ca0ab59880f39864f9a019487c191c1a6a41bbcbaf6d694b81c", - "sbomSha256": "60d5126f46b295de8481d7a5265fba6b22852b8abb35940e69a250829ed459ed", - "bundleSha256": "44a266479685971d8536baf7922f0e0f5eed932048440e532a0189682cb4f48d" + "version": "0.2.14", + "websiteArchive": "https://codetruss.com/downloads/codetruss-cli-0.2.14.tgz", + "archiveSha256": "7fd85c939a3224c0678f6541b52ba4dd7e40b68a8dfaffd5bd6c8e0c12425da1", + "sbomSha256": "3b42ec612bcf2340fd815127a39ab19e923794869cc912ad8358802ba6ee9438", + "bundleSha256": "30c1966c864a945244bff2160f4a0e1a157591e1ef612c52d771ce4374b9a4f4" }