You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
ci: run trusted Linux x86-64 checks on Namespace - #120
Trusted Linux x86-64 pull requests use Namespace. ARM64, fork pull requests, macOS and release publication stay on GitHub runners. Check names, tests, time limits and GitHub caches stay the same.
On the same tested source, the x86-64 source-home job took 25m36s on Namespace and 43m16s on GitHub. Namespace ARM64 exceeded 90 minutes; GitHub ARM64 passed in 36m27s. This PR therefore keeps ARM64 on GitHub. Full timings and comparison limits are in #114.
The manual runner option supports a GitHub comparison on the same source. Release publication runs only on a tag push. The change targets feat/0.7.1-models and depends on that branch.
Validation: actionlint 1.7.12, YAML/routing checks, Home entropy, Rust formatting, diff checks and independent source review passed. Final candidate CI passed all eight checks at 8cc32664 (run 36618604940). Independent source review passed. Ready for Irzhy's review; Anders contacts him.
Merge gate: verify that Namespace itself refuses fork PR jobs, including a fork that changes runner labels. GitHub approval for external workflows does not establish this. Keep this PR open until that check passes. The x86-64 gain is faster checks and earlier failure results; macOS remained the 45–50 minute critical path in the comparison.
@irzhywau ready for your review. CI only: trusted Linux x86-64 jobs move to Namespace; ARM64, macOS, fork PRs and publishing stay on GitHub. Focus: can a fork PR or a manual run reach a Namespace runner or the release job? It merges only after proof from Namespace's side that it refuses fork jobs (tracked in #114).
Security review — #120ci: run trusted Linux x86-64 checks on Namespace
@andersalm — security review of head 8cc32664 against feat/0.7.1-models. Review only; no changes were made to the branch.
Verdict: changes requested. Two blockers (F1, F2) before merge; F3–F4 are small and belong in the same PR.
Severity scale: Critical exploitable now with direct impact · High realistic path to code execution or artifact integrity loss · Medium needs an extra condition (infra compromise, maintainer error) · Low hardening / drift risk · Info no action.
F1 — High · The fork boundary is not enforced by the workflow (merge gate)
Where: every runs-on: expression (.github/workflows/ci.yml:39, 79, 103, 147, 203, 303).
Threat. For pull_request events GitHub runs the workflow file from the PR's merge commit, i.e. the fork's own copy of ci.yml. A fork can replace the expression with runs-on: namespace-profile-elastos-ci-x86 and the head.repo.full_name == github.repository check never executes. This is true as soon as the Namespace GitHub App is installed on the repository, independent of this PR. The repository is public, so this is the "self-hosted runners on a public repo" case GitHub warns about.
Today the only barrier is the fork-approval policy (all_external_contributors): one maintainer clicking Approve and run without reading the workflow diff routes attacker code onto Namespace.
What an attacker gains depends on the Namespace profile:
Persistent state (cache volumes / shared disks): poison caches reused by trusted runs → code execution in later trusted jobs. This is the serious case.
Cloud credentials / OIDC trust attached to the profile → lateral movement.
Otherwise: compute-cost abuse (mining) on the org's Namespace bill.
Fix / acceptance criteria for the owner:
Keep the merge gate stated in the PR body: demonstrate that Namespace refuses a job from a fork PR whose ci.yml hard-codes namespace-profile-elastos-ci-x86. Record the run link.
If (1) cannot be shown and the profile needs cache volumes → do not merge; keep PRs on GitHub runners.
Keep the repo setting "Require approval for all external contributors" on; note it in docs/ as a dependency of this routing.
F2 — High (Medium likelihood) · Namespace-built objects can flow into GitHub-built release binaries
Where:
ci.yml:14 — linux_runner defaults to namespace;
ci.yml:327-332 — source-home-linux uses Swatinem/rust-cache@v2 with prefix-key: ${{ runner.os }}-${{ runner.arch }}-elastos-bd and cache-directories: target-build;
.cargo/config.toml — build-dir = "target-build" holds all dependency rlibs, build-script outputs and fingerprints;
ci.yml:381-384 — the same job uploads the release tarballs consumed by publish-github-release.
Path.
Someone runs a manual dispatch on main (default linux_runner: namespace) → source-home-linux (x86) runs on Namespace and saves its cache in the main scope. The key is identical to the GitHub-hosted one (Linux-X64-elastos-bd-…; same job id, toolchain and lockfile).
A v* tag push runs on GitHub-hosted runners, and tag runs can restore caches from the default branch.
Cargo treats registry-dependency artifacts as fresh by fingerprint (their mtime is not rechecked), so dependency rlibs and build-script outputs compiled on Namespace are linked into the published release binary. Workspace crates rebuild after checkout, but dependencies do not.
Threat. A compromise of the third-party runner infrastructure, or of any job running in main scope on it, becomes a supply-chain injection into signed or published releases. It breaks the invariant that release artifacts are built only on GitHub-hosted infrastructure from reviewed source. The PR body's claim that tag/main publication "stays on GitHub" holds for where the job runs, not for what it restores.
The same key-sharing exists for ci.yml:89-94, 111-127, 157-176 and 261-279 (lint/tests/custody smoke, including the buildx type=gha layer cache). None of those feed a release, so they matter less, but the fix should cover them uniformly.
Fix (recommended):
Make release builds cache-free: on the Swatinem/rust-cache step of source-home-linux (and source-home-macos for symmetry), add if: github.ref_type != 'tag'. Tag builds then compile cold, which costs time only on releases.
Separate cache namespaces per runner provider so a GitHub-hosted job can never restore something Namespace wrote. Add the provider to every cache key:
Apply the same provider segment to: rust-cache prefix-key at lines 92, 114, 163, 264, 330, 411; actions/cache keys at 127, 176, 422; and the buildx cache (--cache-from/--cache-to type=gha,scope=${{ needs.route.outputs.provider }}-custody). needs.route.outputs.provider comes from F4.
Cheaper alternative, if (2) is too invasive: save-if: ${{ github.event_name == 'push' }} on every cache step, so only GitHub-hosted push runs ever write caches. PR re-runs become slower.
Acceptance: show a tag-like dispatch (or a dry-run on a test tag in a fork) whose source-home-linux log reports no cache restored. Show that a Namespace PR run's cache key contains namespace and a GitHub run's contains github.
F3 — Low · Manual dispatch defaults to the third-party runner
Where:ci.yml:10-14.
Threat. The default for the most privileged manual path (dispatch on main, with an arbitrary ref input) is the less-trusted runner, and it is what enables the F2 path. It is surprising for an operator who just wants to "re-run CI on main".
Fix:default: github. Namespace becomes an explicit opt-in for the #114 comparison. No other change needed.
F4 — Low · Routing policy duplicated in six places
Where: the identical long expression at ci.yml:39, 79, 103, 147, 203 and a variant at 303.
Threat. A future edit that updates five of six copies silently routes one job (possibly a new release-relevant job) to the wrong runner. Security policy should have one source of truth.
Fix: add a tiny first job and consume its outputs:
Then source-gate gets needs: route and runs-on: ${{ needs.route.outputs.linux_x86 }}. Every downstream job adds route to needs. For source-home-linux, keep the matrix but select matrix.os == 'ubuntu-24.04' && needs.route.outputs.linux_x86 || matrix.os. Pass values via env: as shown, never by interpolating into run:.
Positive findings (keep)
Info:publish-github-release now requires github.event_name == 'push' (ci.yml:489). Previously a manual dispatch on a v* tag ref could publish a release. This is a real hardening.
Info: ARM64, macOS, main pushes, tag pushes and fork PRs stay on GitHub-hosted runners as intended by the YAML.
Info: no secrets.* are used by any Namespace-routable job; the workflow token stays contents: read (plus pull-requests: read for the custody filter).
Out of scope (existing, not introduced here)
Third-party actions are pinned by tag, not SHA (softprops/action-gh-release@v2 runs with contents: write). Consider SHA pinning in a separate PR.
@irzhywau thank you, this changed the decision. We are not adopting Namespace for now: this PR will be closed without merging, and the trial result plus your findings go into #114, which closes as not adopted. elastos-runtime's access for the Namespace app gets removed, so a fork cannot route jobs there (your F1). The one good part of this PR, publishing only on a tag push, moves into a small separate PR together with cache-free tag builds, and I'll ask you to review that one.
Closing without merge under Anders’s decision: Namespace is not adopted.
The useful publication guard and cache-free tag builds are preserved in #138 (Part of #137), with @irzhywau requested to review.
Irzhy’s review and the adoption decision are recorded in #114.
The branch is retained. App access removal requires an organization owner; the exact settings are in #114 (comment).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #114
Trusted Linux x86-64 pull requests use Namespace. ARM64, fork pull requests, macOS and release publication stay on GitHub runners. Check names, tests, time limits and GitHub caches stay the same.
On the same tested source, the x86-64 source-home job took 25m36s on Namespace and 43m16s on GitHub. Namespace ARM64 exceeded 90 minutes; GitHub ARM64 passed in 36m27s. This PR therefore keeps ARM64 on GitHub. Full timings and comparison limits are in #114.
The manual runner option supports a GitHub comparison on the same source. Release publication runs only on a tag push. The change targets
feat/0.7.1-modelsand depends on that branch.Validation: actionlint 1.7.12, YAML/routing checks, Home entropy, Rust formatting, diff checks and independent source review passed. Final candidate CI passed all eight checks at
8cc32664(run 36618604940). Independent source review passed. Ready for Irzhy's review; Anders contacts him.Merge gate: verify that Namespace itself refuses fork PR jobs, including a fork that changes runner labels. GitHub approval for external workflows does not establish this. Keep this PR open until that check passes. The x86-64 gain is faster checks and earlier failure results; macOS remained the 45–50 minute critical path in the comparison.