feat(phishing): add hash-prefix lookup API for Web3 safebrowsing - #311
feat(phishing): add hash-prefix lookup API for Web3 safebrowsing#311Douglashdaniel wants to merge 2 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a new phishing hash-prefix (k-anonymity) lookup API backed by Redis, with a refresh/reseed workflow and Prometheus instrumentation, enabling wallets to check against the eth-phishing-detect list without sending plaintext URLs.
Changes:
- Introduces
PhishingManagerto fetch/normalize/PSL-expand entries, hash them, bucket by 4-byte prefix, and store in Redis with staleness-aware reseeding. - Adds FastAPI routes for
/api/phishing/v1/lookupand/api/phishing/v1/_admin/refresh, plus Prometheus metrics for lookup/ingest. - Adds tests for route validation and manager refresh/lookup behavior; adds
publicsuffixlistdependency.
Reviewed changes
Copilot reviewed 10 out of 12 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| pyproject.toml | Adds publicsuffixlist dependency for PSL-aware apex expansion. |
| poetry.lock | Locks publicsuffixlist and updates lock metadata hash. |
| app/main.py | Registers phishing router and adds startup reseed task for the phishing index. |
| app/api/phishing/routes.py | Implements lookup + admin refresh endpoints and request parsing/metrics. |
| app/api/phishing/manager.py | Implements blocklist fetch, normalization/expansion, hashing, Redis ingest + lookup, and staleness reseed logic. |
| app/api/phishing/constants.py | Defines list URL, schema versioning, prefix length, and request limits. |
| app/api/phishing/metrics.py | Adds Prometheus counters/gauges/histograms for ingest and lookup. |
| app/api/phishing/models.py | Adds Pydantic response models with camelCase serialization. |
| app/api/phishing/test_routes.py | Adds route tests (lookup success + validation + admin refresh). |
| app/api/phishing/test_manager.py | Adds manager unit tests (normalize/hash/expand + refresh/lookup + staleness lock). |
| app/api/phishing/init.py | Initializes phishing package (empty module). |
| app/api/common/models.py | Adds PHISHING API tag for routing/docs grouping. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| @router.get("/v1/_admin/refresh", response_model=PhishingRefreshResponse) | ||
| async def admin_refresh_phishing_list(): | ||
| try: | ||
| result = await PhishingManager.refresh() |
There was a problem hiding this comment.
Intentional — this matches /api/tokens/v1/_admin/refresh. Gate3 doesn’t do in-app auth on admin routes; access is gated by the Brave Services Key at the edge, same as the rest of the public API
There was a problem hiding this comment.
Is this meant to be the refresh where we pull in the list and compare with our last diff and add the delta to Redis? If so, we could likely get away without even needing an endpoint here and just have it automatically ran by the service.
onyb
left a comment
There was a problem hiding this comment.
With an 8-char prefix, the number of buckets are significantly more than what we may have in the blocklist. IIUC prefix lookups protect the client only if many entries share a prefix (so gate3 can't tell which one the client meant), which doesn't appear to be the case given the prefix length.
Something like 3 chars makes more sense to me. cc @kdenhartog
|
++ let's keep this to fewer characters to get proper bucketing here. Might need to reduce to 2 |
Adds a privacy-preserving phishing lookup API so wallets can check connection requests against MetaMask's
eth-phishing-detectblocklist without sending plaintext URLs to gate3.Ingests the blocklist into Redis (normalize → SHA-256 → PSL apex expansion), buckets full hashes by 4-byte prefix, and serves k-anonymity lookups. Follows the TokenManager refresh/staleness pattern, with Prometheus metrics for ingest and lookup latency.
Refresh list:
curl -s 'http://localhost:8000/api/phishing/v1/_admin/refresh'Response:
{ "status": "success", "message": "Phishing list refreshed successfully", "version": "2", "entryCount": 107363, "hashCount": 108421 }Lookup by hash prefix:
curl -s 'http://localhost:8000/api/phishing/v1/lookup?prefixes=a5821d84'Response:
{ "version": "2", "matches": { "a5821d84": [ "a5821d8435392716848b1b570a64af44a33f0fd440570a94311e12c7cdcc2b18" ] } }