# Security Policy
## Reporting a Vulnerability
For security issues — anything that could let an attacker bypass
captchaforge's authentication / authorization / TLS hardening, leak
captured cookies, exfiltrate operator credentials, or inject
arbitrary code via a malicious rule pack — please **do not** open
a public GitHub issue.
Two accepted private channels — pick whichever you prefer:
1. **GitHub Security Advisory (preferred)** — go to
<https://github.com/santhsecurity/captchaforge/security/advisories>
and click **Report a vulnerability**. Fills a private draft
GHSA visible only to the maintainers; comment threading + fix
coordination + CVE issuance all happen inside the GHSA so
nothing leaks to the public timeline until the patch ships.
2. **Email** — `security@santh.dev`, subject
`[captchaforge] <one-line summary>`. Use this when GitHub is
down or you need to attach materials too large for a GHSA
draft.
Either channel reaches the same on-call rotation.
We aim to:
- Acknowledge within 48 hours.
- Confirm + reproduce within 7 days.
- Ship a fix in the next patch release within 30 days.
For the report itself, please include:
- captchaforge version (`captchaforge --version` or the `version`
field from your `Cargo.toml`).
- chromium version + OS.
- A minimal reproduction (a TOML rule pack, a synthesised page,
or a network capture) that demonstrates the issue without
pointing at any live victim.
- Your assessment of impact (information disclosure / RCE / DoS / …).
## Scope
In scope:
- The `captchaforge` crate (lib + cli + bench + challenge +
rules-crate workspace members).
- The bundled `rules/community.toml` rule pack.
- The Python training scripts under `training/`.
Out of scope:
- The `chromiumoxide` / `tokio` / `reqwest` / `rustls` upstream
dependencies — report those upstream.
- The `captchaforge-chromium` patched-binary fork (separate repo;
has its own SECURITY.md).
- Issues in third-party CAPTCHA solving services (2captcha,
CapSolver, CapMonster, …) called by the `ThirdPartyCaptchaSolver`
— report those to the vendor.
- Vendor (Cloudflare / hCaptcha / reCAPTCHA / …) anti-bot
countermeasures themselves — captchaforge solves visible
captchas; if a vendor adds a new fingerprint surface that
defeats stealth, that's a feature gap, not a security issue.
## Hardening Posture
The captchaforge codebase opts in to the following safety rails:
- `#![forbid(unsafe_code)]` on every shipped crate. No `unsafe`
blocks; no `unsafe { ... }` blocks in the entire workspace.
- `cargo clippy --workspace --all-targets -- -D warnings` is
CI-gated; no warnings ship.
- `cargo doc --workspace --no-deps` warning-free; broken
intra-doc links fail CI.
- TOML rules are data-only — the rule-loading path
(`captchaforge::detect::rules::RuleSet::parse`) never executes
user-supplied JS or Rust. The probe JS the loader emits is
generated from the rule's data via `serde_json::to_string`-
escaped values; an attacker controlling a rule file cannot
break out of the JS string context.
- Static rule validation: `captchaforge validate-rules` checks
every TOML rule pack for selector parseability, priority
collisions, and unknown solver_names. CI gates merges.
- Telemetry never logs full URLs / tokens / cookies — only
domain + outcome + timing + per-vendor success rates. Operators
wanting full request capture must opt into the
`TrainingCorpus` pipeline, which warns about the
privacy-sensitive payload in its docstring.
- The mouse-trace ingest server (`trace_ingest::TraceStore`)
validates every payload against the realistic-trace envelope
before persisting; payloads with sub-millisecond cadence,
> 60s duration, < 3 steps, > 500 steps, or empty vendor
field are rejected (401-class error in the future HTTP
surface).
### HTTP boundary hardening (since 0.2.29)
- **Bounded response reads.** Every HTTP response captchaforge
consumes is read through a max-bytes streaming cap so a hostile
or buggy origin cannot OOM the worker by streaming an unbounded
payload. `captchaforge::solver::token_oracle::TokenValidator`
caps at 64 KiB; `captchaforge_challenge::capture_challenge`
caps at 4 MiB. Overflow truncates rather than errors so
classification still runs on the head.
- **Redirect policy.** The default `captchaforge_challenge`
client allows at most 3 redirect hops and refuses any
`https → non-https` downgrade. The previous reqwest default
(10 hops, no scheme guard) was a hostile-redirect SSRF risk
on challenge fetches.
- **SSRF defence on third-party config.** `Config::build_third_party_solver`
no longer maps unrecognised `third_party.service` strings to
a `Custom { base_url: ... }` solver. Only `service = "custom"`
paired with an explicit `base_url` (which must parse as
`http`/`https`) accepts a freeform endpoint; typos or untrusted
config are rejected with a warning.
- **CORS default-deny.** `captchaforge serve` no longer wires
`CorsLayer::permissive()` — cross-origin requests are blocked
unless the operator explicitly passes `--allow-origin URL`
(one or more times). Prevents browser-based CSRF-shaped abuse
of POST `/solve` / `/detect` / `/transcribe` if the listener
is ever bound beyond strict-localhost.
- **CF challenge success contract.** `captchaforge_challenge::cloudflare::CloudflareSolver`
no longer returns `Ok(SolvedChallenge)` on any successful HTTP
transport — it now requires a 2xx/3xx status AND the actual
`cf_clearance` cookie, and uses the cookie's real Max-Age
expiry rather than fabricating a 30-minute window. An optional
`with_validated_revisions(&[...])` allowlist lets operators
refuse PoW attempts against unrecognised CF revisions instead
of silently producing wrong PoW answers.
- **JA4 spec compliance.** `captchaforge_challenge::ja3::compute_ja4`
now follows the Foxio JA4 spec (sorted hex cipher/extension
lists, real ALPN char extraction, server_name+ALPN excluded
from the extension count, sigalgs joined after the extension
hash). The previous bespoke simplification emitted strings
that did not match real JA4 corpora or WAF dashboards.
## Supported Versions
The latest published `0.2.x` patch release is the only supported
line. Earlier `0.2.x` releases get security backports for 90 days
after their successor ships; older minor versions are not patched.
Pin to a major version (`captchaforge = "0.2"`) in production so
you receive security patches automatically without manual
intervention.