Skip to content

refactor: clarify namespace vs. per-publisher verification checks - #2103

Merged
netomi merged 1 commit into
mainfrom
refactor/namespace-verification-checks
Aug 27, 2026
Merged

refactor: clarify namespace vs. per-publisher verification checks#2103
netomi merged 1 commit into
mainfrom
refactor/namespace-verification-checks

Conversation

@netomi

@netomi netomi commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

RepositoryService conflated two different questions under similarly-named methods: whether a namespace itself is verified (has an owner) vs. whether a specific user counts as a verified publisher for it (member of a namespace that has an owner, or privileged). That ambiguity had already caused a real bug and was an unnecessary dependency in a security scanner.

Changes

  • Add RepositoryService.isVerified(Namespace): the namespace-only check, delegating to the existing hasMemberships(namespace, ROLE_OWNER). Migrated every call site that only ever cared about the namespace itself — the namespace JSON's own verified badge in LocalRegistryService and UserAPI, and SimilarityCheckService's verified-publisher skip — from hasMemberships to this more descriptive method.

  • Fix RelevanceService: it had its own private isVerified(ExtensionVersion) that duplicated RepositoryService.isVerifiedPublisher(ExtensionVersion) almost line for line, but called repositories.isVerified(...) directly instead of isVerifiedPublisher(...) — silently dropping the privileged-user bypass every other verified-badge call site gets. A privileged user's extension version showed as verified everywhere except in search relevance scoring. Removed the duplicate; search now calls the real method.

  • Simplify NamespaceOwnershipCheckScanner: it checked isVerifiedPublisher(namespace, user) using the specific publisher of the scanned version, but self-service namespace creation only ever grants the creator CONTRIBUTOR (never OWNER) — so a namespace can have contributors who can legitimately publish while having no owner at all, which is exactly the squatting scenario this scanner exists to catch. The scanner's own class doc already described the check as namespace-level ("has an owner, not only contributors"), so it now calls isVerified(namespace) directly. This also drops the ExtensionVersion#getPublishedWith() dependency, fixing a latent false-positive: a null publisher (e.g. mirrored/migrated data) used to force a threat regardless of whether the namespace was actually verified.

  • Remove the now-redundant RepositoryService.isVerified(Namespace, UserData) public overload: after the above, its only remaining caller was isVerifiedPublisher(Namespace, UserData) itself. Keeping it exposed a privilege-unaware shortcut that invited exactly the kind of bug just fixed in RelevanceService. Inlined the single jOOQ delegation call directly into isVerifiedPublisher instead.

Tests updated throughout to match: RegistryAPITest, UserAPITest, AdminAPITest, SimilarityCheckServiceTest, DatabaseSearchServiceTest, ElasticSearchServiceTest, NamespaceOwnershipCheckScannerTest, and RepositoryServiceSmokeTest's reflective method-coverage check.

🤖 Generated with Claude Code

RepositoryService conflated two different questions under similarly-named
methods: whether a namespace itself is verified (has an owner) vs. whether a
specific user counts as a verified publisher for it (member of a namespace
that has an owner, or privileged). That ambiguity had already caused a real
bug and was an unnecessary dependency in a security scanner.

- Add RepositoryService.isVerified(Namespace): the namespace-only check,
  delegating to the existing hasMemberships(namespace, ROLE_OWNER). Migrate
  every call site that only ever cared about the namespace itself (the
  namespace JSON's own verified badge in LocalRegistryService and UserAPI,
  and SimilarityCheckService's verified-publisher skip) from hasMemberships
  to this more descriptive method.

- Fix RelevanceService: it had its own private isVerified(ExtensionVersion)
  that duplicated RepositoryService.isVerifiedPublisher(ExtensionVersion)
  almost line for line, but called repositories.isVerified(...) directly
  instead of isVerifiedPublisher(...) - silently dropping the privileged-user
  bypass every other verified-badge call site gets. A privileged user's
  extension version showed as verified everywhere except in search relevance
  scoring. Removed the duplicate; search now calls the real method.

- Simplify NamespaceOwnershipCheckScanner: it checked isVerifiedPublisher
  (namespace, user) using the specific publisher of the scanned version, but
  self-service namespace creation only ever grants the creator CONTRIBUTOR
  (never OWNER) - so a namespace can have contributors who can legitimately
  publish while having no owner at all, which is exactly the squatting
  scenario this scanner exists to catch. The scanner's own class doc already
  described the check as namespace-level ("has an owner, not only
  contributors"), so switch to isVerified(namespace) directly. This also
  drops the ExtensionVersion#getPublishedWith() dependency, fixing a latent
  false-positive: a null publisher (e.g. mirrored/migrated data) used to
  force a threat regardless of whether the namespace was actually verified.

- Remove the now-redundant RepositoryService.isVerified(Namespace, UserData)
  public overload: after the above, its only remaining caller was
  isVerifiedPublisher(Namespace, UserData) itself. Keeping it exposed a
  privilege-unaware shortcut that invited exactly the kind of bug just fixed
  in RelevanceService. Inlined the single jOOQ delegation call directly into
  isVerifiedPublisher instead.

Tests updated throughout to match (RegistryAPITest, UserAPITest, AdminAPITest,
SimilarityCheckServiceTest, DatabaseSearchServiceTest, ElasticSearchServiceTest,
NamespaceOwnershipCheckScannerTest, RepositoryServiceSmokeTest's reflective
method-coverage check).
@netomi
netomi requested a review from cstamas August 27, 2026 08:03
@netomi

netomi commented Aug 27, 2026

Copy link
Copy Markdown
Contributor Author

These inconsistencies came up while reviewing #2065

This PR is supposed to clean them up as a preparation for merging the other.

@cstamas cstamas left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A warm welcome for javadoc! 😃 and cleanup

@netomi
netomi merged commit bea66a2 into main Aug 27, 2026
5 checks passed
@netomi
netomi deleted the refactor/namespace-verification-checks branch August 27, 2026 08:23
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.

2 participants