Skip to content

Security hardening: docs CSP, X-Request-ID validation, log redaction, webhook SSRF guard - #179

Open
Junman140 wants to merge 1 commit into
SmartDropLabs:mainfrom
Junman140:fix/security-hardening-129-133-94-96
Open

Security hardening: docs CSP, X-Request-ID validation, log redaction, webhook SSRF guard#179
Junman140 wants to merge 1 commit into
SmartDropLabs:mainfrom
Junman140:fix/security-hardening-129-133-94-96

Conversation

@Junman140

Copy link
Copy Markdown

Summary

Security hardening addressing issues #129, #133, #94, and #96.

#129 — helmet() CSP breaks Swagger UI at /api-docs

  • Added src/middleware/csp.js exporting docsCspMiddleware, which relaxes the Content-Security-Policy only for the /api-docs route (allows inline scripts/styles that swagger-ui-express injects). The strict default CSP still protects the real API surface.

#133 — X-Request-ID trusted from client with no validation

  • src/middleware/requestId.js now validates any client-supplied X-Request-ID (max 128 chars, charset [A-Za-z0-9_-]). Invalid/oversized values are discarded and a fresh server-generated ID is used instead. Documented in code comments and the README as an optional, non-authoritative correlation hint.

#94 — Log redaction only matched top-level-ish substring keys

  • New src/services/logRedaction.js: redacts arrays as first-class nodes (plain arrays of secret-shaped strings), scans every string value for whsec_ secrets and token=/secret=/key= query params embedded in URLs, and treats api_key/ApiKey etc. as sensitive keys. Preserves the whsec_**** partial reveal and keeps non-sensitive fields (asset_code, price_usd, delivery_id) intact. New test/logger.test.js.

#96 — Webhook test endpoint as SSRF oracle

  • New src/services/ssrfGuard.js: validates targets against private/internal ranges (RFC-1918, loopback, link-local, CGNAT, IPv6 ULA/link-local, IPv4-mapped) at delivery time, re-resolves DNS per request, and pins the connection to the validated public IP (Host header preserved, redirects disabled) to close DNS-rebinding. The /webhooks/:id/test endpoint refuses blocked targets with 422 WEBHOOK_TARGET_BLOCKED (never attempted), and its last_error is now a generic category (unreachable/error_response/delivery_failed) with raw detail kept only in server logs. Per-IP rate-limit aggregation verified by a new test.

Test plan

  • All 441 existing + new tests pass (npm test). New coverage: test/logger.test.js, test/webhooks.ssrf.test.js, extended test/requestId.test.js and test/api-docs.test.js.
  • Updated test/webhooks.routes.test.js and test/webhookDispatcher.test.js to mock the SSRF guard so unit tests stay offline.

Closes #129
Closes #133
Closes #94
Closes #96

…ion, webhook SSRF guard

- SmartDropLabs#129: relax helmet's default CSP for the /api-docs Swagger UI route only
- SmartDropLabs#133: validate client X-Request-ID (max 128 chars, [A-Za-z0-9_-]); fall back to a server-generated ID otherwise; document trust boundary
- SmartDropLabs#94: extend log redaction to arrays, string-embedded secret query params, and Authorization keys; preserve whsec_**** partial reveal; cover non-sensitive fields
- SmartDropLabs#96: add delivery-time SSRF guard (DNS resolve + IP pinning, no redirects) blocking private/internal targets; reduce test-endpoint error detail to a generic category with raw detail kept server-side; add per-IP rate-limit aggregation test
@drips-wave

drips-wave Bot commented Aug 26, 2026

Copy link
Copy Markdown

@Junman140 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment