From 88caef8f98f61f5795a0555a5ce63b58ee0603d3 Mon Sep 17 00:00:00 2001 From: hututuQQQ <245432843+hututuQQQ@users.noreply.github.com> Date: Thu, 6 Aug 2026 13:10:12 +0800 Subject: [PATCH 1/2] fix: decode runtime provenance for v0.1.9 --- apps/desktop/package.json | 2 +- apps/server/package.json | 2 +- apps/web/package.json | 2 +- packages/contracts/package.json | 2 +- pnpm-lock.yaml | 1 + scripts/desktop-release-evidence.test.ts | 70 +++++++++++++++++++----- scripts/desktop-release-evidence.ts | 66 +++++++++++++++++++++- 7 files changed, 126 insertions(+), 19 deletions(-) diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 5843c3a64..1e854b7de 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -1,6 +1,6 @@ { "name": "@t3tools/desktop", - "version": "0.1.8", + "version": "0.1.9", "private": true, "type": "module", "main": "dist-electron/main.cjs", diff --git a/apps/server/package.json b/apps/server/package.json index 7a6789e26..853353f08 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -1,6 +1,6 @@ { "name": "t3", - "version": "0.1.8", + "version": "0.1.9", "private": true, "license": "MIT", "bin": { diff --git a/apps/web/package.json b/apps/web/package.json index 301260620..f535e3a1c 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -1,6 +1,6 @@ { "name": "@t3tools/web", - "version": "0.1.8", + "version": "0.1.9", "private": true, "type": "module", "scripts": { diff --git a/packages/contracts/package.json b/packages/contracts/package.json index c0ccc1faf..e523a4bea 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -1,6 +1,6 @@ { "name": "@t3tools/contracts", - "version": "0.1.8", + "version": "0.1.9", "private": true, "files": [ "dist" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4981a25d3..d6883f01a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5961,6 +5961,7 @@ packages: crypto-js@4.2.0: resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==} + deprecated: Active development of CryptoJS has been discontinued. This library is no longer maintained. css-select@5.2.2: resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==} diff --git a/scripts/desktop-release-evidence.test.ts b/scripts/desktop-release-evidence.test.ts index 33e3c41b6..95bda893c 100644 --- a/scripts/desktop-release-evidence.test.ts +++ b/scripts/desktop-release-evidence.test.ts @@ -6,25 +6,39 @@ import * as NodePath from "node:path"; import { writeDesktopReleaseEvidence } from "./desktop-release-evidence.ts"; +function runtimeProvenance(version = "0.1.9", platform = "darwin", arch = "arm64"): string { + const statement = { + _type: "https://in-toto.io/Statement/v1", + predicateType: "https://slsa.dev/provenance/v1", + predicate: { + buildDefinition: { + buildType: "https://sigma-code.dev/build-types/portable-cli", + externalParameters: { version, targetPlatform: platform, targetArch: arch }, + }, + }, + }; + return `${JSON.stringify({ + payloadType: "application/vnd.in-toto+json", + payload: Buffer.from(`${JSON.stringify(statement, null, 2)}\n`, "utf8").toString("base64"), + signatures: [], + })}\n`; +} + it("writes checksum, runtime provenance, and unsigned preview status", async () => { const root = await NodeFSP.mkdtemp( NodePath.join(NodeOS.tmpdir(), "sigma-desktop-evidence-test-"), ); - const artifactPath = NodePath.join(root, "Sigma-Code-0.1.8-arm64.dmg"); + const artifactPath = NodePath.join(root, "Sigma-Code-0.1.9-arm64.dmg"); const runtimeProvenancePath = NodePath.join(root, "agent-cli-darwin-arm64.provenance.json"); await NodeFSP.writeFile(artifactPath, "dmg fixture", "utf8"); - await NodeFSP.writeFile( - runtimeProvenancePath, - '{"predicate":{"buildDefinition":{"externalParameters":{"version":"0.1.8","targetPlatform":"darwin","targetArch":"arm64"}}}}\n', - "utf8", - ); + await NodeFSP.writeFile(runtimeProvenancePath, runtimeProvenance(), "utf8"); const result = await writeDesktopReleaseEvidence({ artifactPath, runtimeProvenancePath, platform: "darwin", arch: "arm64", - version: "0.1.8", + version: "0.1.9", signingMode: "unsigned", codesign: "not-signed", gatekeeper: "not-applicable", @@ -42,14 +56,10 @@ it("rejects signed evidence unless every notarization gate passed", async () => const root = await NodeFSP.mkdtemp( NodePath.join(NodeOS.tmpdir(), "sigma-desktop-evidence-signed-test-"), ); - const artifactPath = NodePath.join(root, "Sigma-Code-0.1.8-arm64.dmg"); + const artifactPath = NodePath.join(root, "Sigma-Code-0.1.9-arm64.dmg"); const runtimeProvenancePath = NodePath.join(root, "runtime.json"); await NodeFSP.writeFile(artifactPath, "dmg fixture", "utf8"); - await NodeFSP.writeFile( - runtimeProvenancePath, - '{"predicate":{"buildDefinition":{"externalParameters":{"version":"0.1.8","targetPlatform":"darwin","targetArch":"arm64"}}}}\n', - "utf8", - ); + await NodeFSP.writeFile(runtimeProvenancePath, runtimeProvenance(), "utf8"); let failure: unknown; try { await writeDesktopReleaseEvidence({ @@ -57,7 +67,7 @@ it("rejects signed evidence unless every notarization gate passed", async () => runtimeProvenancePath, platform: "darwin", arch: "arm64", - version: "0.1.8", + version: "0.1.9", signingMode: "signed", codesign: "pass", gatekeeper: "pass", @@ -69,3 +79,35 @@ it("rejects signed evidence unless every notarization gate passed", async () => assert.instanceOf(failure, Error); assert.match((failure as Error).message, /stapler=pass/u); }); + +it("rejects a raw provenance statement instead of treating it as a DSSE envelope", async () => { + const root = await NodeFSP.mkdtemp( + NodePath.join(NodeOS.tmpdir(), "sigma-desktop-evidence-envelope-test-"), + ); + const artifactPath = NodePath.join(root, "Sigma-Code-0.1.9-arm64.dmg"); + const runtimeProvenancePath = NodePath.join(root, "runtime.json"); + await NodeFSP.writeFile(artifactPath, "dmg fixture", "utf8"); + await NodeFSP.writeFile( + runtimeProvenancePath, + '{"predicate":{"buildDefinition":{"externalParameters":{"version":"0.1.9","targetPlatform":"darwin","targetArch":"arm64"}}}}\n', + "utf8", + ); + let failure: unknown; + try { + await writeDesktopReleaseEvidence({ + artifactPath, + runtimeProvenancePath, + platform: "darwin", + arch: "arm64", + version: "0.1.9", + signingMode: "unsigned", + codesign: "not-signed", + gatekeeper: "not-applicable", + stapler: "not-applicable", + }); + } catch (error) { + failure = error; + } + assert.instanceOf(failure, Error); + assert.match((failure as Error).message, /DSSE envelope/u); +}); diff --git a/scripts/desktop-release-evidence.ts b/scripts/desktop-release-evidence.ts index b54bfcfad..f5105172d 100644 --- a/scripts/desktop-release-evidence.ts +++ b/scripts/desktop-release-evidence.ts @@ -2,11 +2,14 @@ // @effect-diagnostics nodeBuiltinImport:off import * as NodeCrypto from "node:crypto"; +import * as NodeBuffer from "node:buffer"; import * as NodeFSP from "node:fs/promises"; import * as NodePath from "node:path"; const APP_ID = "io.github.hututuqqq.sigmacode"; const MACOS_MINIMUM_SYSTEM_VERSION = "13.5"; +const RUNTIME_PROVENANCE_PAYLOAD_TYPE = "application/vnd.in-toto+json"; +const MAX_RUNTIME_PROVENANCE_PAYLOAD_BYTES = 1024 * 1024; type Platform = "darwin" | "win32"; type Arch = "arm64" | "x64"; @@ -63,6 +66,67 @@ function validateOptions(options: DesktopReleaseEvidenceOptions): void { } } +function decodeRuntimeProvenanceStatement(value: unknown): Record { + if (!value || typeof value !== "object" || Array.isArray(value)) { + throw new Error("Runtime provenance must be a DSSE envelope object."); + } + const envelope = value as Record; + const keys = Object.keys(envelope).sort(); + if (JSON.stringify(keys) !== JSON.stringify(["payload", "payloadType", "signatures"])) { + throw new Error("Runtime provenance DSSE envelope has unexpected fields."); + } + if (envelope.payloadType !== RUNTIME_PROVENANCE_PAYLOAD_TYPE) { + throw new Error("Runtime provenance uses an unsupported DSSE payload type."); + } + if (!Array.isArray(envelope.signatures)) { + throw new Error("Runtime provenance DSSE signatures must be an array."); + } + if ( + typeof envelope.payload !== "string" || + envelope.payload.length === 0 || + !/^[A-Za-z0-9+/]+={0,2}$/u.test(envelope.payload) || + envelope.payload.length > Math.ceil(MAX_RUNTIME_PROVENANCE_PAYLOAD_BYTES / 3) * 4 + ) { + throw new Error("Runtime provenance DSSE payload is not bounded canonical base64."); + } + const payload = NodeBuffer.Buffer.from(envelope.payload, "base64"); + if ( + payload.length === 0 || + payload.length > MAX_RUNTIME_PROVENANCE_PAYLOAD_BYTES || + payload.toString("base64") !== envelope.payload + ) { + throw new Error("Runtime provenance DSSE payload is not bounded canonical base64."); + } + const payloadText = payload.toString("utf8"); + if (!NodeBuffer.Buffer.from(payloadText, "utf8").equals(payload)) { + throw new Error("Runtime provenance DSSE payload is not valid UTF-8."); + } + let statement: unknown; + try { + statement = JSON.parse(payloadText); + } catch (error) { + throw new Error( + `Runtime provenance DSSE payload is not valid JSON: ${error instanceof Error ? error.message : String(error)}`, + { cause: error }, + ); + } + if (!statement || typeof statement !== "object" || Array.isArray(statement)) { + throw new Error("Runtime provenance DSSE payload must contain an object statement."); + } + const typed = statement as Record; + const predicate = typed.predicate as + | { readonly buildDefinition?: { readonly buildType?: unknown } } + | undefined; + if ( + typed._type !== "https://in-toto.io/Statement/v1" || + typed.predicateType !== "https://slsa.dev/provenance/v1" || + predicate?.buildDefinition?.buildType !== "https://sigma-code.dev/build-types/portable-cli" + ) { + throw new Error("Runtime provenance DSSE payload is not a Sigma portable Runtime statement."); + } + return typed; +} + export async function writeDesktopReleaseEvidence(options: DesktopReleaseEvidenceOptions): Promise<{ readonly checksumPath: string; readonly provenancePath: string; @@ -70,7 +134,7 @@ export async function writeDesktopReleaseEvidence(options: DesktopReleaseEvidenc }> { validateOptions(options); const runtimeProvenanceRaw = await NodeFSP.readFile(options.runtimeProvenancePath, "utf8"); - const runtimeProvenance = JSON.parse(runtimeProvenanceRaw) as Record; + const runtimeProvenance = decodeRuntimeProvenanceStatement(JSON.parse(runtimeProvenanceRaw)); const runtimeParameters = ( runtimeProvenance.predicate as | { readonly buildDefinition?: { readonly externalParameters?: unknown } } From c14506db9f15d8ac016ce1622efdbe92707a70de Mon Sep 17 00:00:00 2001 From: hututuQQQ <245432843+hututuQQQ@users.noreply.github.com> Date: Thu, 6 Aug 2026 13:11:14 +0800 Subject: [PATCH 2/2] chore: avoid unrelated lockfile metadata --- pnpm-lock.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d6883f01a..4981a25d3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5961,7 +5961,6 @@ packages: crypto-js@4.2.0: resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==} - deprecated: Active development of CryptoJS has been discontinued. This library is no longer maintained. css-select@5.2.2: resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==}