feat(#5833): add repo-level foreign allow-list for mint - #5945
Conversation
Extend the mint's foreign authorization system to support repo-level FULLSEND_FOREIGN_<ROLE>_REPOS variables alongside the existing org-level variables (ADR 0060). This enables per-repo foreign grants with scope restricted to specific target repositories, and intra-org cross-repo access for per-repo callers. Changes: - github.go: add GetRepoVariable and ReadForeignAllowlistFromRepo for reading repo-level Actions variables - handler.go: extend mintTokenCrossOrg with repo-level fallback (union semantics), add checkRepoForeignGrants, loadRepoForeignAllowlist, fetchRepoForeignAllowlist; add intra-org cross-repo path for per-repo callers - repos_scope.go: relax validateReposScope to allow non-empty repos in foreign mint requests (repo-level FOREIGN grants validated in mintTokenCrossOrg) - foreign.go: add repoForeignCacheKey helper - ADR 0083: document the repo-level foreign allow-list mechanism - architecture.md: update cross-org and repos scope sections Note: pre-commit could not run due to sandbox network restrictions (cannot fetch origin). The post-script runs pre-commit authoritatively on the runner. Closes #5833
|
🤖 Finished Review · ✅ Success · Started 5:59 PM UTC · Completed 6:13 PM UTC |
Site previewPreview: https://e696e5ab-site.fullsend-ai.workers.dev Commit: |
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
ReviewFindingsMedium
Previous runReviewFindingsMedium
Low
Previous run (2)ReviewFindingsMedium
Low
Previous run (3)ReviewFindingsMedium
Low
Previous run (4)ReviewFindingsMedium
Low
Previous run (5)ReviewFindingsMedium
Low
Labels: PR modifies mint authorization system (internal/mintcore/) with security-relevant changes to foreign grant authorization paths |
|
/fs-fix In mintTokenCrossOrg, when specific repos are requested, Org-level FOREIGN then effectively authorizes only Update ADR 0083 and tests (drop union-for-specific-repos). |
|
🤖 Finished Fix · ✅ Success · Started 5:52 AM UTC · Completed 6:06 AM UTC |
In mintTokenCrossOrg, repo-scoped requests now authorize exclusively via per-repo FOREIGN grants — the org-level variable is not consulted. Org-level FOREIGN effectively authorizes only installation-wide tokens. This eliminates union semantics for specific-repos requests. Also addresses review-agent findings: - Log errors from checkRepoForeignGrants in both cross-org and intra-org paths - Use %v instead of %w in checkRepoForeignGrants for handler-layer consistency - Rename orgVariableResponse to variableResponse (used for both org and repo) - Reword repoForeignCacheKey doc comment for clarity - Add later-note annotations to ADR 0060 and ADR 0077 referencing ADR 0083 - Update ADR 0083 to document authorization boundary (no union semantics) - Add TestHandler_IntraOrgRepoForeignGrant_PartialDenied for partial grants - Update existing tests to not mock org-level endpoints for repo-scoped requests - Rename UnionWithOrgLevel test to OrgLevelForInstallationWide Addresses review feedback on #5945
🔧 Fix agent — iteration 1 (human-triggered)Addressed all 11 findings: 1 core architectural change (human instruction to separate org/repo FOREIGN scopes), ADR 0083 update, test updates, and 8 review-agent findings (error handling, naming, stale docs, missing test). Fixed (11):
Tests: passed Decision points
Next steps:
|
|
🤖 Finished Review · ✅ Success · Started 6:07 AM UTC · Completed 6:22 AM UTC |
|
/fs-fix Add --repo to the foreign CLI for repo-level
--repo should accept owner/repo (consistent with other Without --repo, keep existing org-level behavior. |
|
🤖 Finished Fix · ✅ Success · Started 6:28 AM UTC · Completed 6:44 AM UTC |
Add --repo flag to fullsend admin foreign {allow,list,revoke} for
repo-level FULLSEND_FOREIGN_<role>_REPOS variable management:
fullsend admin foreign allow --repo owner/repo --role <role> --caller <caller>
fullsend admin foreign list --repo owner/repo [--role <role>]
fullsend admin foreign revoke --repo owner/repo --role <role> --caller <caller>
--repo accepts owner/repo (so --org can be omitted) or bare repo name
with --org. Without --repo, existing org-level behavior is preserved.
Review findings addressed:
- Fix "union semantics" wording in architecture.md (disjoint boundaries)
- Guard intra-org fallback more precisely (check per-repo denial message)
- Add INVARIANT comment on validateReposScope for foreign requests
- Add TestHandler_OrgLevelForeignDoesNotAuthorizeRepoScoped
Addresses review feedback on #5945
🔧 Fix agent — iteration 2 (human-triggered)Added --repo flag to fullsend admin foreign CLI (allow/list/revoke) for repo-level FULLSEND_FOREIGN__REPOS management. Addressed 4 review-agent findings: fixed architecture.md union semantics wording, added precise guard on intra-org fallback, added INVARIANT comment on validateReposScope, and added missing test for disjoint authorization boundary. Fixed (6):
Tests: passed Decision points
Next steps:
|
|
🤖 Finished Review · ✅ Success · Started 6:45 AM UTC · Completed 7:04 AM UTC |
|
/fs-fix fix the review findings |
|
🤖 Finished Fix · ✅ Success · Started 7:22 AM UTC · Completed 7:34 AM UTC |
- Fix technical-inaccuracy in docs/architecture.md: replace "either" phrasing with accurate disjoint routing description (org-level grants are not consulted for repo-scoped requests) - Replace fragile string matching in handler.go with sentinel error (errPerRepoCrossRepo) and errors.Is check for per-repo cross-repo denial dispatch - Add --repo flag documentation to e2e-testing.md foreign grant examples - Add cross-org authorization ADR cross-references to mint-administration.md - Add sentinel error test (TestValidateReposScope_PerRepoSentinel) - Keep embed copies in sync Addresses review feedback on #5945
🔧 Fix agent — iteration 3 (human-triggered)Addressed 4 of 5 review findings. Fixed technical-inaccuracy in architecture.md, replaced fragile string matching with sentinel error in handler.go/repos_scope.go, added --repo documentation to e2e-testing.md, added cross-references to mint-administration.md. Disagreed with cli-flag-patterns finding (already documented in CLI help). Fixed (4):
Disagreed (1):
Tests: passed Decision points
Next steps:
|
|
🤖 Finished Review · ✅ Success · Started 7:35 AM UTC · Completed 7:50 AM UTC |
|
/fs-fix |
|
🤖 Finished Fix · ✅ Success · Started 7:59 AM UTC · Completed 8:09 AM UTC |
Return reposScopeShapeForeignRepoScoped from validateReposScope when a foreign request carries non-empty repos, making the repo-level FOREIGN grant authorization requirement visible in the return value rather than relying solely on the INVARIANT comment. Add doc comment to checkRepoForeignGrants clarifying its dual use (cross-org primary authorization and intra-org per-repo fallback). Addresses review feedback on #5945
🔧 Fix agent — iteration 4 (human-triggered)Addressed 2 of 3 review findings. Added reposScopeShapeForeignRepoScoped sentinel shape for foreign+non-empty repos (making the authorization invariant value-visible rather than comment-only) and expanded checkRepoForeignGrants doc comment to clarify dual use. The cli-flag-patterns finding was informational with no remediation suggested. Fixed (2):
Disagreed (1):
Tests: passed Decision points
Next steps:
|
|
🤖 Finished Review · ✅ Success · Started 8:10 AM UTC · Completed 8:23 AM UTC |
|
🤖 Finished Retro · ✅ Success · Started 9:24 AM UTC · Completed 9:44 AM UTC |
Retro: PR #5945 — repo-level foreign allow-list for mintTimeline
Total: 14 agent runs dispatched (1 triage, 1 code, 6 review, 4 fix, 1 retro in-progress, 1 review cancelled as duplicate). 4 fix iterations, of which 2 were human-directed substantive changes and 2 were human-shepherded review finding fixes. Key finding: review agent missed authorization boundary designThe review agent's round 1 found valid but surface-level issues (error handling gaps, naming inconsistencies). It did not evaluate the fundamental design question: should org-level and repo-level FOREIGN authorization use union semantics (consult both) or disjoint scopes (repo-level exclusively for repo-scoped requests)? The initial code implemented union semantics, which would let an org-level grant (intended for installation-wide access) implicitly authorize narrowly-scoped repo requests — a violation of least privilege on a The human (ifireball) identified this ~12 hours later and directed the architectural fix. Notably, once the fix was applied, the review agent correctly flagged stale documentation that still referenced union semantics — showing it can distinguish the two models but only when comparing docs to code, not when evaluating the design itself. This is evidence for #898 ("Review agent misses security-critical findings on large architectural PRs"). Issue #898 identifies three remaining gaps in the security sub-agent after the multi-sub-agent architecture was implemented. This PR reveals a fourth: the security sub-agent does not evaluate how authorization layers compose (whether multiple auth policies interact to create emergent privilege not visible in any single check). The existing remaining gaps focus on individual permission checks (fail-open detection, permission manifest guidance, large-PR prioritization) but not on composition semantics. Evidence for other existing issues
Autonomy assessmentThe review agent correctly applied Proposals filed
|
Summary
Add repo-level
FULLSEND_FOREIGN_<ROLE>_REPOSvariable support to the mint's foreign authorization system, enabling per-repo foreign grants with scope restricted to specific target repositories and intra-org cross-repo access for per-repo callers.Changes
github.go: AddGetRepoVariableandReadForeignAllowlistFromRepofor reading repo-level Actions variables viaGET /repos/{owner}/{repo}/actions/variables/{name}handler.go: ExtendmintTokenCrossOrgwith repo-level FOREIGN fallback (union semantics with org-level grants); addcheckRepoForeignGrants,loadRepoForeignAllowlist,fetchRepoForeignAllowlist; add intra-org cross-repo authorization path for per-repo callers in the main handler flowrepos_scope.go: RelaxvalidateReposScopeto allow non-empty repos in foreign mint requests (repo-level FOREIGN grants are validated separately inmintTokenCrossOrg)foreign.go: AddrepoForeignCacheKeyhelper for distinct cache keysarchitecture.md: Update cross-org authorization and repos scope sectionsTesting
TestHandler_RepoLevelForeignGrant_CrossOrg— cross-org request with specific repos authorized via repo-level FOREIGN variableTestHandler_RepoLevelForeignGrant_Denied— unauthorized caller denied even with repo-level variable setTestHandler_RepoLevelForeignGrant_UnionWithOrgLevel— org-level grant takes precedence for installation-wide requestsTestHandler_IntraOrgRepoForeignGrant— per-repo caller accessing another repo in same org via repo-level grantTestHandler_RepoLevelForeignGrant_ScopeRestriction— repo-level grant on one repo does not authorize other reposTestHandler_CrossOrgNonEmptyReposDeniedfor relaxedvalidateReposScopeGetRepoVariable,ReadForeignAllowlistFromRepo,repoForeignCacheKeyChecklist
!for breaking changes)Closes #5833
Post-script verification
agent/5833-repo-level-foreign-allowlist)78f836dd99749c3db896155ddb8acac422f51f03..HEAD)