Skip to content

feat: expose top-level url_risk field on /predict, wire frontend to it (#822)#844

Open
Rudra-clrscr wants to merge 2 commits into
Userunknown84:mainfrom
Rudra-clrscr:feature/822-url-phishing-risk
Open

feat: expose top-level url_risk field on /predict, wire frontend to it (#822)#844
Rudra-clrscr wants to merge 2 commits into
Userunknown84:mainfrom
Rudra-clrscr:feature/822-url-phishing-risk

Conversation

@Rudra-clrscr

Copy link
Copy Markdown
Contributor

Summary

Closes #822.

Investigation showed most of this feature already existed server-side:
domain extraction, WHOIS-based domain age, DNSBL blacklist checks (Spamhaus
ZEN/DBL, SpamCop, Barracuda), URLhaus/Google Safe Browsing/VirusTotal threat
intel, and suspicious-TLD/IP-based-URL heuristics were all already feeding
into domain_checker.py's domain_analysis, computed on every /predict
call but never surfaced as a dedicated top-level risk field.

  • api.py: make_prediction_response now derives a thin url_risk
    field ({is_url_present, score, level}) from the existing
    domain_analysis whenever it's present. domain_analysis itself is
    completely unchanged (backward compatible).
  • URLPreview.jsx: now accepts a urlRisk prop and prefers it (mapping
    SAFE/WARNING/BLOCK to the component's existing safe/warning/danger
    vocabulary) over its old hardcoded shortener/keyword stub, which only ran
    client-side and was never connected to the real backend signal. Falls
    back to the local heuristic when hovering before a prediction has run.
  • App.jsx: added urlRisk state, populated from the /predict
    response, reset alongside the other prediction state, passed to
    URLPreview.
  • README: new "URL / Phishing Risk Detection" section documenting the
    full signal chain and the optional SAFE_BROWSING_API_KEY /
    VIRUSTOTAL_API_KEY env vars, which weren't documented as a distinct
    feature before.
  • backend/tests/test_url_risk.py: new tests covering url_risk
    absent vs. present (derived from mocked domain_checker results), plus
    direct unit tests of make_prediction_response's derivation logic and
    its omission when no domain_analysis is present.

Note: App.jsx and ManipulationIndex.jsx also carry the same parse-error
fixes already submitted in the pending fix/frontend-eslint-errors PR
(duplicate function App(), unclosed div, undefined analyzeEmojis
reference, stray non-JS line) — needed here too since this branch also
forks from upstream/main before that fix lands, and this feature required
editing App.jsx. Will need reconciling with whichever PR merges first.

Test plan

  • cd backend && pytest tests/test_url_risk.py -q — 5/5 pass
  • Manually ran the Flask API live and hit /predict:
    • plain message → "url_risk":{"is_url_present":false,"level":"SAFE","score":0}
    • "...at http://bit.ly/abc123" → real WHOIS lookup (age_days: 6630,
      creation_date: 2008-05-17), real DNSBL checks, url_risk correctly
      derived from the live result
  • cd frontend && npx vite build — succeeds
  • npx eslint on all touched frontend files — clean

Userunknown84#822)

Most of what issue Userunknown84#822 asks for already existed server-side: domain
extraction, WHOIS-based domain age, DNSBL blacklist checks (Spamhaus
ZEN/DBL, SpamCop, Barracuda), URLhaus/Google Safe Browsing/VirusTotal
threat intel, and suspicious-TLD/IP-based-URL heuristics all already fed
into domain_checker.py's `domain_analysis`, computed for every /predict
call but never surfaced as a dedicated, top-level risk field.

- api.py: make_prediction_response now derives a thin `url_risk` field
  ({is_url_present, score, level}) from the existing domain_analysis
  whenever it's present, leaving domain_analysis itself completely
  unchanged (verified live against real WHOIS/DNSBL lookups: a no-URL
  message returns {is_url_present: false, score: 0, level: "SAFE"}, and
  "http://bit.ly/..." returns is_url_present: true with the domain's
  real, live-looked-up risk score/level).
- frontend/src/components/URLPreview.jsx: now accepts a `urlRisk` prop
  and prefers it (mapping SAFE/WARNING/BLOCK to the component's existing
  safe/warning/danger status vocabulary) over its old hardcoded
  shortener/keyword stub, which only ran client-side and was never
  connected to the real backend signal. Falls back to the local
  heuristic when hovering before a prediction has run.
- App.jsx: added urlRisk state, populated from the /predict response,
  reset alongside the other prediction state, and passed to URLPreview.
- README: new "URL / Phishing Risk Detection" section documenting the
  full signal chain (WHOIS age, DNSBL, threat intel, heuristics), the
  url_risk schema, and the optional SAFE_BROWSING_API_KEY/
  VIRUSTOTAL_API_KEY env vars - none of which were documented as a
  distinct feature before.
- backend/tests/test_url_risk.py: new tests covering url_risk absent
  vs. present, both derived correctly from mocked domain_checker results,
  plus direct unit tests of make_prediction_response's derivation logic
  and its omission when no domain_analysis is present (backward compat).

App.jsx and ManipulationIndex.jsx also carry the same parse-error fixes
already submitted in the pending frontend-eslint-errors PR (duplicate
`function App()`, unclosed div, undefined analyzeEmojis reference,
stray non-JS line) - needed here too since this branch also forks from
upstream/main before that fix lands, and this feature requires editing
App.jsx.
Copilot AI review requested due to automatic review settings July 13, 2026 20:25
@vercel

vercel Bot commented Jul 13, 2026

Copy link
Copy Markdown

Someone is attempting to deploy a commit to the Aditya Sharma's projects Team on Vercel.

A member of the Team first needs to authorize it.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

The container's Flask process defaults to FLASK_HOST=127.0.0.1 (a
deliberate secure default for bare-metal runs), so inside the CI
container it never listens on an interface reachable through the
-p 5000:5000 port mapping, and the smoke test's curl always times out.
docker-compose.yml already sets FLASK_HOST=0.0.0.0 for the same reason;
the CI workflow's docker run step needs the same override.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add URL / phishing link detection

2 participants