feat(search): Google-style search operators, server security hardening, Material 3 web UI#97
Merged
Merged
Conversation
Parses "exact phrase", -term, site:/-site:, intitle:, inurl:, filetype: (alias ext:), before:/after: dates, and OR/| into an engine query (operators the engines understand, forwarded verbatim), an operator-free clean text (for relevance, correction, and the summary lookup), and the structural filters that are enforced locally over the merged results. Mirrors engine/query/QueryOperators.kt, including tokenizer robustness against unterminated quotes and blank phrases. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
EngineContext gains a ranking_terms field (mirroring the Android SearchQuery.rankingTerms): when the fetched query carries scoping clauses, the aggregator's lexical blend and language affinity reason about the operator-free text instead, so a vertical's internal site: OR group or a user's operators never count as subject matter in the match score. None falls back to the query, keeping existing callers unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Both /search routes and the in-app search parse the query once at the boundary (right after the scope-token pass): the engine query is forwarded upstream (scoped for the active vertical), every structural filter is enforced locally over the merged results before sort/ personalization/rules (drops only, scores untouched), and the operator-free clean text drives the summary lookup, the freshness heuristic, click training, and lexical scoring. The on-device spell corrector is skipped for operator-laden queries, whose syntax it would just mangle. Mirrors MetaSearchResultProvider.kt. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Port the Android served-page security pass:
- Reject a literal "Origin: null" on mutation POSTs. An attacker page
can force that opaque value onto a genuinely cross-site POST (a page
served with Referrer-Policy: no-referrer, or a sandboxed iframe), and
it used to slip through because urlsplit("null") yields an empty host
and an empty host is allowed for HTTP/1.0 clients.
- Serve Referrer-Policy: same-origin instead of no-referrer, so our own
forms carry a real origin the CSRF check can verify. Cross-origin
navigation still sends nothing, and result links keep rel=noreferrer.
- Send Content-Security-Policy, Cache-Control: no-store, and
Permissions-Policy on every response, so queries never persist in a
browser cache and no external script/style/frame can ever load.
- Compare the network-access token in constant time
(secrets.compare_digest) and accept it via Authorization: Bearer or
X-SearchMob-Token headers as well as ?token= (which stays for the
OpenSearch templates but leaks into history and bookmarks).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…t sheet Match the Android served pages' Material 3 character while keeping the CSS-variable theming (all 14 palettes, light and dark) and the existing class structure: an elevated search bar with hover and focus-within states, rounded 16px cards, pill buttons, accent state layers (color-mix with rgba fallbacks), quiet hover surfaces on results, and keyboard focus rings on every interactive element including details summaries. The active category chip derives from the theme accent instead of a fixed color pair. The home page gains a collapsible "Search operators" cheat-sheet card (a native details element, keyboard-operable with no JavaScript) listing the supported operators with short descriptions. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Merged
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 & why
Brings the desktop app to parity with the Android 26.07.01 feature set (SearchMob PR #96).
Google-style search operators. New
engines/query_operators.py(a port of Android'sQueryOperators.ktwith the same semantics and test cases):"exact phrase",-term,site:/-site:,intitle:,inurl:,filetype:(orext:),before:/after:dates, andOR. Parsed once at the route/GUI boundary; operators the engines understand are forwarded, and every structural filter is also enforced locally over the merged results, so they behave consistently across engines. The operator-free text drives relevance scoring, sort freshness, the summary, and spell correction, so scoping clauses (including the verticals' internalsite:groups, which previously fed the lexical scorer) never pollute the match score. The served home page gains a collapsible operator cheat sheet.Served-page security hardening.
Origin: nullon a mutation POST is now rejected as cross-site (it used to slip through becauseurlsplit("null")has an empty netloc and empty hosts are allowed for HTTP/1.0 clients);Referrer-Policymoves fromno-referrertosame-originso our own forms carry a real origin (cross-origin navigation still leaks nothing, and result links keeprel="noreferrer").Content-Security-Policy,Cache-Control: no-store, andPermissions-Policy.secrets.compare_digest) and can be sent viaAuthorization: BearerorX-SearchMob-Tokenheaders instead of only the?token=query parameter (which stays for OpenSearch templates).Material 3 served UI. The served pages are restyled to the Android M3 look: elevated search bar with focus states, rounded cards, pill buttons, chip state layers, theme-derived colors. The CSS-variable theming is unchanged and all 14 palettes keep working in light and dark.
Vault-locked fail-soft behavior needed no changes: every store on the served-search path already degrades to defaults, and the new operator path is pure.
Verification
Origin: nullrejection, header presence, token transport/constant-time matrix, cheat-sheet markup); ruff and mypy --strict green.Origin: nulland cross-site POSTs get 403 while same-origin passes,site:/-site:queries return only matching hosts on the served page and/api/search, cheat sheet renders and expands, results and home pages screenshot-checked in dark and light themes.🤖 Generated with Claude Code