Gate publishing on the managed source's claims - #849
Merged
rdimitrov merged 1 commit intoJul 17, 2026
Conversation
`POST /v1/entries` validated the entry's claims against the publisher's
JWT but never checked the managed source's own claims, so a
`manageEntries` writer could publish into a source they don't cover —
e.g. squat names in another tenant's shared managed source. `auth.md` §4
already promised the opposite ("no non-super-admin caller can publish to
it").
Populate the source's claims in `getManagedSource` (they were silently
discarded) and add a visibility check — the caller's JWT must cover the
source's claims (OR-within-array, §3) — in the server and skill publish
transactions, mirroring how referencing a source in a registry is gated.
An untagged managed source is now publishable only by super-admin
(default-deny, §4). Delete and update-claims are unchanged: they are
already gated on the entry's own claims.
Document the gate in `auth.md` §4/§5.
Fixes #845
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## rdimitrov/validate-issue-843 #849 +/- ##
================================================================
+ Coverage 62.03% 62.07% +0.04%
================================================================
Files 109 109
Lines 10719 10724 +5
================================================================
+ Hits 6649 6657 +8
+ Misses 3484 3482 -2
+ Partials 586 585 -1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
danbarr
approved these changes
Jul 17, 2026
JAORMX
approved these changes
Jul 17, 2026
7 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.
What
Fixes #845: publishing into the managed source now requires the caller's JWT to cover the source's claims — not just the entry's claims.
Why
POST /v1/entrieswas gated by themanageEntriesrole and an entry-claims ⊆ JWT check, but nothing validated the managed source's own claims (getManagedSourceeven discarded theClaimsfield).auth.md§4 already stated the intended behavior — "tag the managed source … otherwise no non-super-admin caller can publish to it" — so the code was the outlier.The gap: entry claims are subset-validated, so a
contosowriter can't create an{org:acme}entry — but they could publish an{org:contoso}entry into acme's shared managed source, squatting names in the first-come-first-served namespace. This closes that cross-tenant hole.Change
getManagedSourcenow populatesClaims(previously dropped).auth.md§3), mirroring howresolveSourceIDsWithGategates referencing a source.auth.md§4/§5 updated.Scope: publish only (deliberate)
Delete and update-claims are not changed — they already gate on the entry's own claims (
validateClaimsVisibleBytes(existing.Claims)), which for a published entry is the meaningful per-entry authorization. Adding a redundant source-claims check there isn't necessary. Publish is the actual gap (no entry exists yet to gate on).Semantics: visibility (OR), not subset (AND)
The check compares the caller against the source's existing claims, so per the #848 taxonomy it's the visibility direction (OR-within-array) — consistent with referencing a source. (The issue tentatively assumed subset/AND; visibility keeps it uniform with every other existing-claims gate.)
A managed source with no claims, running with authz on, becomes super-admin-only for publishing. This is exactly what
auth.md§4 prescribes (unlabeled ≠ public), but it will affect existing deployments that ran with an untagged managed source. Recovery: tag the managed source with a tenant-wide claim (e.g.{org: "acme"}) in config. This is a maintainer-facing decision — the issue explicitly flagged "fix code vs fix spec"; this PR is the "fix code" proposal.Testing
TestPublish{Server,Skill}Version_SourceClaimsGate(integration): covered / not-covered / untagged-default-deny / nil-JWT-bypass / array-OR / super-admin-bypass{org:acme}) — all greeninternal/service/db+internal/authzintegration suites pass (real Postgres);go vet,gofmt,golangci-lintcleanFixes #845
🤖 Generated with Claude Code