feat(broker): a pushing forge credential finally has a compliant home (catalogue v1.8.0) - #2277
Merged
Merged
Conversation
… (v1.8.0) `git push` needs a CREDENTIAL, not a proxied API call. git speaks the smart-HTTP pack protocol, so there is no single request for the broker to make on the caller's behalf and no header for it to substitute. The host-locked `github` entry therefore returns null from `resolveInjectable()` BY DESIGN — a routing signal meaning "use request()", not a denial — but `request()` cannot express a push at all. So a stage that needs to push has had no compliant credential home: either the secret sits somewhere it should not, or the push cannot happen. `github-push` is that home. inject_only, no baseUrl, no allowRules — exactly the shape `anthropic-cli` already uses, and for the same reason: the broker cannot bound this call, so it refuses to make it and the guarded `resolveInjectable()` path applies instead.⚠️ A SEPARATE ENTRY, not `github` flipped to inject_only. That one-line version would have silently widened EVERY existing github credential in the fleet from "its secret never leaves OpenRegister" to "its secret is handed to the calling app". Two entries keeps the pushing credential greppable, reviewable and revocable on its own — and the test asserts both halves, so the shortcut cannot be taken later by accident. What the catalogue CANNOT enforce is written down where whoever mints the token will read it: a fine-grained PAT (or better, a GitHub App installation token, which expires in an hour) scoped to ONE repository, `Contents: Read and write`, and explicitly NOT `Workflows` — a credential that can edit `.github/workflows` obtains code execution on the forge's runners and escapes every other control around it. Not a classic PAT, whose `repo` scope is account-wide and carries workflow write. The $comment names the probe that ASSERTS the absence rather than assuming it (attempt a write under `.github/workflows/` and require the refusal; a 201 means revoke, do not store). The compensating controls live where the credential is used — the hermiq sidecar refuses a push to another repository, to any branch that is not `feature/<issue>/*`, or with a diff touching workflows, a dependency manifest or anything outside the issue's scope, and withholds the credential from the process that reads repository content. The forge's branch rulesets are the backstop. Two tests, both mutation-checked: - renaming `github-push` away -> the first test fails; - re-adding a workflow-dispatch allow-rule to `github` -> the second fails, naming the rule. That grant existed once (openregister#2240) and was reverted (#2242); nothing until now would have caught it coming back. 29 tests, 512 assertions, green.
Contributor
Quality Report — ConductionNL/openregister @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ❌ | ||||
| psalm | ❌ | ||||
| phpstan | ❌ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| composer | ✅ | ✅ 174/174 | |||
| npm | ✅ | ✅ 713/713 | |||
| PHPUnit | ⏭️ | ||||
| Newman | ⏭️ | ||||
| Playwright | ⏭️ |
Quality workflow — 2026-08-02 15:48 UTC
Download the full PDF report from the workflow artifacts.
4 tasks
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.
Part of hydra's
builder-write-access-hardening, task 1 — the credential.The gap
git pushneeds a credential, not a proxied API call. git speaks thesmart-HTTP pack protocol, so there is no single request for the broker to make on
the caller's behalf and no header for it to substitute.
The host-locked
githubentry therefore returnsnullfromresolveInjectable()by design — and that null is a routing signalmeaning "use
request()", not a denial. Butrequest()cannot express a push atall. So a stage that needs to push has had no compliant credential home: either
the secret sits somewhere it should not, or the push cannot happen.
What this adds
github-push—inject_only, nobaseUrl, noallowRules. Exactly the shapeanthropic-clialready uses and for the same stated reason: the broker cannotbound this call, so it refuses to make it and the guarded
resolveInjectable()path applies instead.
githubflipped toinject_onlyThat would have been the one-line version of this change, and it would have
silently widened every existing github credential in the fleet from "its
secret never leaves OpenRegister" to "its secret is handed to the calling app".
Two entries keeps the pushing credential greppable, reviewable and revocable on
its own.
testTheGithubPushCredentialIsInjectOnlyAndTheProxyEntryIsNot()assertsboth halves, so the shortcut cannot be taken later by accident.
What the catalogue cannot enforce, written where it will be read
The
$commentis long on purpose — it is the only place whoever mints the tokenwill look. In short: a fine-grained PAT (or better, a GitHub App installation
token, which expires in an hour) scoped to one repository,
Contents: Read and write, and explicitly notWorkflows. A credential that can edit.github/workflowsobtains code execution on the forge's runners and escapesevery other control around it. Not a classic PAT — its
reposcope isaccount-wide and carries workflow write.
It also names the probe that asserts the absence rather than assuming it:
attempt
PUT /repos/{owner}/{repo}/contents/.github/workflows/probe.ymlandrequire the refusal
refusing to allow a Personal Access Token to create or update workflow ... without 'workflows' scope. A 201 means revoke, do not store.The compensating controls live where the credential is used — see
ConductionNL/hermiq#118: the sidecar refuses a push to another repository, to any
branch that is not
feature/<issue>/*, or with a diff touching workflows, adependency manifest or anything outside the issue's scope; and it withholds the
credential from the process that reads repository content, so none of it depends
on a model's compliance. The forge's branch rulesets are the backstop.
Both tests mutation-checked
github-pushawaytestTheGithubPushCredentialIsInjectOnlyAndTheProxyEntryIsNotfailsgithubtestNoProviderCanReachAWorkflowDefinitionfails, naming the ruleThat second grant existed once (#2240) and was reverted (#2242). Nothing until
now would have caught it coming back.
29 tests, 512 assertions, green.