feat(deploy): authenticate the checkout fetch so private repos can be previewed (#26) - #30
Merged
Merged
Conversation
… previewed (#26) Part A — authenticate the fetch. `fetch_checkout` ran `git fetch` against the plain https URL with no credential, so a private repository could not be checked out. It now authenticates with the short-lived GitHub App installation token the daemon already mints (used for PR comments and Deployment statuses): - The token is injected as a transient `http.extraheader` (`Authorization: Basic base64("x-access-token:<token>")`) via `-c` on the single git process it prefixes — never written into the checkout's persisted `.git/config`. - It is kept out of every log line and error message: the credential lives in a separate `auth` prefix, and the only string a failed fetch builds is `fetch_args.join(" ")`, which never contains it. stderr stays discarded. - `None` (no App creds / no installation id) keeps today's behaviour: public repos still fetch unauthenticated; a private one fails with a clear message naming `--app-id`/`--app-key` and the `contents: read` permission, instead of a bare git error. Tests: the credential decodes to `x-access-token:<token>`; the public path carries none; the token is absent from the redacted error surface; and two git-backed integration tests prove an authenticated fetch checks the code out while leaving no token (and no `extraheader`) in the persisted git config, and that a tokenless fetch still works. Part B — gate the preview URL. A deployed preview is still world-readable. The clean enforcement point is ePHPm's request-phase middleware (covers the static-file path too and fails closed), not switchboard, whose only per-site channel — the deliberately-closed two-key override file — cannot carry a per-preview credential. So this delivers the written design + threat model in docs/preview-access-gate.md and defers the switchboard-side credential work to a follow-up gated on a companion ePHPm change, rather than shipping a prepend-based gate that would be bypassable for static assets. README documents the gap honestly.
|
ePHPm Preview — removed Preview deployment has been torn down. |
|
ePHPm Preview — deployed (health check pending)
Preview updates automatically on each push to this PR. |
|
ePHPm Preview — deployed (health check pending)
Preview updates automatically on each push to this PR. |
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Closes #26 (Part A). Part B is designed and scoped as a companion ePHPm issue — see below.
Part A — authenticate the fetch (implemented)
deployer.rs::fetch_checkoutrangit fetchagainst the plainhttps://github.com/<owner>/<repo>.gitwith no credential, so a private repository could not be checked out. It now uses the short-lived GitHub App installation token the daemon already mints (for PR comments, Deployment statuses, the claim-time PR-state check).Mechanism — token kept out of persisted config and logs:
http.extraheader: Authorization: Basic base64("x-access-token:<token>")via-con the single git process it prefixes (fetch_auth_config+run_git_fetch). Never written to the checkout's persisted.git/config.authprefix and is not part of thefetch_argsslice — the only string a failed fetch joins into its error/log context. stderr stays discarded, so a git error can't leak it either.handle_deploy(fetch_installation_token) with the same guards as the reporting path and passed intoDeployContext.fetch_token.Fail-open for public, clear failure for private: with no App credentials / no installation id the fetch stays unauthenticated — public repos work exactly as before; a private repo fails with a message naming
--app-id/--app-keyand thecontents: readpermission the installation needs, not a bare git error. (Confirm the App installation carriescontents: read.)Tests (
src/deployer.rs):a_token_becomes_a_scoped_extraheader_basic_credential— decodes tox-access-token:<token>.no_token_means_no_credential— public path carries none.the_token_is_kept_out_of_the_error_and_log_surface— token absent from the redacted error string (and base64-encoded, never literal, in the header).private_repo_hint_only_when_unauthenticated.an_authenticated_fetch_checks_out_and_never_persists_the_token— git-backed integration test: the code checks out and.git/configcontains no token and noextraheader. Asserts the "not persisted" property directly.a_public_repo_still_fetches_without_a_token— tokenless path still works.Part B — gate the preview URL (designed, deferred to ePHPm)
A deployed preview is still world-readable. The clean enforcement point is ePHPm, not switchboard, so this PR ships the design rather than a half-gate:
static_request_phase, ephpm#395) before bytes leave disk. A prepend-based gate (switchboard'sauto_prepend_file) runs only on the PHP path, so it's bypassable for static assets — worse than an honest gap.[[middleware]]mounts are global.Chosen mechanism: per-preview HTTP Basic auth, credential generated by switchboard and posted in the PR comment (visible only to users with repo read access), enforced by an ePHPm request-phase gate fed a per-site verifier via a new override key. Full threat model + alternatives (URL token, IP allowlist, GitHub OAuth) in
docs/preview-access-gate.md.Threat model: defends preview privacy (a random internet visitor who knows the hostname). Explicitly not hardened multi-tenant isolation, not protection against someone who already has repo access, not against a compromised reviewer account.
Scope split: this PR = Part A + design. Companion ePHPm issue = the request-phase per-site access gate. Follow-up switchboard PR (after the ePHPm key ships) = generate the credential, write the verifier, post it in the PR comment, rotate per deploy.
Docs
README documents the now-authenticated fetch and, honestly, the open preview-privacy gap pointing at the design doc.
Verification
cargo +nightly fmt --all --check,cargo clippy --all-targets -- -D warnings, andcargo test(223 pass, +6 new) all green.Do not merge — for review.