import { spawnSync } from "node:child_process"; import { mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs"; import { join } from "node:path"; import { afterEach, describe, expect, it } from "vitest"; import { parse } from "yaml"; import { useAutoCleanupTempDirTracker } from "../helpers/temp-dir.js"; const RELEASE_CHECKS_PATH = ".github/workflows/openclaw-release-checks.yml"; const WORKFLOW_PATH = ".github/workflows/openclaw-release-telegram-qa.yml"; const tempDirs = useAutoCleanupTempDirTracker(afterEach); type WorkflowJob = { "continue-on-error"?: boolean; environment?: string; if?: string; needs?: string[]; outputs?: Record; permissions?: Record; "runs-on"?: unknown; "timeout-minutes"?: unknown; steps?: Array<{ env?: Record; if?: string; name?: string; run?: string; uses?: string; with?: Record; }>; uses?: string; with?: Record; }; function workflowJob(name: string): WorkflowJob { const workflow = parse(readFileSync(WORKFLOW_PATH, "utf8")) as { jobs?: Record; }; const job = workflow.jobs?.[name]; if (!job) { throw new Error(`Expected workflow job ${name}`); } return job; } function workflowStep(job: WorkflowJob, name: string) { const step = job.steps?.find((candidate) => candidate.name === name); if (!step) { throw new Error(`Expected workflow step ${name}`); } return step; } function runIdentityVerification(params: { expectedTrustedWorkflowSha: string; invocation?: "dispatch" | "reusable"; oidcJobWorkflowSha?: string; oidcWorkflowSha?: string; workflowSha?: string; }) { const repository = "openclaw/openclaw"; const trustedWorkflowRef = `${repository}/.github/workflows/openclaw-release-telegram-qa.yml@refs/heads/main`; const invocation = params.invocation ?? "dispatch"; const workflowRef = invocation === "dispatch" ? trustedWorkflowRef : `${repository}/.github/workflows/openclaw-release-checks.yml@refs/heads/release-ci/test`; const workflowRefName = invocation === "dispatch" ? "refs/heads/main" : "refs/heads/release-ci/test"; const targetSha = "a".repeat(40); const workdir = tempDirs.make("openclaw-telegram-identity-"); const fakeBin = join(workdir, "bin"); const curlPath = join(fakeBin, "curl"); const githubOutput = join(workdir, "github-output"); mkdirSync(fakeBin); const workflowSha = params.workflowSha ?? params.expectedTrustedWorkflowSha; const oidcWorkflowSha = params.oidcWorkflowSha ?? workflowSha; const payload = { aud: "openclaw-release-telegram-qa", event_name: "workflow_dispatch", iss: "https://token.actions.githubusercontent.com", ...(invocation === "reusable" ? { job_workflow_ref: trustedWorkflowRef, job_workflow_sha: params.oidcJobWorkflowSha ?? params.expectedTrustedWorkflowSha, } : {}), ref: workflowRefName, repository, runner_environment: "github-hosted", sha: workflowSha, workflow_ref: workflowRef, workflow_sha: oidcWorkflowSha, }; const token = [ Buffer.from("{}").toString("base64url"), Buffer.from(JSON.stringify(payload)).toString("base64url"), "signature", ].join("."); writeFileSync(curlPath, "#!/usr/bin/env bash\nprintf '%s\\n' \"$FAKE_OIDC_JSON\"\n", { mode: 0o755, }); const script = workflowStep( workflowJob("trusted_identity"), "Verify dispatched-main identity", ).run; if (!script) { throw new Error("Expected trusted identity script"); } return spawnSync("bash", ["-c", script], { cwd: workdir, encoding: "utf8", env: { ACTIONS_ID_TOKEN_REQUEST_TOKEN: "test-token", ACTIONS_ID_TOKEN_REQUEST_URL: "https://example.invalid/oidc?", CALLER_WORKFLOW_REF: workflowRef, CALLER_WORKFLOW_SHA: workflowSha, EXPECTED_TRUSTED_WORKFLOW_SHA: params.expectedTrustedWorkflowSha, FAKE_OIDC_JSON: JSON.stringify({ value: token }), GITHUB_EVENT_NAME: "workflow_dispatch", GITHUB_OUTPUT: githubOutput, GITHUB_REF: workflowRefName, GITHUB_REPOSITORY: repository, GITHUB_SHA: workflowSha, JOB_CONTEXT: JSON.stringify({ workflow_ref: trustedWorkflowRef, workflow_repository: repository, workflow_sha: params.expectedTrustedWorkflowSha, }), PATH: `${fakeBin}:${process.env.PATH}`, TARGET_REF: "refs/heads/release/2026.7.1", TARGET_SHA: targetSha, WORKFLOW_REF: workflowRef, WORKFLOW_SHA: workflowSha, }, }); } function runAdvisoryStatus(overrides: Record = {}) { const runId = "123456"; const runAttempt = "1"; const targetSha = "a".repeat(40); const workflowSha = "b".repeat(40); const workdir = tempDirs.make("openclaw-telegram-advisory-status-"); const githubOutput = join(workdir, "github-output"); const script = workflowStep(workflowJob("advisory_status"), "Record advisory status").run; if (!script) { throw new Error("Expected advisory status script"); } const result = spawnSync("bash", ["-c", script], { cwd: workdir, encoding: "utf8", env: { ARCHIVE_NAME: `release-telegram-candidate-${runId}-${runAttempt}-${targetSha}.tar.zst`, ARCHIVE_SHA256: "c".repeat(64), ATTESTATION_RESULT: "success", ATTESTATION_STATUS: "success", BUILD_RESULT: "success", BUILD_STATUS: "success", CANDIDATE_ARTIFACT_DIGEST: "d".repeat(64), CANDIDATE_ARTIFACT_ID: "123", CANDIDATE_VERSION: "2026.7.1-beta.3", EVIDENCE_ARTIFACT_DIGEST: "e".repeat(64), EVIDENCE_ARTIFACT_ID: "456", EVIDENCE_ARTIFACT_NAME: `release-qa-live-telegram-${runId}-${runAttempt}-${targetSha}`, EXECUTION_STATUS: "success", GITHUB_OUTPUT: githubOutput, GITHUB_RUN_ATTEMPT: runAttempt, GITHUB_RUN_ID: runId, IDENTITY_RESULT: "success", IDENTITY_STATUS: "success", PATH: process.env.PATH, RUN_RESULT: "success", TARGET_SHA: targetSha, WORKFLOW_SHA: workflowSha, ...overrides, }, }); const output = result.status === 0 ? readFileSync(githubOutput, "utf8") : ""; const status = output.match(/^status=(.*)$/mu)?.[1] ?? ""; const requireScript = workflowStep( workflowJob("advisory_status"), "Require successful Telegram release check", ).run; if (!requireScript) { throw new Error("Expected terminal Telegram status script"); } const requireResult = spawnSync("bash", ["-c", requireScript], { cwd: workdir, encoding: "utf8", env: { PATH: process.env.PATH, STATUS: status, }, }); const statusFile = join( workdir, ".artifacts", "release-check-status", `qa_live_telegram_release_checks-${runId}-${runAttempt}.env`, ); return { output, recordResult: result, requireResult, status, statusFile: result.status === 0 ? readFileSync(statusFile, "utf8") : "", }; } describe("release Telegram QA workflow", () => { it("attributes GitHub web-flow and unsigned release merges to their exact maintainer merger", () => { const source = readFileSync(WORKFLOW_PATH, "utf8"); expect(source.match(/associatedPullRequests\(first:10\)/gu)).toHaveLength(2); expect(source.match(/if \.signature == null then "missing"/gu)).toHaveLength(2); expect(source.match(/\$signature_status" == "invalid"/gu)).toHaveLength(2); expect( source.match(/\$signature_status" == "missing" \|\| "\$signer" == "web-flow"/gu), ).toHaveLength(2); expect(source.match(/\.mergeCommit\.oid == \$sha/gu)).toHaveLength(2); expect(source.match(/\.baseRefName == \$base/gu)).toHaveLength(2); expect(source.match(/\.baseRepository\.nameWithOwner == \$repo/gu)).toHaveLength(2); expect(source.match(/\.mergedBy\.login\] \| unique \| select\(length == 1\)/gu)).toHaveLength( 2, ); expect(source.match(/collaborators\/\$\{permission_actor\}\/permission/gu)).toHaveLength(2); expect((source.match(/extended-stable\/\[0-9\]/gu) ?? []).length).toBeGreaterThanOrEqual(2); expect(source).not.toContain("collaborators/${signer}/permission"); }); it("resolves only candidate-specific provenance refs without fetching histories", () => { const source = readFileSync(WORKFLOW_PATH, "utf8"); expect(source.match(/branches-where-head/gu)).toHaveLength(2); expect(source.match(/gh api --paginate/gu)).toHaveLength(2); expect( source.match(/git(?: -C \.candidate)? ls-remote --exit-code --refs origin/gu), ).toHaveLength(2); expect( source.match(/git(?: -C \.candidate)? ls-remote origin 'refs\/tags\/v\*'/gu), ).toHaveLength(2); expect(source).not.toContain("'+refs/heads/release/*:refs/remotes/origin/release/*'"); expect(source).not.toContain( "'+refs/heads/extended-stable/*:refs/remotes/origin/extended-stable/*'", ); expect(source).not.toContain("'+refs/tags/v*:refs/tags/v*'"); }); it("dispatches one accepted trusted-main child from release checks", () => { const releaseSource = readFileSync(RELEASE_CHECKS_PATH, "utf8"); const reusableSource = readFileSync(WORKFLOW_PATH, "utf8"); const releaseWorkflow = parse(releaseSource) as { jobs?: Record; }; const caller = releaseWorkflow.jobs?.qa_live_telegram_release_checks; expect(caller?.needs).toEqual(["resolve_target"]); expect(caller?.if).toContain("needs.resolve_target.outputs.qa_live_telegram_enabled == 'true'"); expect(caller?.permissions).toEqual({ actions: "write", contents: "read", }); expect(caller?.["timeout-minutes"]).toBe(210); expect(caller?.["continue-on-error"]).toBeUndefined(); expect(caller?.["runs-on"]).toBe("ubuntu-24.04"); expect(caller?.environment).toBeUndefined(); const dispatch = caller?.steps?.find( (step) => step.name === "Dispatch and await trusted Telegram QA", ); expect(dispatch?.run).toContain('gh workflow run "$workflow"'); expect(dispatch?.run).toContain('--repo "$GITHUB_REPOSITORY"'); expect(dispatch?.run).toContain("-F event=workflow_dispatch"); expect(dispatch?.run).toContain(".display_title == env.RUN_NAME"); expect(dispatch?.run).toContain("$(openssl rand -hex 16)"); expect(dispatch?.run).toContain("trap cancel_child_on_failure EXIT"); expect(dispatch?.run).toContain("for _ in $(seq 1 6)"); expect(dispatch?.run).toContain("/actions/runs/${run_id}/cancel"); expect(dispatch?.run).toContain("for dispatch_attempt in 1 2 3 4 5"); expect(dispatch?.run).toContain('gh api "repos/${GITHUB_REPOSITORY}/commits/main"'); expect(dispatch?.run).toContain( 'if [[ "$child_head_sha" == "$expected_trusted_workflow_sha" ]]; then', ); expect(dispatch?.run).toContain("Trusted main moved from"); expect(dispatch?.run).toContain("Trusted main kept moving"); expect(dispatch?.run).toContain("for _ in $(seq 1 1080)"); expect(dispatch?.run).toContain("Trusted Telegram QA concluded ${conclusion}"); expect( releaseSource.match( /"qa_live_telegram_release_checks=\$\{QA_LIVE_TELEGRAM_RELEASE_CHECKS_RESULT\}"/gu, ), ).toHaveLength(1); expect(releaseSource).not.toContain( "qa_live_matrix_release_checks|qa_live_telegram_release_checks|qa_live_discord_release_checks", ); expect(releaseSource).not.toContain("persist-credentials: true"); const dispatchers = readdirSync(".github/workflows") .filter((name) => name.endsWith(".yml")) .flatMap((name) => { const path = `.github/workflows/${name}`; return readFileSync(path, "utf8").includes('workflow="openclaw-release-telegram-qa.yml"') ? [path] : []; }); expect(dispatchers).toEqual([RELEASE_CHECKS_PATH]); expect(reusableSource).toContain( "openclaw/openclaw/.github/workflows/openclaw-release-telegram-qa.yml@refs/heads/main", ); expect(reusableSource).toContain( '--cert-identity "https://github.com/openclaw/openclaw/.github/workflows/openclaw-release-telegram-qa.yml@refs/heads/main"', ); expect(reusableSource).toContain('--signer-digest "$CALLED_WORKFLOW_SHA"'); const resolveJob = releaseWorkflow.jobs?.resolve_target; expect(resolveJob?.outputs?.trusted_workflow_sha).toBeUndefined(); expect( resolveJob?.steps?.find((step) => step.name === "Resolve trusted main Telegram workflow SHA"), ).toBeUndefined(); const dispatchedWorkflow = parse(reusableSource) as { on?: { workflow_call?: { inputs?: Record; secrets?: Record; }; workflow_dispatch?: { inputs?: Record; }; }; }; expect(dispatchedWorkflow.on?.workflow_dispatch?.inputs?.expected_trusted_workflow_sha).toEqual( { description: "Resolved main SHA authorized for this trusted workflow", required: true, type: "string", }, ); expect(dispatchedWorkflow.on?.workflow_dispatch?.inputs?.dispatch_id).toEqual({ description: "Unique parent release-check dispatch identifier", required: true, type: "string", }); expect(dispatchedWorkflow.on?.workflow_call?.inputs?.expected_trusted_workflow_sha).toEqual({ description: "Resolved main SHA authorized for this trusted workflow", required: true, type: "string", }); expect(dispatchedWorkflow.on?.workflow_call?.secrets).toHaveProperty( "OPENCLAW_QA_CONVEX_SECRET_CI", ); }); it("binds dispatched and legacy reusable OIDC identity to the resolved main SHA", () => { const trustedSha = "b".repeat(40); const success = runIdentityVerification({ expectedTrustedWorkflowSha: trustedSha, }); expect(success.status).toBe(0); const oidcDrifted = runIdentityVerification({ expectedTrustedWorkflowSha: trustedSha, oidcWorkflowSha: "c".repeat(40), }); expect(oidcDrifted.status).toBe(1); expect(oidcDrifted.stderr).toContain("OIDC workflow_sha mismatch"); const mainMoved = runIdentityVerification({ expectedTrustedWorkflowSha: trustedSha, workflowSha: "c".repeat(40), }); expect(mainMoved.status).toBe(1); expect(mainMoved.stderr).toBe(""); const reusableSuccess = runIdentityVerification({ expectedTrustedWorkflowSha: trustedSha, invocation: "reusable", workflowSha: "d".repeat(40), }); expect(reusableSuccess.status).toBe(0); const reusableDrifted = runIdentityVerification({ expectedTrustedWorkflowSha: trustedSha, invocation: "reusable", oidcJobWorkflowSha: "c".repeat(40), workflowSha: "d".repeat(40), }); expect(reusableDrifted.status).toBe(1); expect(reusableDrifted.stderr).toContain("OIDC job_workflow_sha mismatch"); }); it("keeps candidate construction secretless and credentials inside the isolated runner", () => { const source = readFileSync(WORKFLOW_PATH, "utf8"); const workflow = parse(source) as { jobs?: Record; }; const buildJob = workflow.jobs?.build_candidate; const runJob = workflow.jobs?.run_telegram; expect(JSON.stringify(buildJob)).not.toContain("secrets."); expect(runJob?.environment).toBe("qa-live-shared"); const secretSteps = runJob?.steps ?.filter((step) => JSON.stringify(step).includes("secrets.")) .map((step) => step.name); expect(secretSteps).toEqual(["Validate required QA credential env", "Run Telegram live lane"]); expect(source).not.toContain("secrets: inherit"); expect(source).not.toContain("persist-credentials: true"); expect(source).toContain("trusted_scenario_source=verified_trusted_workflow_sha"); for (const [jobName, job] of Object.entries(workflow.jobs ?? {})) { for (const step of job.steps ?? []) { if (step.uses?.startsWith("actions/checkout@")) { expect(step.with?.["persist-credentials"], `${jobName}:${step.name}`).toBe(false); } } } }); it("allows the tracked-file index to exceed Node's default child-process buffer", () => { const compareStep = workflowStep( workflowJob("attest_candidate"), "Compare candidate tracked source and tree", ); expect(compareStep.run).toContain("maxBuffer: 16 * 1024 * 1024"); }); it("emits the release-check terminal status contract and fails closed", () => { const workflow = parse(readFileSync(WORKFLOW_PATH, "utf8")) as { jobs?: Record; }; const statusJob = workflow.jobs?.advisory_status; const recordStep = statusJob?.steps?.find((step) => step.name === "Record advisory status"); const uploadStep = statusJob?.steps?.find((step) => step.name === "Upload advisory status"); const requireStep = statusJob?.steps?.find( (step) => step.name === "Require successful Telegram release check", ); for (const jobName of [ "trusted_identity", "build_candidate", "attest_candidate", "run_telegram", ]) { expect(workflow.jobs?.[jobName]?.["continue-on-error"], jobName).toBeUndefined(); } for (const jobName of ["build_candidate", "attest_candidate", "run_telegram"]) { expect(workflow.jobs?.[jobName]?.if, jobName).toBe("always()"); } expect(statusJob?.if).toBe("always()"); expect(recordStep?.run).toContain( "qa_live_telegram_release_checks-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}.env", ); for (const field of [ "run_id", "run_attempt", "target_sha", "workflow_sha", "job", "variant", "status", "job_status", "step_outcomes", ]) { expect(recordStep?.run).toContain(`printf '${field}=`); } expect(uploadStep?.if).toBe("always()"); expect(uploadStep?.with?.name).toBe( "release-check-status-qa-live-telegram-${{ inputs.target_sha }}-${{ github.run_id }}-${{ github.run_attempt }}", ); expect(uploadStep?.with?.path).toContain("${{ steps.record_status.outputs.status_file }}"); expect(uploadStep?.with?.path).toContain("${{ steps.record_status.outputs.evidence_file }}"); expect(requireStep?.if).toBe("always()"); expect(requireStep?.run).toContain('[[ "$STATUS" == "success" ]]'); }); it("records producer failure and rejects the terminal status", () => { const result = runAdvisoryStatus({ BUILD_RESULT: "failure", BUILD_STATUS: "failure", }); expect(result.recordResult.status).toBe(0); expect(result.status).toBe("failure"); expect(result.statusFile).toContain("status=failure\n"); expect(result.statusFile).toContain("build:failure"); expect(result.requireResult.status).toBe(1); }); it("records empty producer output and rejects the terminal status", () => { const result = runAdvisoryStatus({ IDENTITY_STATUS: "", }); expect(result.recordResult.status).toBe(0); expect(result.status).toBe("failure"); expect(result.statusFile).toContain("status=failure\n"); expect(result.statusFile).toContain("identity: "); expect(result.requireResult.status).toBe(1); }); it("accepts only complete successful producer evidence", () => { const result = runAdvisoryStatus(); expect(result.recordResult.status).toBe(0); expect(result.status).toBe("success"); expect(result.statusFile).toContain("status=success\n"); expect(result.requireResult.status).toBe(0); }); it("keeps the isolated SUT lifetime below the credential lease TTL", () => { const workflow = parse(readFileSync(WORKFLOW_PATH, "utf8")) as { jobs?: Record; }; const job = workflow.jobs?.run_telegram; expect(job?.["runs-on"]).toBe("ubuntu-24.04"); expect(job?.["timeout-minutes"]).toBe(60); const validateStep = job?.steps?.find( (step) => step.name === "Validate required QA credential env", ); expect(validateStep?.env?.RUNNER_ENVIRONMENT).toBe("${{ runner.environment }}"); expect(validateStep?.env?.CREDENTIAL_ACQUIRE_TIMEOUT_MS).toBe("60000"); expect(validateStep?.env?.JOB_TIMEOUT_MINUTES).toBe("60"); expect(validateStep?.env?.LEASE_TTL_MS).toBe("7200000"); expect(validateStep?.run).toContain('[[ "$RUNNER_ENVIRONMENT" == "github-hosted" ]]'); expect(validateStep?.run).toContain("JOB_TIMEOUT_MINUTES * 60 * 1000 < LEASE_TTL_MS"); const runStep = job?.steps?.find((step) => step.name === "Run Telegram live lane"); expect(runStep?.env?.OPENCLAW_QA_CREDENTIAL_ACQUIRE_TIMEOUT_MS).toBe("60000"); expect(runStep?.env?.OPENCLAW_QA_CREDENTIAL_LEASE_TTL_MS).toBe("7200000"); expect(runStep?.env?.OPENCLAW_QA_TELEGRAM_SUT_CLEANUP_TIMEOUT_MS).toBe("60000"); expect(runStep?.run).toContain("trap terminate_sut_uid_on_exit EXIT"); expect(runStep?.run).toContain('"$OPENCLAW_QA_TELEGRAM_SUT_OPENCLAW_COMMAND" --terminate-uid'); expect(runStep?.run).toContain("run_qa_attempt preflight --scenario channel-canary"); expect(runStep?.run).toContain( "Telegram channel canary failed; skipping the remaining scenarios.", ); expect(runStep?.run).toContain("--list-scenarios"); expect(runStep?.run).toContain('"$scenario_id" != "channel-canary"'); expect(runStep?.run).toContain( 'run_qa_attempt "attempt-${attempt}" "${remaining_scenarios[@]}"', ); expect( runStep?.run?.indexOf("run_qa_attempt preflight --scenario channel-canary"), ).toBeLessThan(runStep?.run?.indexOf("for attempt in 1 2") ?? -1); }); it("serializes stderr behind the workflow-command pause", () => { const workflow = parse(readFileSync(WORKFLOW_PATH, "utf8")) as { jobs?: Record; }; const runStep = workflow.jobs?.run_telegram?.steps?.find( (step) => step.name === "Run Telegram live lane", ); expect(runStep?.run).toMatch( /run_qa_attempt\(\) \(\n\s+set -euo pipefail\n\s+exec 2>&1\n\s+output_name=/u, ); expect(runStep?.run).toContain("::stop-commands::%s"); }); it("derives SUT-writable paths from the verified runtime root after sudo", () => { const source = readFileSync(WORKFLOW_PATH, "utf8"); expect(source).toContain("Telegram SUT launcher failed: stage=%s line=%s status=%s"); expect(source).toContain("launcher_stage=root-run-setup"); expect(source).toContain("launcher_stage=enter-mount-namespace"); expect(source).toContain("set_launcher_stage mask-host-paths"); expect(source).toContain('launcher_stage_file="${RUNTIME_ROOT}/launcher-stage-${BASHPID}"'); expect(source).toContain('set_launcher_stage "mask-host-path:${masked_path}"'); expect(source).toContain("set_launcher_stage mount-proc"); expect(source).toContain("set_launcher_stage write-identity"); expect(source).toContain("set_launcher_stage launch-runtime"); expect(source).toMatch(/set_launcher_stage launch-runtime\n\s+unset launcher_stage_file/u); expect(source).toContain("Telegram SUT runtime preflight failed: stage=%s line=%s status=%s"); expect(source).toContain("runtime_stage=verify-runtime-identity"); expect(source).toContain("runtime_stage=verify-runtime-privileges"); expect(source).toContain("runtime_stage=verify-parent-proc-hidden"); expect(source).toContain("runtime_stage=verify-proc-visibility"); expect(source).toContain("for _ in {1..100}; do"); expect(source).toMatch( /if tr "\\0" "\\n" <"\/proc\/\$\{control_pid\}\/environ" 2>\/dev\/null \|\n\s+grep -Fxq "OPENCLAW_QA_PROC_CONTROL=visible"; then/u, ); expect(source).toContain("proc_marker_visible=true\n break"); expect(source).toContain("sleep 0.05"); expect(source).toContain('[[ "$proc_marker_visible" == "true" ]]'); expect(source).toMatch( /kill "\$control_pid" >\/dev\/null 2>&1 \|\| true\n\s+wait "\$control_pid" \|\| true/u, ); expect(source).toContain("runtime_stage=verify-secret-env-hidden"); expect(source).toContain("runtime_stage=verify-runner-fds-hidden"); expect(source).toContain("runtime_stage=verify-runtime-files"); expect(source).toContain("runtime_stage=verify-host-paths-hidden"); expect(source).toContain("runtime_stage=sanitize-runtime-env"); expect(source).toContain("runtime_stage=verify-runtime-env"); expect(source).toContain("runtime_stage=write-sandbox-proof"); expect(source).toContain("runtime_stage=exec-runtime"); expect(source).toContain('TMPDIR="${SUT_RUNTIME_ROOT}/tmp"'); expect(source).toContain('"$RUNTIME_ROOT"/tmp/openclaw-qa-suite-*'); expect(source.indexOf("launcher_stage=enter-mount-namespace")).toBeLessThan( source.indexOf("/usr/bin/unshare"), ); expect(source).not.toContain("exec /usr/bin/unshare"); expect(source).not.toContain("set -x"); expect(source).toContain('source_node_bin="$(realpath -e "$(command -v node)")"'); expect(source).toContain('node_bin="${runtime_root}/node"'); expect(source).toContain('sudo install -o root -g root -m 0555 "$source_node_bin" "$node_bin"'); expect(source).toContain( '[[ "$(stat -c \'%F:%a:%u:%g\' "$node_bin")" == "regular file:555:0:0" ]]', ); expect(source).toContain('"$node_bin" --version >/dev/null'); expect(source).toContain('for masked_path in "$RUNNER_HOME" /tmp /var/tmp /dev/shm'); expect(source).not.toMatch(/^\s+node_bin="\$\(realpath -e "\$\(command -v node\)"\)"$/mu); expect(source).toContain('temp_root="$(realpath -e "${OPENCLAW_QA_TEMP_ROOT:?}")"'); expect(source).toContain("sudo install -d -o root -g root -m 0700 /tmp/openclaw"); expect(source).toContain( '-m 0711 \\\n "${runtime_root}/tmp/openclaw-${runner_uid}"', ); expect(source).toContain('"$RUNTIME_ROOT"/tmp/openclaw-"$RUNNER_UID"/openclaw-qa-suite-*'); expect(source).toContain('proc_stat="$(cat "/proc/${pid}/stat")"'); expect(source).not.toContain('proc_stat="$(cat /proc/self/stat)"'); expect(source).toContain('if [[ "${1:-}" == "--root-verify" ]]'); expect(source).toContain("signal.pidfd_send_signal(pidfd, signal_value)"); expect(source).toContain('actual_executable="$(realpath -e "/proc/${pid}/exe")"'); expect(source).toContain("cmdlineSha256: $cmdlineSha256"); expect(source).toContain('export HOME="${temp_root}/home"'); expect(source).toContain('export XDG_CONFIG_HOME="${temp_root}/xdg-config"'); expect(source).toContain('if [[ "${1:-}" == "--root-terminate-uid" ]]'); }); it("keeps the generated SUT launcher valid bash", () => { const createSutStep = workflowStep( workflowJob("run_telegram"), "Create isolated Telegram SUT identity and launcher", ); const launcherSource = createSutStep.run?.match( /<<'LAUNCHER'\n([\s\S]*?)\nLAUNCHER(?:\n|$)/u, )?.[1]; expect(launcherSource).toBeTruthy(); const result = spawnSync("bash", ["-n"], { encoding: "utf8", input: launcherSource, }); expect(result.status, result.stderr).toBe(0); }); it("arms the boundary preload only in the gateway main thread", () => { const createSutStep = workflowStep( workflowJob("run_telegram"), "Create isolated Telegram SUT identity and launcher", ); const preloadSource = createSutStep.run?.match(/<<'PRELOAD'\n([\s\S]*?)\nPRELOAD/u)?.[1]; expect(preloadSource).toBeTruthy(); const workdir = tempDirs.make("openclaw-telegram-preload-"); const preloadPath = join(workdir, "preload.mjs"); writeFileSync(preloadPath, preloadSource ?? ""); const env = { ...process.env }; delete env.OPENCLAW_QA_SUT_PREENTRY_STOP; const mainResult = spawnSync(process.execPath, ["--import", preloadPath, "-e", ""], { encoding: "utf8", env, }); expect(mainResult.status).not.toBe(0); expect(mainResult.stderr).toContain("trusted Telegram SUT preload was not armed"); const workerResult = spawnSync( process.execPath, [ "-e", `const { Worker } = require("node:worker_threads");\n` + `const worker = new Worker('require("node:worker_threads").parentPort.postMessage("ready")', { eval: true, execArgv: ["--import", ${JSON.stringify(preloadPath)}] });\n` + `worker.once("message", () => process.exit(0));\n` + `worker.once("error", (error) => { console.error(error); process.exit(1); });`, ], { encoding: "utf8", env, killSignal: "SIGKILL", timeout: 5_000 }, ); expect(workerResult.status, workerResult.stderr).toBe(0); }); it("reports the namespace-entry stage when its supervised child fails", () => { const source = readFileSync(WORKFLOW_PATH, "utf8"); const trapLine = source.match(/^\s+(trap 'exit_status=.*' ERR)$/mu)?.[1]; expect(trapLine).toBeTruthy(); const result = spawnSync( "bash", [ "-c", `set -Eeuo pipefail\nlauncher_stage=enter-mount-namespace\n${trapLine}\nbash -c 'exit 23'`, ], { encoding: "utf8" }, ); expect(result.status).toBe(23); expect(result.stderr).toMatch( /Telegram SUT launcher failed: stage=enter-mount-namespace line=[0-9]+ status=23/u, ); }); it("reports the persisted inner launcher stage when namespace supervision fails", () => { const source = readFileSync(WORKFLOW_PATH, "utf8"); const trapLine = source.match(/^\s+(trap 'exit_status=.*' ERR)$/mu)?.[1]; expect(trapLine).toBeTruthy(); const workdir = tempDirs.make("openclaw-telegram-launcher-stage-"); const stagePath = join(workdir, "stage"); writeFileSync(stagePath, "mount-proc\n"); const result = spawnSync( "bash", [ "-c", `set -Eeuo pipefail\nlauncher_stage=enter-mount-namespace\nlauncher_stage_file=${JSON.stringify(stagePath)}\n${trapLine}\nbash -c 'exit 23'`, ], { encoding: "utf8" }, ); expect(result.status).toBe(23); expect(result.stderr).toMatch( /Telegram SUT launcher failed: stage=mount-proc line=[0-9]+ status=23/u, ); }); it("reports the exact failing runtime preflight line", () => { const source = readFileSync(WORKFLOW_PATH, "utf8"); const diagnosticSource = source.match( /^\s+(fail_runtime_stage\(\) \{[\s\S]*?^\s+\}\n\s+trap "fail_runtime_stage \\?\$\? \\?\$LINENO" ERR)$/mu, )?.[1]; expect(diagnosticSource).toBeTruthy(); const script = `set -Eeuo pipefail\nruntime_stage=runtime-test\n${diagnosticSource}\nfalse`; const failureLine = script.split("\n").findIndex((line) => line === "false") + 1; const result = spawnSync("bash", ["-c", script], { encoding: "utf8" }); expect(result.status).toBe(1); expect(result.stderr).toContain( `Telegram SUT runtime preflight failed: stage=runtime-test line=${failureLine} status=1`, ); }); });