blocker: an exception reads like a block, so make it behave like one
`youtube.com` on the block list catches `music.youtube.com` — a plain entry covers its subtree. `youtube.com` on the *allow* list caught only itself, because exceptions were glob-matched whatever they looked like and a plain name has no metacharacters. The same text in two boxes of the same form meant a subtree in one and a single hostname in the other, with nothing in the UI saying so. A plain exception now covers its subdomains; a masked one is glob-matched exactly as before, which is still how you spare something narrower than a whole subtree (`docs.*`, `*.docs.google.com`). Three enforcers decide this and all three had to move together, or an exception would work in the browser and not at the resolver: `blocker_service.is_allowed`, `:core`'s `isAllowed`, and the desktop DNS stub's own allow check, which was reusing the generic glob matcher. Parity tests on both sides, plus the two sentences in the web UI and the README that promised the old behaviour. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
7 files changed
+120
-9
No files matched your search
@@ -74,9 +74,34 @@ def parse_expansions(raw: list[str]) -> Expansion:
|
||||
return Expansion(labels=labels, wildcard=wildcard, rejected=rejected)
|
||||
|
||||
|
||||
#: What makes an entry a mask rather than a name, `fnmatch`'s own metacharacters.
|
||||
_WILDCARDS = "*?["
|
||||
|
||||
|
||||
def is_mask(entry: str) -> bool:
|
||||
return any(char in entry for char in _WILDCARDS)
|
||||
|
||||
|
||||
def is_allowed(host: str, allow_masks: list[str]) -> bool:
|
||||
"""Whether an allow-list entry (plain or masked) covers this hostname."""
|
||||
return any(fnmatch(host, mask) for mask in allow_masks)
|
||||
"""Whether an allow-list entry (plain or masked) covers this hostname.
|
||||
|
||||
A plain entry covers itself *and its subdomains*, which is the same rule a
|
||||
plain entry follows on the block side — `youtube.com` blocked catches
|
||||
`music.youtube.com`. It did not used to: an exception was glob-matched, so a
|
||||
plain one meant exactly one hostname while a plain block meant a subtree, and
|
||||
the two halves of one list read the same and behaved differently.
|
||||
|
||||
A masked entry is glob-matched as it always was, so `docs.*` and
|
||||
`*.docs.google.com` are unchanged — and still the way to allow something
|
||||
*narrower* than a subtree.
|
||||
"""
|
||||
for mask in allow_masks:
|
||||
if is_mask(mask):
|
||||
if fnmatch(host, mask):
|
||||
return True
|
||||
elif host == mask or host.endswith(f".{mask}"):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def expand_entry(raw: str, expansion: Expansion) -> tuple[list[str], str | None]:
|
||||
|
||||
Reference in new issue
Block a user