open-multi-agent-kit 0.98.1 → 0.98.2
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 +14 -0
- package/README.md +2 -2
- package/dist/cli/mcp-attach.d.ts +15 -0
- package/dist/cli/mcp-attach.d.ts.map +1 -0
- package/dist/cli/mcp-attach.js +14 -0
- package/dist/cli/mcp-attach.js.map +1 -0
- package/dist/core/agent-session.d.ts +7 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +66 -14
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-storage.d.ts +19 -2
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +57 -6
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/compaction/oauth-recovery.d.ts +29 -0
- package/dist/core/compaction/oauth-recovery.d.ts.map +1 -0
- package/dist/core/compaction/oauth-recovery.js +40 -0
- package/dist/core/compaction/oauth-recovery.js.map +1 -0
- package/dist/core/model-registry.d.ts +10 -1
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +16 -2
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/provider-resilience.d.ts +8 -0
- package/dist/core/provider-resilience.d.ts.map +1 -1
- package/dist/core/provider-resilience.js +17 -0
- package/dist/core/provider-resilience.js.map +1 -1
- package/dist/core/provider-usage.d.ts +1 -1
- package/dist/core/provider-usage.d.ts.map +1 -1
- package/dist/core/provider-usage.js +3 -2
- package/dist/core/provider-usage.js.map +1 -1
- package/dist/core/session-failure-cause.d.ts.map +1 -1
- package/dist/core/session-failure-cause.js +7 -1
- package/dist/core/session-failure-cause.js.map +1 -1
- package/dist/core/session-termination.d.ts.map +1 -1
- package/dist/core/session-termination.js +3 -1
- package/dist/core/session-termination.js.map +1 -1
- package/dist/core/verified-bash-runtime.d.ts.map +1 -1
- package/dist/core/verified-bash-runtime.js +6 -4
- package/dist/core/verified-bash-runtime.js.map +1 -1
- package/dist/guardrails/workspace-fingerprint.d.ts +2 -0
- package/dist/guardrails/workspace-fingerprint.d.ts.map +1 -1
- package/dist/guardrails/workspace-fingerprint.js +12 -10
- package/dist/guardrails/workspace-fingerprint.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +7 -0
- package/dist/main.js.map +1 -1
- package/docs/book-to-skill.md +17 -0
- package/docs/docs.json +4 -0
- package/docs/index.md +2 -0
- package/docs/mcp.md +10 -2
- package/docs/security.md +4 -9
- package/examples/extensions/custom-provider-anthropic/index.ts +4 -1
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/npm-shrinkwrap.json +18 -18
- package/package.json +6 -6
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace-fingerprint.d.ts","sourceRoot":"","sources":["../../src/guardrails/workspace-fingerprint.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAEX,aAAa,EAGb,iBAAiB,EACjB,SAAS,EACT,oBAAoB,EACpB,cAAc,EACd,MAAM,sBAAsB,CAAC;AAuC9B,MAAM,MAAM,qCAAqC,GAAG,sBAAsB,CAAC;AAE3E,MAAM,WAAW,kCAAkC;IAClD,oFAAoF;IACpF,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,qCAAqC,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;CACtG;AA0KD,uFAAuF;AACvF,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,SAAS,aAAa,EAAE,GAAG,SAAS,CAEpH;AAED,8GAA8G;AAC9G,wBAAgB,8BAA8B,CAC7C,YAAY,EAAE,SAAS,MAAM,EAAE,EAC/B,gBAAgB,EAAE,SAAS,EAC3B,kBAAkB,EAAE,SAAS,EAC7B,SAAS,EAAE,SAAS,aAAa,EAAE,GACjC,SAAS,CAYX;AAED,2FAA2F;AAC3F,wBAAgB,iCAAiC,CAChD,KAAK,EAAE,cAAc,EACrB,SAAS,EAAE,SAAS,aAAa,EAAE,EACnC,GAAG,EAAE,iBAAiB,GACpB,SAAS,CAkBX;AA6ED,iGAAiG;AACjG,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,GAAG,oBAAoB,CAU9E;AAsVD;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CAC1C,KAAK,EAAE,cAAc,EACrB,OAAO,GAAE,kCAAuC,GAC9C,oBAAoB,CA2CtB","sourcesContent":["import { spawnSync } from \"node:child_process\";\nimport { createHash, timingSafeEqual } from \"node:crypto\";\nimport type { Stats } from \"node:fs\";\nimport {\n\tcloseSync,\n\tconstants as fsConstants,\n\tfstatSync,\n\tlstatSync,\n\topenSync,\n\treadFileSync,\n\trealpathSync,\n} from \"node:fs\";\nimport { isAbsolute, join, posix, relative, resolve, sep, win32 } from \"node:path\";\nimport type {\n\tArtifactSetWorkspaceFingerprint,\n\tArtifactState,\n\tFileArtifactState,\n\tGitWorkspaceFingerprint,\n\tGitWorkspaceState,\n\tSha256Hex,\n\tWorkspaceFingerprint,\n\tWorkspaceScope,\n} from \"../types/evidence.ts\";\n\nconst SHA256_HEX = /^[0-9a-f]{64}$/;\nconst WORKSPACE_MANIFEST_DOMAIN = \"omk:evidence:workspace-fingerprint:v1\\0\";\nconst GIT_DIRTY_DOMAIN = \"omk:evidence:workspace-fingerprint:git-dirty:v1\\0\";\n/** Full SHA-1 or SHA-256 git object names. */\nconst GIT_COMMIT_HEX = /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/;\n/** Hard bound on bytes accepted from one git child process. */\nconst MAX_GIT_OUTPUT_BYTES = 16 * 1024 * 1024;\nconst GIT_CAPTURE_TIMEOUT_MS = 30_000;\n/** Inherited variables that could redirect git away from the trusted root; always stripped. */\nconst UNSAFE_GIT_ENV_VARS = [\n\t\"GIT_ALTERNATE_OBJECT_DIRECTORIES\",\n\t\"GIT_CEILING_DIRECTORIES\",\n\t\"GIT_COMMON_DIR\",\n\t\"GIT_DIFF_OPTS\",\n\t\"GIT_DIR\",\n\t\"GIT_EXTERNAL_DIFF\",\n\t\"GIT_INDEX_FILE\",\n\t\"GIT_NAMESPACE\",\n\t\"GIT_OBJECT_DIRECTORY\",\n\t\"GIT_PREFIX\",\n\t\"GIT_WORK_TREE\",\n] as const;\n/** Deterministic scope-limited diff rendering, independent of local prefix/ext-diff config. */\nconst GIT_DIFF_ARGS = [\n\t\"--full-index\",\n\t\"--binary\",\n\t\"--no-color\",\n\t\"--no-ext-diff\",\n\t\"--no-textconv\",\n\t\"--no-renames\",\n\t\"--ignore-submodules=none\",\n\t\"--src-prefix=a/\",\n\t\"--dst-prefix=b/\",\n] as const;\n\ntype FileStat = Stats;\n\nexport type WorkspaceFingerprintCaptureFaultStage = \"before-artifact-open\";\n\nexport interface WorkspaceFingerprintCaptureOptions {\n\t/** Tests only: mutate a selected path immediately before its artifact is opened. */\n\treadonly faultInjector?: (stage: WorkspaceFingerprintCaptureFaultStage, artifactPath: string) => void;\n}\n\ninterface ParentIdentity {\n\treadonly path: string;\n\treadonly stat: FileStat;\n}\n\nfunction errorCode(error: unknown): string | undefined {\n\treturn typeof error === \"object\" && error !== null && \"code\" in error\n\t\t? String((error as { code?: unknown }).code)\n\t\t: undefined;\n}\n\nfunction sha256(value: Uint8Array | string): Sha256Hex {\n\treturn createHash(\"sha256\").update(value).digest(\"hex\") as Sha256Hex;\n}\n\nfunction exactObject(value: unknown, label: string, keys: readonly string[]): Record<string, unknown> {\n\tif (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n\t\tthrow new Error(`${label} must be an object`);\n\t}\n\tconst descriptors = Object.getOwnPropertyDescriptors(value);\n\tconst actual = Object.entries(descriptors)\n\t\t.filter(([, descriptor]) => descriptor.enumerable)\n\t\t.map(([key]) => key)\n\t\t.sort();\n\tconst expected = [...keys].sort();\n\tif (\n\t\tactual.length !== expected.length ||\n\t\tactual.some((key, index) => key !== expected[index]) ||\n\t\tkeys.some((key) => {\n\t\t\tconst descriptor = descriptors[key];\n\t\t\treturn descriptor === undefined || descriptor.enumerable !== true || !(\"value\" in descriptor);\n\t\t})\n\t) {\n\t\tthrow new Error(`${label} has an invalid key set`);\n\t}\n\tconst snapshot: Record<string, unknown> = {};\n\tfor (const key of actual) {\n\t\tconst descriptor = descriptors[key];\n\t\tif (descriptor === undefined || !(\"value\" in descriptor)) {\n\t\t\tthrow new Error(`${label} has an invalid key set`);\n\t\t}\n\t\tsnapshot[key] = descriptor.value;\n\t}\n\treturn snapshot;\n}\n\nfunction ownEnumerableDataProperty(record: Record<string, unknown>, label: string, key: string): unknown {\n\tconst descriptor = Object.getOwnPropertyDescriptor(record, key);\n\tif (descriptor === undefined || descriptor.enumerable !== true || !(\"value\" in descriptor)) {\n\t\tthrow new Error(`${label} has an invalid key set`);\n\t}\n\treturn descriptor.value;\n}\n\nfunction exactArray(value: unknown, label: string): unknown[] {\n\tif (!Array.isArray(value)) throw new Error(`${label} must be an array`);\n\tconst descriptors = Object.getOwnPropertyDescriptors(value);\n\tconst lengthDescriptor = descriptors.length as PropertyDescriptor | undefined;\n\tif (lengthDescriptor === undefined || !(\"value\" in lengthDescriptor)) {\n\t\tthrow new Error(`${label} has invalid index properties`);\n\t}\n\tconst rawLength = lengthDescriptor.value;\n\tif (!Number.isSafeInteger(rawLength) || (rawLength as number) < 0) {\n\t\tthrow new Error(`${label} has invalid index properties`);\n\t}\n\tconst length = rawLength as number;\n\tconst snapshot: unknown[] = [];\n\tfor (let index = 0; index < length; index++) {\n\t\tconst descriptor = descriptors[String(index)];\n\t\tif (descriptor === undefined || descriptor.enumerable !== true || !(\"value\" in descriptor)) {\n\t\t\tthrow new Error(`${label} must use own enumerable data index properties`);\n\t\t}\n\t\tsnapshot.push(descriptor.value);\n\t}\n\tconst enumerableKeys = Object.entries(descriptors)\n\t\t.filter(([, descriptor]) => descriptor.enumerable)\n\t\t.map(([key]) => key);\n\tif (enumerableKeys.length !== length || enumerableKeys.some((key, index) => key !== String(index))) {\n\t\tthrow new Error(`${label} must use own enumerable data index properties`);\n\t}\n\treturn snapshot;\n}\n\nfunction assertNormalizedArtifactPath(path: unknown, label = \"artifact path\"): asserts path is string {\n\tif (\n\t\ttypeof path !== \"string\" ||\n\t\tpath.length === 0 ||\n\t\tpath.includes(\"\\0\") ||\n\t\tpath.includes(\"\\\\\") ||\n\t\tisAbsolute(path) ||\n\t\twin32.isAbsolute(path) ||\n\t\tpath === \".\" ||\n\t\tposix.normalize(path) !== path ||\n\t\tpath.split(\"/\").some((segment) => segment === \"\" || segment === \".\" || segment === \"..\")\n\t) {\n\t\tthrow new Error(`${label} must be a normalized root-relative path`);\n\t}\n}\n\nfunction assertSortedUniquePaths(paths: readonly string[], label: string): void {\n\tfor (let index = 0; index < paths.length; index++) {\n\t\tassertNormalizedArtifactPath(paths[index], `${label}[${index}]`);\n\t\tif (index > 0 && paths[index - 1] >= paths[index]) {\n\t\t\tthrow new Error(`${label} must be sorted and contain no duplicates`);\n\t\t}\n\t}\n}\n\nfunction parseSha256(value: unknown, label: string): Sha256Hex {\n\tif (typeof value !== \"string\" || !SHA256_HEX.test(value)) {\n\t\tthrow new Error(`${label} must be a lowercase SHA-256 hex digest`);\n\t}\n\treturn value as Sha256Hex;\n}\n\nfunction parseScope(value: unknown): WorkspaceScope {\n\tconst scope = exactObject(value, \"workspace scope\", [\"root\", \"artifactPaths\"]);\n\tif (typeof scope.root !== \"string\" || scope.root.length === 0 || !isAbsolute(scope.root)) {\n\t\tthrow new Error(\"workspace scope root must be an absolute path\");\n\t}\n\tconst rawArtifactPaths = exactArray(scope.artifactPaths, \"workspace scope artifactPaths\");\n\tif (!rawArtifactPaths.every((path) => typeof path === \"string\")) {\n\t\tthrow new Error(\"workspace scope artifactPaths must be a string array\");\n\t}\n\tconst artifactPaths = [...rawArtifactPaths] as string[];\n\tassertSortedUniquePaths(artifactPaths, \"workspace scope artifactPaths\");\n\treturn Object.freeze({ root: scope.root, artifactPaths: Object.freeze(artifactPaths) });\n}\n\nfunction parseArtifact(value: unknown, index: number): ArtifactState {\n\tif (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n\t\tthrow new Error(`workspace artifact[${index}] must be an object`);\n\t}\n\tconst candidate = value as Record<string, unknown>;\n\tconst state = ownEnumerableDataProperty(candidate, `workspace artifact[${index}]`, \"state\");\n\tif (state === \"missing\") {\n\t\tconst artifact = exactObject(value, `workspace artifact[${index}]`, [\"path\", \"state\"]);\n\t\tassertNormalizedArtifactPath(artifact.path, `workspace artifact[${index}].path`);\n\t\treturn Object.freeze({ path: artifact.path, state: \"missing\" });\n\t}\n\tif (state === \"file\") {\n\t\tconst artifact = exactObject(value, `workspace artifact[${index}]`, [\"path\", \"state\", \"sha256\", \"size\"]);\n\t\tassertNormalizedArtifactPath(artifact.path, `workspace artifact[${index}].path`);\n\t\tif (!Number.isSafeInteger(artifact.size) || (artifact.size as number) < 0) {\n\t\t\tthrow new Error(`workspace artifact[${index}].size must be a non-negative safe integer`);\n\t\t}\n\t\treturn Object.freeze({\n\t\t\tpath: artifact.path,\n\t\t\tstate: \"file\",\n\t\t\tsha256: parseSha256(artifact.sha256, `workspace artifact[${index}].sha256`),\n\t\t\tsize: artifact.size as number,\n\t\t});\n\t}\n\tthrow new Error(`workspace artifact[${index}].state must be file or missing`);\n}\n\nfunction canonicalManifest(scope: WorkspaceScope, artifacts: readonly ArtifactState[]): string {\n\treturn JSON.stringify({\n\t\tkind: \"artifact-set\",\n\t\tscope: { root: scope.root, artifactPaths: scope.artifactPaths },\n\t\tartifacts: artifacts.map((artifact) =>\n\t\t\tartifact.state === \"file\"\n\t\t\t\t? { path: artifact.path, state: artifact.state, sha256: artifact.sha256, size: artifact.size }\n\t\t\t\t: { path: artifact.path, state: artifact.state },\n\t\t),\n\t});\n}\n\n/** Compute the domain-separated digest for an already validated canonical manifest. */\nexport function computeWorkspaceManifestSha256(scope: WorkspaceScope, artifacts: readonly ArtifactState[]): Sha256Hex {\n\treturn sha256(`${WORKSPACE_MANIFEST_DOMAIN}${canonicalManifest(scope, artifacts)}`);\n}\n\n/** Canonical digest committing sorted changed paths, both diff digests, and every selected artifact state. */\nexport function computeGitWorkspaceDirtySha256(\n\tchangedPaths: readonly string[],\n\tstagedDiffSha256: Sha256Hex,\n\tunstagedDiffSha256: Sha256Hex,\n\tartifacts: readonly ArtifactState[],\n): Sha256Hex {\n\tconst canonical = JSON.stringify({\n\t\tchangedPaths,\n\t\tstagedDiffSha256,\n\t\tunstagedDiffSha256,\n\t\tartifacts: artifacts.map((artifact) =>\n\t\t\tartifact.state === \"file\"\n\t\t\t\t? { path: artifact.path, state: artifact.state, sha256: artifact.sha256, size: artifact.size }\n\t\t\t\t: { path: artifact.path, state: artifact.state },\n\t\t),\n\t});\n\treturn sha256(`${GIT_DIRTY_DOMAIN}${canonical}`);\n}\n\n/** Compute the domain-separated digest for an already validated canonical git manifest. */\nexport function computeGitWorkspaceManifestSha256(\n\tscope: WorkspaceScope,\n\tartifacts: readonly ArtifactState[],\n\tgit: GitWorkspaceState,\n): Sha256Hex {\n\tconst canonical = JSON.stringify({\n\t\tkind: \"git\",\n\t\tscope: { root: scope.root, artifactPaths: scope.artifactPaths },\n\t\tartifacts: artifacts.map((artifact) =>\n\t\t\tartifact.state === \"file\"\n\t\t\t\t? { path: artifact.path, state: artifact.state, sha256: artifact.sha256, size: artifact.size }\n\t\t\t\t: { path: artifact.path, state: artifact.state },\n\t\t),\n\t\tgit: {\n\t\t\theadCommit: git.headCommit,\n\t\t\tchangedPaths: git.changedPaths,\n\t\t\tstagedDiffSha256: git.stagedDiffSha256,\n\t\t\tunstagedDiffSha256: git.unstagedDiffSha256,\n\t\t\tdirtySha256: git.dirtySha256,\n\t\t},\n\t});\n\treturn sha256(`${WORKSPACE_MANIFEST_DOMAIN}${canonical}`);\n}\n\n/** A changed path is inside scope when it equals a selected path or lies under a selected directory. */\nfunction isWithinScope(path: string, artifactPaths: readonly string[]): boolean {\n\treturn artifactPaths.some((artifact) => path === artifact || path.startsWith(`${artifact}/`));\n}\n\nfunction digestsEqual(left: Sha256Hex, right: Sha256Hex): boolean {\n\treturn timingSafeEqual(Buffer.from(left, \"hex\"), Buffer.from(right, \"hex\"));\n}\n\nfunction parseGitFingerprint(value: unknown): GitWorkspaceFingerprint {\n\tconst raw = exactObject(value, \"workspace fingerprint\", [\"kind\", \"scope\", \"artifacts\", \"git\", \"manifestSha256\"]);\n\tconst scope = parseScope(raw.scope);\n\tconst rawArtifacts = exactArray(raw.artifacts, \"workspace fingerprint artifacts\");\n\tconst artifacts = rawArtifacts.map((artifact, index) => parseArtifact(artifact, index));\n\tconst artifactPaths = artifacts.map((artifact) => artifact.path);\n\tassertSortedUniquePaths(artifactPaths, \"workspace artifact paths\");\n\tif (\n\t\tartifactPaths.length !== scope.artifactPaths.length ||\n\t\tartifactPaths.some((path, index) => path !== scope.artifactPaths[index])\n\t) {\n\t\tthrow new Error(\"workspace artifacts must exactly match the selected scope paths\");\n\t}\n\tconst rawGit = exactObject(raw.git, \"workspace git state\", [\n\t\t\"headCommit\",\n\t\t\"changedPaths\",\n\t\t\"stagedDiffSha256\",\n\t\t\"unstagedDiffSha256\",\n\t\t\"dirtySha256\",\n\t]);\n\tconst headCommit = rawGit.headCommit;\n\tif (headCommit !== null && (typeof headCommit !== \"string\" || !GIT_COMMIT_HEX.test(headCommit))) {\n\t\tthrow new Error(\"workspace git headCommit must be null or a full lowercase hex object name\");\n\t}\n\tconst rawChangedPaths = exactArray(rawGit.changedPaths, \"workspace git changedPaths\");\n\tif (!rawChangedPaths.every((path) => typeof path === \"string\")) {\n\t\tthrow new Error(\"workspace git changedPaths must be a string array\");\n\t}\n\tconst changedPaths = [...rawChangedPaths] as string[];\n\tassertSortedUniquePaths(changedPaths, \"workspace git changedPaths\");\n\tfor (const path of changedPaths) {\n\t\tif (!isWithinScope(path, scope.artifactPaths)) {\n\t\t\tthrow new Error(`workspace git changed path escapes the selected scope: ${path}`);\n\t\t}\n\t}\n\tconst stagedDiffSha256 = parseSha256(rawGit.stagedDiffSha256, \"workspace git stagedDiffSha256\");\n\tconst unstagedDiffSha256 = parseSha256(rawGit.unstagedDiffSha256, \"workspace git unstagedDiffSha256\");\n\tconst dirtySha256 = parseSha256(rawGit.dirtySha256, \"workspace git dirtySha256\");\n\tif (\n\t\t!digestsEqual(\n\t\t\tdirtySha256,\n\t\t\tcomputeGitWorkspaceDirtySha256(changedPaths, stagedDiffSha256, unstagedDiffSha256, artifacts),\n\t\t)\n\t) {\n\t\tthrow new Error(\"workspace git dirty digest mismatch\");\n\t}\n\tconst git: GitWorkspaceState = Object.freeze({\n\t\theadCommit,\n\t\tchangedPaths: Object.freeze(changedPaths),\n\t\tstagedDiffSha256,\n\t\tunstagedDiffSha256,\n\t\tdirtySha256,\n\t});\n\tconst manifestSha256 = parseSha256(raw.manifestSha256, \"workspace manifestSha256\");\n\tif (!digestsEqual(manifestSha256, computeGitWorkspaceManifestSha256(scope, artifacts, git))) {\n\t\tthrow new Error(\"workspace manifest digest mismatch\");\n\t}\n\treturn Object.freeze({\n\t\tkind: \"git\",\n\t\tscope,\n\t\tartifacts: Object.freeze(artifacts),\n\t\tgit,\n\t\tmanifestSha256,\n\t});\n}\n\n/** Strictly parse and validate a fingerprint of either kind, including its canonical digests. */\nexport function parseWorkspaceFingerprint(value: unknown): WorkspaceFingerprint {\n\tif (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n\t\tthrow new Error(\"workspace fingerprint must be an object\");\n\t}\n\tconst kind = ownEnumerableDataProperty(value as Record<string, unknown>, \"workspace fingerprint\", \"kind\");\n\tif (kind === \"git\") return parseGitFingerprint(value);\n\tif (kind !== \"artifact-set\") {\n\t\tthrow new Error(\"workspace fingerprint kind must be artifact-set or git\");\n\t}\n\treturn parseArtifactSetFingerprint(value);\n}\n\nfunction parseArtifactSetFingerprint(value: unknown): ArtifactSetWorkspaceFingerprint {\n\tconst raw = exactObject(value, \"workspace fingerprint\", [\"kind\", \"scope\", \"artifacts\", \"manifestSha256\"]);\n\tif (raw.kind !== \"artifact-set\") {\n\t\tthrow new Error(\"workspace fingerprint kind must be artifact-set\");\n\t}\n\tconst scope = parseScope(raw.scope);\n\tconst rawArtifacts = exactArray(raw.artifacts, \"workspace fingerprint artifacts\");\n\tconst artifacts = rawArtifacts.map((artifact, index) => parseArtifact(artifact, index));\n\tconst artifactPaths = artifacts.map((artifact) => artifact.path);\n\tassertSortedUniquePaths(artifactPaths, \"workspace artifact paths\");\n\tif (\n\t\tartifactPaths.length !== scope.artifactPaths.length ||\n\t\tartifactPaths.some((path, index) => path !== scope.artifactPaths[index])\n\t) {\n\t\tthrow new Error(\"workspace artifacts must exactly match the selected scope paths\");\n\t}\n\tconst manifestSha256 = parseSha256(raw.manifestSha256, \"workspace manifestSha256\");\n\tconst expected = computeWorkspaceManifestSha256(scope, artifacts);\n\tif (!digestsEqual(manifestSha256, expected)) {\n\t\tthrow new Error(\"workspace manifest digest mismatch\");\n\t}\n\treturn Object.freeze({\n\t\tkind: \"artifact-set\",\n\t\tscope,\n\t\tartifacts: Object.freeze(artifacts),\n\t\tmanifestSha256,\n\t});\n}\n\nfunction statPathWithoutSymlinks(root: string, artifactPath: string): FileStat | undefined {\n\tlet current = root;\n\tconst segments = artifactPath.split(\"/\");\n\tfor (let index = 0; index < segments.length; index++) {\n\t\tcurrent = join(current, segments[index]);\n\t\tlet stat: FileStat;\n\t\ttry {\n\t\t\tstat = lstatSync(current);\n\t\t} catch (error) {\n\t\t\tif (errorCode(error) === \"ENOENT\") return undefined;\n\t\t\tthrow error;\n\t\t}\n\t\tif (stat.isSymbolicLink()) {\n\t\t\tthrow new Error(`workspace artifact path contains a symlink: ${artifactPath}`);\n\t\t}\n\t\tif (index < segments.length - 1 && !stat.isDirectory()) {\n\t\t\tthrow new Error(`workspace artifact parent is not a directory: ${artifactPath}`);\n\t\t}\n\t\tif (index === segments.length - 1) return stat;\n\t}\n\treturn undefined;\n}\n\nfunction captureParentIdentities(root: string, artifactPath: string): readonly ParentIdentity[] {\n\tconst parents: ParentIdentity[] = [];\n\tlet current = root;\n\tfor (const segment of artifactPath.split(\"/\").slice(0, -1)) {\n\t\tcurrent = join(current, segment);\n\t\tconst stat = lstatSync(current);\n\t\tif (stat.isSymbolicLink()) {\n\t\t\tthrow new Error(`workspace artifact path contains a symlink: ${artifactPath}`);\n\t\t}\n\t\tif (!stat.isDirectory()) {\n\t\t\tthrow new Error(`workspace artifact parent is not a directory: ${artifactPath}`);\n\t\t}\n\t\tparents.push({ path: current, stat });\n\t}\n\treturn parents;\n}\n\nfunction hasFileIdentity(stat: FileStat): boolean {\n\treturn stat.dev !== 0 || stat.ino !== 0;\n}\n\nfunction sameFileIdentity(expected: FileStat, actual: FileStat): boolean {\n\tconst expectedHasIdentity = hasFileIdentity(expected);\n\tconst actualHasIdentity = hasFileIdentity(actual);\n\treturn (\n\t\t(!expectedHasIdentity && !actualHasIdentity) ||\n\t\t(expectedHasIdentity && actualHasIdentity && expected.dev === actual.dev && expected.ino === actual.ino)\n\t);\n}\n\nfunction isStrictlyUnder(root: string, path: string): boolean {\n\tconst relativePath = relative(root, path);\n\treturn (\n\t\trelativePath !== \"\" && relativePath !== \"..\" && !relativePath.startsWith(`..${sep}`) && !isAbsolute(relativePath)\n\t);\n}\n\nfunction assertTrustedRoot(root: string, trustedRootStat: FileStat): void {\n\tconst liveRootStat = lstatSync(root);\n\tif (\n\t\tliveRootStat.isSymbolicLink() ||\n\t\t!liveRootStat.isDirectory() ||\n\t\trealpathSync(root) !== root ||\n\t\t!sameFileIdentity(trustedRootStat, liveRootStat)\n\t) {\n\t\tthrow new Error(\"workspace root changed while artifacts were captured\");\n\t}\n}\n\nfunction assertParentIdentities(parents: readonly ParentIdentity[], artifactPath: string): void {\n\tfor (const parent of parents) {\n\t\tconst stat = lstatSync(parent.path);\n\t\tif (stat.isSymbolicLink() || !stat.isDirectory() || !sameFileIdentity(parent.stat, stat)) {\n\t\t\tthrow new Error(`workspace artifact parent changed while it was captured: ${artifactPath}`);\n\t\t}\n\t}\n}\n\nfunction assertLiveArtifactPath(\n\troot: string,\n\ttrustedRootStat: FileStat,\n\ttrustedParents: readonly ParentIdentity[],\n\tartifactPath: string,\n\tfdStat: FileStat,\n): void {\n\tassertTrustedRoot(root, trustedRootStat);\n\tassertParentIdentities(trustedParents, artifactPath);\n\tconst pathStat = statPathWithoutSymlinks(root, artifactPath);\n\tif (pathStat === undefined || !pathStat.isFile() || !sameFileIdentity(fdStat, pathStat)) {\n\t\tthrow new Error(`workspace artifact path was rebound while it was captured: ${artifactPath}`);\n\t}\n\tconst fullPath = join(root, ...artifactPath.split(\"/\"));\n\tconst livePath = realpathSync(fullPath);\n\tif (!isStrictlyUnder(root, livePath)) {\n\t\tthrow new Error(`workspace artifact resolved outside the trusted root: ${artifactPath}`);\n\t}\n\tconst livePathStat = lstatSync(livePath);\n\tif (!livePathStat.isFile() || !sameFileIdentity(fdStat, livePathStat)) {\n\t\tthrow new Error(`workspace artifact path was rebound while it was captured: ${artifactPath}`);\n\t}\n\tassertParentIdentities(trustedParents, artifactPath);\n\tassertTrustedRoot(root, trustedRootStat);\n}\n\nfunction captureFile(\n\troot: string,\n\ttrustedRootStat: FileStat,\n\ttrustedParents: readonly ParentIdentity[],\n\tartifactPath: string,\n): FileArtifactState {\n\tconst fullPath = join(root, ...artifactPath.split(\"/\"));\n\tconst flags = fsConstants.O_RDONLY | (fsConstants.O_NOFOLLOW ?? 0);\n\tlet fd: number;\n\ttry {\n\t\tfd = openSync(fullPath, flags);\n\t} catch (error) {\n\t\tif (errorCode(error) === \"ELOOP\") {\n\t\t\tthrow new Error(`workspace artifact path contains a symlink: ${artifactPath}`);\n\t\t}\n\t\tthrow error;\n\t}\n\ttry {\n\t\tconst before = fstatSync(fd);\n\t\tif (!before.isFile()) {\n\t\t\tthrow new Error(`workspace artifact is not a regular file: ${artifactPath}`);\n\t\t}\n\t\tassertLiveArtifactPath(root, trustedRootStat, trustedParents, artifactPath, before);\n\t\tconst bytes = readFileSync(fd);\n\t\tconst after = fstatSync(fd);\n\t\tassertLiveArtifactPath(root, trustedRootStat, trustedParents, artifactPath, after);\n\t\tif (\n\t\t\t!after.isFile() ||\n\t\t\t!sameFileIdentity(before, after) ||\n\t\t\tbefore.size !== after.size ||\n\t\t\tbefore.mtimeMs !== after.mtimeMs ||\n\t\t\tbefore.ctimeMs !== after.ctimeMs ||\n\t\t\tafter.size !== bytes.byteLength\n\t\t) {\n\t\t\tthrow new Error(`workspace artifact changed while it was captured: ${artifactPath}`);\n\t\t}\n\t\treturn Object.freeze({ path: artifactPath, state: \"file\", sha256: sha256(bytes), size: bytes.byteLength });\n\t} finally {\n\t\tcloseSync(fd);\n\t}\n}\n\nfunction captureSelectedArtifact(\n\troot: string,\n\ttrustedRootStat: FileStat,\n\tartifactPath: string,\n\toptions: WorkspaceFingerprintCaptureOptions,\n): ArtifactState {\n\tassertTrustedRoot(root, trustedRootStat);\n\tconst stat = statPathWithoutSymlinks(root, artifactPath);\n\tif (stat === undefined) {\n\t\tassertTrustedRoot(root, trustedRootStat);\n\t\treturn Object.freeze({ path: artifactPath, state: \"missing\" });\n\t}\n\tif (!stat.isFile()) {\n\t\tthrow new Error(`workspace artifact is not a regular file: ${artifactPath}`);\n\t}\n\tconst trustedParents = captureParentIdentities(root, artifactPath);\n\toptions.faultInjector?.(\"before-artifact-open\", artifactPath);\n\treturn captureFile(root, trustedRootStat, trustedParents, artifactPath);\n}\n\n/** True when the trusted root itself hosts a `.git` entry; a symlinked `.git` fails closed. */\nfunction isGitWorkspaceRoot(root: string): boolean {\n\tlet stat: FileStat;\n\ttry {\n\t\tstat = lstatSync(join(root, \".git\"));\n\t} catch (error) {\n\t\tif (errorCode(error) === \"ENOENT\") return false;\n\t\tthrow error;\n\t}\n\tif (stat.isSymbolicLink()) {\n\t\tthrow new Error(\"workspace root .git must not be a symlink\");\n\t}\n\treturn true;\n}\n\nfunction gitCaptureEnv(): NodeJS.ProcessEnv {\n\tconst env: NodeJS.ProcessEnv = { ...process.env };\n\tfor (const name of UNSAFE_GIT_ENV_VARS) delete env[name];\n\tenv.GIT_OPTIONAL_LOCKS = \"0\";\n\tenv.LC_ALL = \"C\";\n\treturn env;\n}\n\n/** Run one git subcommand with argv boundaries (never a shell) and bounded, time-limited output. */\nfunction runGitCapture(\n\troot: string,\n\targs: readonly string[],\n\tallowedExitCodes: readonly number[] = [0],\n): { status: number; stdout: Buffer } {\n\tconst result = spawnSync(\"git\", [\"-C\", root, ...args], {\n\t\tenv: gitCaptureEnv(),\n\t\tmaxBuffer: MAX_GIT_OUTPUT_BYTES,\n\t\ttimeout: GIT_CAPTURE_TIMEOUT_MS,\n\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\twindowsHide: true,\n\t});\n\tif (result.error !== undefined) {\n\t\tconst code = errorCode(result.error);\n\t\tthrow new Error(`workspace git capture failed to run git: ${code ?? result.error.message}`);\n\t}\n\tif (result.status === null || !allowedExitCodes.includes(result.status)) {\n\t\tthrow new Error(`workspace git capture: git exited with status ${String(result.status)}`);\n\t}\n\treturn { status: result.status, stdout: result.stdout };\n}\n\ninterface GitScopeStatus {\n\treadonly changedPaths: readonly string[];\n}\n\nfunction parseGitStatusOutput(stdout: Buffer, artifactPaths: readonly string[]): GitScopeStatus {\n\tconst changed = new Set<string>();\n\tfor (const entry of stdout.toString(\"utf8\").split(\"\\0\")) {\n\t\tif (entry.length === 0) continue;\n\t\tif (entry.length < 4 || entry[2] !== \" \") {\n\t\t\tthrow new Error(\"workspace git capture: unexpected status entry shape\");\n\t\t}\n\t\tconst path = entry.slice(3);\n\t\tassertNormalizedArtifactPath(path, \"workspace git status path\");\n\t\tif (!isWithinScope(path, artifactPaths)) {\n\t\t\tthrow new Error(`workspace git status path escapes the selected scope: ${path}`);\n\t\t}\n\t\tchanged.add(path);\n\t}\n\treturn { changedPaths: [...changed].sort() };\n}\n\n/**\n * Capture HEAD plus a scope-limited canonical dirty digest for a git work-tree root.\n * All git invocations are argv-only, env-sanitized, bounded, and pathspec-limited with\n * literal magic. Every exact selected path is also captured with symlink-free,\n * root-contained file reads, independent of ignore rules and index flags.\n */\nfunction captureGitWorkspaceFingerprint(\n\troot: string,\n\ttrustedRootStat: FileStat,\n\tscope: WorkspaceScope,\n\toptions: WorkspaceFingerprintCaptureOptions,\n): GitWorkspaceFingerprint {\n\tconst toplevel = runGitCapture(root, [\"rev-parse\", \"--show-toplevel\"]).stdout.toString(\"utf8\").replace(/\\n$/, \"\");\n\tif (toplevel.length === 0 || realpathSync(toplevel) !== root) {\n\t\tthrow new Error(\"workspace root must be the git work-tree top level\");\n\t}\n\tconst head = runGitCapture(root, [\"rev-parse\", \"--verify\", \"--quiet\", \"HEAD^{commit}\"], [0, 1]);\n\tlet headCommit: string | null = null;\n\tif (head.status === 0) {\n\t\tconst hex = head.stdout.toString(\"utf8\").trim();\n\t\tif (!GIT_COMMIT_HEX.test(hex)) {\n\t\t\tthrow new Error(\"workspace git capture: HEAD is not a full hex object name\");\n\t\t}\n\t\theadCommit = hex;\n\t}\n\tconst pathspecs = scope.artifactPaths.map((path) => `:(literal)${path}`);\n\tlet status: GitScopeStatus = { changedPaths: [] };\n\tlet stagedDiff: Buffer = Buffer.alloc(0);\n\tlet unstagedDiff: Buffer = Buffer.alloc(0);\n\tif (pathspecs.length > 0) {\n\t\tstatus = parseGitStatusOutput(\n\t\t\trunGitCapture(root, [\n\t\t\t\t\"-c\",\n\t\t\t\t\"core.fsmonitor=false\",\n\t\t\t\t\"status\",\n\t\t\t\t\"--porcelain=v1\",\n\t\t\t\t\"-z\",\n\t\t\t\t\"--untracked-files=all\",\n\t\t\t\t\"--no-renames\",\n\t\t\t\t\"--ignore-submodules=none\",\n\t\t\t\t\"--\",\n\t\t\t\t...pathspecs,\n\t\t\t]).stdout,\n\t\t\tscope.artifactPaths,\n\t\t);\n\t\tstagedDiff = runGitCapture(root, [\"diff\", \"--cached\", ...GIT_DIFF_ARGS, \"--\", ...pathspecs]).stdout;\n\t\tunstagedDiff = runGitCapture(root, [\"diff\", ...GIT_DIFF_ARGS, \"--\", ...pathspecs]).stdout;\n\t}\n\tconst artifacts = scope.artifactPaths.map((artifactPath) =>\n\t\tcaptureSelectedArtifact(root, trustedRootStat, artifactPath, options),\n\t);\n\tassertTrustedRoot(root, trustedRootStat);\n\tconst changedPaths = Object.freeze([...status.changedPaths]);\n\tconst stagedDiffSha256 = sha256(stagedDiff);\n\tconst unstagedDiffSha256 = sha256(unstagedDiff);\n\tconst git: GitWorkspaceState = Object.freeze({\n\t\theadCommit,\n\t\tchangedPaths,\n\t\tstagedDiffSha256,\n\t\tunstagedDiffSha256,\n\t\tdirtySha256: computeGitWorkspaceDirtySha256(changedPaths, stagedDiffSha256, unstagedDiffSha256, artifacts),\n\t});\n\tconst parsed = parseWorkspaceFingerprint({\n\t\tkind: \"git\",\n\t\tscope,\n\t\tartifacts: Object.freeze(artifacts),\n\t\tgit,\n\t\tmanifestSha256: computeGitWorkspaceManifestSha256(scope, artifacts, git),\n\t});\n\tif (parsed.kind !== \"git\") {\n\t\tthrow new Error(\"workspace git capture produced a non-git fingerprint\");\n\t}\n\treturn parsed;\n}\n\n/**\n * Hash the selected workspace scope. When the trusted root is itself a git work-tree\n * top level, capture HEAD plus a scope-limited canonical dirty digest; otherwise hash\n * the caller-selected artifact set directly and perform no git commands.\n * Phase A assumes a trusted, quiescent workspace: pathname identity checks reject\n * observed rebinds, but do not prove an immutable snapshot against a same-UID adversary.\n */\nexport function captureWorkspaceFingerprint(\n\tscope: WorkspaceScope,\n\toptions: WorkspaceFingerprintCaptureOptions = {},\n): WorkspaceFingerprint {\n\tconst rawScope = exactObject(scope, \"workspace scope\", [\"root\", \"artifactPaths\"]);\n\tconst rawRoot = rawScope.root;\n\tif (typeof rawRoot !== \"string\" || rawRoot.length === 0) {\n\t\tthrow new Error(\"workspace scope root must be a non-empty string\");\n\t}\n\tconst rawArtifactPaths = exactArray(rawScope.artifactPaths, \"workspace scope artifactPaths\");\n\n\tconst seen = new Set<string>();\n\tfor (let index = 0; index < rawArtifactPaths.length; index++) {\n\t\tconst artifactPath = rawArtifactPaths[index];\n\t\tassertNormalizedArtifactPath(artifactPath, `workspace scope artifactPaths[${index}]`);\n\t\tif (seen.has(artifactPath)) {\n\t\t\tthrow new Error(`duplicate workspace artifact path: ${artifactPath}`);\n\t\t}\n\t\tseen.add(artifactPath);\n\t}\n\n\tconst resolvedRoot = resolve(rawRoot);\n\tconst rootStat = lstatSync(resolvedRoot);\n\tif (rootStat.isSymbolicLink()) {\n\t\tthrow new Error(\"workspace root must not be a symlink\");\n\t}\n\tif (!rootStat.isDirectory()) {\n\t\tthrow new Error(\"workspace root must be a directory\");\n\t}\n\tconst root = realpathSync(resolvedRoot);\n\tconst trustedRootStat = lstatSync(root);\n\tconst artifactPaths = (rawArtifactPaths as string[]).sort();\n\tconst canonicalScope: WorkspaceScope = Object.freeze({ root, artifactPaths: Object.freeze(artifactPaths) });\n\tif (isGitWorkspaceRoot(root)) {\n\t\treturn captureGitWorkspaceFingerprint(root, trustedRootStat, canonicalScope, options);\n\t}\n\tconst artifacts = artifactPaths.map((artifactPath) =>\n\t\tcaptureSelectedArtifact(root, trustedRootStat, artifactPath, options),\n\t);\n\tconst fingerprint = {\n\t\tkind: \"artifact-set\",\n\t\tscope: canonicalScope,\n\t\tartifacts: Object.freeze(artifacts),\n\t\tmanifestSha256: computeWorkspaceManifestSha256(canonicalScope, artifacts),\n\t};\n\treturn parseWorkspaceFingerprint(fingerprint);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"workspace-fingerprint.d.ts","sourceRoot":"","sources":["../../src/guardrails/workspace-fingerprint.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAEX,aAAa,EAGb,iBAAiB,EACjB,SAAS,EACT,oBAAoB,EACpB,cAAc,EACd,MAAM,sBAAsB,CAAC;AAuC9B,MAAM,MAAM,qCAAqC,GAAG,sBAAsB,CAAC;AAE3E,MAAM,WAAW,kCAAkC;IAClD,oFAAoF;IACpF,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,qCAAqC,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;CACtG;AAqFD,6FAA6F;AAC7F,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,MAAM,CAWtE;AA2ED,uFAAuF;AACvF,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,SAAS,aAAa,EAAE,GAAG,SAAS,CAEpH;AAED,8GAA8G;AAC9G,wBAAgB,8BAA8B,CAC7C,YAAY,EAAE,SAAS,MAAM,EAAE,EAC/B,gBAAgB,EAAE,SAAS,EAC3B,kBAAkB,EAAE,SAAS,EAC7B,SAAS,EAAE,SAAS,aAAa,EAAE,GACjC,SAAS,CAYX;AAED,2FAA2F;AAC3F,wBAAgB,iCAAiC,CAChD,KAAK,EAAE,cAAc,EACrB,SAAS,EAAE,SAAS,aAAa,EAAE,EACnC,GAAG,EAAE,iBAAiB,GACpB,SAAS,CAkBX;AA6ED,iGAAiG;AACjG,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,GAAG,oBAAoB,CAU9E;AAsVD;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CAC1C,KAAK,EAAE,cAAc,EACrB,OAAO,GAAE,kCAAuC,GAC9C,oBAAoB,CA2CtB","sourcesContent":["import { spawnSync } from \"node:child_process\";\nimport { createHash, timingSafeEqual } from \"node:crypto\";\nimport type { Stats } from \"node:fs\";\nimport {\n\tcloseSync,\n\tconstants as fsConstants,\n\tfstatSync,\n\tlstatSync,\n\topenSync,\n\treadFileSync,\n\trealpathSync,\n} from \"node:fs\";\nimport { isAbsolute, join, posix, relative, resolve, sep, win32 } from \"node:path\";\nimport type {\n\tArtifactSetWorkspaceFingerprint,\n\tArtifactState,\n\tFileArtifactState,\n\tGitWorkspaceFingerprint,\n\tGitWorkspaceState,\n\tSha256Hex,\n\tWorkspaceFingerprint,\n\tWorkspaceScope,\n} from \"../types/evidence.ts\";\n\nconst SHA256_HEX = /^[0-9a-f]{64}$/;\nconst WORKSPACE_MANIFEST_DOMAIN = \"omk:evidence:workspace-fingerprint:v1\\0\";\nconst GIT_DIRTY_DOMAIN = \"omk:evidence:workspace-fingerprint:git-dirty:v1\\0\";\n/** Full SHA-1 or SHA-256 git object names. */\nconst GIT_COMMIT_HEX = /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/;\n/** Hard bound on bytes accepted from one git child process. */\nconst MAX_GIT_OUTPUT_BYTES = 16 * 1024 * 1024;\nconst GIT_CAPTURE_TIMEOUT_MS = 30_000;\n/** Inherited variables that could redirect git away from the trusted root; always stripped. */\nconst UNSAFE_GIT_ENV_VARS = [\n\t\"GIT_ALTERNATE_OBJECT_DIRECTORIES\",\n\t\"GIT_CEILING_DIRECTORIES\",\n\t\"GIT_COMMON_DIR\",\n\t\"GIT_DIFF_OPTS\",\n\t\"GIT_DIR\",\n\t\"GIT_EXTERNAL_DIFF\",\n\t\"GIT_INDEX_FILE\",\n\t\"GIT_NAMESPACE\",\n\t\"GIT_OBJECT_DIRECTORY\",\n\t\"GIT_PREFIX\",\n\t\"GIT_WORK_TREE\",\n] as const;\n/** Deterministic scope-limited diff rendering, independent of local prefix/ext-diff config. */\nconst GIT_DIFF_ARGS = [\n\t\"--full-index\",\n\t\"--binary\",\n\t\"--no-color\",\n\t\"--no-ext-diff\",\n\t\"--no-textconv\",\n\t\"--no-renames\",\n\t\"--ignore-submodules=none\",\n\t\"--src-prefix=a/\",\n\t\"--dst-prefix=b/\",\n] as const;\n\ntype FileStat = Stats;\n\nexport type WorkspaceFingerprintCaptureFaultStage = \"before-artifact-open\";\n\nexport interface WorkspaceFingerprintCaptureOptions {\n\t/** Tests only: mutate a selected path immediately before its artifact is opened. */\n\treadonly faultInjector?: (stage: WorkspaceFingerprintCaptureFaultStage, artifactPath: string) => void;\n}\n\ninterface ParentIdentity {\n\treadonly path: string;\n\treadonly stat: FileStat;\n}\n\nfunction errorCode(error: unknown): string | undefined {\n\treturn typeof error === \"object\" && error !== null && \"code\" in error\n\t\t? String((error as { code?: unknown }).code)\n\t\t: undefined;\n}\n\nfunction sha256(value: Uint8Array | string): Sha256Hex {\n\treturn createHash(\"sha256\").update(value).digest(\"hex\") as Sha256Hex;\n}\n\nfunction exactObject(value: unknown, label: string, keys: readonly string[]): Record<string, unknown> {\n\tif (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n\t\tthrow new Error(`${label} must be an object`);\n\t}\n\tconst descriptors = Object.getOwnPropertyDescriptors(value);\n\tconst actual = Object.entries(descriptors)\n\t\t.filter(([, descriptor]) => descriptor.enumerable)\n\t\t.map(([key]) => key)\n\t\t.sort();\n\tconst expected = [...keys].sort();\n\tif (\n\t\tactual.length !== expected.length ||\n\t\tactual.some((key, index) => key !== expected[index]) ||\n\t\tkeys.some((key) => {\n\t\t\tconst descriptor = descriptors[key];\n\t\t\treturn descriptor === undefined || descriptor.enumerable !== true || !(\"value\" in descriptor);\n\t\t})\n\t) {\n\t\tthrow new Error(`${label} has an invalid key set`);\n\t}\n\tconst snapshot: Record<string, unknown> = {};\n\tfor (const key of actual) {\n\t\tconst descriptor = descriptors[key];\n\t\tif (descriptor === undefined || !(\"value\" in descriptor)) {\n\t\t\tthrow new Error(`${label} has an invalid key set`);\n\t\t}\n\t\tsnapshot[key] = descriptor.value;\n\t}\n\treturn snapshot;\n}\n\nfunction ownEnumerableDataProperty(record: Record<string, unknown>, label: string, key: string): unknown {\n\tconst descriptor = Object.getOwnPropertyDescriptor(record, key);\n\tif (descriptor === undefined || descriptor.enumerable !== true || !(\"value\" in descriptor)) {\n\t\tthrow new Error(`${label} has an invalid key set`);\n\t}\n\treturn descriptor.value;\n}\n\nfunction exactArray(value: unknown, label: string): unknown[] {\n\tif (!Array.isArray(value)) throw new Error(`${label} must be an array`);\n\tconst descriptors = Object.getOwnPropertyDescriptors(value);\n\tconst lengthDescriptor = descriptors.length as PropertyDescriptor | undefined;\n\tif (lengthDescriptor === undefined || !(\"value\" in lengthDescriptor)) {\n\t\tthrow new Error(`${label} has invalid index properties`);\n\t}\n\tconst rawLength = lengthDescriptor.value;\n\tif (!Number.isSafeInteger(rawLength) || (rawLength as number) < 0) {\n\t\tthrow new Error(`${label} has invalid index properties`);\n\t}\n\tconst length = rawLength as number;\n\tconst snapshot: unknown[] = [];\n\tfor (let index = 0; index < length; index++) {\n\t\tconst descriptor = descriptors[String(index)];\n\t\tif (descriptor === undefined || descriptor.enumerable !== true || !(\"value\" in descriptor)) {\n\t\t\tthrow new Error(`${label} must use own enumerable data index properties`);\n\t\t}\n\t\tsnapshot.push(descriptor.value);\n\t}\n\tconst enumerableKeys = Object.entries(descriptors)\n\t\t.filter(([, descriptor]) => descriptor.enumerable)\n\t\t.map(([key]) => key);\n\tif (enumerableKeys.length !== length || enumerableKeys.some((key, index) => key !== String(index))) {\n\t\tthrow new Error(`${label} must use own enumerable data index properties`);\n\t}\n\treturn snapshot;\n}\n\n/** Scope producers filter with this so one stray dirty entry cannot poison every receipt. */\nexport function isNormalizedArtifactPath(path: unknown): path is string {\n\treturn (\n\t\ttypeof path === \"string\" &&\n\t\tpath.length > 0 &&\n\t\t!path.includes(\"\\0\") &&\n\t\t!path.includes(\"\\\\\") &&\n\t\t!isAbsolute(path) &&\n\t\t!win32.isAbsolute(path) &&\n\t\tposix.normalize(path) === path &&\n\t\t!path.split(\"/\").some((segment) => segment === \"\" || segment === \".\" || segment === \"..\")\n\t);\n}\n\nfunction assertNormalizedArtifactPath(path: unknown, label = \"artifact path\"): asserts path is string {\n\tif (!isNormalizedArtifactPath(path)) throw new Error(`${label} must be a normalized root-relative path`);\n}\n\nfunction assertSortedUniquePaths(paths: readonly string[], label: string): void {\n\tfor (let index = 0; index < paths.length; index++) {\n\t\tassertNormalizedArtifactPath(paths[index], `${label}[${index}]`);\n\t\tif (index > 0 && paths[index - 1] >= paths[index]) {\n\t\t\tthrow new Error(`${label} must be sorted and contain no duplicates`);\n\t\t}\n\t}\n}\n\nfunction parseSha256(value: unknown, label: string): Sha256Hex {\n\tif (typeof value !== \"string\" || !SHA256_HEX.test(value)) {\n\t\tthrow new Error(`${label} must be a lowercase SHA-256 hex digest`);\n\t}\n\treturn value as Sha256Hex;\n}\n\nfunction parseScope(value: unknown): WorkspaceScope {\n\tconst scope = exactObject(value, \"workspace scope\", [\"root\", \"artifactPaths\"]);\n\tif (typeof scope.root !== \"string\" || scope.root.length === 0 || !isAbsolute(scope.root)) {\n\t\tthrow new Error(\"workspace scope root must be an absolute path\");\n\t}\n\tconst rawArtifactPaths = exactArray(scope.artifactPaths, \"workspace scope artifactPaths\");\n\tif (!rawArtifactPaths.every((path) => typeof path === \"string\")) {\n\t\tthrow new Error(\"workspace scope artifactPaths must be a string array\");\n\t}\n\tconst artifactPaths = [...rawArtifactPaths] as string[];\n\tassertSortedUniquePaths(artifactPaths, \"workspace scope artifactPaths\");\n\treturn Object.freeze({ root: scope.root, artifactPaths: Object.freeze(artifactPaths) });\n}\n\nfunction parseArtifact(value: unknown, index: number): ArtifactState {\n\tif (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n\t\tthrow new Error(`workspace artifact[${index}] must be an object`);\n\t}\n\tconst candidate = value as Record<string, unknown>;\n\tconst state = ownEnumerableDataProperty(candidate, `workspace artifact[${index}]`, \"state\");\n\tif (state === \"missing\") {\n\t\tconst artifact = exactObject(value, `workspace artifact[${index}]`, [\"path\", \"state\"]);\n\t\tassertNormalizedArtifactPath(artifact.path, `workspace artifact[${index}].path`);\n\t\treturn Object.freeze({ path: artifact.path, state: \"missing\" });\n\t}\n\tif (state === \"file\") {\n\t\tconst artifact = exactObject(value, `workspace artifact[${index}]`, [\"path\", \"state\", \"sha256\", \"size\"]);\n\t\tassertNormalizedArtifactPath(artifact.path, `workspace artifact[${index}].path`);\n\t\tif (!Number.isSafeInteger(artifact.size) || (artifact.size as number) < 0) {\n\t\t\tthrow new Error(`workspace artifact[${index}].size must be a non-negative safe integer`);\n\t\t}\n\t\treturn Object.freeze({\n\t\t\tpath: artifact.path,\n\t\t\tstate: \"file\",\n\t\t\tsha256: parseSha256(artifact.sha256, `workspace artifact[${index}].sha256`),\n\t\t\tsize: artifact.size as number,\n\t\t});\n\t}\n\tthrow new Error(`workspace artifact[${index}].state must be file or missing`);\n}\n\nfunction canonicalManifest(scope: WorkspaceScope, artifacts: readonly ArtifactState[]): string {\n\treturn JSON.stringify({\n\t\tkind: \"artifact-set\",\n\t\tscope: { root: scope.root, artifactPaths: scope.artifactPaths },\n\t\tartifacts: artifacts.map((artifact) =>\n\t\t\tartifact.state === \"file\"\n\t\t\t\t? { path: artifact.path, state: artifact.state, sha256: artifact.sha256, size: artifact.size }\n\t\t\t\t: { path: artifact.path, state: artifact.state },\n\t\t),\n\t});\n}\n\n/** Compute the domain-separated digest for an already validated canonical manifest. */\nexport function computeWorkspaceManifestSha256(scope: WorkspaceScope, artifacts: readonly ArtifactState[]): Sha256Hex {\n\treturn sha256(`${WORKSPACE_MANIFEST_DOMAIN}${canonicalManifest(scope, artifacts)}`);\n}\n\n/** Canonical digest committing sorted changed paths, both diff digests, and every selected artifact state. */\nexport function computeGitWorkspaceDirtySha256(\n\tchangedPaths: readonly string[],\n\tstagedDiffSha256: Sha256Hex,\n\tunstagedDiffSha256: Sha256Hex,\n\tartifacts: readonly ArtifactState[],\n): Sha256Hex {\n\tconst canonical = JSON.stringify({\n\t\tchangedPaths,\n\t\tstagedDiffSha256,\n\t\tunstagedDiffSha256,\n\t\tartifacts: artifacts.map((artifact) =>\n\t\t\tartifact.state === \"file\"\n\t\t\t\t? { path: artifact.path, state: artifact.state, sha256: artifact.sha256, size: artifact.size }\n\t\t\t\t: { path: artifact.path, state: artifact.state },\n\t\t),\n\t});\n\treturn sha256(`${GIT_DIRTY_DOMAIN}${canonical}`);\n}\n\n/** Compute the domain-separated digest for an already validated canonical git manifest. */\nexport function computeGitWorkspaceManifestSha256(\n\tscope: WorkspaceScope,\n\tartifacts: readonly ArtifactState[],\n\tgit: GitWorkspaceState,\n): Sha256Hex {\n\tconst canonical = JSON.stringify({\n\t\tkind: \"git\",\n\t\tscope: { root: scope.root, artifactPaths: scope.artifactPaths },\n\t\tartifacts: artifacts.map((artifact) =>\n\t\t\tartifact.state === \"file\"\n\t\t\t\t? { path: artifact.path, state: artifact.state, sha256: artifact.sha256, size: artifact.size }\n\t\t\t\t: { path: artifact.path, state: artifact.state },\n\t\t),\n\t\tgit: {\n\t\t\theadCommit: git.headCommit,\n\t\t\tchangedPaths: git.changedPaths,\n\t\t\tstagedDiffSha256: git.stagedDiffSha256,\n\t\t\tunstagedDiffSha256: git.unstagedDiffSha256,\n\t\t\tdirtySha256: git.dirtySha256,\n\t\t},\n\t});\n\treturn sha256(`${WORKSPACE_MANIFEST_DOMAIN}${canonical}`);\n}\n\n/** A changed path is inside scope when it equals a selected path or lies under a selected directory. */\nfunction isWithinScope(path: string, artifactPaths: readonly string[]): boolean {\n\treturn artifactPaths.some((artifact) => path === artifact || path.startsWith(`${artifact}/`));\n}\n\nfunction digestsEqual(left: Sha256Hex, right: Sha256Hex): boolean {\n\treturn timingSafeEqual(Buffer.from(left, \"hex\"), Buffer.from(right, \"hex\"));\n}\n\nfunction parseGitFingerprint(value: unknown): GitWorkspaceFingerprint {\n\tconst raw = exactObject(value, \"workspace fingerprint\", [\"kind\", \"scope\", \"artifacts\", \"git\", \"manifestSha256\"]);\n\tconst scope = parseScope(raw.scope);\n\tconst rawArtifacts = exactArray(raw.artifacts, \"workspace fingerprint artifacts\");\n\tconst artifacts = rawArtifacts.map((artifact, index) => parseArtifact(artifact, index));\n\tconst artifactPaths = artifacts.map((artifact) => artifact.path);\n\tassertSortedUniquePaths(artifactPaths, \"workspace artifact paths\");\n\tif (\n\t\tartifactPaths.length !== scope.artifactPaths.length ||\n\t\tartifactPaths.some((path, index) => path !== scope.artifactPaths[index])\n\t) {\n\t\tthrow new Error(\"workspace artifacts must exactly match the selected scope paths\");\n\t}\n\tconst rawGit = exactObject(raw.git, \"workspace git state\", [\n\t\t\"headCommit\",\n\t\t\"changedPaths\",\n\t\t\"stagedDiffSha256\",\n\t\t\"unstagedDiffSha256\",\n\t\t\"dirtySha256\",\n\t]);\n\tconst headCommit = rawGit.headCommit;\n\tif (headCommit !== null && (typeof headCommit !== \"string\" || !GIT_COMMIT_HEX.test(headCommit))) {\n\t\tthrow new Error(\"workspace git headCommit must be null or a full lowercase hex object name\");\n\t}\n\tconst rawChangedPaths = exactArray(rawGit.changedPaths, \"workspace git changedPaths\");\n\tif (!rawChangedPaths.every((path) => typeof path === \"string\")) {\n\t\tthrow new Error(\"workspace git changedPaths must be a string array\");\n\t}\n\tconst changedPaths = [...rawChangedPaths] as string[];\n\tassertSortedUniquePaths(changedPaths, \"workspace git changedPaths\");\n\tfor (const path of changedPaths) {\n\t\tif (!isWithinScope(path, scope.artifactPaths)) {\n\t\t\tthrow new Error(`workspace git changed path escapes the selected scope: ${path}`);\n\t\t}\n\t}\n\tconst stagedDiffSha256 = parseSha256(rawGit.stagedDiffSha256, \"workspace git stagedDiffSha256\");\n\tconst unstagedDiffSha256 = parseSha256(rawGit.unstagedDiffSha256, \"workspace git unstagedDiffSha256\");\n\tconst dirtySha256 = parseSha256(rawGit.dirtySha256, \"workspace git dirtySha256\");\n\tif (\n\t\t!digestsEqual(\n\t\t\tdirtySha256,\n\t\t\tcomputeGitWorkspaceDirtySha256(changedPaths, stagedDiffSha256, unstagedDiffSha256, artifacts),\n\t\t)\n\t) {\n\t\tthrow new Error(\"workspace git dirty digest mismatch\");\n\t}\n\tconst git: GitWorkspaceState = Object.freeze({\n\t\theadCommit,\n\t\tchangedPaths: Object.freeze(changedPaths),\n\t\tstagedDiffSha256,\n\t\tunstagedDiffSha256,\n\t\tdirtySha256,\n\t});\n\tconst manifestSha256 = parseSha256(raw.manifestSha256, \"workspace manifestSha256\");\n\tif (!digestsEqual(manifestSha256, computeGitWorkspaceManifestSha256(scope, artifacts, git))) {\n\t\tthrow new Error(\"workspace manifest digest mismatch\");\n\t}\n\treturn Object.freeze({\n\t\tkind: \"git\",\n\t\tscope,\n\t\tartifacts: Object.freeze(artifacts),\n\t\tgit,\n\t\tmanifestSha256,\n\t});\n}\n\n/** Strictly parse and validate a fingerprint of either kind, including its canonical digests. */\nexport function parseWorkspaceFingerprint(value: unknown): WorkspaceFingerprint {\n\tif (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n\t\tthrow new Error(\"workspace fingerprint must be an object\");\n\t}\n\tconst kind = ownEnumerableDataProperty(value as Record<string, unknown>, \"workspace fingerprint\", \"kind\");\n\tif (kind === \"git\") return parseGitFingerprint(value);\n\tif (kind !== \"artifact-set\") {\n\t\tthrow new Error(\"workspace fingerprint kind must be artifact-set or git\");\n\t}\n\treturn parseArtifactSetFingerprint(value);\n}\n\nfunction parseArtifactSetFingerprint(value: unknown): ArtifactSetWorkspaceFingerprint {\n\tconst raw = exactObject(value, \"workspace fingerprint\", [\"kind\", \"scope\", \"artifacts\", \"manifestSha256\"]);\n\tif (raw.kind !== \"artifact-set\") {\n\t\tthrow new Error(\"workspace fingerprint kind must be artifact-set\");\n\t}\n\tconst scope = parseScope(raw.scope);\n\tconst rawArtifacts = exactArray(raw.artifacts, \"workspace fingerprint artifacts\");\n\tconst artifacts = rawArtifacts.map((artifact, index) => parseArtifact(artifact, index));\n\tconst artifactPaths = artifacts.map((artifact) => artifact.path);\n\tassertSortedUniquePaths(artifactPaths, \"workspace artifact paths\");\n\tif (\n\t\tartifactPaths.length !== scope.artifactPaths.length ||\n\t\tartifactPaths.some((path, index) => path !== scope.artifactPaths[index])\n\t) {\n\t\tthrow new Error(\"workspace artifacts must exactly match the selected scope paths\");\n\t}\n\tconst manifestSha256 = parseSha256(raw.manifestSha256, \"workspace manifestSha256\");\n\tconst expected = computeWorkspaceManifestSha256(scope, artifacts);\n\tif (!digestsEqual(manifestSha256, expected)) {\n\t\tthrow new Error(\"workspace manifest digest mismatch\");\n\t}\n\treturn Object.freeze({\n\t\tkind: \"artifact-set\",\n\t\tscope,\n\t\tartifacts: Object.freeze(artifacts),\n\t\tmanifestSha256,\n\t});\n}\n\nfunction statPathWithoutSymlinks(root: string, artifactPath: string): FileStat | undefined {\n\tlet current = root;\n\tconst segments = artifactPath.split(\"/\");\n\tfor (let index = 0; index < segments.length; index++) {\n\t\tcurrent = join(current, segments[index]);\n\t\tlet stat: FileStat;\n\t\ttry {\n\t\t\tstat = lstatSync(current);\n\t\t} catch (error) {\n\t\t\tif (errorCode(error) === \"ENOENT\") return undefined;\n\t\t\tthrow error;\n\t\t}\n\t\tif (stat.isSymbolicLink()) {\n\t\t\tthrow new Error(`workspace artifact path contains a symlink: ${artifactPath}`);\n\t\t}\n\t\tif (index < segments.length - 1 && !stat.isDirectory()) {\n\t\t\tthrow new Error(`workspace artifact parent is not a directory: ${artifactPath}`);\n\t\t}\n\t\tif (index === segments.length - 1) return stat;\n\t}\n\treturn undefined;\n}\n\nfunction captureParentIdentities(root: string, artifactPath: string): readonly ParentIdentity[] {\n\tconst parents: ParentIdentity[] = [];\n\tlet current = root;\n\tfor (const segment of artifactPath.split(\"/\").slice(0, -1)) {\n\t\tcurrent = join(current, segment);\n\t\tconst stat = lstatSync(current);\n\t\tif (stat.isSymbolicLink()) {\n\t\t\tthrow new Error(`workspace artifact path contains a symlink: ${artifactPath}`);\n\t\t}\n\t\tif (!stat.isDirectory()) {\n\t\t\tthrow new Error(`workspace artifact parent is not a directory: ${artifactPath}`);\n\t\t}\n\t\tparents.push({ path: current, stat });\n\t}\n\treturn parents;\n}\n\nfunction hasFileIdentity(stat: FileStat): boolean {\n\treturn stat.dev !== 0 || stat.ino !== 0;\n}\n\nfunction sameFileIdentity(expected: FileStat, actual: FileStat): boolean {\n\tconst expectedHasIdentity = hasFileIdentity(expected);\n\tconst actualHasIdentity = hasFileIdentity(actual);\n\treturn (\n\t\t(!expectedHasIdentity && !actualHasIdentity) ||\n\t\t(expectedHasIdentity && actualHasIdentity && expected.dev === actual.dev && expected.ino === actual.ino)\n\t);\n}\n\nfunction isStrictlyUnder(root: string, path: string): boolean {\n\tconst relativePath = relative(root, path);\n\treturn (\n\t\trelativePath !== \"\" && relativePath !== \"..\" && !relativePath.startsWith(`..${sep}`) && !isAbsolute(relativePath)\n\t);\n}\n\nfunction assertTrustedRoot(root: string, trustedRootStat: FileStat): void {\n\tconst liveRootStat = lstatSync(root);\n\tif (\n\t\tliveRootStat.isSymbolicLink() ||\n\t\t!liveRootStat.isDirectory() ||\n\t\trealpathSync(root) !== root ||\n\t\t!sameFileIdentity(trustedRootStat, liveRootStat)\n\t) {\n\t\tthrow new Error(\"workspace root changed while artifacts were captured\");\n\t}\n}\n\nfunction assertParentIdentities(parents: readonly ParentIdentity[], artifactPath: string): void {\n\tfor (const parent of parents) {\n\t\tconst stat = lstatSync(parent.path);\n\t\tif (stat.isSymbolicLink() || !stat.isDirectory() || !sameFileIdentity(parent.stat, stat)) {\n\t\t\tthrow new Error(`workspace artifact parent changed while it was captured: ${artifactPath}`);\n\t\t}\n\t}\n}\n\nfunction assertLiveArtifactPath(\n\troot: string,\n\ttrustedRootStat: FileStat,\n\ttrustedParents: readonly ParentIdentity[],\n\tartifactPath: string,\n\tfdStat: FileStat,\n): void {\n\tassertTrustedRoot(root, trustedRootStat);\n\tassertParentIdentities(trustedParents, artifactPath);\n\tconst pathStat = statPathWithoutSymlinks(root, artifactPath);\n\tif (pathStat === undefined || !pathStat.isFile() || !sameFileIdentity(fdStat, pathStat)) {\n\t\tthrow new Error(`workspace artifact path was rebound while it was captured: ${artifactPath}`);\n\t}\n\tconst fullPath = join(root, ...artifactPath.split(\"/\"));\n\tconst livePath = realpathSync(fullPath);\n\tif (!isStrictlyUnder(root, livePath)) {\n\t\tthrow new Error(`workspace artifact resolved outside the trusted root: ${artifactPath}`);\n\t}\n\tconst livePathStat = lstatSync(livePath);\n\tif (!livePathStat.isFile() || !sameFileIdentity(fdStat, livePathStat)) {\n\t\tthrow new Error(`workspace artifact path was rebound while it was captured: ${artifactPath}`);\n\t}\n\tassertParentIdentities(trustedParents, artifactPath);\n\tassertTrustedRoot(root, trustedRootStat);\n}\n\nfunction captureFile(\n\troot: string,\n\ttrustedRootStat: FileStat,\n\ttrustedParents: readonly ParentIdentity[],\n\tartifactPath: string,\n): FileArtifactState {\n\tconst fullPath = join(root, ...artifactPath.split(\"/\"));\n\tconst flags = fsConstants.O_RDONLY | (fsConstants.O_NOFOLLOW ?? 0);\n\tlet fd: number;\n\ttry {\n\t\tfd = openSync(fullPath, flags);\n\t} catch (error) {\n\t\tif (errorCode(error) === \"ELOOP\") {\n\t\t\tthrow new Error(`workspace artifact path contains a symlink: ${artifactPath}`);\n\t\t}\n\t\tthrow error;\n\t}\n\ttry {\n\t\tconst before = fstatSync(fd);\n\t\tif (!before.isFile()) {\n\t\t\tthrow new Error(`workspace artifact is not a regular file: ${artifactPath}`);\n\t\t}\n\t\tassertLiveArtifactPath(root, trustedRootStat, trustedParents, artifactPath, before);\n\t\tconst bytes = readFileSync(fd);\n\t\tconst after = fstatSync(fd);\n\t\tassertLiveArtifactPath(root, trustedRootStat, trustedParents, artifactPath, after);\n\t\tif (\n\t\t\t!after.isFile() ||\n\t\t\t!sameFileIdentity(before, after) ||\n\t\t\tbefore.size !== after.size ||\n\t\t\tbefore.mtimeMs !== after.mtimeMs ||\n\t\t\tbefore.ctimeMs !== after.ctimeMs ||\n\t\t\tafter.size !== bytes.byteLength\n\t\t) {\n\t\t\tthrow new Error(`workspace artifact changed while it was captured: ${artifactPath}`);\n\t\t}\n\t\treturn Object.freeze({ path: artifactPath, state: \"file\", sha256: sha256(bytes), size: bytes.byteLength });\n\t} finally {\n\t\tcloseSync(fd);\n\t}\n}\n\nfunction captureSelectedArtifact(\n\troot: string,\n\ttrustedRootStat: FileStat,\n\tartifactPath: string,\n\toptions: WorkspaceFingerprintCaptureOptions,\n): ArtifactState {\n\tassertTrustedRoot(root, trustedRootStat);\n\tconst stat = statPathWithoutSymlinks(root, artifactPath);\n\tif (stat === undefined) {\n\t\tassertTrustedRoot(root, trustedRootStat);\n\t\treturn Object.freeze({ path: artifactPath, state: \"missing\" });\n\t}\n\tif (!stat.isFile()) {\n\t\tthrow new Error(`workspace artifact is not a regular file: ${artifactPath}`);\n\t}\n\tconst trustedParents = captureParentIdentities(root, artifactPath);\n\toptions.faultInjector?.(\"before-artifact-open\", artifactPath);\n\treturn captureFile(root, trustedRootStat, trustedParents, artifactPath);\n}\n\n/** True when the trusted root itself hosts a `.git` entry; a symlinked `.git` fails closed. */\nfunction isGitWorkspaceRoot(root: string): boolean {\n\tlet stat: FileStat;\n\ttry {\n\t\tstat = lstatSync(join(root, \".git\"));\n\t} catch (error) {\n\t\tif (errorCode(error) === \"ENOENT\") return false;\n\t\tthrow error;\n\t}\n\tif (stat.isSymbolicLink()) {\n\t\tthrow new Error(\"workspace root .git must not be a symlink\");\n\t}\n\treturn true;\n}\n\nfunction gitCaptureEnv(): NodeJS.ProcessEnv {\n\tconst env: NodeJS.ProcessEnv = { ...process.env };\n\tfor (const name of UNSAFE_GIT_ENV_VARS) delete env[name];\n\tenv.GIT_OPTIONAL_LOCKS = \"0\";\n\tenv.LC_ALL = \"C\";\n\treturn env;\n}\n\n/** Run one git subcommand with argv boundaries (never a shell) and bounded, time-limited output. */\nfunction runGitCapture(\n\troot: string,\n\targs: readonly string[],\n\tallowedExitCodes: readonly number[] = [0],\n): { status: number; stdout: Buffer } {\n\tconst result = spawnSync(\"git\", [\"-C\", root, ...args], {\n\t\tenv: gitCaptureEnv(),\n\t\tmaxBuffer: MAX_GIT_OUTPUT_BYTES,\n\t\ttimeout: GIT_CAPTURE_TIMEOUT_MS,\n\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\twindowsHide: true,\n\t});\n\tif (result.error !== undefined) {\n\t\tconst code = errorCode(result.error);\n\t\tthrow new Error(`workspace git capture failed to run git: ${code ?? result.error.message}`);\n\t}\n\tif (result.status === null || !allowedExitCodes.includes(result.status)) {\n\t\tthrow new Error(`workspace git capture: git exited with status ${String(result.status)}`);\n\t}\n\treturn { status: result.status, stdout: result.stdout };\n}\n\ninterface GitScopeStatus {\n\treadonly changedPaths: readonly string[];\n}\n\nfunction parseGitStatusOutput(stdout: Buffer, artifactPaths: readonly string[]): GitScopeStatus {\n\tconst changed = new Set<string>();\n\tfor (const entry of stdout.toString(\"utf8\").split(\"\\0\")) {\n\t\tif (entry.length === 0) continue;\n\t\tif (entry.length < 4 || entry[2] !== \" \") {\n\t\t\tthrow new Error(\"workspace git capture: unexpected status entry shape\");\n\t\t}\n\t\tconst path = entry.slice(3);\n\t\tassertNormalizedArtifactPath(path, \"workspace git status path\");\n\t\tif (!isWithinScope(path, artifactPaths)) {\n\t\t\tthrow new Error(`workspace git status path escapes the selected scope: ${path}`);\n\t\t}\n\t\tchanged.add(path);\n\t}\n\treturn { changedPaths: [...changed].sort() };\n}\n\n/**\n * Capture HEAD plus a scope-limited canonical dirty digest for a git work-tree root.\n * All git invocations are argv-only, env-sanitized, bounded, and pathspec-limited with\n * literal magic. Every exact selected path is also captured with symlink-free,\n * root-contained file reads, independent of ignore rules and index flags.\n */\nfunction captureGitWorkspaceFingerprint(\n\troot: string,\n\ttrustedRootStat: FileStat,\n\tscope: WorkspaceScope,\n\toptions: WorkspaceFingerprintCaptureOptions,\n): GitWorkspaceFingerprint {\n\tconst toplevel = runGitCapture(root, [\"rev-parse\", \"--show-toplevel\"]).stdout.toString(\"utf8\").replace(/\\n$/, \"\");\n\tif (toplevel.length === 0 || realpathSync(toplevel) !== root) {\n\t\tthrow new Error(\"workspace root must be the git work-tree top level\");\n\t}\n\tconst head = runGitCapture(root, [\"rev-parse\", \"--verify\", \"--quiet\", \"HEAD^{commit}\"], [0, 1]);\n\tlet headCommit: string | null = null;\n\tif (head.status === 0) {\n\t\tconst hex = head.stdout.toString(\"utf8\").trim();\n\t\tif (!GIT_COMMIT_HEX.test(hex)) {\n\t\t\tthrow new Error(\"workspace git capture: HEAD is not a full hex object name\");\n\t\t}\n\t\theadCommit = hex;\n\t}\n\tconst pathspecs = scope.artifactPaths.map((path) => `:(literal)${path}`);\n\tlet status: GitScopeStatus = { changedPaths: [] };\n\tlet stagedDiff: Buffer = Buffer.alloc(0);\n\tlet unstagedDiff: Buffer = Buffer.alloc(0);\n\tif (pathspecs.length > 0) {\n\t\tstatus = parseGitStatusOutput(\n\t\t\trunGitCapture(root, [\n\t\t\t\t\"-c\",\n\t\t\t\t\"core.fsmonitor=false\",\n\t\t\t\t\"status\",\n\t\t\t\t\"--porcelain=v1\",\n\t\t\t\t\"-z\",\n\t\t\t\t\"--untracked-files=all\",\n\t\t\t\t\"--no-renames\",\n\t\t\t\t\"--ignore-submodules=none\",\n\t\t\t\t\"--\",\n\t\t\t\t...pathspecs,\n\t\t\t]).stdout,\n\t\t\tscope.artifactPaths,\n\t\t);\n\t\tstagedDiff = runGitCapture(root, [\"diff\", \"--cached\", ...GIT_DIFF_ARGS, \"--\", ...pathspecs]).stdout;\n\t\tunstagedDiff = runGitCapture(root, [\"diff\", ...GIT_DIFF_ARGS, \"--\", ...pathspecs]).stdout;\n\t}\n\tconst artifacts = scope.artifactPaths.map((artifactPath) =>\n\t\tcaptureSelectedArtifact(root, trustedRootStat, artifactPath, options),\n\t);\n\tassertTrustedRoot(root, trustedRootStat);\n\tconst changedPaths = Object.freeze([...status.changedPaths]);\n\tconst stagedDiffSha256 = sha256(stagedDiff);\n\tconst unstagedDiffSha256 = sha256(unstagedDiff);\n\tconst git: GitWorkspaceState = Object.freeze({\n\t\theadCommit,\n\t\tchangedPaths,\n\t\tstagedDiffSha256,\n\t\tunstagedDiffSha256,\n\t\tdirtySha256: computeGitWorkspaceDirtySha256(changedPaths, stagedDiffSha256, unstagedDiffSha256, artifacts),\n\t});\n\tconst parsed = parseWorkspaceFingerprint({\n\t\tkind: \"git\",\n\t\tscope,\n\t\tartifacts: Object.freeze(artifacts),\n\t\tgit,\n\t\tmanifestSha256: computeGitWorkspaceManifestSha256(scope, artifacts, git),\n\t});\n\tif (parsed.kind !== \"git\") {\n\t\tthrow new Error(\"workspace git capture produced a non-git fingerprint\");\n\t}\n\treturn parsed;\n}\n\n/**\n * Hash the selected workspace scope. When the trusted root is itself a git work-tree\n * top level, capture HEAD plus a scope-limited canonical dirty digest; otherwise hash\n * the caller-selected artifact set directly and perform no git commands.\n * Phase A assumes a trusted, quiescent workspace: pathname identity checks reject\n * observed rebinds, but do not prove an immutable snapshot against a same-UID adversary.\n */\nexport function captureWorkspaceFingerprint(\n\tscope: WorkspaceScope,\n\toptions: WorkspaceFingerprintCaptureOptions = {},\n): WorkspaceFingerprint {\n\tconst rawScope = exactObject(scope, \"workspace scope\", [\"root\", \"artifactPaths\"]);\n\tconst rawRoot = rawScope.root;\n\tif (typeof rawRoot !== \"string\" || rawRoot.length === 0) {\n\t\tthrow new Error(\"workspace scope root must be a non-empty string\");\n\t}\n\tconst rawArtifactPaths = exactArray(rawScope.artifactPaths, \"workspace scope artifactPaths\");\n\n\tconst seen = new Set<string>();\n\tfor (let index = 0; index < rawArtifactPaths.length; index++) {\n\t\tconst artifactPath = rawArtifactPaths[index];\n\t\tassertNormalizedArtifactPath(artifactPath, `workspace scope artifactPaths[${index}]`);\n\t\tif (seen.has(artifactPath)) {\n\t\t\tthrow new Error(`duplicate workspace artifact path: ${artifactPath}`);\n\t\t}\n\t\tseen.add(artifactPath);\n\t}\n\n\tconst resolvedRoot = resolve(rawRoot);\n\tconst rootStat = lstatSync(resolvedRoot);\n\tif (rootStat.isSymbolicLink()) {\n\t\tthrow new Error(\"workspace root must not be a symlink\");\n\t}\n\tif (!rootStat.isDirectory()) {\n\t\tthrow new Error(\"workspace root must be a directory\");\n\t}\n\tconst root = realpathSync(resolvedRoot);\n\tconst trustedRootStat = lstatSync(root);\n\tconst artifactPaths = (rawArtifactPaths as string[]).sort();\n\tconst canonicalScope: WorkspaceScope = Object.freeze({ root, artifactPaths: Object.freeze(artifactPaths) });\n\tif (isGitWorkspaceRoot(root)) {\n\t\treturn captureGitWorkspaceFingerprint(root, trustedRootStat, canonicalScope, options);\n\t}\n\tconst artifacts = artifactPaths.map((artifactPath) =>\n\t\tcaptureSelectedArtifact(root, trustedRootStat, artifactPath, options),\n\t);\n\tconst fingerprint = {\n\t\tkind: \"artifact-set\",\n\t\tscope: canonicalScope,\n\t\tartifacts: Object.freeze(artifacts),\n\t\tmanifestSha256: computeWorkspaceManifestSha256(canonicalScope, artifacts),\n\t};\n\treturn parseWorkspaceFingerprint(fingerprint);\n}\n"]}
|
|
@@ -108,18 +108,20 @@ function exactArray(value, label) {
|
|
|
108
108
|
}
|
|
109
109
|
return snapshot;
|
|
110
110
|
}
|
|
111
|
+
/** Scope producers filter with this so one stray dirty entry cannot poison every receipt. */
|
|
112
|
+
export function isNormalizedArtifactPath(path) {
|
|
113
|
+
return (typeof path === "string" &&
|
|
114
|
+
path.length > 0 &&
|
|
115
|
+
!path.includes("\0") &&
|
|
116
|
+
!path.includes("\\") &&
|
|
117
|
+
!isAbsolute(path) &&
|
|
118
|
+
!win32.isAbsolute(path) &&
|
|
119
|
+
posix.normalize(path) === path &&
|
|
120
|
+
!path.split("/").some((segment) => segment === "" || segment === "." || segment === ".."));
|
|
121
|
+
}
|
|
111
122
|
function assertNormalizedArtifactPath(path, label = "artifact path") {
|
|
112
|
-
if (
|
|
113
|
-
path.length === 0 ||
|
|
114
|
-
path.includes("\0") ||
|
|
115
|
-
path.includes("\\") ||
|
|
116
|
-
isAbsolute(path) ||
|
|
117
|
-
win32.isAbsolute(path) ||
|
|
118
|
-
path === "." ||
|
|
119
|
-
posix.normalize(path) !== path ||
|
|
120
|
-
path.split("/").some((segment) => segment === "" || segment === "." || segment === "..")) {
|
|
123
|
+
if (!isNormalizedArtifactPath(path))
|
|
121
124
|
throw new Error(`${label} must be a normalized root-relative path`);
|
|
122
|
-
}
|
|
123
125
|
}
|
|
124
126
|
function assertSortedUniquePaths(paths, label) {
|
|
125
127
|
for (let index = 0; index < paths.length; index++) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace-fingerprint.js","sourceRoot":"","sources":["../../src/guardrails/workspace-fingerprint.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawnSync } from \"node:child_process\";\nimport { createHash, timingSafeEqual } from \"node:crypto\";\nimport type { Stats } from \"node:fs\";\nimport {\n\tcloseSync,\n\tconstants as fsConstants,\n\tfstatSync,\n\tlstatSync,\n\topenSync,\n\treadFileSync,\n\trealpathSync,\n} from \"node:fs\";\nimport { isAbsolute, join, posix, relative, resolve, sep, win32 } from \"node:path\";\nimport type {\n\tArtifactSetWorkspaceFingerprint,\n\tArtifactState,\n\tFileArtifactState,\n\tGitWorkspaceFingerprint,\n\tGitWorkspaceState,\n\tSha256Hex,\n\tWorkspaceFingerprint,\n\tWorkspaceScope,\n} from \"../types/evidence.ts\";\n\nconst SHA256_HEX = /^[0-9a-f]{64}$/;\nconst WORKSPACE_MANIFEST_DOMAIN = \"omk:evidence:workspace-fingerprint:v1\\0\";\nconst GIT_DIRTY_DOMAIN = \"omk:evidence:workspace-fingerprint:git-dirty:v1\\0\";\n/** Full SHA-1 or SHA-256 git object names. */\nconst GIT_COMMIT_HEX = /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/;\n/** Hard bound on bytes accepted from one git child process. */\nconst MAX_GIT_OUTPUT_BYTES = 16 * 1024 * 1024;\nconst GIT_CAPTURE_TIMEOUT_MS = 30_000;\n/** Inherited variables that could redirect git away from the trusted root; always stripped. */\nconst UNSAFE_GIT_ENV_VARS = [\n\t\"GIT_ALTERNATE_OBJECT_DIRECTORIES\",\n\t\"GIT_CEILING_DIRECTORIES\",\n\t\"GIT_COMMON_DIR\",\n\t\"GIT_DIFF_OPTS\",\n\t\"GIT_DIR\",\n\t\"GIT_EXTERNAL_DIFF\",\n\t\"GIT_INDEX_FILE\",\n\t\"GIT_NAMESPACE\",\n\t\"GIT_OBJECT_DIRECTORY\",\n\t\"GIT_PREFIX\",\n\t\"GIT_WORK_TREE\",\n] as const;\n/** Deterministic scope-limited diff rendering, independent of local prefix/ext-diff config. */\nconst GIT_DIFF_ARGS = [\n\t\"--full-index\",\n\t\"--binary\",\n\t\"--no-color\",\n\t\"--no-ext-diff\",\n\t\"--no-textconv\",\n\t\"--no-renames\",\n\t\"--ignore-submodules=none\",\n\t\"--src-prefix=a/\",\n\t\"--dst-prefix=b/\",\n] as const;\n\ntype FileStat = Stats;\n\nexport type WorkspaceFingerprintCaptureFaultStage = \"before-artifact-open\";\n\nexport interface WorkspaceFingerprintCaptureOptions {\n\t/** Tests only: mutate a selected path immediately before its artifact is opened. */\n\treadonly faultInjector?: (stage: WorkspaceFingerprintCaptureFaultStage, artifactPath: string) => void;\n}\n\ninterface ParentIdentity {\n\treadonly path: string;\n\treadonly stat: FileStat;\n}\n\nfunction errorCode(error: unknown): string | undefined {\n\treturn typeof error === \"object\" && error !== null && \"code\" in error\n\t\t? String((error as { code?: unknown }).code)\n\t\t: undefined;\n}\n\nfunction sha256(value: Uint8Array | string): Sha256Hex {\n\treturn createHash(\"sha256\").update(value).digest(\"hex\") as Sha256Hex;\n}\n\nfunction exactObject(value: unknown, label: string, keys: readonly string[]): Record<string, unknown> {\n\tif (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n\t\tthrow new Error(`${label} must be an object`);\n\t}\n\tconst descriptors = Object.getOwnPropertyDescriptors(value);\n\tconst actual = Object.entries(descriptors)\n\t\t.filter(([, descriptor]) => descriptor.enumerable)\n\t\t.map(([key]) => key)\n\t\t.sort();\n\tconst expected = [...keys].sort();\n\tif (\n\t\tactual.length !== expected.length ||\n\t\tactual.some((key, index) => key !== expected[index]) ||\n\t\tkeys.some((key) => {\n\t\t\tconst descriptor = descriptors[key];\n\t\t\treturn descriptor === undefined || descriptor.enumerable !== true || !(\"value\" in descriptor);\n\t\t})\n\t) {\n\t\tthrow new Error(`${label} has an invalid key set`);\n\t}\n\tconst snapshot: Record<string, unknown> = {};\n\tfor (const key of actual) {\n\t\tconst descriptor = descriptors[key];\n\t\tif (descriptor === undefined || !(\"value\" in descriptor)) {\n\t\t\tthrow new Error(`${label} has an invalid key set`);\n\t\t}\n\t\tsnapshot[key] = descriptor.value;\n\t}\n\treturn snapshot;\n}\n\nfunction ownEnumerableDataProperty(record: Record<string, unknown>, label: string, key: string): unknown {\n\tconst descriptor = Object.getOwnPropertyDescriptor(record, key);\n\tif (descriptor === undefined || descriptor.enumerable !== true || !(\"value\" in descriptor)) {\n\t\tthrow new Error(`${label} has an invalid key set`);\n\t}\n\treturn descriptor.value;\n}\n\nfunction exactArray(value: unknown, label: string): unknown[] {\n\tif (!Array.isArray(value)) throw new Error(`${label} must be an array`);\n\tconst descriptors = Object.getOwnPropertyDescriptors(value);\n\tconst lengthDescriptor = descriptors.length as PropertyDescriptor | undefined;\n\tif (lengthDescriptor === undefined || !(\"value\" in lengthDescriptor)) {\n\t\tthrow new Error(`${label} has invalid index properties`);\n\t}\n\tconst rawLength = lengthDescriptor.value;\n\tif (!Number.isSafeInteger(rawLength) || (rawLength as number) < 0) {\n\t\tthrow new Error(`${label} has invalid index properties`);\n\t}\n\tconst length = rawLength as number;\n\tconst snapshot: unknown[] = [];\n\tfor (let index = 0; index < length; index++) {\n\t\tconst descriptor = descriptors[String(index)];\n\t\tif (descriptor === undefined || descriptor.enumerable !== true || !(\"value\" in descriptor)) {\n\t\t\tthrow new Error(`${label} must use own enumerable data index properties`);\n\t\t}\n\t\tsnapshot.push(descriptor.value);\n\t}\n\tconst enumerableKeys = Object.entries(descriptors)\n\t\t.filter(([, descriptor]) => descriptor.enumerable)\n\t\t.map(([key]) => key);\n\tif (enumerableKeys.length !== length || enumerableKeys.some((key, index) => key !== String(index))) {\n\t\tthrow new Error(`${label} must use own enumerable data index properties`);\n\t}\n\treturn snapshot;\n}\n\nfunction assertNormalizedArtifactPath(path: unknown, label = \"artifact path\"): asserts path is string {\n\tif (\n\t\ttypeof path !== \"string\" ||\n\t\tpath.length === 0 ||\n\t\tpath.includes(\"\\0\") ||\n\t\tpath.includes(\"\\\\\") ||\n\t\tisAbsolute(path) ||\n\t\twin32.isAbsolute(path) ||\n\t\tpath === \".\" ||\n\t\tposix.normalize(path) !== path ||\n\t\tpath.split(\"/\").some((segment) => segment === \"\" || segment === \".\" || segment === \"..\")\n\t) {\n\t\tthrow new Error(`${label} must be a normalized root-relative path`);\n\t}\n}\n\nfunction assertSortedUniquePaths(paths: readonly string[], label: string): void {\n\tfor (let index = 0; index < paths.length; index++) {\n\t\tassertNormalizedArtifactPath(paths[index], `${label}[${index}]`);\n\t\tif (index > 0 && paths[index - 1] >= paths[index]) {\n\t\t\tthrow new Error(`${label} must be sorted and contain no duplicates`);\n\t\t}\n\t}\n}\n\nfunction parseSha256(value: unknown, label: string): Sha256Hex {\n\tif (typeof value !== \"string\" || !SHA256_HEX.test(value)) {\n\t\tthrow new Error(`${label} must be a lowercase SHA-256 hex digest`);\n\t}\n\treturn value as Sha256Hex;\n}\n\nfunction parseScope(value: unknown): WorkspaceScope {\n\tconst scope = exactObject(value, \"workspace scope\", [\"root\", \"artifactPaths\"]);\n\tif (typeof scope.root !== \"string\" || scope.root.length === 0 || !isAbsolute(scope.root)) {\n\t\tthrow new Error(\"workspace scope root must be an absolute path\");\n\t}\n\tconst rawArtifactPaths = exactArray(scope.artifactPaths, \"workspace scope artifactPaths\");\n\tif (!rawArtifactPaths.every((path) => typeof path === \"string\")) {\n\t\tthrow new Error(\"workspace scope artifactPaths must be a string array\");\n\t}\n\tconst artifactPaths = [...rawArtifactPaths] as string[];\n\tassertSortedUniquePaths(artifactPaths, \"workspace scope artifactPaths\");\n\treturn Object.freeze({ root: scope.root, artifactPaths: Object.freeze(artifactPaths) });\n}\n\nfunction parseArtifact(value: unknown, index: number): ArtifactState {\n\tif (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n\t\tthrow new Error(`workspace artifact[${index}] must be an object`);\n\t}\n\tconst candidate = value as Record<string, unknown>;\n\tconst state = ownEnumerableDataProperty(candidate, `workspace artifact[${index}]`, \"state\");\n\tif (state === \"missing\") {\n\t\tconst artifact = exactObject(value, `workspace artifact[${index}]`, [\"path\", \"state\"]);\n\t\tassertNormalizedArtifactPath(artifact.path, `workspace artifact[${index}].path`);\n\t\treturn Object.freeze({ path: artifact.path, state: \"missing\" });\n\t}\n\tif (state === \"file\") {\n\t\tconst artifact = exactObject(value, `workspace artifact[${index}]`, [\"path\", \"state\", \"sha256\", \"size\"]);\n\t\tassertNormalizedArtifactPath(artifact.path, `workspace artifact[${index}].path`);\n\t\tif (!Number.isSafeInteger(artifact.size) || (artifact.size as number) < 0) {\n\t\t\tthrow new Error(`workspace artifact[${index}].size must be a non-negative safe integer`);\n\t\t}\n\t\treturn Object.freeze({\n\t\t\tpath: artifact.path,\n\t\t\tstate: \"file\",\n\t\t\tsha256: parseSha256(artifact.sha256, `workspace artifact[${index}].sha256`),\n\t\t\tsize: artifact.size as number,\n\t\t});\n\t}\n\tthrow new Error(`workspace artifact[${index}].state must be file or missing`);\n}\n\nfunction canonicalManifest(scope: WorkspaceScope, artifacts: readonly ArtifactState[]): string {\n\treturn JSON.stringify({\n\t\tkind: \"artifact-set\",\n\t\tscope: { root: scope.root, artifactPaths: scope.artifactPaths },\n\t\tartifacts: artifacts.map((artifact) =>\n\t\t\tartifact.state === \"file\"\n\t\t\t\t? { path: artifact.path, state: artifact.state, sha256: artifact.sha256, size: artifact.size }\n\t\t\t\t: { path: artifact.path, state: artifact.state },\n\t\t),\n\t});\n}\n\n/** Compute the domain-separated digest for an already validated canonical manifest. */\nexport function computeWorkspaceManifestSha256(scope: WorkspaceScope, artifacts: readonly ArtifactState[]): Sha256Hex {\n\treturn sha256(`${WORKSPACE_MANIFEST_DOMAIN}${canonicalManifest(scope, artifacts)}`);\n}\n\n/** Canonical digest committing sorted changed paths, both diff digests, and every selected artifact state. */\nexport function computeGitWorkspaceDirtySha256(\n\tchangedPaths: readonly string[],\n\tstagedDiffSha256: Sha256Hex,\n\tunstagedDiffSha256: Sha256Hex,\n\tartifacts: readonly ArtifactState[],\n): Sha256Hex {\n\tconst canonical = JSON.stringify({\n\t\tchangedPaths,\n\t\tstagedDiffSha256,\n\t\tunstagedDiffSha256,\n\t\tartifacts: artifacts.map((artifact) =>\n\t\t\tartifact.state === \"file\"\n\t\t\t\t? { path: artifact.path, state: artifact.state, sha256: artifact.sha256, size: artifact.size }\n\t\t\t\t: { path: artifact.path, state: artifact.state },\n\t\t),\n\t});\n\treturn sha256(`${GIT_DIRTY_DOMAIN}${canonical}`);\n}\n\n/** Compute the domain-separated digest for an already validated canonical git manifest. */\nexport function computeGitWorkspaceManifestSha256(\n\tscope: WorkspaceScope,\n\tartifacts: readonly ArtifactState[],\n\tgit: GitWorkspaceState,\n): Sha256Hex {\n\tconst canonical = JSON.stringify({\n\t\tkind: \"git\",\n\t\tscope: { root: scope.root, artifactPaths: scope.artifactPaths },\n\t\tartifacts: artifacts.map((artifact) =>\n\t\t\tartifact.state === \"file\"\n\t\t\t\t? { path: artifact.path, state: artifact.state, sha256: artifact.sha256, size: artifact.size }\n\t\t\t\t: { path: artifact.path, state: artifact.state },\n\t\t),\n\t\tgit: {\n\t\t\theadCommit: git.headCommit,\n\t\t\tchangedPaths: git.changedPaths,\n\t\t\tstagedDiffSha256: git.stagedDiffSha256,\n\t\t\tunstagedDiffSha256: git.unstagedDiffSha256,\n\t\t\tdirtySha256: git.dirtySha256,\n\t\t},\n\t});\n\treturn sha256(`${WORKSPACE_MANIFEST_DOMAIN}${canonical}`);\n}\n\n/** A changed path is inside scope when it equals a selected path or lies under a selected directory. */\nfunction isWithinScope(path: string, artifactPaths: readonly string[]): boolean {\n\treturn artifactPaths.some((artifact) => path === artifact || path.startsWith(`${artifact}/`));\n}\n\nfunction digestsEqual(left: Sha256Hex, right: Sha256Hex): boolean {\n\treturn timingSafeEqual(Buffer.from(left, \"hex\"), Buffer.from(right, \"hex\"));\n}\n\nfunction parseGitFingerprint(value: unknown): GitWorkspaceFingerprint {\n\tconst raw = exactObject(value, \"workspace fingerprint\", [\"kind\", \"scope\", \"artifacts\", \"git\", \"manifestSha256\"]);\n\tconst scope = parseScope(raw.scope);\n\tconst rawArtifacts = exactArray(raw.artifacts, \"workspace fingerprint artifacts\");\n\tconst artifacts = rawArtifacts.map((artifact, index) => parseArtifact(artifact, index));\n\tconst artifactPaths = artifacts.map((artifact) => artifact.path);\n\tassertSortedUniquePaths(artifactPaths, \"workspace artifact paths\");\n\tif (\n\t\tartifactPaths.length !== scope.artifactPaths.length ||\n\t\tartifactPaths.some((path, index) => path !== scope.artifactPaths[index])\n\t) {\n\t\tthrow new Error(\"workspace artifacts must exactly match the selected scope paths\");\n\t}\n\tconst rawGit = exactObject(raw.git, \"workspace git state\", [\n\t\t\"headCommit\",\n\t\t\"changedPaths\",\n\t\t\"stagedDiffSha256\",\n\t\t\"unstagedDiffSha256\",\n\t\t\"dirtySha256\",\n\t]);\n\tconst headCommit = rawGit.headCommit;\n\tif (headCommit !== null && (typeof headCommit !== \"string\" || !GIT_COMMIT_HEX.test(headCommit))) {\n\t\tthrow new Error(\"workspace git headCommit must be null or a full lowercase hex object name\");\n\t}\n\tconst rawChangedPaths = exactArray(rawGit.changedPaths, \"workspace git changedPaths\");\n\tif (!rawChangedPaths.every((path) => typeof path === \"string\")) {\n\t\tthrow new Error(\"workspace git changedPaths must be a string array\");\n\t}\n\tconst changedPaths = [...rawChangedPaths] as string[];\n\tassertSortedUniquePaths(changedPaths, \"workspace git changedPaths\");\n\tfor (const path of changedPaths) {\n\t\tif (!isWithinScope(path, scope.artifactPaths)) {\n\t\t\tthrow new Error(`workspace git changed path escapes the selected scope: ${path}`);\n\t\t}\n\t}\n\tconst stagedDiffSha256 = parseSha256(rawGit.stagedDiffSha256, \"workspace git stagedDiffSha256\");\n\tconst unstagedDiffSha256 = parseSha256(rawGit.unstagedDiffSha256, \"workspace git unstagedDiffSha256\");\n\tconst dirtySha256 = parseSha256(rawGit.dirtySha256, \"workspace git dirtySha256\");\n\tif (\n\t\t!digestsEqual(\n\t\t\tdirtySha256,\n\t\t\tcomputeGitWorkspaceDirtySha256(changedPaths, stagedDiffSha256, unstagedDiffSha256, artifacts),\n\t\t)\n\t) {\n\t\tthrow new Error(\"workspace git dirty digest mismatch\");\n\t}\n\tconst git: GitWorkspaceState = Object.freeze({\n\t\theadCommit,\n\t\tchangedPaths: Object.freeze(changedPaths),\n\t\tstagedDiffSha256,\n\t\tunstagedDiffSha256,\n\t\tdirtySha256,\n\t});\n\tconst manifestSha256 = parseSha256(raw.manifestSha256, \"workspace manifestSha256\");\n\tif (!digestsEqual(manifestSha256, computeGitWorkspaceManifestSha256(scope, artifacts, git))) {\n\t\tthrow new Error(\"workspace manifest digest mismatch\");\n\t}\n\treturn Object.freeze({\n\t\tkind: \"git\",\n\t\tscope,\n\t\tartifacts: Object.freeze(artifacts),\n\t\tgit,\n\t\tmanifestSha256,\n\t});\n}\n\n/** Strictly parse and validate a fingerprint of either kind, including its canonical digests. */\nexport function parseWorkspaceFingerprint(value: unknown): WorkspaceFingerprint {\n\tif (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n\t\tthrow new Error(\"workspace fingerprint must be an object\");\n\t}\n\tconst kind = ownEnumerableDataProperty(value as Record<string, unknown>, \"workspace fingerprint\", \"kind\");\n\tif (kind === \"git\") return parseGitFingerprint(value);\n\tif (kind !== \"artifact-set\") {\n\t\tthrow new Error(\"workspace fingerprint kind must be artifact-set or git\");\n\t}\n\treturn parseArtifactSetFingerprint(value);\n}\n\nfunction parseArtifactSetFingerprint(value: unknown): ArtifactSetWorkspaceFingerprint {\n\tconst raw = exactObject(value, \"workspace fingerprint\", [\"kind\", \"scope\", \"artifacts\", \"manifestSha256\"]);\n\tif (raw.kind !== \"artifact-set\") {\n\t\tthrow new Error(\"workspace fingerprint kind must be artifact-set\");\n\t}\n\tconst scope = parseScope(raw.scope);\n\tconst rawArtifacts = exactArray(raw.artifacts, \"workspace fingerprint artifacts\");\n\tconst artifacts = rawArtifacts.map((artifact, index) => parseArtifact(artifact, index));\n\tconst artifactPaths = artifacts.map((artifact) => artifact.path);\n\tassertSortedUniquePaths(artifactPaths, \"workspace artifact paths\");\n\tif (\n\t\tartifactPaths.length !== scope.artifactPaths.length ||\n\t\tartifactPaths.some((path, index) => path !== scope.artifactPaths[index])\n\t) {\n\t\tthrow new Error(\"workspace artifacts must exactly match the selected scope paths\");\n\t}\n\tconst manifestSha256 = parseSha256(raw.manifestSha256, \"workspace manifestSha256\");\n\tconst expected = computeWorkspaceManifestSha256(scope, artifacts);\n\tif (!digestsEqual(manifestSha256, expected)) {\n\t\tthrow new Error(\"workspace manifest digest mismatch\");\n\t}\n\treturn Object.freeze({\n\t\tkind: \"artifact-set\",\n\t\tscope,\n\t\tartifacts: Object.freeze(artifacts),\n\t\tmanifestSha256,\n\t});\n}\n\nfunction statPathWithoutSymlinks(root: string, artifactPath: string): FileStat | undefined {\n\tlet current = root;\n\tconst segments = artifactPath.split(\"/\");\n\tfor (let index = 0; index < segments.length; index++) {\n\t\tcurrent = join(current, segments[index]);\n\t\tlet stat: FileStat;\n\t\ttry {\n\t\t\tstat = lstatSync(current);\n\t\t} catch (error) {\n\t\t\tif (errorCode(error) === \"ENOENT\") return undefined;\n\t\t\tthrow error;\n\t\t}\n\t\tif (stat.isSymbolicLink()) {\n\t\t\tthrow new Error(`workspace artifact path contains a symlink: ${artifactPath}`);\n\t\t}\n\t\tif (index < segments.length - 1 && !stat.isDirectory()) {\n\t\t\tthrow new Error(`workspace artifact parent is not a directory: ${artifactPath}`);\n\t\t}\n\t\tif (index === segments.length - 1) return stat;\n\t}\n\treturn undefined;\n}\n\nfunction captureParentIdentities(root: string, artifactPath: string): readonly ParentIdentity[] {\n\tconst parents: ParentIdentity[] = [];\n\tlet current = root;\n\tfor (const segment of artifactPath.split(\"/\").slice(0, -1)) {\n\t\tcurrent = join(current, segment);\n\t\tconst stat = lstatSync(current);\n\t\tif (stat.isSymbolicLink()) {\n\t\t\tthrow new Error(`workspace artifact path contains a symlink: ${artifactPath}`);\n\t\t}\n\t\tif (!stat.isDirectory()) {\n\t\t\tthrow new Error(`workspace artifact parent is not a directory: ${artifactPath}`);\n\t\t}\n\t\tparents.push({ path: current, stat });\n\t}\n\treturn parents;\n}\n\nfunction hasFileIdentity(stat: FileStat): boolean {\n\treturn stat.dev !== 0 || stat.ino !== 0;\n}\n\nfunction sameFileIdentity(expected: FileStat, actual: FileStat): boolean {\n\tconst expectedHasIdentity = hasFileIdentity(expected);\n\tconst actualHasIdentity = hasFileIdentity(actual);\n\treturn (\n\t\t(!expectedHasIdentity && !actualHasIdentity) ||\n\t\t(expectedHasIdentity && actualHasIdentity && expected.dev === actual.dev && expected.ino === actual.ino)\n\t);\n}\n\nfunction isStrictlyUnder(root: string, path: string): boolean {\n\tconst relativePath = relative(root, path);\n\treturn (\n\t\trelativePath !== \"\" && relativePath !== \"..\" && !relativePath.startsWith(`..${sep}`) && !isAbsolute(relativePath)\n\t);\n}\n\nfunction assertTrustedRoot(root: string, trustedRootStat: FileStat): void {\n\tconst liveRootStat = lstatSync(root);\n\tif (\n\t\tliveRootStat.isSymbolicLink() ||\n\t\t!liveRootStat.isDirectory() ||\n\t\trealpathSync(root) !== root ||\n\t\t!sameFileIdentity(trustedRootStat, liveRootStat)\n\t) {\n\t\tthrow new Error(\"workspace root changed while artifacts were captured\");\n\t}\n}\n\nfunction assertParentIdentities(parents: readonly ParentIdentity[], artifactPath: string): void {\n\tfor (const parent of parents) {\n\t\tconst stat = lstatSync(parent.path);\n\t\tif (stat.isSymbolicLink() || !stat.isDirectory() || !sameFileIdentity(parent.stat, stat)) {\n\t\t\tthrow new Error(`workspace artifact parent changed while it was captured: ${artifactPath}`);\n\t\t}\n\t}\n}\n\nfunction assertLiveArtifactPath(\n\troot: string,\n\ttrustedRootStat: FileStat,\n\ttrustedParents: readonly ParentIdentity[],\n\tartifactPath: string,\n\tfdStat: FileStat,\n): void {\n\tassertTrustedRoot(root, trustedRootStat);\n\tassertParentIdentities(trustedParents, artifactPath);\n\tconst pathStat = statPathWithoutSymlinks(root, artifactPath);\n\tif (pathStat === undefined || !pathStat.isFile() || !sameFileIdentity(fdStat, pathStat)) {\n\t\tthrow new Error(`workspace artifact path was rebound while it was captured: ${artifactPath}`);\n\t}\n\tconst fullPath = join(root, ...artifactPath.split(\"/\"));\n\tconst livePath = realpathSync(fullPath);\n\tif (!isStrictlyUnder(root, livePath)) {\n\t\tthrow new Error(`workspace artifact resolved outside the trusted root: ${artifactPath}`);\n\t}\n\tconst livePathStat = lstatSync(livePath);\n\tif (!livePathStat.isFile() || !sameFileIdentity(fdStat, livePathStat)) {\n\t\tthrow new Error(`workspace artifact path was rebound while it was captured: ${artifactPath}`);\n\t}\n\tassertParentIdentities(trustedParents, artifactPath);\n\tassertTrustedRoot(root, trustedRootStat);\n}\n\nfunction captureFile(\n\troot: string,\n\ttrustedRootStat: FileStat,\n\ttrustedParents: readonly ParentIdentity[],\n\tartifactPath: string,\n): FileArtifactState {\n\tconst fullPath = join(root, ...artifactPath.split(\"/\"));\n\tconst flags = fsConstants.O_RDONLY | (fsConstants.O_NOFOLLOW ?? 0);\n\tlet fd: number;\n\ttry {\n\t\tfd = openSync(fullPath, flags);\n\t} catch (error) {\n\t\tif (errorCode(error) === \"ELOOP\") {\n\t\t\tthrow new Error(`workspace artifact path contains a symlink: ${artifactPath}`);\n\t\t}\n\t\tthrow error;\n\t}\n\ttry {\n\t\tconst before = fstatSync(fd);\n\t\tif (!before.isFile()) {\n\t\t\tthrow new Error(`workspace artifact is not a regular file: ${artifactPath}`);\n\t\t}\n\t\tassertLiveArtifactPath(root, trustedRootStat, trustedParents, artifactPath, before);\n\t\tconst bytes = readFileSync(fd);\n\t\tconst after = fstatSync(fd);\n\t\tassertLiveArtifactPath(root, trustedRootStat, trustedParents, artifactPath, after);\n\t\tif (\n\t\t\t!after.isFile() ||\n\t\t\t!sameFileIdentity(before, after) ||\n\t\t\tbefore.size !== after.size ||\n\t\t\tbefore.mtimeMs !== after.mtimeMs ||\n\t\t\tbefore.ctimeMs !== after.ctimeMs ||\n\t\t\tafter.size !== bytes.byteLength\n\t\t) {\n\t\t\tthrow new Error(`workspace artifact changed while it was captured: ${artifactPath}`);\n\t\t}\n\t\treturn Object.freeze({ path: artifactPath, state: \"file\", sha256: sha256(bytes), size: bytes.byteLength });\n\t} finally {\n\t\tcloseSync(fd);\n\t}\n}\n\nfunction captureSelectedArtifact(\n\troot: string,\n\ttrustedRootStat: FileStat,\n\tartifactPath: string,\n\toptions: WorkspaceFingerprintCaptureOptions,\n): ArtifactState {\n\tassertTrustedRoot(root, trustedRootStat);\n\tconst stat = statPathWithoutSymlinks(root, artifactPath);\n\tif (stat === undefined) {\n\t\tassertTrustedRoot(root, trustedRootStat);\n\t\treturn Object.freeze({ path: artifactPath, state: \"missing\" });\n\t}\n\tif (!stat.isFile()) {\n\t\tthrow new Error(`workspace artifact is not a regular file: ${artifactPath}`);\n\t}\n\tconst trustedParents = captureParentIdentities(root, artifactPath);\n\toptions.faultInjector?.(\"before-artifact-open\", artifactPath);\n\treturn captureFile(root, trustedRootStat, trustedParents, artifactPath);\n}\n\n/** True when the trusted root itself hosts a `.git` entry; a symlinked `.git` fails closed. */\nfunction isGitWorkspaceRoot(root: string): boolean {\n\tlet stat: FileStat;\n\ttry {\n\t\tstat = lstatSync(join(root, \".git\"));\n\t} catch (error) {\n\t\tif (errorCode(error) === \"ENOENT\") return false;\n\t\tthrow error;\n\t}\n\tif (stat.isSymbolicLink()) {\n\t\tthrow new Error(\"workspace root .git must not be a symlink\");\n\t}\n\treturn true;\n}\n\nfunction gitCaptureEnv(): NodeJS.ProcessEnv {\n\tconst env: NodeJS.ProcessEnv = { ...process.env };\n\tfor (const name of UNSAFE_GIT_ENV_VARS) delete env[name];\n\tenv.GIT_OPTIONAL_LOCKS = \"0\";\n\tenv.LC_ALL = \"C\";\n\treturn env;\n}\n\n/** Run one git subcommand with argv boundaries (never a shell) and bounded, time-limited output. */\nfunction runGitCapture(\n\troot: string,\n\targs: readonly string[],\n\tallowedExitCodes: readonly number[] = [0],\n): { status: number; stdout: Buffer } {\n\tconst result = spawnSync(\"git\", [\"-C\", root, ...args], {\n\t\tenv: gitCaptureEnv(),\n\t\tmaxBuffer: MAX_GIT_OUTPUT_BYTES,\n\t\ttimeout: GIT_CAPTURE_TIMEOUT_MS,\n\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\twindowsHide: true,\n\t});\n\tif (result.error !== undefined) {\n\t\tconst code = errorCode(result.error);\n\t\tthrow new Error(`workspace git capture failed to run git: ${code ?? result.error.message}`);\n\t}\n\tif (result.status === null || !allowedExitCodes.includes(result.status)) {\n\t\tthrow new Error(`workspace git capture: git exited with status ${String(result.status)}`);\n\t}\n\treturn { status: result.status, stdout: result.stdout };\n}\n\ninterface GitScopeStatus {\n\treadonly changedPaths: readonly string[];\n}\n\nfunction parseGitStatusOutput(stdout: Buffer, artifactPaths: readonly string[]): GitScopeStatus {\n\tconst changed = new Set<string>();\n\tfor (const entry of stdout.toString(\"utf8\").split(\"\\0\")) {\n\t\tif (entry.length === 0) continue;\n\t\tif (entry.length < 4 || entry[2] !== \" \") {\n\t\t\tthrow new Error(\"workspace git capture: unexpected status entry shape\");\n\t\t}\n\t\tconst path = entry.slice(3);\n\t\tassertNormalizedArtifactPath(path, \"workspace git status path\");\n\t\tif (!isWithinScope(path, artifactPaths)) {\n\t\t\tthrow new Error(`workspace git status path escapes the selected scope: ${path}`);\n\t\t}\n\t\tchanged.add(path);\n\t}\n\treturn { changedPaths: [...changed].sort() };\n}\n\n/**\n * Capture HEAD plus a scope-limited canonical dirty digest for a git work-tree root.\n * All git invocations are argv-only, env-sanitized, bounded, and pathspec-limited with\n * literal magic. Every exact selected path is also captured with symlink-free,\n * root-contained file reads, independent of ignore rules and index flags.\n */\nfunction captureGitWorkspaceFingerprint(\n\troot: string,\n\ttrustedRootStat: FileStat,\n\tscope: WorkspaceScope,\n\toptions: WorkspaceFingerprintCaptureOptions,\n): GitWorkspaceFingerprint {\n\tconst toplevel = runGitCapture(root, [\"rev-parse\", \"--show-toplevel\"]).stdout.toString(\"utf8\").replace(/\\n$/, \"\");\n\tif (toplevel.length === 0 || realpathSync(toplevel) !== root) {\n\t\tthrow new Error(\"workspace root must be the git work-tree top level\");\n\t}\n\tconst head = runGitCapture(root, [\"rev-parse\", \"--verify\", \"--quiet\", \"HEAD^{commit}\"], [0, 1]);\n\tlet headCommit: string | null = null;\n\tif (head.status === 0) {\n\t\tconst hex = head.stdout.toString(\"utf8\").trim();\n\t\tif (!GIT_COMMIT_HEX.test(hex)) {\n\t\t\tthrow new Error(\"workspace git capture: HEAD is not a full hex object name\");\n\t\t}\n\t\theadCommit = hex;\n\t}\n\tconst pathspecs = scope.artifactPaths.map((path) => `:(literal)${path}`);\n\tlet status: GitScopeStatus = { changedPaths: [] };\n\tlet stagedDiff: Buffer = Buffer.alloc(0);\n\tlet unstagedDiff: Buffer = Buffer.alloc(0);\n\tif (pathspecs.length > 0) {\n\t\tstatus = parseGitStatusOutput(\n\t\t\trunGitCapture(root, [\n\t\t\t\t\"-c\",\n\t\t\t\t\"core.fsmonitor=false\",\n\t\t\t\t\"status\",\n\t\t\t\t\"--porcelain=v1\",\n\t\t\t\t\"-z\",\n\t\t\t\t\"--untracked-files=all\",\n\t\t\t\t\"--no-renames\",\n\t\t\t\t\"--ignore-submodules=none\",\n\t\t\t\t\"--\",\n\t\t\t\t...pathspecs,\n\t\t\t]).stdout,\n\t\t\tscope.artifactPaths,\n\t\t);\n\t\tstagedDiff = runGitCapture(root, [\"diff\", \"--cached\", ...GIT_DIFF_ARGS, \"--\", ...pathspecs]).stdout;\n\t\tunstagedDiff = runGitCapture(root, [\"diff\", ...GIT_DIFF_ARGS, \"--\", ...pathspecs]).stdout;\n\t}\n\tconst artifacts = scope.artifactPaths.map((artifactPath) =>\n\t\tcaptureSelectedArtifact(root, trustedRootStat, artifactPath, options),\n\t);\n\tassertTrustedRoot(root, trustedRootStat);\n\tconst changedPaths = Object.freeze([...status.changedPaths]);\n\tconst stagedDiffSha256 = sha256(stagedDiff);\n\tconst unstagedDiffSha256 = sha256(unstagedDiff);\n\tconst git: GitWorkspaceState = Object.freeze({\n\t\theadCommit,\n\t\tchangedPaths,\n\t\tstagedDiffSha256,\n\t\tunstagedDiffSha256,\n\t\tdirtySha256: computeGitWorkspaceDirtySha256(changedPaths, stagedDiffSha256, unstagedDiffSha256, artifacts),\n\t});\n\tconst parsed = parseWorkspaceFingerprint({\n\t\tkind: \"git\",\n\t\tscope,\n\t\tartifacts: Object.freeze(artifacts),\n\t\tgit,\n\t\tmanifestSha256: computeGitWorkspaceManifestSha256(scope, artifacts, git),\n\t});\n\tif (parsed.kind !== \"git\") {\n\t\tthrow new Error(\"workspace git capture produced a non-git fingerprint\");\n\t}\n\treturn parsed;\n}\n\n/**\n * Hash the selected workspace scope. When the trusted root is itself a git work-tree\n * top level, capture HEAD plus a scope-limited canonical dirty digest; otherwise hash\n * the caller-selected artifact set directly and perform no git commands.\n * Phase A assumes a trusted, quiescent workspace: pathname identity checks reject\n * observed rebinds, but do not prove an immutable snapshot against a same-UID adversary.\n */\nexport function captureWorkspaceFingerprint(\n\tscope: WorkspaceScope,\n\toptions: WorkspaceFingerprintCaptureOptions = {},\n): WorkspaceFingerprint {\n\tconst rawScope = exactObject(scope, \"workspace scope\", [\"root\", \"artifactPaths\"]);\n\tconst rawRoot = rawScope.root;\n\tif (typeof rawRoot !== \"string\" || rawRoot.length === 0) {\n\t\tthrow new Error(\"workspace scope root must be a non-empty string\");\n\t}\n\tconst rawArtifactPaths = exactArray(rawScope.artifactPaths, \"workspace scope artifactPaths\");\n\n\tconst seen = new Set<string>();\n\tfor (let index = 0; index < rawArtifactPaths.length; index++) {\n\t\tconst artifactPath = rawArtifactPaths[index];\n\t\tassertNormalizedArtifactPath(artifactPath, `workspace scope artifactPaths[${index}]`);\n\t\tif (seen.has(artifactPath)) {\n\t\t\tthrow new Error(`duplicate workspace artifact path: ${artifactPath}`);\n\t\t}\n\t\tseen.add(artifactPath);\n\t}\n\n\tconst resolvedRoot = resolve(rawRoot);\n\tconst rootStat = lstatSync(resolvedRoot);\n\tif (rootStat.isSymbolicLink()) {\n\t\tthrow new Error(\"workspace root must not be a symlink\");\n\t}\n\tif (!rootStat.isDirectory()) {\n\t\tthrow new Error(\"workspace root must be a directory\");\n\t}\n\tconst root = realpathSync(resolvedRoot);\n\tconst trustedRootStat = lstatSync(root);\n\tconst artifactPaths = (rawArtifactPaths as string[]).sort();\n\tconst canonicalScope: WorkspaceScope = Object.freeze({ root, artifactPaths: Object.freeze(artifactPaths) });\n\tif (isGitWorkspaceRoot(root)) {\n\t\treturn captureGitWorkspaceFingerprint(root, trustedRootStat, canonicalScope, options);\n\t}\n\tconst artifacts = artifactPaths.map((artifactPath) =>\n\t\tcaptureSelectedArtifact(root, trustedRootStat, artifactPath, options),\n\t);\n\tconst fingerprint = {\n\t\tkind: \"artifact-set\",\n\t\tscope: canonicalScope,\n\t\tartifacts: Object.freeze(artifacts),\n\t\tmanifestSha256: computeWorkspaceManifestSha256(canonicalScope, artifacts),\n\t};\n\treturn parseWorkspaceFingerprint(fingerprint);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"workspace-fingerprint.js","sourceRoot":"","sources":["../../src/guardrails/workspace-fingerprint.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawnSync } from \"node:child_process\";\nimport { createHash, timingSafeEqual } from \"node:crypto\";\nimport type { Stats } from \"node:fs\";\nimport {\n\tcloseSync,\n\tconstants as fsConstants,\n\tfstatSync,\n\tlstatSync,\n\topenSync,\n\treadFileSync,\n\trealpathSync,\n} from \"node:fs\";\nimport { isAbsolute, join, posix, relative, resolve, sep, win32 } from \"node:path\";\nimport type {\n\tArtifactSetWorkspaceFingerprint,\n\tArtifactState,\n\tFileArtifactState,\n\tGitWorkspaceFingerprint,\n\tGitWorkspaceState,\n\tSha256Hex,\n\tWorkspaceFingerprint,\n\tWorkspaceScope,\n} from \"../types/evidence.ts\";\n\nconst SHA256_HEX = /^[0-9a-f]{64}$/;\nconst WORKSPACE_MANIFEST_DOMAIN = \"omk:evidence:workspace-fingerprint:v1\\0\";\nconst GIT_DIRTY_DOMAIN = \"omk:evidence:workspace-fingerprint:git-dirty:v1\\0\";\n/** Full SHA-1 or SHA-256 git object names. */\nconst GIT_COMMIT_HEX = /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/;\n/** Hard bound on bytes accepted from one git child process. */\nconst MAX_GIT_OUTPUT_BYTES = 16 * 1024 * 1024;\nconst GIT_CAPTURE_TIMEOUT_MS = 30_000;\n/** Inherited variables that could redirect git away from the trusted root; always stripped. */\nconst UNSAFE_GIT_ENV_VARS = [\n\t\"GIT_ALTERNATE_OBJECT_DIRECTORIES\",\n\t\"GIT_CEILING_DIRECTORIES\",\n\t\"GIT_COMMON_DIR\",\n\t\"GIT_DIFF_OPTS\",\n\t\"GIT_DIR\",\n\t\"GIT_EXTERNAL_DIFF\",\n\t\"GIT_INDEX_FILE\",\n\t\"GIT_NAMESPACE\",\n\t\"GIT_OBJECT_DIRECTORY\",\n\t\"GIT_PREFIX\",\n\t\"GIT_WORK_TREE\",\n] as const;\n/** Deterministic scope-limited diff rendering, independent of local prefix/ext-diff config. */\nconst GIT_DIFF_ARGS = [\n\t\"--full-index\",\n\t\"--binary\",\n\t\"--no-color\",\n\t\"--no-ext-diff\",\n\t\"--no-textconv\",\n\t\"--no-renames\",\n\t\"--ignore-submodules=none\",\n\t\"--src-prefix=a/\",\n\t\"--dst-prefix=b/\",\n] as const;\n\ntype FileStat = Stats;\n\nexport type WorkspaceFingerprintCaptureFaultStage = \"before-artifact-open\";\n\nexport interface WorkspaceFingerprintCaptureOptions {\n\t/** Tests only: mutate a selected path immediately before its artifact is opened. */\n\treadonly faultInjector?: (stage: WorkspaceFingerprintCaptureFaultStage, artifactPath: string) => void;\n}\n\ninterface ParentIdentity {\n\treadonly path: string;\n\treadonly stat: FileStat;\n}\n\nfunction errorCode(error: unknown): string | undefined {\n\treturn typeof error === \"object\" && error !== null && \"code\" in error\n\t\t? String((error as { code?: unknown }).code)\n\t\t: undefined;\n}\n\nfunction sha256(value: Uint8Array | string): Sha256Hex {\n\treturn createHash(\"sha256\").update(value).digest(\"hex\") as Sha256Hex;\n}\n\nfunction exactObject(value: unknown, label: string, keys: readonly string[]): Record<string, unknown> {\n\tif (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n\t\tthrow new Error(`${label} must be an object`);\n\t}\n\tconst descriptors = Object.getOwnPropertyDescriptors(value);\n\tconst actual = Object.entries(descriptors)\n\t\t.filter(([, descriptor]) => descriptor.enumerable)\n\t\t.map(([key]) => key)\n\t\t.sort();\n\tconst expected = [...keys].sort();\n\tif (\n\t\tactual.length !== expected.length ||\n\t\tactual.some((key, index) => key !== expected[index]) ||\n\t\tkeys.some((key) => {\n\t\t\tconst descriptor = descriptors[key];\n\t\t\treturn descriptor === undefined || descriptor.enumerable !== true || !(\"value\" in descriptor);\n\t\t})\n\t) {\n\t\tthrow new Error(`${label} has an invalid key set`);\n\t}\n\tconst snapshot: Record<string, unknown> = {};\n\tfor (const key of actual) {\n\t\tconst descriptor = descriptors[key];\n\t\tif (descriptor === undefined || !(\"value\" in descriptor)) {\n\t\t\tthrow new Error(`${label} has an invalid key set`);\n\t\t}\n\t\tsnapshot[key] = descriptor.value;\n\t}\n\treturn snapshot;\n}\n\nfunction ownEnumerableDataProperty(record: Record<string, unknown>, label: string, key: string): unknown {\n\tconst descriptor = Object.getOwnPropertyDescriptor(record, key);\n\tif (descriptor === undefined || descriptor.enumerable !== true || !(\"value\" in descriptor)) {\n\t\tthrow new Error(`${label} has an invalid key set`);\n\t}\n\treturn descriptor.value;\n}\n\nfunction exactArray(value: unknown, label: string): unknown[] {\n\tif (!Array.isArray(value)) throw new Error(`${label} must be an array`);\n\tconst descriptors = Object.getOwnPropertyDescriptors(value);\n\tconst lengthDescriptor = descriptors.length as PropertyDescriptor | undefined;\n\tif (lengthDescriptor === undefined || !(\"value\" in lengthDescriptor)) {\n\t\tthrow new Error(`${label} has invalid index properties`);\n\t}\n\tconst rawLength = lengthDescriptor.value;\n\tif (!Number.isSafeInteger(rawLength) || (rawLength as number) < 0) {\n\t\tthrow new Error(`${label} has invalid index properties`);\n\t}\n\tconst length = rawLength as number;\n\tconst snapshot: unknown[] = [];\n\tfor (let index = 0; index < length; index++) {\n\t\tconst descriptor = descriptors[String(index)];\n\t\tif (descriptor === undefined || descriptor.enumerable !== true || !(\"value\" in descriptor)) {\n\t\t\tthrow new Error(`${label} must use own enumerable data index properties`);\n\t\t}\n\t\tsnapshot.push(descriptor.value);\n\t}\n\tconst enumerableKeys = Object.entries(descriptors)\n\t\t.filter(([, descriptor]) => descriptor.enumerable)\n\t\t.map(([key]) => key);\n\tif (enumerableKeys.length !== length || enumerableKeys.some((key, index) => key !== String(index))) {\n\t\tthrow new Error(`${label} must use own enumerable data index properties`);\n\t}\n\treturn snapshot;\n}\n\n/** Scope producers filter with this so one stray dirty entry cannot poison every receipt. */\nexport function isNormalizedArtifactPath(path: unknown): path is string {\n\treturn (\n\t\ttypeof path === \"string\" &&\n\t\tpath.length > 0 &&\n\t\t!path.includes(\"\\0\") &&\n\t\t!path.includes(\"\\\\\") &&\n\t\t!isAbsolute(path) &&\n\t\t!win32.isAbsolute(path) &&\n\t\tposix.normalize(path) === path &&\n\t\t!path.split(\"/\").some((segment) => segment === \"\" || segment === \".\" || segment === \"..\")\n\t);\n}\n\nfunction assertNormalizedArtifactPath(path: unknown, label = \"artifact path\"): asserts path is string {\n\tif (!isNormalizedArtifactPath(path)) throw new Error(`${label} must be a normalized root-relative path`);\n}\n\nfunction assertSortedUniquePaths(paths: readonly string[], label: string): void {\n\tfor (let index = 0; index < paths.length; index++) {\n\t\tassertNormalizedArtifactPath(paths[index], `${label}[${index}]`);\n\t\tif (index > 0 && paths[index - 1] >= paths[index]) {\n\t\t\tthrow new Error(`${label} must be sorted and contain no duplicates`);\n\t\t}\n\t}\n}\n\nfunction parseSha256(value: unknown, label: string): Sha256Hex {\n\tif (typeof value !== \"string\" || !SHA256_HEX.test(value)) {\n\t\tthrow new Error(`${label} must be a lowercase SHA-256 hex digest`);\n\t}\n\treturn value as Sha256Hex;\n}\n\nfunction parseScope(value: unknown): WorkspaceScope {\n\tconst scope = exactObject(value, \"workspace scope\", [\"root\", \"artifactPaths\"]);\n\tif (typeof scope.root !== \"string\" || scope.root.length === 0 || !isAbsolute(scope.root)) {\n\t\tthrow new Error(\"workspace scope root must be an absolute path\");\n\t}\n\tconst rawArtifactPaths = exactArray(scope.artifactPaths, \"workspace scope artifactPaths\");\n\tif (!rawArtifactPaths.every((path) => typeof path === \"string\")) {\n\t\tthrow new Error(\"workspace scope artifactPaths must be a string array\");\n\t}\n\tconst artifactPaths = [...rawArtifactPaths] as string[];\n\tassertSortedUniquePaths(artifactPaths, \"workspace scope artifactPaths\");\n\treturn Object.freeze({ root: scope.root, artifactPaths: Object.freeze(artifactPaths) });\n}\n\nfunction parseArtifact(value: unknown, index: number): ArtifactState {\n\tif (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n\t\tthrow new Error(`workspace artifact[${index}] must be an object`);\n\t}\n\tconst candidate = value as Record<string, unknown>;\n\tconst state = ownEnumerableDataProperty(candidate, `workspace artifact[${index}]`, \"state\");\n\tif (state === \"missing\") {\n\t\tconst artifact = exactObject(value, `workspace artifact[${index}]`, [\"path\", \"state\"]);\n\t\tassertNormalizedArtifactPath(artifact.path, `workspace artifact[${index}].path`);\n\t\treturn Object.freeze({ path: artifact.path, state: \"missing\" });\n\t}\n\tif (state === \"file\") {\n\t\tconst artifact = exactObject(value, `workspace artifact[${index}]`, [\"path\", \"state\", \"sha256\", \"size\"]);\n\t\tassertNormalizedArtifactPath(artifact.path, `workspace artifact[${index}].path`);\n\t\tif (!Number.isSafeInteger(artifact.size) || (artifact.size as number) < 0) {\n\t\t\tthrow new Error(`workspace artifact[${index}].size must be a non-negative safe integer`);\n\t\t}\n\t\treturn Object.freeze({\n\t\t\tpath: artifact.path,\n\t\t\tstate: \"file\",\n\t\t\tsha256: parseSha256(artifact.sha256, `workspace artifact[${index}].sha256`),\n\t\t\tsize: artifact.size as number,\n\t\t});\n\t}\n\tthrow new Error(`workspace artifact[${index}].state must be file or missing`);\n}\n\nfunction canonicalManifest(scope: WorkspaceScope, artifacts: readonly ArtifactState[]): string {\n\treturn JSON.stringify({\n\t\tkind: \"artifact-set\",\n\t\tscope: { root: scope.root, artifactPaths: scope.artifactPaths },\n\t\tartifacts: artifacts.map((artifact) =>\n\t\t\tartifact.state === \"file\"\n\t\t\t\t? { path: artifact.path, state: artifact.state, sha256: artifact.sha256, size: artifact.size }\n\t\t\t\t: { path: artifact.path, state: artifact.state },\n\t\t),\n\t});\n}\n\n/** Compute the domain-separated digest for an already validated canonical manifest. */\nexport function computeWorkspaceManifestSha256(scope: WorkspaceScope, artifacts: readonly ArtifactState[]): Sha256Hex {\n\treturn sha256(`${WORKSPACE_MANIFEST_DOMAIN}${canonicalManifest(scope, artifacts)}`);\n}\n\n/** Canonical digest committing sorted changed paths, both diff digests, and every selected artifact state. */\nexport function computeGitWorkspaceDirtySha256(\n\tchangedPaths: readonly string[],\n\tstagedDiffSha256: Sha256Hex,\n\tunstagedDiffSha256: Sha256Hex,\n\tartifacts: readonly ArtifactState[],\n): Sha256Hex {\n\tconst canonical = JSON.stringify({\n\t\tchangedPaths,\n\t\tstagedDiffSha256,\n\t\tunstagedDiffSha256,\n\t\tartifacts: artifacts.map((artifact) =>\n\t\t\tartifact.state === \"file\"\n\t\t\t\t? { path: artifact.path, state: artifact.state, sha256: artifact.sha256, size: artifact.size }\n\t\t\t\t: { path: artifact.path, state: artifact.state },\n\t\t),\n\t});\n\treturn sha256(`${GIT_DIRTY_DOMAIN}${canonical}`);\n}\n\n/** Compute the domain-separated digest for an already validated canonical git manifest. */\nexport function computeGitWorkspaceManifestSha256(\n\tscope: WorkspaceScope,\n\tartifacts: readonly ArtifactState[],\n\tgit: GitWorkspaceState,\n): Sha256Hex {\n\tconst canonical = JSON.stringify({\n\t\tkind: \"git\",\n\t\tscope: { root: scope.root, artifactPaths: scope.artifactPaths },\n\t\tartifacts: artifacts.map((artifact) =>\n\t\t\tartifact.state === \"file\"\n\t\t\t\t? { path: artifact.path, state: artifact.state, sha256: artifact.sha256, size: artifact.size }\n\t\t\t\t: { path: artifact.path, state: artifact.state },\n\t\t),\n\t\tgit: {\n\t\t\theadCommit: git.headCommit,\n\t\t\tchangedPaths: git.changedPaths,\n\t\t\tstagedDiffSha256: git.stagedDiffSha256,\n\t\t\tunstagedDiffSha256: git.unstagedDiffSha256,\n\t\t\tdirtySha256: git.dirtySha256,\n\t\t},\n\t});\n\treturn sha256(`${WORKSPACE_MANIFEST_DOMAIN}${canonical}`);\n}\n\n/** A changed path is inside scope when it equals a selected path or lies under a selected directory. */\nfunction isWithinScope(path: string, artifactPaths: readonly string[]): boolean {\n\treturn artifactPaths.some((artifact) => path === artifact || path.startsWith(`${artifact}/`));\n}\n\nfunction digestsEqual(left: Sha256Hex, right: Sha256Hex): boolean {\n\treturn timingSafeEqual(Buffer.from(left, \"hex\"), Buffer.from(right, \"hex\"));\n}\n\nfunction parseGitFingerprint(value: unknown): GitWorkspaceFingerprint {\n\tconst raw = exactObject(value, \"workspace fingerprint\", [\"kind\", \"scope\", \"artifacts\", \"git\", \"manifestSha256\"]);\n\tconst scope = parseScope(raw.scope);\n\tconst rawArtifacts = exactArray(raw.artifacts, \"workspace fingerprint artifacts\");\n\tconst artifacts = rawArtifacts.map((artifact, index) => parseArtifact(artifact, index));\n\tconst artifactPaths = artifacts.map((artifact) => artifact.path);\n\tassertSortedUniquePaths(artifactPaths, \"workspace artifact paths\");\n\tif (\n\t\tartifactPaths.length !== scope.artifactPaths.length ||\n\t\tartifactPaths.some((path, index) => path !== scope.artifactPaths[index])\n\t) {\n\t\tthrow new Error(\"workspace artifacts must exactly match the selected scope paths\");\n\t}\n\tconst rawGit = exactObject(raw.git, \"workspace git state\", [\n\t\t\"headCommit\",\n\t\t\"changedPaths\",\n\t\t\"stagedDiffSha256\",\n\t\t\"unstagedDiffSha256\",\n\t\t\"dirtySha256\",\n\t]);\n\tconst headCommit = rawGit.headCommit;\n\tif (headCommit !== null && (typeof headCommit !== \"string\" || !GIT_COMMIT_HEX.test(headCommit))) {\n\t\tthrow new Error(\"workspace git headCommit must be null or a full lowercase hex object name\");\n\t}\n\tconst rawChangedPaths = exactArray(rawGit.changedPaths, \"workspace git changedPaths\");\n\tif (!rawChangedPaths.every((path) => typeof path === \"string\")) {\n\t\tthrow new Error(\"workspace git changedPaths must be a string array\");\n\t}\n\tconst changedPaths = [...rawChangedPaths] as string[];\n\tassertSortedUniquePaths(changedPaths, \"workspace git changedPaths\");\n\tfor (const path of changedPaths) {\n\t\tif (!isWithinScope(path, scope.artifactPaths)) {\n\t\t\tthrow new Error(`workspace git changed path escapes the selected scope: ${path}`);\n\t\t}\n\t}\n\tconst stagedDiffSha256 = parseSha256(rawGit.stagedDiffSha256, \"workspace git stagedDiffSha256\");\n\tconst unstagedDiffSha256 = parseSha256(rawGit.unstagedDiffSha256, \"workspace git unstagedDiffSha256\");\n\tconst dirtySha256 = parseSha256(rawGit.dirtySha256, \"workspace git dirtySha256\");\n\tif (\n\t\t!digestsEqual(\n\t\t\tdirtySha256,\n\t\t\tcomputeGitWorkspaceDirtySha256(changedPaths, stagedDiffSha256, unstagedDiffSha256, artifacts),\n\t\t)\n\t) {\n\t\tthrow new Error(\"workspace git dirty digest mismatch\");\n\t}\n\tconst git: GitWorkspaceState = Object.freeze({\n\t\theadCommit,\n\t\tchangedPaths: Object.freeze(changedPaths),\n\t\tstagedDiffSha256,\n\t\tunstagedDiffSha256,\n\t\tdirtySha256,\n\t});\n\tconst manifestSha256 = parseSha256(raw.manifestSha256, \"workspace manifestSha256\");\n\tif (!digestsEqual(manifestSha256, computeGitWorkspaceManifestSha256(scope, artifacts, git))) {\n\t\tthrow new Error(\"workspace manifest digest mismatch\");\n\t}\n\treturn Object.freeze({\n\t\tkind: \"git\",\n\t\tscope,\n\t\tartifacts: Object.freeze(artifacts),\n\t\tgit,\n\t\tmanifestSha256,\n\t});\n}\n\n/** Strictly parse and validate a fingerprint of either kind, including its canonical digests. */\nexport function parseWorkspaceFingerprint(value: unknown): WorkspaceFingerprint {\n\tif (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n\t\tthrow new Error(\"workspace fingerprint must be an object\");\n\t}\n\tconst kind = ownEnumerableDataProperty(value as Record<string, unknown>, \"workspace fingerprint\", \"kind\");\n\tif (kind === \"git\") return parseGitFingerprint(value);\n\tif (kind !== \"artifact-set\") {\n\t\tthrow new Error(\"workspace fingerprint kind must be artifact-set or git\");\n\t}\n\treturn parseArtifactSetFingerprint(value);\n}\n\nfunction parseArtifactSetFingerprint(value: unknown): ArtifactSetWorkspaceFingerprint {\n\tconst raw = exactObject(value, \"workspace fingerprint\", [\"kind\", \"scope\", \"artifacts\", \"manifestSha256\"]);\n\tif (raw.kind !== \"artifact-set\") {\n\t\tthrow new Error(\"workspace fingerprint kind must be artifact-set\");\n\t}\n\tconst scope = parseScope(raw.scope);\n\tconst rawArtifacts = exactArray(raw.artifacts, \"workspace fingerprint artifacts\");\n\tconst artifacts = rawArtifacts.map((artifact, index) => parseArtifact(artifact, index));\n\tconst artifactPaths = artifacts.map((artifact) => artifact.path);\n\tassertSortedUniquePaths(artifactPaths, \"workspace artifact paths\");\n\tif (\n\t\tartifactPaths.length !== scope.artifactPaths.length ||\n\t\tartifactPaths.some((path, index) => path !== scope.artifactPaths[index])\n\t) {\n\t\tthrow new Error(\"workspace artifacts must exactly match the selected scope paths\");\n\t}\n\tconst manifestSha256 = parseSha256(raw.manifestSha256, \"workspace manifestSha256\");\n\tconst expected = computeWorkspaceManifestSha256(scope, artifacts);\n\tif (!digestsEqual(manifestSha256, expected)) {\n\t\tthrow new Error(\"workspace manifest digest mismatch\");\n\t}\n\treturn Object.freeze({\n\t\tkind: \"artifact-set\",\n\t\tscope,\n\t\tartifacts: Object.freeze(artifacts),\n\t\tmanifestSha256,\n\t});\n}\n\nfunction statPathWithoutSymlinks(root: string, artifactPath: string): FileStat | undefined {\n\tlet current = root;\n\tconst segments = artifactPath.split(\"/\");\n\tfor (let index = 0; index < segments.length; index++) {\n\t\tcurrent = join(current, segments[index]);\n\t\tlet stat: FileStat;\n\t\ttry {\n\t\t\tstat = lstatSync(current);\n\t\t} catch (error) {\n\t\t\tif (errorCode(error) === \"ENOENT\") return undefined;\n\t\t\tthrow error;\n\t\t}\n\t\tif (stat.isSymbolicLink()) {\n\t\t\tthrow new Error(`workspace artifact path contains a symlink: ${artifactPath}`);\n\t\t}\n\t\tif (index < segments.length - 1 && !stat.isDirectory()) {\n\t\t\tthrow new Error(`workspace artifact parent is not a directory: ${artifactPath}`);\n\t\t}\n\t\tif (index === segments.length - 1) return stat;\n\t}\n\treturn undefined;\n}\n\nfunction captureParentIdentities(root: string, artifactPath: string): readonly ParentIdentity[] {\n\tconst parents: ParentIdentity[] = [];\n\tlet current = root;\n\tfor (const segment of artifactPath.split(\"/\").slice(0, -1)) {\n\t\tcurrent = join(current, segment);\n\t\tconst stat = lstatSync(current);\n\t\tif (stat.isSymbolicLink()) {\n\t\t\tthrow new Error(`workspace artifact path contains a symlink: ${artifactPath}`);\n\t\t}\n\t\tif (!stat.isDirectory()) {\n\t\t\tthrow new Error(`workspace artifact parent is not a directory: ${artifactPath}`);\n\t\t}\n\t\tparents.push({ path: current, stat });\n\t}\n\treturn parents;\n}\n\nfunction hasFileIdentity(stat: FileStat): boolean {\n\treturn stat.dev !== 0 || stat.ino !== 0;\n}\n\nfunction sameFileIdentity(expected: FileStat, actual: FileStat): boolean {\n\tconst expectedHasIdentity = hasFileIdentity(expected);\n\tconst actualHasIdentity = hasFileIdentity(actual);\n\treturn (\n\t\t(!expectedHasIdentity && !actualHasIdentity) ||\n\t\t(expectedHasIdentity && actualHasIdentity && expected.dev === actual.dev && expected.ino === actual.ino)\n\t);\n}\n\nfunction isStrictlyUnder(root: string, path: string): boolean {\n\tconst relativePath = relative(root, path);\n\treturn (\n\t\trelativePath !== \"\" && relativePath !== \"..\" && !relativePath.startsWith(`..${sep}`) && !isAbsolute(relativePath)\n\t);\n}\n\nfunction assertTrustedRoot(root: string, trustedRootStat: FileStat): void {\n\tconst liveRootStat = lstatSync(root);\n\tif (\n\t\tliveRootStat.isSymbolicLink() ||\n\t\t!liveRootStat.isDirectory() ||\n\t\trealpathSync(root) !== root ||\n\t\t!sameFileIdentity(trustedRootStat, liveRootStat)\n\t) {\n\t\tthrow new Error(\"workspace root changed while artifacts were captured\");\n\t}\n}\n\nfunction assertParentIdentities(parents: readonly ParentIdentity[], artifactPath: string): void {\n\tfor (const parent of parents) {\n\t\tconst stat = lstatSync(parent.path);\n\t\tif (stat.isSymbolicLink() || !stat.isDirectory() || !sameFileIdentity(parent.stat, stat)) {\n\t\t\tthrow new Error(`workspace artifact parent changed while it was captured: ${artifactPath}`);\n\t\t}\n\t}\n}\n\nfunction assertLiveArtifactPath(\n\troot: string,\n\ttrustedRootStat: FileStat,\n\ttrustedParents: readonly ParentIdentity[],\n\tartifactPath: string,\n\tfdStat: FileStat,\n): void {\n\tassertTrustedRoot(root, trustedRootStat);\n\tassertParentIdentities(trustedParents, artifactPath);\n\tconst pathStat = statPathWithoutSymlinks(root, artifactPath);\n\tif (pathStat === undefined || !pathStat.isFile() || !sameFileIdentity(fdStat, pathStat)) {\n\t\tthrow new Error(`workspace artifact path was rebound while it was captured: ${artifactPath}`);\n\t}\n\tconst fullPath = join(root, ...artifactPath.split(\"/\"));\n\tconst livePath = realpathSync(fullPath);\n\tif (!isStrictlyUnder(root, livePath)) {\n\t\tthrow new Error(`workspace artifact resolved outside the trusted root: ${artifactPath}`);\n\t}\n\tconst livePathStat = lstatSync(livePath);\n\tif (!livePathStat.isFile() || !sameFileIdentity(fdStat, livePathStat)) {\n\t\tthrow new Error(`workspace artifact path was rebound while it was captured: ${artifactPath}`);\n\t}\n\tassertParentIdentities(trustedParents, artifactPath);\n\tassertTrustedRoot(root, trustedRootStat);\n}\n\nfunction captureFile(\n\troot: string,\n\ttrustedRootStat: FileStat,\n\ttrustedParents: readonly ParentIdentity[],\n\tartifactPath: string,\n): FileArtifactState {\n\tconst fullPath = join(root, ...artifactPath.split(\"/\"));\n\tconst flags = fsConstants.O_RDONLY | (fsConstants.O_NOFOLLOW ?? 0);\n\tlet fd: number;\n\ttry {\n\t\tfd = openSync(fullPath, flags);\n\t} catch (error) {\n\t\tif (errorCode(error) === \"ELOOP\") {\n\t\t\tthrow new Error(`workspace artifact path contains a symlink: ${artifactPath}`);\n\t\t}\n\t\tthrow error;\n\t}\n\ttry {\n\t\tconst before = fstatSync(fd);\n\t\tif (!before.isFile()) {\n\t\t\tthrow new Error(`workspace artifact is not a regular file: ${artifactPath}`);\n\t\t}\n\t\tassertLiveArtifactPath(root, trustedRootStat, trustedParents, artifactPath, before);\n\t\tconst bytes = readFileSync(fd);\n\t\tconst after = fstatSync(fd);\n\t\tassertLiveArtifactPath(root, trustedRootStat, trustedParents, artifactPath, after);\n\t\tif (\n\t\t\t!after.isFile() ||\n\t\t\t!sameFileIdentity(before, after) ||\n\t\t\tbefore.size !== after.size ||\n\t\t\tbefore.mtimeMs !== after.mtimeMs ||\n\t\t\tbefore.ctimeMs !== after.ctimeMs ||\n\t\t\tafter.size !== bytes.byteLength\n\t\t) {\n\t\t\tthrow new Error(`workspace artifact changed while it was captured: ${artifactPath}`);\n\t\t}\n\t\treturn Object.freeze({ path: artifactPath, state: \"file\", sha256: sha256(bytes), size: bytes.byteLength });\n\t} finally {\n\t\tcloseSync(fd);\n\t}\n}\n\nfunction captureSelectedArtifact(\n\troot: string,\n\ttrustedRootStat: FileStat,\n\tartifactPath: string,\n\toptions: WorkspaceFingerprintCaptureOptions,\n): ArtifactState {\n\tassertTrustedRoot(root, trustedRootStat);\n\tconst stat = statPathWithoutSymlinks(root, artifactPath);\n\tif (stat === undefined) {\n\t\tassertTrustedRoot(root, trustedRootStat);\n\t\treturn Object.freeze({ path: artifactPath, state: \"missing\" });\n\t}\n\tif (!stat.isFile()) {\n\t\tthrow new Error(`workspace artifact is not a regular file: ${artifactPath}`);\n\t}\n\tconst trustedParents = captureParentIdentities(root, artifactPath);\n\toptions.faultInjector?.(\"before-artifact-open\", artifactPath);\n\treturn captureFile(root, trustedRootStat, trustedParents, artifactPath);\n}\n\n/** True when the trusted root itself hosts a `.git` entry; a symlinked `.git` fails closed. */\nfunction isGitWorkspaceRoot(root: string): boolean {\n\tlet stat: FileStat;\n\ttry {\n\t\tstat = lstatSync(join(root, \".git\"));\n\t} catch (error) {\n\t\tif (errorCode(error) === \"ENOENT\") return false;\n\t\tthrow error;\n\t}\n\tif (stat.isSymbolicLink()) {\n\t\tthrow new Error(\"workspace root .git must not be a symlink\");\n\t}\n\treturn true;\n}\n\nfunction gitCaptureEnv(): NodeJS.ProcessEnv {\n\tconst env: NodeJS.ProcessEnv = { ...process.env };\n\tfor (const name of UNSAFE_GIT_ENV_VARS) delete env[name];\n\tenv.GIT_OPTIONAL_LOCKS = \"0\";\n\tenv.LC_ALL = \"C\";\n\treturn env;\n}\n\n/** Run one git subcommand with argv boundaries (never a shell) and bounded, time-limited output. */\nfunction runGitCapture(\n\troot: string,\n\targs: readonly string[],\n\tallowedExitCodes: readonly number[] = [0],\n): { status: number; stdout: Buffer } {\n\tconst result = spawnSync(\"git\", [\"-C\", root, ...args], {\n\t\tenv: gitCaptureEnv(),\n\t\tmaxBuffer: MAX_GIT_OUTPUT_BYTES,\n\t\ttimeout: GIT_CAPTURE_TIMEOUT_MS,\n\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\twindowsHide: true,\n\t});\n\tif (result.error !== undefined) {\n\t\tconst code = errorCode(result.error);\n\t\tthrow new Error(`workspace git capture failed to run git: ${code ?? result.error.message}`);\n\t}\n\tif (result.status === null || !allowedExitCodes.includes(result.status)) {\n\t\tthrow new Error(`workspace git capture: git exited with status ${String(result.status)}`);\n\t}\n\treturn { status: result.status, stdout: result.stdout };\n}\n\ninterface GitScopeStatus {\n\treadonly changedPaths: readonly string[];\n}\n\nfunction parseGitStatusOutput(stdout: Buffer, artifactPaths: readonly string[]): GitScopeStatus {\n\tconst changed = new Set<string>();\n\tfor (const entry of stdout.toString(\"utf8\").split(\"\\0\")) {\n\t\tif (entry.length === 0) continue;\n\t\tif (entry.length < 4 || entry[2] !== \" \") {\n\t\t\tthrow new Error(\"workspace git capture: unexpected status entry shape\");\n\t\t}\n\t\tconst path = entry.slice(3);\n\t\tassertNormalizedArtifactPath(path, \"workspace git status path\");\n\t\tif (!isWithinScope(path, artifactPaths)) {\n\t\t\tthrow new Error(`workspace git status path escapes the selected scope: ${path}`);\n\t\t}\n\t\tchanged.add(path);\n\t}\n\treturn { changedPaths: [...changed].sort() };\n}\n\n/**\n * Capture HEAD plus a scope-limited canonical dirty digest for a git work-tree root.\n * All git invocations are argv-only, env-sanitized, bounded, and pathspec-limited with\n * literal magic. Every exact selected path is also captured with symlink-free,\n * root-contained file reads, independent of ignore rules and index flags.\n */\nfunction captureGitWorkspaceFingerprint(\n\troot: string,\n\ttrustedRootStat: FileStat,\n\tscope: WorkspaceScope,\n\toptions: WorkspaceFingerprintCaptureOptions,\n): GitWorkspaceFingerprint {\n\tconst toplevel = runGitCapture(root, [\"rev-parse\", \"--show-toplevel\"]).stdout.toString(\"utf8\").replace(/\\n$/, \"\");\n\tif (toplevel.length === 0 || realpathSync(toplevel) !== root) {\n\t\tthrow new Error(\"workspace root must be the git work-tree top level\");\n\t}\n\tconst head = runGitCapture(root, [\"rev-parse\", \"--verify\", \"--quiet\", \"HEAD^{commit}\"], [0, 1]);\n\tlet headCommit: string | null = null;\n\tif (head.status === 0) {\n\t\tconst hex = head.stdout.toString(\"utf8\").trim();\n\t\tif (!GIT_COMMIT_HEX.test(hex)) {\n\t\t\tthrow new Error(\"workspace git capture: HEAD is not a full hex object name\");\n\t\t}\n\t\theadCommit = hex;\n\t}\n\tconst pathspecs = scope.artifactPaths.map((path) => `:(literal)${path}`);\n\tlet status: GitScopeStatus = { changedPaths: [] };\n\tlet stagedDiff: Buffer = Buffer.alloc(0);\n\tlet unstagedDiff: Buffer = Buffer.alloc(0);\n\tif (pathspecs.length > 0) {\n\t\tstatus = parseGitStatusOutput(\n\t\t\trunGitCapture(root, [\n\t\t\t\t\"-c\",\n\t\t\t\t\"core.fsmonitor=false\",\n\t\t\t\t\"status\",\n\t\t\t\t\"--porcelain=v1\",\n\t\t\t\t\"-z\",\n\t\t\t\t\"--untracked-files=all\",\n\t\t\t\t\"--no-renames\",\n\t\t\t\t\"--ignore-submodules=none\",\n\t\t\t\t\"--\",\n\t\t\t\t...pathspecs,\n\t\t\t]).stdout,\n\t\t\tscope.artifactPaths,\n\t\t);\n\t\tstagedDiff = runGitCapture(root, [\"diff\", \"--cached\", ...GIT_DIFF_ARGS, \"--\", ...pathspecs]).stdout;\n\t\tunstagedDiff = runGitCapture(root, [\"diff\", ...GIT_DIFF_ARGS, \"--\", ...pathspecs]).stdout;\n\t}\n\tconst artifacts = scope.artifactPaths.map((artifactPath) =>\n\t\tcaptureSelectedArtifact(root, trustedRootStat, artifactPath, options),\n\t);\n\tassertTrustedRoot(root, trustedRootStat);\n\tconst changedPaths = Object.freeze([...status.changedPaths]);\n\tconst stagedDiffSha256 = sha256(stagedDiff);\n\tconst unstagedDiffSha256 = sha256(unstagedDiff);\n\tconst git: GitWorkspaceState = Object.freeze({\n\t\theadCommit,\n\t\tchangedPaths,\n\t\tstagedDiffSha256,\n\t\tunstagedDiffSha256,\n\t\tdirtySha256: computeGitWorkspaceDirtySha256(changedPaths, stagedDiffSha256, unstagedDiffSha256, artifacts),\n\t});\n\tconst parsed = parseWorkspaceFingerprint({\n\t\tkind: \"git\",\n\t\tscope,\n\t\tartifacts: Object.freeze(artifacts),\n\t\tgit,\n\t\tmanifestSha256: computeGitWorkspaceManifestSha256(scope, artifacts, git),\n\t});\n\tif (parsed.kind !== \"git\") {\n\t\tthrow new Error(\"workspace git capture produced a non-git fingerprint\");\n\t}\n\treturn parsed;\n}\n\n/**\n * Hash the selected workspace scope. When the trusted root is itself a git work-tree\n * top level, capture HEAD plus a scope-limited canonical dirty digest; otherwise hash\n * the caller-selected artifact set directly and perform no git commands.\n * Phase A assumes a trusted, quiescent workspace: pathname identity checks reject\n * observed rebinds, but do not prove an immutable snapshot against a same-UID adversary.\n */\nexport function captureWorkspaceFingerprint(\n\tscope: WorkspaceScope,\n\toptions: WorkspaceFingerprintCaptureOptions = {},\n): WorkspaceFingerprint {\n\tconst rawScope = exactObject(scope, \"workspace scope\", [\"root\", \"artifactPaths\"]);\n\tconst rawRoot = rawScope.root;\n\tif (typeof rawRoot !== \"string\" || rawRoot.length === 0) {\n\t\tthrow new Error(\"workspace scope root must be a non-empty string\");\n\t}\n\tconst rawArtifactPaths = exactArray(rawScope.artifactPaths, \"workspace scope artifactPaths\");\n\n\tconst seen = new Set<string>();\n\tfor (let index = 0; index < rawArtifactPaths.length; index++) {\n\t\tconst artifactPath = rawArtifactPaths[index];\n\t\tassertNormalizedArtifactPath(artifactPath, `workspace scope artifactPaths[${index}]`);\n\t\tif (seen.has(artifactPath)) {\n\t\t\tthrow new Error(`duplicate workspace artifact path: ${artifactPath}`);\n\t\t}\n\t\tseen.add(artifactPath);\n\t}\n\n\tconst resolvedRoot = resolve(rawRoot);\n\tconst rootStat = lstatSync(resolvedRoot);\n\tif (rootStat.isSymbolicLink()) {\n\t\tthrow new Error(\"workspace root must not be a symlink\");\n\t}\n\tif (!rootStat.isDirectory()) {\n\t\tthrow new Error(\"workspace root must be a directory\");\n\t}\n\tconst root = realpathSync(resolvedRoot);\n\tconst trustedRootStat = lstatSync(root);\n\tconst artifactPaths = (rawArtifactPaths as string[]).sort();\n\tconst canonicalScope: WorkspaceScope = Object.freeze({ root, artifactPaths: Object.freeze(artifactPaths) });\n\tif (isGitWorkspaceRoot(root)) {\n\t\treturn captureGitWorkspaceFingerprint(root, trustedRootStat, canonicalScope, options);\n\t}\n\tconst artifacts = artifactPaths.map((artifactPath) =>\n\t\tcaptureSelectedArtifact(root, trustedRootStat, artifactPath, options),\n\t);\n\tconst fingerprint = {\n\t\tkind: \"artifact-set\",\n\t\tscope: canonicalScope,\n\t\tartifacts: Object.freeze(artifacts),\n\t\tmanifestSha256: computeWorkspaceManifestSha256(canonicalScope, artifacts),\n\t};\n\treturn parseWorkspaceFingerprint(fingerprint);\n}\n"]}
|