Files
openclaw/test/scripts/openclaw-release-telegram-qa-workflow.test.ts
Peter Steinberger 4efe056d32 fix(ci): stop Telegram release checks from fetching history (#105219)
* fix(ci): bound Telegram provenance lookups

* docs(agents): note linked worktree invocations
2026-07-12 10:18:39 +01:00

738 lines
30 KiB
TypeScript

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<string, unknown>;
permissions?: Record<string, string>;
"runs-on"?: unknown;
"timeout-minutes"?: unknown;
steps?: Array<{
env?: Record<string, unknown>;
if?: string;
name?: string;
run?: string;
uses?: string;
with?: Record<string, unknown>;
}>;
uses?: string;
with?: Record<string, unknown>;
};
function workflowJob(name: string): WorkflowJob {
const workflow = parse(readFileSync(WORKFLOW_PATH, "utf8")) as {
jobs?: Record<string, WorkflowJob>;
};
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<string, string> = {}) {
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<string, WorkflowJob>;
};
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<string, { required?: boolean; type?: string }>;
secrets?: Record<string, { required?: boolean }>;
};
workflow_dispatch?: {
inputs?: Record<string, { required?: boolean; type?: string }>;
};
};
};
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<string, WorkflowJob>;
};
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<string, WorkflowJob>;
};
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<string, WorkflowJob>;
};
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<string, WorkflowJob>;
};
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`,
);
});
});