Conversation
queryDnsbl issued its Spamhaus DQS lookup as DoH GET, putting the query name (which embeds the secret key) in the request URL. Workers Traces records outbound fetch spans' url.full/url.query with no scrubbing hook, so enabling the DNSBL analyzer alongside traces would leak the key into every captured span. Switch to an RFC 8484 DoH POST with an RFC 1035 wire-format body: the request URL becomes the bare endpoint with no query string, and the name travels in the body instead, which traces do not record. queryDoh is left untouched — its names are user-supplied domains, not secrets. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0165np2hMSKNJBp3a2coEmm1
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
dmarcheck | 5765aeb | Sep 12 2026, 05:15 PM |
|
Auto-merge routine: escalating to human review — not merging. Two independent reasons, either alone sufficient to block auto-merge:
Note for a human reviewer: this touches Separately: the missing shipofclaudius plugin skills mean this auto-merge routine currently cannot evaluate any PR's review/scan step, not just this one — worth checking why they aren't installed for this account. Generated by Claude Code |
|
Held pending #736, by owner decision on 2026-09-12. The transport change here is accepted — moving the DQS key out of the URL into an RFC 8484 POST body is the right fix for #728. What is not accepted is the hand-rolled RFC 1035 codec ( I checked viability rather than assuming it. Both candidates encode and decode this exact query shape inside workerd (the To be fair to this PR: no defect was found in the hand-rolled code. The call was made on the guardrail, and it trades ~60 scoped lines for a runtime dependency. #736 carries the full swap as an implementable prompt, including the constraint that this PR's Not merging this, and not closing it. |
PR #732's queryDnsbl fix (moving the Spamhaus DQS key out of the DoH URL) added a hand-rolled RFC 1035 encoder/decoder. Per the owner's review and the standing guardrail against hand-rolled binary parsing, replace encodeDnsQueryA/decodeDnsResponseA/skipDnsName with the vetted dns-packet library (already verified to work in workerd). queryDnsbl's external behavior, its request URL/method/body shape, and all of test/dns-client.test.ts's assertions are unchanged. Adds a workerd integration test proving the encode/decode round-trip under the real Workers runtime, not just Node. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0165np2hMSKNJBp3a2coEmm1
|
Pushed the swap requested in review (and detailed in #736):
This PR now touches Generated by Claude Code Generated by Claude Code |
|
#736 is implemented in #737, which merges this PR's commit ( I didn't push the replacement directly onto this branch as suggested in the comment above: this session's designated working branch is fixed to Recommend closing this PR once #737 merges. Leaving that to you rather than doing it myself, since you'd previously asked for it to stay open pending #736. Generated by Claude Code |
Summary
queryDnsbl(src/dns/client.ts) issued its Spamhaus DQS lookup as a DoH GET, putting the query name — which embeds the secret DQS key — in the request URL's query string. Workers Traces records outbound fetch spans'url.full/url.querywith no scrubbing hook, so enabling the DNSBL analyzer and Workers Traces together would leak the key into every captured span.https://cloudflare-dns.com/dns-queryendpoint with no query string at all, and the query name travels in the POST body instead, which traces do not record.dns-packetlibrary (runtime dependency) — no hand-rolled binary DNS parsing remains.queryDnsbl's external behavior, request URL/method/body shape, and every existing test assertion are unchanged.queryDohis untouched either way, per the issue's scope: its names are user-supplied domains, not secrets.Owner / zone steps
None — this is a code-only transport change.
DNSBL_DQS_KEYremains unset in production per the issue's "Current state (not exploitable)" note.Security notes
<reversed-ip>.<key>.<zone>, and thrown errors stay generic (defense-in-depth even though the URL itself is now static and carries nothing sensitive).src/shared/scoring.tsis untouched — andMAX_DNSBL_HOSTNAMES/MAX_DNSBL_IPScaps plusScanBudgetthreading insrc/analyzers/dnsbl.tsare untouched.dns-packet.decode()throws on a malformed/truncated buffer; that's caught and surfaces as the same genericDnsLookupErroras any other failure — never an unhandled throw and never a false "not listed".dns-packet's types don't distinguish A records from other "string data" record types, soqueryDnsbl'sisARecordAnswerguard re-validatesdatais actually a string at runtime.CLAUDE.mdandpackage.json/package-lock.json, all CODEOWNERS-gated paths — not auto-merging, opening for human review per repo policy.Testing
npm test— full suite: 1553 passing, 1 failing (pre-existing, unrelated —test/integration/mta-sts-runtime.test.tsis a live-network integration test that fails identically on unmodifiedmainin this environment). Coverage-gated node re-run: 1518 passing, 0 failing, per-file floors onsrc/analyzers/**andsrc/shared/scoring.tsunaffected (neither file was touched).npm run typecheck— clean, no output.npm run lint—Checked 191 files in 373ms. No fixes applied.test/dns-client.test.ts'squeryDnsbltests (added in the first commit) pass unmodified against thedns-packet-based implementation — including the assertion that the request URL contains neither the key nor the reversed IP.test/integration/dnsbl-wire-codec.test.tsruns in theworkersvitest project (real workerd, not Node) and proves thedns-packetencode/decode round-trip for the exact query shapequeryDnsbluses, plus that a malformed buffer throws rather than silently returning garbage.Choices made
wrangler.toml/client.ts/CLAUDE.md, so there is nothing to remove. Updated theCLAUDE.mdDNSBL bullet to describe the new transport instead.Deferred
Refs
Closes #728
Closes #736
🤖 Generated with Claude Code
https://claude.ai/code/session_0165np2hMSKNJBp3a2coEmm1