ci(house): authenticate house workflows as the Resnovas Bot GitHub App - #16
Conversation
PR Summary by QodoAuthenticate house workflows with the Resnovas Bot GitHub App
AI Description
Diagram
High-Level Assessment
Files changed (8)
|
Code Review by Qodo
1.
|
There was a problem hiding this comment.
Agentic security review completed on the current head. One high finding: the smartcloud App token lists Resnovas/.github in the same mint as the calling repository without permission-* downscope, so the house source of truth inherits the App's full write permissions.
Sent by Cursor Security Agent: Security Reviewer
| - name: Repositories the token may reach | ||
| id: scope | ||
| run: | | ||
| name="${GITHUB_REPOSITORY#*/}" | ||
| if [ "$name" = .github ]; then echo "repositories=.github"; else echo "repositories=$name,.github"; fi >> "$GITHUB_OUTPUT" | ||
|
|
||
| # A failed mint falls back to the workflow token rather than failing the run. | ||
| - name: Mint the Resnovas Bot token | ||
| id: app | ||
| if: github.event.pull_request.head.repo.fork != true && github.actor != 'dependabot[bot]' | ||
| continue-on-error: true | ||
| uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 | ||
| with: | ||
| # GitHub accepts the App ID wherever the client ID is asked for. | ||
| client-id: ${{ vars.RESNOVAS_BOT_APP_ID }} | ||
| private-key: ${{ secrets.RESNOVAS_BOT_PRIVATE_KEY }} | ||
| owner: Resnovas | ||
| repositories: ${{ steps.scope.outputs.repositories }} |
There was a problem hiding this comment.
🔒 Agentic Security Review
Severity: HIGH
This mint lists the calling repository and Resnovas/.github together and omits permission-* inputs, so actions/create-github-app-token issues an installation token with every permission the App has on both repos (Administration, Contents, Workflows, Checks, Issues, Pull requests, and related settings).
The previous PAT was documented as write on synced repos and read on .github. Installation tokens cannot split permissions per repository, so .github now receives the same write set as the caller. Same-repo pull_request runs still mint (forks and Dependabot are skipped), and the token is passed to unpinned resnovas/smartcloud@v2. A workflow change on a leaf repo, or a retagged v2, can therefore mutate the organisation house source of truth, not only the calling repository.
The Graphify refresh job in this PR already mints for the current repository only with explicit contents and pull-request write. For smartcloud, keep a write-scoped mint for the caller and a separate contents/metadata read mint for .github (or equivalent), and pin the smartcloud action to a SHA.
Impact: A less-privileged actor with write on a synced repository, or control of the unpinned v2 tag, can use this token to change house presets, trusted-bot lists, and synced templates in Resnovas/.github.
Reviewed by Cursor Security Reviewer for commit 9a5d4ba. Configure here.
There was a problem hiding this comment.
Valid. An installation token can't split permissions per repository, so the shared mint gives Resnovas/.github the caller's write set. Jonathan's decision: this PR merges as it is (it replaces a PAT that had org-wide reach), and the fix is SMC-128 (Urgent), done right after this landing. It adds a write-scoped mint for the calling repository only, a separate contents:read mint for Resnovas/.github, and github.token for in-repo work. Leaving this thread open until SMC-128 lands. Pinning resnovas/smartcloud to a SHA has to wait for the first v2 release.
There was a problem hiding this comment.
Stale comment
Risk: high. Left a non-blocking comment and did not approve: Cursor Security Agent reported an unresolved high finding on the smartcloud App token scope, and this authentication change is above the medium approval threshold. Human review is needed; no reviewers were assigned because the only CODEOWNER is the PR author.
Sent by Cursor Approval Agent: Pull Request Router and Approver
The smartcloud template mints a Resnovas Bot token (RESNOVAS_BOT_APP_ID, RESNOVAS_BOT_PRIVATE_KEY) with a SHA-pinned actions/create-github-app-token, scoped to the repository and Resnovas/.github, instead of passing the ACCESS_TOKEN PAT. Forks and Dependabot mint nothing and fall back to the workflow token. The Graphify refresh mints its own token, only for the calling repository with contents and pull request access, and commits with sign-commits so GitHub signs the refresh commit, signed off as the app's bot. The reusable workflow keeps the token secret as a deprecated fallback so callers not yet synced keep working. resnovas-smartcloud[bot] joins the trusted bots, and the README documents the app. Refs SMC-121 Signed-off-by: Jonathan Stevens <jonathan@resnovas.com>
Review fix. The smartcloud job tried the mint with empty inputs wherever the app's ID or key was missing, as in a repository outside the organisation, and relied on continue-on-error to fall back. The mint now runs only when both are set, as the Graphify refresh already does. Refs: SMC-121 Signed-off-by: Jonathan Stevens <jonathan@resnovas.com>
9a5d4ba to
48461f3
Compare
There was a problem hiding this comment.
Graphify reviewed this change.
Worth a look — the grounded gate found no coupling regressions or blocking issues, but 5 advisory finding(s) below merit a look before merge.
Graphify review — findings
Migrates the house workflows from the ACCESS_TOKEN PAT to the Resnovas Bot GitHub App, minting a short-lived, repository-scoped token via actions/create-github-app-token from RESNOVAS_BOT_APP_ID and RESNOVAS_BOT_PRIVATE_KEY. The smartcloud token is scoped to the calling repo plus Resnovas/.github, while the Graphify refresh mints a contents/pull-requests token only when the graph is stale, commits through the API so GitHub signs the commit, and attributes the commit to the bot identity. Falls back to the workflow token when no app key is present or the mint fails (forks, Dependabot, out-of-org repos), keeping token/ACCESS_TOKEN as a deprecated path for unsynced callers.
Worth a look
- Smartcloud app token mint uses unsupported input name —
.github/workflows/smartcloud.yml:62· Escalate · high- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- GitHub App token action uses unsupported input name —
.github/workflows/graphify.yml:108· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- Smartcloud App token is minted with all installation permissions —
.github/workflows/smartcloud.yml:57· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- Reusable Graphify workflow receives the long-lived GitHub App private key —
templates/.github/workflows/house-graphify.yml:31· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- Smartcloud drops ACCESS_TOKEN fallback —
.github/workflows/smartcloud.yml:69· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 32 functions depend on the 32 functions this change touches.
Health — grade A; no new coupling hotspots.
Verification — 32 functions in the blast radius were not formally verified this run (proofs are advisory here).
Health delta baseline: last indexed commit 9b4e74e, 2 commit(s) behind this PR's base.
Gate & verification
graphify gate
PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.
Advisory (not blocking):
- verification_scope: 32 function(s) in the blast radius were not formally verified this run
There was a problem hiding this comment.
Risk: high. Left a non-blocking comment and did not approve: Cursor Security Agent still has an unresolved high finding on the smartcloud App token scope, and this authentication change is above the medium approval threshold. Human review is needed; no reviewers were assigned because the only CODEOWNER is the PR author.
Sent by Cursor Approval Agent: Pull Request Router and Approver




