Skip to content

fix: keep the GitHub token out of build containers, lock down node edges - #1

Merged
one-more-refactor merged 1 commit into
mainfrom
fix/audit-2026-11-07
Nov 7, 2026
Merged

one-more-refactor merged 1 commit into
mainfrom
fix/audit-2026-11-07

Conversation

@one-more-refactor

Copy link
Copy Markdown
Owner

Fixes the Critical and High findings, plus two related Medium ones, from the 2026-11-07 audit.

ID Finding Fix
C1 The owner's GitHub token is readable inside every build container. buildpipe.rs:110-112 put the gh auth token into the origin URL, and git writes that URL to .git/config in the workspace. runtime.rs:122 then bind-mounts that workspace read-write at /workspace, so any build step or npm postinstall script could cat /workspace/.git/config. docs/ARCHITECTURE.md:113 claimed the opposite. The token now goes to git as http.https://github.com/.extraheader through GIT_CONFIG_COUNT/KEY/VALUE environment variables. It is no longer in the URL or in argv, and nothing is written to disk. Same change in heal.rs, where the agent works in the clone.
H1 A visitor can choose the target on a node edge. edge::proxy copied every request header, including the visitor's own x-openpages-target, then appended the panel's spec. reqwest appends rather than replaces, and the node reads the first value, so curl -H 'x-openpages-target: container:6379' https://site.example was proxied to any localhost port on the node. Strip x-openpages-target and x-openpages-node-key from forwarded headers.
H2 The node edge (0.0.0.0:8789) has no authentication, so it was an open proxy to the node's localhost for anyone who can reach it. The panel sends x-openpages-node-key (the token_hash it already stores), and the node compares it in constant time against sha256 of its own secret. Upgrade the panel and nodes together.
H3 Any node token can hijack any deployment. node_result/node_logs didn't check that the deployment belonged to the calling node. A node could mark a local deployment ready with artifact_path: "/", which serves the panel host's filesystem, or with any container_port. claimed_by() requires deployment.node_id == node.id. Results are only accepted while the deployment is building.
H4 Symlinks in the build output were followed. pick_file used is_file()/fs::read, which follow links, so ln -s ~/.config/gh/hosts.yml dist/x from the repo or an install script published host files. A symlinked output or root dir made place() rename a directory from anywhere on the host. Serving canonicalises and requires the file to be inside the deployment. Placing refuses a symlinked output dir, or one that resolves outside the workspace.
M1 branch = git_ref.rsplit('/'), so a push to refs/heads/fix/main deployed to production, and tags counted as branches. Only refs/heads/* is accepted, with the prefix stripped.

Verification

  • cargo fmt --check, cargo clippy --all-targets -- -D warnings and cargo test (103 pass, 5 new) all pass locally.
  • New tests: a_visitor_cannot_choose_the_node_target (a real proxy round-trip against a stand-in node), symlinks_out_of_the_deployment_are_not_served, a_symlinked_or_escaping_output_directory_is_refused, only_the_hash_of_our_own_secret_opens_the_edge, auth_env_carries_the_token_as_a_github_scoped_header. I reverted the two edge fixes and confirmed their tests fail: the node received container:6379,static:blog/1, and the symlinked file was served with 200.
  • I ran the env-header fetch by hand against a private repo: it succeeds, and the token doesn't appear anywhere under .git/.

Not changed: update.rs:264 still clones openpages' own source with the token in the URL (Low; the code is trusted). Remote nodes still receive the owner's full gh token in each job (Medium, design; see the audit report).

…e edges

- buildpipe/heal: the gh token was embedded in the remote URL, which git
  writes to .git/config inside the workspace that is bind-mounted into the
  build container (and the heal checkout the agent works in). It now goes to
  git as an http.extraheader for github.com via GIT_CONFIG_* env vars.
- edge: never forward a visitor's x-openpages-target. reqwest appends, the
  node reads the first value, so any visitor could pick the local port on a
  node to proxy to.
- node edge: require x-openpages-node-key (sha256 of the node secret, which
  the panel already stores) so the 0.0.0.0:8789 listener is not an open
  proxy to the node's localhost.
- api: a node may only post logs/results for a deployment queued for it and
  still building; before, any node token could mark a local deployment ready
  with artifact_path=/ or an arbitrary container_port.
- edge/buildpipe: refuse symlinks that leave the deployment when serving,
  and refuse a symlinked or escaping output directory when placing it.
- webhook: only refs/heads/* deploys; refs/heads/fix/main no longer counts as
  a push to main, tags no longer count as branches.
@coderabbitai

coderabbitai Bot commented Nov 7, 2026

Copy link
Copy Markdown

Important

  • Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 3796f1c8-f5d7-4316-b4f3-32f29d442ad0
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@one-more-refactor
one-more-refactor merged commit 936f5ed into main Nov 7, 2026
4 checks passed
@one-more-refactor
one-more-refactor deleted the fix/audit-2026-11-07 branch November 7, 2026 09:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant