train T1: merge #3616 at cbc09f6699

This commit is contained in:
garrytan committed 2000-01-01 00:00:00 +00:00
commit a06708c43a
3 files changed
+94

No files matched your search

+30
View File
@@ -288,6 +288,21 @@ jobs:
installed="${chrome%/*}/chrome-sandbox"
test -f "$helper" && test ! -L "$helper"
sudo install -T -o root -g root -m 4755 "$helper" "$installed"
# Runner image 20261026.1.0 keeps kernel.apparmor_restrict_unprivileged_userns=1
# but loads no AppArmor profiles, so Chromium's namespace sandbox aborts with
# "credentials.cc: Permission denied". Grant user namespaces to this one
# resolved binary, the way Ubuntu's own chrome profile does.
- name: Let the bundled Chromium create its sandbox namespaces
run: |
set -euo pipefail
chrome=$(bun -e 'import { chromium } from "playwright"; import { realpathSync } from "node:fs"; console.log(realpathSync(chromium.executablePath()))')
case "$chrome" in
"$HOME"/.cache/ms-playwright/chromium-*/chrome-linux*/chrome) ;;
*) echo "Unexpected Chromium installation path" >&2; exit 1 ;;
esac
profile=/etc/apparmor.d/gstack-playwright-chromium
printf 'abi <abi/4.0>,\ninclude <tunables/global>\nprofile gstack-playwright-chromium %s flags=(unconfined) {\n userns,\n}\n' "$chrome" | sudo tee "$profile" > /dev/null
sudo apparmor_parser -r "$profile"
- name: Install Xvfb + X11 utilities + gate tools
run: sudo apt-get install -y --no-install-recommends xvfb x11-utils poppler-utils fonts-noto-color-emoji zsh
- name: Configure git identity (tests init temp repos)
@@ -397,6 +412,21 @@ jobs:
sudo install -T -o root -g root -m 4755 "$helper" "$installed"
test "$(stat -c '%u:%a' "$installed")" = '0:4755'
cmp -s "$helper" "$installed"
# Runner image 20261026.1.0 keeps kernel.apparmor_restrict_unprivileged_userns=1
# but loads no AppArmor profiles, so Chromium's namespace sandbox aborts with
# "credentials.cc: Permission denied". Grant user namespaces to this one
# resolved binary, the way Ubuntu's own chrome profile does.
- name: Let the bundled Chromium create its sandbox namespaces
run: |
set -euo pipefail
chrome=$(bun -e 'import { chromium } from "playwright"; import { realpathSync } from "node:fs"; console.log(realpathSync(chromium.executablePath()))')
case "$chrome" in
"$HOME"/.cache/ms-playwright/chromium-*/chrome-linux*/chrome) ;;
*) echo "Unexpected Chromium installation path" >&2; exit 1 ;;
esac
profile=/etc/apparmor.d/gstack-playwright-chromium
printf 'abi <abi/4.0>,\ninclude <tunables/global>\nprofile gstack-playwright-chromium %s flags=(unconfined) {\n userns,\n}\n' "$chrome" | sudo tee "$profile" > /dev/null
sudo apparmor_parser -r "$profile"
# Headed-browser tests (handoff, extension sidepanel DOM) need a real
# DISPLAY — first Linux run failed with Playwright's "launched a headed
+10
View File
@@ -1,5 +1,15 @@
# Changelog
## [Unreleased]
**Free Tests passes again on Ubicloud runner image 20261026.1.0.** Every Linux job that landed on that image failed the cookie-import Playwright pipe smoke, in-shard and again on the serial retry, and a rerun passed only when it happened to land on the older 20261021.1.0 image. Both images keep `kernel.apparmor_restrict_unprivileged_userns=1`, but 20261026 loads three AppArmor profiles instead of 123, so nothing grants the bundled Chromium a user namespace and its sandbox aborts with `credentials.cc:131 ... Permission denied`. The two Linux jobs that launch Chromium now grant user namespaces to that one binary, the way Ubuntu's own `chrome` profile does.
### Fixed
- **`.github/workflows/free-tests.yml`**: `free-suite` and `quick-bun-default` gain a step after the SUID sandbox helper step. It resolves Playwright's Chromium, refuses any path outside `~/.cache/ms-playwright/chromium-*/chrome-linux*/chrome`, and loads an AppArmor profile that grants `userns` to that path alone. The kernel restriction stays on for every other binary. The SUID helper step is unchanged, and no step passes `--no-sandbox` or runs `sysctl`. Diagnostic dispatch 40921806393 ran 17 jobs: all 9 on 20261026 failed the smoke without the profile and passed with it, and all 8 on 20261021 passed throughout.
### Tests
- `test/chromium-sandbox-ci.test.ts`: four cases, two per job, run the real step with stubbed `bun` and `sudo`. They check that the step sits after the helper and before the suite, the exact profile bytes and the `apparmor_parser -r` call, that no protection-lowering flag appears, and that an unexpected Chromium path is refused before any privileged call. They fail against main's workflow.
## [1.91.320.0] - 2026-10-29
**A branch that adds its own pregate lane now learns why it still fails, and `gstack-post pr-body` no longer keeps a stale copy of a section.** Pregate reads its policy from the base branch so a branch cannot loosen its own gate. A branch that had already declared a lane in its own `.gstack/pregate.json` was still told to declare it, and went round in a circle. Separately, a PR body that held `## What changed` twice (an old description pasted under the /ship body) kept the old copy every time /ship reposted.
+54
View File
@@ -75,3 +75,57 @@ test('the actual CI setup requires root ownership, setuid mode, and unchanged he
expect(calls).not.toBeNull();
}
});
const NAMESPACE_STEP = 'Let the bundled Chromium create its sandbox namespaces';
const PROFILE = '/etc/apparmor.d/gstack-playwright-chromium';
function grantNamespaces(command: string, options: { outside?: boolean } = {}) {
const fixture = mkdtempSync(path.join(tmpdir(), 'chromium-userns-'));
fixtures.push(fixture);
const bin = path.join(fixture, 'bin');
const home = path.join(fixture, 'home');
const directory = options.outside ? path.join(fixture, 'outside') : path.join(home, '.cache/ms-playwright/chromium-1234/chrome-linux64');
mkdirSync(bin, { recursive: true });
mkdirSync(directory, { recursive: true });
const executable = path.join(directory, 'chrome');
const receipt = path.join(fixture, 'sudo.jsonl');
writeFileSync(executable, 'synthetic browser');
writeFileSync(path.join(bin, 'bun'), '#!/bin/sh\nprintf "%s\\n" "$FIXTURE_CHROME"\n', { mode: 0o755 });
writeFileSync(path.join(bin, 'sudo'), `#!/usr/bin/env node
const fs = require('node:fs');
const args = process.argv.slice(2);
const stdin = args[0] === 'tee' ? fs.readFileSync(0, 'utf8') : null;
fs.appendFileSync(process.env.FIXTURE_RECEIPT, JSON.stringify({ args, stdin }) + '\\n');
`, { mode: 0o755 });
const result = spawnSync('/bin/bash', ['-c', command], {
cwd: root, encoding: 'utf8', timeout: 10_000,
env: { ...process.env, HOME: home, PATH: bin + path.delimiter + process.env.PATH, FIXTURE_CHROME: executable, FIXTURE_RECEIPT: receipt },
});
const calls = existsSync(receipt) ? readFileSync(receipt, 'utf8').trim().split('\n').map(line => JSON.parse(line)) : [];
return { result, executable, calls };
}
for (const [job, suiteStep] of [['free-suite', 'Run free suite'], ['quick-bun-default', 'Run the quick free tier on the install-default Bun']]) {
const jobSteps = workflow.jobs[job].steps;
const at = jobSteps.findIndex((step: any) => step.name === NAMESPACE_STEP);
const grant = jobSteps[at]?.run;
test(`${job} grants user namespaces to the resolved bundled Chromium alone, after its helper and before its tests`, () => {
expect(typeof grant).toBe('string');
expect(at).toBeGreaterThan(jobSteps.findIndex((step: any) => step.name === 'Configure the bundled Chromium sandbox helper'));
expect(at).toBeLessThan(jobSteps.findIndex((step: any) => step.name === suiteStep));
expect(grant).not.toMatch(/--no-sandbox|chromiumSandbox:\s*false|sysctl|apparmor_restrict/);
const { result, executable, calls } = grantNamespaces(grant);
expect(result.status).toBe(0);
expect(calls).toEqual([
{ args: ['tee', PROFILE], stdin: `abi <abi/4.0>,\ninclude <tunables/global>\nprofile gstack-playwright-chromium ${executable} flags=(unconfined) {\n userns,\n}\n` },
{ args: ['apparmor_parser', '-r', PROFILE], stdin: null },
]);
});
test(`${job} refuses to grant namespaces to an unexpected Chromium path`, () => {
const { result, calls } = grantNamespaces(grant, { outside: true });
expect(result.status).not.toBe(0);
expect(calls).toEqual([]);
});
}