crates/icaptcha-client/tests/icaptcha_mock_consumed.rs (#184) guards against a swallowed dual-contact leak with two
halves: mock-consumed asserts, plus a proxy blackhole (NO_PROXY for the mock's
host, 127.0.0.1,localhost, every proxy var pointed at a closed port) so a request
to any other origin fails instead of silently succeeding.
The blackhole half works as documented for leaks whose error propagates. Verified by
mutation at the #184 head:
cfg.url pointed at DEFAULT_URL with the blackhole armed: RED, failing in the
blackholed tunnel (tunnel error ... Connection refused), exactly as the test's
doc comment claims.
- A dual-contact leak injected into
obtain_proof (a stray call alongside the
sanctioned flow: client.get(DEFAULT_URL).send().context("leak")? before the
challenge request): RED on the injected call, aborting before the mock is
reached. The green direction is inferred, not re-run: without the blackhole the
request bypasses the proxy, and on a runner that can reach DEFAULT_URL it
would succeed (send() does not error on non-2xx statuses), leaving the test
green with the mock consumed. So the env setup is what makes this class fail.
Two gaps remain, both hardening rather than a current vulnerability:
-
A swallowed leak stays green. Change the injected leak to
let _ = client.get(DEFAULT_URL).send(); and the test passes: the mock is
consumed and the leak's transport error is discarded. The closed-port design can
only surface leaks whose failure propagates into the flow's result. Catching
fire-and-forget exfiltration needs an observer rather than a dead end, e.g. a
local listener standing in as the proxy that counts connection attempts, with the
test asserting zero. Scope caveat: an observer only witnesses paths the test
drives. The current test exercises a single passing round (200 on both endpoints,
no PoW, no continue/failed branch), so a leak living on an error or retry branch
makes no connection during the run and a zero-count assertion stays green. An
observer design should either drive those branches too or state that coverage is
limited to the exercised path, and its guarantee covers only proxy-honoring
transports. Candidate direction, not a vetted design.
-
The blackhole mechanism has no committed tripwire. obtain_proof builds a
stock reqwest::blocking::Client, which is why the proxy env binds its transport.
If the builder later gains .no_proxy() or a custom connector, the guard disarms
silently and every existing run stays green. A committed negative case should lock
the mechanism (a test asserting that a non-loopback cfg.url fails while the
blackhole is armed). Design note: the naive versions are wrong in one of two ways.
Pointing at an unresolvable host keeps the request failing even when the guard is
disarmed, so a tripwire asserting failure stays green vacuously (a DNS failure
masquerades as the blackhole working), and pointing at a real host makes a live
network call the moment the guard disarms, degrading into the same masquerade on
any runner that cannot reach that host (the connect error passes for the
blackhole working). A robust tripwire needs a destination that would succeed if
and only if the blackhole is not applied, e.g. a local listener on a loopback
alias that NO_PROXY does not cover; note this depends on reqwest's NO_PROXY
matching by exact IP (holds in the pinned reqwest/hyper-util, recheck on
upgrade) and on the OS routing 127/8 aliases (default on Linux, macOS needs an
explicit alias). Also a candidate direction, not a vetted design.
Neither gap blocks #184, which is why this is an issue and not a review comment.
crates/icaptcha-client/tests/icaptcha_mock_consumed.rs(#184) guards against a swallowed dual-contact leak with twohalves: mock-consumed asserts, plus a proxy blackhole (
NO_PROXYfor the mock'shost,
127.0.0.1,localhost, every proxy var pointed at a closed port) so a requestto any other origin fails instead of silently succeeding.
The blackhole half works as documented for leaks whose error propagates. Verified by
mutation at the #184 head:
cfg.urlpointed atDEFAULT_URLwith the blackhole armed: RED, failing in theblackholed tunnel (
tunnel error ... Connection refused), exactly as the test'sdoc comment claims.
obtain_proof(a stray call alongside thesanctioned flow:
client.get(DEFAULT_URL).send().context("leak")?before thechallenge request): RED on the injected call, aborting before the mock is
reached. The green direction is inferred, not re-run: without the blackhole the
request bypasses the proxy, and on a runner that can reach
DEFAULT_URLitwould succeed (
send()does not error on non-2xx statuses), leaving the testgreen with the mock consumed. So the env setup is what makes this class fail.
Two gaps remain, both hardening rather than a current vulnerability:
A swallowed leak stays green. Change the injected leak to
let _ = client.get(DEFAULT_URL).send();and the test passes: the mock isconsumed and the leak's transport error is discarded. The closed-port design can
only surface leaks whose failure propagates into the flow's result. Catching
fire-and-forget exfiltration needs an observer rather than a dead end, e.g. a
local listener standing in as the proxy that counts connection attempts, with the
test asserting zero. Scope caveat: an observer only witnesses paths the test
drives. The current test exercises a single passing round (200 on both endpoints,
no PoW, no continue/failed branch), so a leak living on an error or retry branch
makes no connection during the run and a zero-count assertion stays green. An
observer design should either drive those branches too or state that coverage is
limited to the exercised path, and its guarantee covers only proxy-honoring
transports. Candidate direction, not a vetted design.
The blackhole mechanism has no committed tripwire.
obtain_proofbuilds astock
reqwest::blocking::Client, which is why the proxy env binds its transport.If the builder later gains
.no_proxy()or a custom connector, the guard disarmssilently and every existing run stays green. A committed negative case should lock
the mechanism (a test asserting that a non-loopback
cfg.urlfails while theblackhole is armed). Design note: the naive versions are wrong in one of two ways.
Pointing at an unresolvable host keeps the request failing even when the guard is
disarmed, so a tripwire asserting failure stays green vacuously (a DNS failure
masquerades as the blackhole working), and pointing at a real host makes a live
network call the moment the guard disarms, degrading into the same masquerade on
any runner that cannot reach that host (the connect error passes for the
blackhole working). A robust tripwire needs a destination that would succeed if
and only if the blackhole is not applied, e.g. a local listener on a loopback
alias that
NO_PROXYdoes not cover; note this depends on reqwest'sNO_PROXYmatching by exact IP (holds in the pinned reqwest/hyper-util, recheck on
upgrade) and on the OS routing 127/8 aliases (default on Linux, macOS needs an
explicit alias). Also a candidate direction, not a vetted design.
Neither gap blocks #184, which is why this is an issue and not a review comment.