Verified protected-develop defect
Fresh protected develop@1fadec04195805722829b386475a09a15f8cd926 accepts an owner/admin supplied outbound webhook URL with only /^https?:\/\//, persists it, and later passes that URL directly to server-side fetch() in sendWebhook(). A tenant administrator can therefore direct ScopeWeave at loopback, RFC 1918, IPv4 link-local / metadata, IPv6 loopback/link-local/ULA, IPv4-mapped IPv6, or a hostname that DNS-rebinds to those targets. The current fetch client also owns redirect behavior rather than a fail-closed webhook transport boundary.
Protected evidence:
server/app.mjs webhook creation accepts any http(s) string;
sendWebhook() issues the POST directly to the persisted URL;
- no destination DNS/IP authorization or pinned lookup is performed before network I/O.
This is a buyer-visible tenant-isolation / cloud-control-plane SSRF defect, not merely hardening.
Required repair contract
Implement this as one bounded outbound-webhook transport slice. Preserve current webhook signing, event filtering, retry accounting, secret non-disclosure, and ordinary public HTTPS delivery.
- RED first: add realistic regressions proving protected behavior is unsafe before production changes. Cover direct IPv4/IPv6 loopback, RFC 1918, IPv4 link-local (including metadata-style addresses), IPv6 link-local/ULA, IPv4-mapped IPv6, localhost-like names, DNS answers that are private, mixed public+private answer sets, and DNS rebinding between attempts. Include a real public-IP-shaped deterministic success seam without external network dependence.
- Parse with
URL; production webhook destinations are HTTPS. Reject credentials and fragments. Keep normal path/query support. If explicit development HTTP compatibility is genuinely required, limit it to SCOPEWEAVE_DEV=1 plus loopback only and test that production never inherits it.
- Resolve A/AAAA before connection and fail closed unless every candidate address is permitted public-unicast destination authority. Treat special-use/non-routable/private/link-local/loopback/unspecified/multicast/broadcast/documentation/benchmark/reserved ranges and IPv4-mapped forms conservatively.
- Prevent DNS pinning/rebinding TOCTOU: the network connection must use a lookup/address that is cryptographically/TLS-hostname compatible with the original hostname but pinned to a destination that was just validated. Validation followed by an unrelated second resolver decision is not sufficient. Node's HTTPS request
lookup seam can be used to bind the validated address while preserving the original hostname/SNI.
- Do not follow redirects. A redirect response is a delivery failure and must never transplant the webhook secret/signature/body to another authority.
- Keep the existing 3-second request budget and bounded retry semantics unless tests prove a smaller root-cause change is necessary. Each retry must freshly resolve, validate, and pin its destination; never reuse a later unvalidated DNS result.
- Keep delivery recording stable and non-secret. Resolver/TLS/transport failure must not surface internal addresses or credentials to customer-visible error bodies/logs.
- Put destination-policy and transport logic in beginner-readable testable production modules rather than expanding the monolithic route with untestable inline branches. Register every owned module/test in canonical unit and c8 coverage and maintain exact 100% owned statement/branch/function/line coverage where tooling exposes it.
- Add doctoring/traceability with APA 7 references and explicit active-PR vs protected-shipped truth. Primary implementation basis: OWASP SSRF Prevention Cheat Sheet (custom webhooks; redirect disabling; A+AAAA validation / DNS pinning), Node.js 22 HTTPS/HTTP custom
lookup support, RFC 1918 private IPv4, RFC 3927 IPv4 link-local, RFC 4193 IPv6 ULA, and current IPv6 special-use guidance.
- Do not touch central
.github, auth/session semantics, Clearfolio, Stripe inbound-webhook code, dependency lockfiles, branch protection, or unrelated buyer slices.
Acceptance
- Existing broad webhook API behavior remains functional for valid public HTTPS endpoints.
- No server-side connection is attempted to a denied destination, including after DNS changes or redirect responses.
- Exact deterministic RED→GREEN evidence is preserved in PR history/description.
- Current protected base is refetched before branch creation/push; no force-push/destructive rebase.
- Fresh exact-head repo CI/browser/coverage/SAST/security/dependency/supply-chain and live organization gates after every head move.
- No model/status-only evidence is treated as independent approval; do not merge until live governance is genuinely satisfied.
Verified protected-develop defect
Fresh protected
develop@1fadec04195805722829b386475a09a15f8cd926accepts an owner/admin supplied outbound webhook URL with only/^https?:\/\//, persists it, and later passes that URL directly to server-sidefetch()insendWebhook(). A tenant administrator can therefore direct ScopeWeave at loopback, RFC 1918, IPv4 link-local / metadata, IPv6 loopback/link-local/ULA, IPv4-mapped IPv6, or a hostname that DNS-rebinds to those targets. The current fetch client also owns redirect behavior rather than a fail-closed webhook transport boundary.Protected evidence:
server/app.mjswebhook creation accepts anyhttp(s)string;sendWebhook()issues the POST directly to the persisted URL;This is a buyer-visible tenant-isolation / cloud-control-plane SSRF defect, not merely hardening.
Required repair contract
Implement this as one bounded outbound-webhook transport slice. Preserve current webhook signing, event filtering, retry accounting, secret non-disclosure, and ordinary public HTTPS delivery.
URL; production webhook destinations are HTTPS. Reject credentials and fragments. Keep normal path/query support. If explicit development HTTP compatibility is genuinely required, limit it toSCOPEWEAVE_DEV=1plus loopback only and test that production never inherits it.lookupseam can be used to bind the validated address while preserving the original hostname/SNI.lookupsupport, RFC 1918 private IPv4, RFC 3927 IPv4 link-local, RFC 4193 IPv6 ULA, and current IPv6 special-use guidance..github, auth/session semantics, Clearfolio, Stripe inbound-webhook code, dependency lockfiles, branch protection, or unrelated buyer slices.Acceptance