Fix includeSubdomains dropping all subdomain URLs + broken cache-clear - #15
Merged
Conversation
## includeSubdomains filter map on www.sonarsource.com with includeSubdomains:true returned only 1,727 URLs — all on www. — even though discovery found 38k+ across docs./community./ etc. `URLValidationUtils.matchesDomain` compared candidates against the full SEED hostname: for a `www.<domain>` seed it matched only `www.<domain>` and `*.www.<domain>`, so `docs.<domain>` failed and was filtered out — discarding exactly the subdomains includeSubdomains exists to include. Match on the REGISTRABLE domain instead. New `URLValidationUtils.registrable Domain()` reduces www.sonarsource.com and docs.sonarsource.com both to sonarsource.com (with a curated two-level-TLD set so docs.a.co.uk -> a.co.uk, not co.uk). matchesDomain(includeSubdomains) now compares registrable domains. The discovery service's baseDomainOf delegates to the same helper so the subdomains it *finds* and the subdomains the filter *keeps* can't disagree. Result (Docker, live sites): www.sonarsource.com + includeSubdomains goes 1,727 -> 30,000 (community 19,921 / docs 8,350 / www 1,727 / events 2), zero cross-domain leakage. Look-alikes (evilsonarsource.com, sonarsource.com.attacker .net) still correctly excluded. ## cache-clear no-op POST /api/map/cache/clear returned success but cleared nothing. Cache keys were `url-discovery:<sha256>` (URL folded into the hash), yet clearCache matched keys with `.includes(encodeURIComponent(url))` — which never appears in a hash. Embed a normalized URL token in the key (`url-discovery:<urlToken>:<optionsHash>`) and clear by that prefix. clearCache now returns the count; the controller reports `keysCleared`. Verified: prime -> fromCache:true -> clear (keysCleared:1) -> fromCache:false. tsc, eslint, 201 tests (186 + 15 new), openapi:check all pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.
Bug 1 —
includeSubdomains:truedropped every subdomain URLPOST /api/maponhttps://www.sonarsource.comwithincludeSubdomains:truereturned 1,727 URLs, all onwww.— even though discovery found 38k+ acrossdocs./community./etc. (the sitemaps were pulled). The subdomains it discovered were then thrown away by the filter.Cause:
URLValidationUtils.matchesDomaincompared each candidate against the full seed hostname. For awww.<domain>seed it kept onlywww.<domain>and*.www.<domain>, sodocs.sonarsource.comfailed both checks and was filtered out — discarding exactly whatincludeSubdomainsexists to include.Fix: match on the registrable domain. New
URLValidationUtils.registrableDomain()reduceswww.sonarsource.comanddocs.sonarsource.comboth tosonarsource.com(with a curated two-level-TLD set sodocs.a.co.uk → a.co.uk, notco.uk— which would otherwise match every.co.uksite). The discovery service'sbaseDomainOfnow delegates to the same helper, so the subdomains it finds and the subdomains the filter keeps can't disagree.Bug 2 —
cache/clearwas a silent no-opPOST /api/map/cache/clearreturnedsuccess:truebut cleared nothing. Keys wereurl-discovery:<sha256>(URL folded into the hash), yetclearCachefiltered keys with.includes(encodeURIComponent(url))— which never appears in a hash. Fixed by embedding a normalized URL token in the key (url-discovery:<urlToken>:<optionsHash>) and clearing by that prefix;clearCachenow returns the count and the controller reportskeysCleared.Verification (Docker, against the live sites)
www.sonarsource.com+includeSubdomainsevilsonarsource.com,sonarsource.com.attacker.net)fromCache:true→clearkeysCleared:1→fromCache:falsetsc✅ ·eslint✅ · 201 tests ✅ (186 + 15 new) ·openapi:check✅🤖 Generated with Claude Code