If you've found a security issue in this project, please do not open a public GitHub issue. Instead, report it privately so the fix can ship before the details are public.
How to report:
- Open a private security advisory on GitHub, or
- Email the maintainer with the subject line
SECURITY: phishing-detectionand a clear description.
What to include:
- Affected component (file path, analyzer name, or endpoint)
- Reproduction steps or a proof-of-concept payload (a sample
.emlis ideal) - Impact you observed and impact you believe is reachable
- Whether you've tested against
mainor a specific commit
Response expectations:
- Acknowledgement: within a few days
- Triage and severity assessment: shortly after acknowledgement
- Fix or mitigation: depends on severity; critical issues are prioritized
- Public disclosure: coordinated with the reporter once a fix is available
I'm a solo maintainer, not a vendor SOC. I'll do my best on response times but cannot commit to enterprise SLAs.
This is a portfolio / research project. Only the main branch is supported. There are no LTS branches. Fixes are committed to main; users running older commits should rebase.
Vulnerabilities in any of these are in scope and welcome:
- Parsers — EML, MIME, attachment handlers, QR decoder, header parser. Crafted inputs that cause RCE, infinite loops, or memory exhaustion.
- Analyzer pipeline — race conditions, async deadlocks, SSRF via URL detonation, request smuggling against external APIs.
- Scoring & override rules — verdict bypasses where a malicious email reliably scores CLEAN, or where a benign email reliably scores CONFIRMED_PHISHING due to a logic flaw (not just a tunable false-positive).
- Browser sandbox — container escape, host filesystem access, host network egress that should be blocked.
- Feedback API — auth bypass, IDOR, SQL injection, label-poisoning attacks beyond the documented "no-auth-by-default" residual risk.
- Secrets handling — credentials leaking into logs, reports, STIX exports, dashboard responses, or git history.
- Passkey step-up - privileged owner/admin SaaS mutations should run with
PHISHANALYZE_PASSKEY_ENFORCEMENT=monitoruntil passkeys are enrolled, then move toenforcefor team, mailbox, billing, passkey registration/deletion, scan deletion, incident case mutation/remediation planning, and simulation ingest actions. The/api/saas/security/policypayload exposes the covered action matrix. A fresh passkey step-up can also authenticate the private admin browser surface only when the owner/admin account is independently listed inPHISHANALYZE_ADMIN_USER_EMAILS. Tenant role alone is insufficient. Password-only sessions and legacy analyst tokens are not phishing-resistant. - Operational alert privacy - alert payloads for analyzer outages, repeated campaigns, payment-risk escalation, and login throttling must reject mailbox content, addresses, subjects, URLs, client IPs, raw users, and raw tenant identifiers. Schema bypasses or webhook disclosure are in scope.
- Export integrity — shareable STIX/Sigma file exports require an Ed25519 signing key and a signed manifest. Validate manifests before sharing threat intel externally.
- Dependency-chain issues — vulnerable pinned dependencies in
requirements.txt.
These are intentionally not security issues:
- False positives or false negatives in detection. The pipeline is probabilistic. Tuning is not a security report. (Exception: a reliable, weaponizable bypass of the override rules — that is in scope.)
- Vendor-side issues. If VirusTotal or urlscan returns a wrong answer, that's not this project's bug.
- Sandbox-evading malware. Documented limitation (
THREAT_MODEL.md§6 R8). Bring a novel evasion against the parser or container, not against an upstream sandbox. - Self-DoS via giant attachments. The handler has size limits; reports must demonstrate bypass of those limits.
- Anything requiring physical access to the host running the pipeline.
- Social engineering of the maintainer.
If you're running this in any non-laptop context, do at minimum:
- The server defaults to binding
127.0.0.1(loopback only). To expose it elsewhere, you must setANALYST_API_TOKENto a high-entropy value AND pass--host <addr>explicitly. The server refuses to start with a non-loopback host if the token is unset. For internet exposure, put it behind a reverse proxy with TLS termination. - Prefer the owner passkey path for browser admin access. Sign in at
/settings, register and verify a passkey, then open/adminwhile the step-up is fresh./admin/loginandAuthorization: Bearer <ANALYST_API_TOKEN>remain compatibility paths for internal clients. - Treat
PUBLIC_DEMO_MODE=trueas sample-only. It opens/demo, not the real dashboard, live upload analysis, mailbox monitoring, feedback learning, paid API-backed checks, or account management. KeepANALYST_API_TOKENconfigured. - Treat
/apppublic signup as a privacy switch. KeepSAAS_PUBLIC_SIGNUP_ENABLED=falseuntil you are ready to accept visitor email uploads, set a high-entropySAAS_SESSION_SECRET, and have retention/support/abuse handling in place. User signup, login, and password-reset routes require same-originOriginorRefererheaders before setting cookies. - Keep the token path internal. Use
PHISHANALYZE_ADMIN_AUTH_MODE=token_or_owner_passkeyfor the user-bound browser bridge and explicitly list platform administrators inPHISHANALYZE_ADMIN_USER_EMAILS. An empty allowlist disables the bridge. Never add every workspace owner. The legacy analyst token is useful for diagnostics and API clients, but it is shared and not phishing-resistant. - Register Stripe webhooks only over HTTPS and keep
STRIPE_WEBHOOK_SECRETsecret./api/stripe/webhookverifies Stripe signatures before changing subscription state. Do not disable signature checks. - Run the
browser-sandboxcontainer on its own Docker network. Do not give it host networking. The defaultdocker-compose.ymlalready separates it; verify before deploying. - Treat
.envas secret material. Don't commit it. Don't bake it into images. Mount it at runtime. - Back up runtime data and purge it on a schedule. Results, phishing alerts, feedback labels, sender profiles, and SaaS scan results can contain regulated personal data. Operational alerts use a separate closed schema and are retained by the same command. Use
python scripts/backup_runtime_data.pyfor non-secret backups andpython main.py purge --target all --dry-runbefore deleting rows. - Monitor uptime and mailbox freshness. Run
python scripts/production_health_check.py --require-monitor-runningfrom cron or an external monitor and send failures to a webhook. - Configure operational alert delivery. Set
OPERATIONAL_ALERT_WEBHOOK_URLand a separateOPERATIONAL_ALERT_HASH_KEY. Circuit-open transitions, repeated campaigns, high payment-risk decisions, and login throttle thresholds then reach the operations channel without mailbox evidence. Reviewdata/operational_alerts.jsonlif the webhook is unavailable. - Install from the hash-pinned lock and keep the advisory gate green. Use
pip install --require-hashes -r requirements.lockin production and runpython -m pip_audit -r requirements.lock --disable-pipafter every lock refresh. The July 2026 lock has no known vulnerabilities. - Pin the brand reference set. Treat
brand_references/as detection content under change control. Anyone who can write to that directory can blind the visual similarity analyzer.
I'm happy to coordinate disclosure timelines with reporters, credit researchers in release notes (or keep them anonymous, your call), and work with downstream consumers if a vulnerability affects their TI workflows.
If you've made a good-faith effort to report a vulnerability privately and haven't gotten a response, you're welcome to escalate by opening a GitHub issue that says only "I sent a security report, please check your inbox." Do not include details in the issue.
Thanks to anyone who's reported issues responsibly. (None to acknowledge yet — be the first.)