Skip to content

Require claims on publish and fix nil-gap in consistency check#727

Merged
rdimitrov merged 4 commits intomainfrom
rdimitrov/fix-publish-claims-validation
Apr 20, 2026
Merged

Require claims on publish and fix nil-gap in consistency check#727
rdimitrov merged 4 commits intomainfrom
rdimitrov/fix-publish-claims-validation

Conversation

@rdimitrov
Copy link
Copy Markdown
Member

Summary

  • Reject publish requests (POST /v1/entries) that omit claims when authentication is enabled (JWT present) — returns 400 Bad Request
  • Fix the claim consistency check on subsequent publishes to catch asymmetric nil/non-nil mismatches — returns 409 Conflict
  • Extract the inline claim consistency logic into a shared checkClaimConsistency helper used by both MCP and skills publish paths

Problem

Two related bugs in the publish endpoint:

  1. Claims not required when auth is on. Publishing without claims creates an entry with NULL claims in the database. The per-user filter (checkClaims) returns false for empty recordJSON, making these entries invisible to all non-super-admin users — permanently dead entries with no error signal.

  2. Nil-gap in claim consistency check. The guard if claimsJSON != nil && existing.Claims != nil only fires when both sides are non-nil. Publishing without claims against an entry that has claims (or vice versa) silently succeeds with 201, giving the publisher no indication of the inconsistency.

Changes

File Change
internal/api/v1/entries.go Add claims-required guard when JWT is present
internal/service/db/claims_filter.go Add checkClaimConsistency with symmetric nil handling
internal/service/db/impl_mcp.go Replace inline check with checkClaimConsistency call
internal/service/db/impl_skills.go Replace inline check with checkClaimConsistency call
internal/api/v1/entries_test.go 3 test cases for the API guard
internal/service/db/claims_filter_validate_test.go 8 test cases for checkClaimConsistency

Test plan

  • TestPublishEntryClaimsRequired — authenticated without claims → 400, with claims → 201, unauthenticated without claims → 201
  • TestCheckClaimConsistency — both nil, both empty, asymmetric nil (both directions), identical claims, different values, extra keys (both directions)
  • Existing TestPublishServerVersion_ClaimsSubset and TestPublishSkill_ClaimsSubset still pass
  • Existing TestDeleteServerVersion_ClaimsAuthorization and TestDeleteSkillVersion_ClaimsAuthorization still pass
  • Lint passes (task lint-fix)

Fixes #726

🤖 Generated with Claude Code

@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented Apr 15, 2026

Codecov Report

❌ Patch coverage is 75.60976% with 10 lines in your changes missing coverage. Please review.
✅ Project coverage is 60.34%. Comparing base (e9ccced) to head (8e5c713).

Files with missing lines Patch % Lines
internal/service/db/claims_filter.go 73.33% 2 Missing and 2 partials ⚠️
internal/api/server.go 33.33% 2 Missing ⚠️
internal/service/db/impl_mcp.go 0.00% 1 Missing and 1 partial ⚠️
internal/service/db/impl_skills.go 0.00% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #727      +/-   ##
==========================================
+ Coverage   59.80%   60.34%   +0.54%     
==========================================
  Files         106      106              
  Lines       10306    10325      +19     
==========================================
+ Hits         6163     6231      +68     
+ Misses       3600     3550      -50     
- Partials      543      544       +1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Comment thread internal/api/v1/entries.go Outdated
@rdimitrov rdimitrov requested a review from blkt April 20, 2026 14:13
rdimitrov and others added 4 commits April 20, 2026 17:57
The publish endpoint accepted entries without claims when auth was
enabled, creating entries invisible to all non-super-admin users.
The per-user filter returns false for NULL-claims entries, so these
became permanently undiscoverable.

Additionally, the claim consistency check on subsequent publishes
only compared claims when both sides were non-nil. Publishing with
claims against a claimless entry (or vice versa) silently succeeded
with no mismatch error.

The API handler now rejects publish requests that omit claims when
a JWT is present (400 Bad Request). A new `checkClaimConsistency`
helper replaces the inline checks in both the MCP and skills publish
paths, treating asymmetric nil/non-nil as a mismatch (409 Conflict).

Fixes #726

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The `TestAuthzIntegration_EmptyClaimsBehavior` test was written to
document the previous behavior where publishing with empty claims
created invisible entries. Now that the API rejects empty/missing
claims with 400 when auth is enabled, the test verifies the rejection
instead and removes the dead-entry visibility checks.

Fixes #726

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The claims-required check in `publishEntry` previously fired based on
whether the request carried JWT claims in its context, conflating a
per-request signal with a server-wide policy. If this endpoint were ever
added to `PublicPaths` or the auth middleware chain changed, the guard
would silently disarm and the NULL-claims bug would reappear.

Thread `*config.AuthConfig` into the v1 router and derive an
`authEnabled` flag from `Mode != anonymous`. The publish handler now
gates on that flag, so the requirement tracks configuration rather than
per-request state.

Signed-off-by: Radoslav Dimitrov <radoslav@stacklok.com>
Three test fixtures still declared their inline file data in the old
ToolHive format (`{"servers":{}}` at root), which has been unsupported
since #724. They now emit the upstream format
(`{"meta":{...},"data":{"servers":[...]}}`) and include a placeholder
server entry because the validator rejects empty server lists.

Fixes the `TestAuthzIntegration_SourceCRUD` integration failure and the
`admin_claims_test.go` suite — both were failing with "meta is required"
/ "data is required" before, and then "upstream registry must contain
at least one server" once the envelope was added.
@rdimitrov rdimitrov force-pushed the rdimitrov/fix-publish-claims-validation branch from d4a5527 to 8e5c713 Compare April 20, 2026 14:59
@rdimitrov rdimitrov merged commit e7678fa into main Apr 20, 2026
15 checks passed
@rdimitrov rdimitrov deleted the rdimitrov/fix-publish-claims-validation branch April 20, 2026 15:25
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.

Publish endpoint accepts entries without claims, creating invisible entries

3 participants