Skip to content

Commit 36c94d4

Browse files
committed
ci(e2e): constrain apt host tool installs
1 parent 8baba85 commit 36c94d4

3 files changed

Lines changed: 46 additions & 7 deletions

File tree

.github/actions/install-apt-packages/action.yaml

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,13 @@ runs:
4646
echo "::warning::apt-get update attempt ${attempt} failed; retrying." >&2
4747
sleep $((attempt * 5))
4848
done
49-
# Keep runner setup small and predictable. The explicit package list
50-
# plus --no-install-recommends intentionally: (1) minimizes attack
51-
# surface, (2) reduces install time, (3) avoids pulling unnecessary
52-
# recommended packages, and (4) makes callers request every additional
53-
# host tool explicitly.
49+
# Trust model: this CI-only action intentionally trusts the apt sources
50+
# configured on GitHub/NVIDIA Ubuntu runners for small host tools.
51+
# Callers must pass fixed, reviewed package literals from trusted
52+
# workflow YAML, not target-ref, workflow_dispatch, matrix, or
53+
# script-derived values. Keep runner setup small and predictable; the
54+
# explicit package list plus --no-install-recommends intentionally:
55+
# (1) minimizes attack surface, (2) reduces install time, (3) avoids
56+
# pulling unnecessary recommended packages, and (4) makes callers
57+
# request every additional host tool explicitly.
5458
sudo apt-get install -y --no-install-recommends "${packages[@]}"

.github/workflows/nightly-e2e.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,6 @@ jobs:
224224
always_artifact_name: "cloud-onboard-traces"
225225
always_artifact_path: "/tmp/nemoclaw-trace-summary/"
226226
always_artifact_trace_source_path: "/tmp/nemoclaw-traces/"
227-
apt_packages: expect
228227
env_json: '{"NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE":"1","NEMOCLAW_NON_INTERACTIVE":"1","NEMOCLAW_POLICY_MODE":"custom","NEMOCLAW_POLICY_PRESETS":"npm,pypi","NEMOCLAW_RECREATE_SANDBOX":"1","NEMOCLAW_SANDBOX_NAME":"e2e-cloud-onboard","NEMOCLAW_TRACE_DIR":"/tmp/nemoclaw-traces"}'
229228
checked_out_ref_env: "NEMOCLAW_PUBLIC_INSTALL_REF"
230229
nvidia_api_key: true

test/e2e-script-workflow.test.ts

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1013,7 +1013,7 @@ describe("E2E reusable workflow contract", () => {
10131013
expect(installStepIndex).toBeLessThan(stepIndex("Export CI inference environment"));
10141014
expect(installStepIndex).toBeLessThan(stepIndex("Run E2E script"));
10151015

1016-
expect(nightlyWorkflow.jobs["cloud-onboard-e2e"].with?.apt_packages).toBe("expect");
1016+
expect(nightlyWorkflow.jobs["cloud-onboard-e2e"].with?.apt_packages).toBeUndefined();
10171017
expect(nightlyWorkflow.jobs["network-policy-e2e"].with?.apt_packages).toBe("expect");
10181018
expect(
10191019
nightlyWorkflow.jobs["issue-4434-tui-unreachable-inference-e2e"].steps?.find(
@@ -1084,6 +1084,42 @@ describe("E2E reusable workflow contract", () => {
10841084
}
10851085
});
10861086

1087+
it("keeps apt package requests tied to reviewed host-tool consumers", () => {
1088+
const reviewedAptPackageLiterals = new Set(["expect", "expect iptables"]);
1089+
const reusableExpectConsumers: Record<string, string> = {};
1090+
1091+
for (const [name, job] of Object.entries(nightlyWorkflow.jobs)) {
1092+
const aptPackages = job.with?.apt_packages;
1093+
if (aptPackages !== undefined) {
1094+
expect(reviewedAptPackageLiterals.has(aptPackages), name).toBe(true);
1095+
expect(aptPackages, name).not.toMatch(
1096+
/\$\{\{|matrix\.|inputs\.|github\.event\.inputs|env\./,
1097+
);
1098+
if (aptPackages.split(/\s+/).includes("expect")) {
1099+
reusableExpectConsumers[name] = String(job.with?.script ?? "");
1100+
}
1101+
}
1102+
1103+
for (const step of job.steps ?? []) {
1104+
if (!String(step.uses ?? "").includes("install-apt-packages")) continue;
1105+
1106+
const packages = String(step.with?.packages ?? "");
1107+
expect(reviewedAptPackageLiterals.has(packages), `${name}:${step.name ?? ""}`).toBe(true);
1108+
expect(packages, `${name}:${step.name ?? ""}`).not.toMatch(
1109+
/\$\{\{|matrix\.|inputs\.|github\.event\.inputs|env\./,
1110+
);
1111+
}
1112+
}
1113+
1114+
expect(reusableExpectConsumers).toEqual({
1115+
"network-policy-e2e": "test/e2e/test-network-policy.sh",
1116+
});
1117+
for (const [name, script] of Object.entries(reusableExpectConsumers)) {
1118+
const scriptText = readFileSync(new URL(`../${script}`, import.meta.url), "utf8");
1119+
expect(scriptText, name).toContain("command -v expect");
1120+
}
1121+
});
1122+
10871123
it("keeps the apt package validator scoped to simple host tool packages", () => {
10881124
for (const packageName of [
10891125
"expect",

0 commit comments

Comments
 (0)