What changed
templates/.github/workflows/smartcloud.yml): mints a Resnovas Bot token withactions/create-github-app-token(pinned to the v3.2.0 SHA,owner: Resnovas), using theRESNOVAS_BOT_APP_IDvariable andRESNOVAS_BOT_PRIVATE_KEYsecret. It replacessecrets.ACCESS_TOKEN. The token reaches only the repository andResnovas/.github.graphify.yml): mints its own token for the calling repository only, with contents and pull request write access, and commits withsign-commits: true, so GitHub signs the refresh commit. The sign-off names the app's bot, which is looked up fromapp-slug.house-graphify.yml): passesapp-idandprivate-key(never on pull requests) instead ofACCESS_TOKEN.resnovas-smartcloud[bot](the app, ID 5089542) joins the trusted bots. The preset comments and the README's token section now describe the app.Why
The PAT's rate limit was shared across every run. It could not create check runs, and its sync and Graphify commits were unsigned. App tokens are short-lived and scoped. GitHub signs commits made with them through the API, so required signatures need no bypass (SMC-48).
Decisions
github.token. A failed mint (continue-on-error) falls back the same way instead of failing the run. No token is minted in a job that runs pull request code. Everything in-repo, such as CI and the Graphify check, keepsgithub.token.ACCESS_TOKEN: removed from the workflows. The reusable Graphify workflow still accepts thetokensecret as a deprecated fallback, so repositories that haven't synced the new caller keep working. The README says to delete the secret once every repository has synced.app-idinput that the oldgraphify.yml@maindoesn't declare.Closes SMC-121
Stacks on #12. Companion: Resnovas/smartcloud#649 (signed sync commits and the self-hosted workflow).
Note
Medium Risk
Org-wide CI auth changes how sync, settings, and Graphify refresh push and sign commits; callers must merge after
graphify.ymlexposes the new inputs or refresh runs can break.Overview
House automation stops using the organisation
ACCESS_TOKENPAT and instead mints short-lived Resnovas Bot GitHub App tokens viaactions/create-github-app-token, usingRESNOVAS_BOT_APP_IDandRESNOVAS_BOT_PRIVATE_KEY.The smartcloud workflow (template and rendered copy) scopes each mint to the current repo plus
Resnovas/.github, skips minting on forks/Dependabot or when credentials are missing, and falls back togithub.tokenif minting fails (continue-on-error). Graphify refresh gains optionalapp-id/private-keyon the reusable workflow, mints a repo-only token when the graph is stale, sets author/committer to the app bot, and opens refresh PRs withsign-commits: trueand API commits so GitHub signs them. house-graphify callers pass the app inputs instead ofACCESS_TOKEN; the oldtokensecret remains as a deprecated fallback for repos not yet synced.README and smartcloud/house.yml comments describe the app, required permissions, and retiring
ACCESS_TOKENafter downstream sync.Reviewed by Cursor Bugbot for commit 48461f3. Bugbot is set up for automated code reviews on this repo. Configure here.