From cfe03ccd7e7e500447c1ec684ef172dcb66c407b Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 9 Nov 2026 10:24:19 +0000 Subject: [PATCH] feat(jobs): cmake commands inherit a validated CMAKE_PREFIX_PATH on Windows CineCore configure on the P520 failed with 'could not find Qt6' (job f8fa47c3) because the job environment is allowlisted and never carried CMAKE_PREFIX_PATH. Only gates starting with command:cmake_ inherit it, only on win32, only when every entry is an absolute directory without control characters or '..'. Also documents the open Windows MAX_PATH gap for lane builds (FTK1011). Co-Authored-By: Claude Sonnet 5.5 Claude-Session: https://claude.ai/code/session_01AjjQnxAvtbQCFYE3y8yrq8 --- docs/COMMAND-PROFILE-SCHEMA.md | 8 +++++- src/jobs.js | 24 +++++++++++++++- tests/windows-job-environment.test.js | 41 +++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 2 deletions(-) diff --git a/docs/COMMAND-PROFILE-SCHEMA.md b/docs/COMMAND-PROFILE-SCHEMA.md index 7ce055c8c..2f695c231 100644 --- a/docs/COMMAND-PROFILE-SCHEMA.md +++ b/docs/COMMAND-PROFILE-SCHEMA.md @@ -92,7 +92,13 @@ Fixed built-in commands (not config-driven, fixed variants): `browser_public_qa` - No explicit output-size/log-byte cap or memory/CPU limit is documented here for the generic runner; only `timeoutMs` and `--parallel 2` are enforced by profile. Verify against `src/jobs.js` before claiming bounds. -- Qt-specific profiles do not exist; Qt builds go through CMake only. +- Qt-specific profiles do not exist; Qt builds go through CMake only. Commands + whose gate starts with `command:cmake_` additionally inherit `CMAKE_PREFIX_PATH` + on Windows (at most 16 absolute directories, no control characters, no `..` + segments, at most 4096 characters); no other job inherits it. +- Windows MAX_PATH: a CMake project configured inside an isolated lane under the + agent state folder can exceed the Visual Studio generator's path limit + (FileTracker error FTK1011 seen on the P520). Status: open; no fix claimed. - Any GUI/HIL interaction needs explicit user consent and stays pending. ## 6. What is not claimed diff --git a/src/jobs.js b/src/jobs.js index f81c56e72..341b701c4 100644 --- a/src/jobs.js +++ b/src/jobs.js @@ -22,7 +22,7 @@ export function redact(text) { export function jobEnvironment( source = process.env, platform = process.platform, - { allowReaProviderPaths = false } = {}, + { allowReaProviderPaths = false, allowCmakePrefixPath = false } = {}, ) { // Only standard OS paths needed for native build tools may be inherited. const env = { @@ -99,6 +99,27 @@ export function jobEnvironment( env[key] = value; } } + // Qt and other CMake packages are found through CMAKE_PREFIX_PATH. Only the + // approved cmake-standard commands inherit it, and only when every entry is + // an absolute Windows directory without control characters or ".." parts. + if (allowCmakePrefixPath) { + const value = source.CMAKE_PREFIX_PATH; + const parts = + typeof value === "string" && value.length <= 4096 + ? value.split(";").filter((part) => part.length > 0) + : []; + if ( + parts.length > 0 && + parts.length <= 16 && + parts.every( + (part) => + path.win32.isAbsolute(part) && + !Array.from(part).some((ch) => ch.charCodeAt(0) < 32) && + !part.split(/[\\/]/).includes(".."), + ) + ) + env.CMAKE_PREFIX_PATH = parts.join(";"); + } } return env; } @@ -560,6 +581,7 @@ export class JobRunner { j.gate.startsWith("command:rea_doctor:check:inspect@"); const env = jobEnvironment(process.env, process.platform, { allowReaProviderPaths: isReaDoctor, + allowCmakePrefixPath: j.gate.startsWith("command:cmake_"), }); // Composer requires HOME even when the job runner deliberately strips // inherited user environment. Use a private, deterministic directory in diff --git a/tests/windows-job-environment.test.js b/tests/windows-job-environment.test.js index 64a5df2c9..a65dae7e3 100644 --- a/tests/windows-job-environment.test.js +++ b/tests/windows-job-environment.test.js @@ -98,3 +98,44 @@ test("REA doctor opt-in refuses relative, control-character and oversized provid ); assert.equal(Object.hasOwn(longEnv, "JAVA_HOME"), false); }); + +test("CMAKE_PREFIX_PATH is inherited only by approved cmake commands and only when safe", () => { + const base = { PATH: "C:\\Windows\\System32", SystemRoot: "C:\\Windows" }; + const good = "C:\\Qt\\6.10.3\\msvc2022_64;D:\\libs\\opencv"; + const withPrefix = { ...base, CMAKE_PREFIX_PATH: good }; + assert.equal( + Object.hasOwn(jobEnvironment(withPrefix, "win32"), "CMAKE_PREFIX_PATH"), + false, + ); + assert.equal( + jobEnvironment(withPrefix, "win32", { allowCmakePrefixPath: true }) + .CMAKE_PREFIX_PATH, + good, + ); + assert.equal( + Object.hasOwn( + jobEnvironment(withPrefix, "linux", { allowCmakePrefixPath: true }), + "CMAKE_PREFIX_PATH", + ), + false, + ); + for (const bad of [ + "relative\\qt", + "C:\\Qt;..\\evil", + "C:\\Qt\\..\\evil", + "C:\\Qt" + String.fromCharCode(10) + "x", + "C:\\" + "a".repeat(5000), + ";;", + ]) { + assert.equal( + Object.hasOwn( + jobEnvironment({ ...base, CMAKE_PREFIX_PATH: bad }, "win32", { + allowCmakePrefixPath: true, + }), + "CMAKE_PREFIX_PATH", + ), + false, + bad.slice(0, 20), + ); + } +});