mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-14 22:50:51 +00:00
feat(security): pin resolved IP on outbound fetches to defeat DNS rebinding
Closes issue 217. The isPublicUrl input-time guard catches every RFC1918 / CGNAT / loopback / link-local alt-notation, but a low-TTL DNS record can flip between accept and dispatch. The new pinnedPublicDispatcher resolves the hostname literally inside undici.Agent's connect hook, refuses any address isPublicIp would reject, and pins the connection to the first valid public address. The five outbound paths that accept a user-supplied host route through the dispatcher via safeFetch's requirePublicHost flag — moodlog sync, moodlog reverse-push, the moodlog credentials probe, the local AI client (already gated in commit 1), and the ntfy sender. The flag now buys both the input-time check and the connect-time pin in one call. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
425503e095
commit
b43b57f657
8 files changed
+381
-7
No files matched your search
@@ -116,7 +116,9 @@ export const POST = apiHandler(async (request: NextRequest) => {
|
||||
try {
|
||||
// safeFetch keeps the manual-redirect guard (the apiKey would
|
||||
// otherwise leak on a 302 to 169.254.169.254 / RFC1918) and adds
|
||||
// the AbortSignal.timeout convention.
|
||||
// the AbortSignal.timeout convention. requirePublicHost layers in
|
||||
// the connect-time IP pin so a DNS rebinding cannot flip the host
|
||||
// between accept and dispatch (issue #217).
|
||||
const res = await safeFetch(
|
||||
probeUrl.toString(),
|
||||
{
|
||||
@@ -124,7 +126,7 @@ export const POST = apiHandler(async (request: NextRequest) => {
|
||||
headers: { Authorization: `Bearer ${apiKey}` },
|
||||
cache: "no-store",
|
||||
},
|
||||
{ timeoutMs: TIMEOUT_MS },
|
||||
{ timeoutMs: TIMEOUT_MS, requirePublicHost: true },
|
||||
);
|
||||
const latencyMs = Math.round(performance.now() - start);
|
||||
|
||||
|
||||
Reference in new issue
Block a user