Skip to content

Security: bind deploy approvals to the approved commit and artifact - #21

Open
ndbroadbent wants to merge 1 commit into
nathan/security-authzfrom
nathan/security-approval-binding
Open

ndbroadbent wants to merge 1 commit into
nathan/security-authzfrom
nathan/security-approval-binding

Conversation

@ndbroadbent

@ndbroadbent ndbroadbent commented Oct 9, 2026 •

Copy link
Copy Markdown
Member

This PR is stacked on #19. It fixes RG-06 (plus TOKENS-4/5/6/8/14 and PROXY-6) from the 2026-10-09 audit.

Before, an approval authorized "a build" for a token and app. A compromised CI job could ship any tarball or image under someone's approval of a different commit.

What changes

  • Archive binding: a token build must use the archive uploaded under that same approval.
  • Commit binding:
    • The commit always comes from the approval record. A client git-sha, if sent, must match it.
    • Approvals require a full 40-hex SHA.
  • Image binding:
    • Every service image must be pre-built and tagged <sha> or <sha>-<suffix>. Services built from source are rejected.
    • service_image_patterns is now required, so the image repository is pinned as well as the tag; otherwise ghcr.io/attacker/x:<sha> would pass.
    • Patterns are anchored, and the substituted commit is regex-escaped.
  • approved_deploy_commands fails closed: an empty list now allows no commands.
  • GitHub verification:
    • "branch" mode checks the commit is on the feature branch (compare status behind or identical).
    • "latest" mode compares full SHAs.
  • CircleCI auto-approve first checks that the workflow's pipeline is building the approved commit of vcs_repo, and refuses fork pipelines.
  • No secrets in job args: CircleCI and GitHub tokens are no longer stored in river_job.args, and the Jobs API redacts secret-looking keys.

⚠️ Deploy order: set service_image_patterns right AFTER this deploys

Once this is deployed, token builds are refused until each rack's docspring app has:

{"*": "docker\\.io/docspringcom/app:{{GIT_COMMIT}}-amd64"}

This matches what CI already produces: docker.io/docspringcom/app:${CIRCLE_SHA1}-amd64.

Do not set it before deploying. The gateway currently in production (v0.1.1) requires a client git-sha whenever patterns are configured, and DocSpring CI never sends one, so every CI deploy would fail. Deploy this first, then set the patterns immediately. approved_deploy_commands is already set on all three racks.

Testing

  • New tests:
    • binding (other commit, other repository, built from source, wrong object, git-sha mismatch, missing patterns)
    • GitHub branch verification
    • CircleCI pipeline verification
    • job-arg redaction
  • The CLI E2E seed for approved_deploy_commands was in the wrong format. It was silently read as an empty list, which the old code treated as "allow anything"; it's now a plain array.
  • Local results:
    • task go:test: all pass
    • lint: clean
    • CLI E2E: green
    • web E2E: 56/56. One earlier run had a failure caused by CLI E2E leaving seeded settings in the shared shard-1 DB; running web E2E on its own is green.

An approval used to authorize "a build" for the token and app, so a
compromised CI job could ship any tarball or image under it: git-sha was
optional (DocSpring CI never sends one), the build's archive wasn't
compared with the upload, and image patterns were optional and unanchored.

Token builds under an approval now:
- must use the archive uploaded under that same approval, chosen
  deterministically (no "most recent approved" lookup);
- take the commit from the approval record, never from the client. A
  git-sha, if sent, must equal it, and approvals need a full 40-hex SHA;
- may only reference pre-built images tagged <sha> or <sha>-<suffix>.
  Services built from source are rejected;
- require service_image_patterns, so the image repository is pinned
  too. Patterns are anchored and the substituted commit is regex-escaped.

Also:
- approved_deploy_commands fails closed: an empty list allows no
  commands under an approval. The CLI E2E seed stored it as
  {"commands": [...]}, which was silently read as empty and so allowed
  anything; it now uses the plain array production uses.
- GitHub verification: "branch" mode requires the commit to be on the
  feature branch (compare status behind/identical, not any 200),
  "latest" mode compares full SHAs, and URL segments are path-escaped.
- CircleCI auto-approve checks the workflow's pipeline revision and
  repository against the approval (and rejects fork pipelines) before
  approving the hold job. workflow_id must be a UUID.
- CircleCI and GitHub API tokens are no longer stored in River job
  args; workers read them from config. The Jobs API redacts token-,
  secret-, password- and key-like arg keys.
@coderabbitai

coderabbitai Bot commented Oct 9, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Essentials
  • Run ID: 0dc477fb-9332-49cd-961e-0fad6ee865a2

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@deepsource-io

deepsource-io Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

DeepSource Code Review

We reviewed changes in 46bf350...31424c5 on this pull request. Below is the summary for the review, and you can see the individual issues we found as inline review comments.

See full review on DeepSource ↗

PR Report Card

Overall Grade   Security  

Reliability  

Complexity  

Hygiene  

Code Review Summary

Analyzer Status Updated (UTC) Details
JavaScript Oct 9, 2026 4:01a.m. Review ↗
Go Oct 9, 2026 4:01a.m. Review ↗

Important

AI Review is run only on demand for your team. We're only showing results of static analysis review right now. To trigger AI Review, comment @deepsourcebot review on this thread.

This branch has not been deployed

No deployments
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