Skip to content

ci: run Layer-1 PR gates on BuildBuddy from main-controlled workflows #447

Description

@vicondoa

Problem

The required Layer-1 GitHub Actions workflow currently forces every Bazel job to D2B_BAZEL_PROFILE=local with D2B_BAZEL_UNTRUSTED=1. CI therefore cannot reuse the BuildBuddy cache or remote execution used by developers.

The PR workflow is accepted for both main and v3. Any credential-bearing replacement must always execute workflow and bootstrap code from the default branch (main), regardless of the PR base branch, so an older or PR-modified workflow cannot control the privileged path.

GitHub changed pull_request_target in December 2025 to always source workflow code and the default checkout from the repository default branch: https://github.blog/changelog/2025-11-07-actions-pull_request_target-and-environment-branch-protections-changes/

That trigger alone is not sufficient. GitHub explicitly classifies checking out and executing untrusted PR code in a secret-bearing pull_request_target or workflow_run job as a pwn-request vulnerability: https://docs.github.com/en/actions/reference/security/securely-using-pull_request_target

Scope

  • Run every remote-eligible required Layer-1 push and PR gate through BuildBuddy while preserving local-only Nix, fixture, hardware, and tagged actions.
  • Source privileged orchestration, pinned actions, remote policy, and credential handling exclusively from main, including for PRs targeting v3 or any future protected branch.
  • Keep PR code execution in an unprivileged, isolated context. Do not implement pull_request_target plus PR-head checkout plus make in a job that contains repository secrets or a privileged token.
  • Capture and verify immutable base, head, and tested merge OIDs. A review approval, rerun, or label must not authorize a later pushed head implicitly.
  • Broker BuildBuddy authentication so the long-lived API key never enters PR-controlled environment variables, argv, files, action environments, test environments, credential helpers, Bazel rc files, or executable code.
    • The PR must not be able to replace tests/tools/bazel-check, redirect the remote/BES endpoint, add authentication headers, or read the credential source.
    • If GitHub-hosted runners cannot provide this isolation directly, use a dedicated credential proxy or equivalent external boundary. Do not expose the key as a normal Actions secret to executed PR code.
  • Isolate cache writes by trust domain and PR/head identity. Untrusted PRs may read an approved seed namespace but must not poison trusted push/developer cache entries; trusted main/v3 pushes remain the only seed authority.
  • Use least-privilege GITHUB_TOKEN permissions, persist-credentials: false, SHA-pinned actions, ephemeral runners, and no execution of PR-produced artifacts in a privileged follow-up workflow.
  • Preserve the Bazel facade's redaction, typed failure classification, exact target sets, and fail-closed behavior. Authentication or broker failure must not become a success-shaped reduced gate.
  • Replace the current policy assertions requiring local/untrusted CI with assertions for the new trust split and default-branch workflow ownership.
  • Add security-focused Layer-1 policy tests covering malicious edits to workflow files, .bazelrc, the credential helper, endpoints, and PR metadata.

Acceptance criteria

  • Required PR gates targeting both main and v3 use the workflow definition and trusted bootstrap from main.
  • All eligible Bazel actions show BuildBuddy cache/remote-execution activity; explicitly local-only actions remain local.
  • A PR that modifies workflows, Make, .bazelrc, tests/tools/bazel-check, tests, or build scripts cannot obtain the BuildBuddy credential, change the authenticated endpoint, write trusted cache entries, or gain repository write access.
  • Fork PRs either use the same brokered, isolated BuildBuddy path safely or remain blocked from the remote path until that boundary exists; no long-lived secret is exposed as a workaround.
  • Pushes to main and v3 can seed the trusted cache under the existing security-digest contract.
  • The stable required GitHub check result and existing fixed Layer-1 target coverage remain unchanged.
  • BuildBuddy displays the correct tested revision and PR/run linkage rather than the default-branch workflow SHA.

Depends on: #446

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    automationAutomated repository maintenanceenhancementNew feature or requesttestingTest infrastructure

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions