fix: harden OpenResponses URL input fetching

This commit is contained in:
Peter Steinberger
2026-02-13 01:38:15 +01:00
parent 4199f9889f
commit 99f28031e5
13 changed files with 431 additions and 11 deletions

View File

@@ -1,10 +1,11 @@
import type { Dispatcher } from "undici";
import { logWarn } from "../../logger.js";
import {
closeDispatcher,
createPinnedDispatcher,
resolvePinnedHostname,
resolvePinnedHostnameWithPolicy,
type LookupFn,
SsrFBlockedError,
type SsrFPolicy,
} from "./ssrf.js";
@@ -20,6 +21,7 @@ export type GuardedFetchOptions = {
policy?: SsrFPolicy;
lookupFn?: LookupFn;
pinDns?: boolean;
auditContext?: string;
};
export type GuardedFetchResult = {
@@ -113,15 +115,10 @@ export async function fetchWithSsrFGuard(params: GuardedFetchOptions): Promise<G
let dispatcher: Dispatcher | null = null;
try {
const usePolicy = Boolean(
params.policy?.allowPrivateNetwork || params.policy?.allowedHostnames?.length,
);
const pinned = usePolicy
? await resolvePinnedHostnameWithPolicy(parsedUrl.hostname, {
lookupFn: params.lookupFn,
policy: params.policy,
})
: await resolvePinnedHostname(parsedUrl.hostname, params.lookupFn);
const pinned = await resolvePinnedHostnameWithPolicy(parsedUrl.hostname, {
lookupFn: params.lookupFn,
policy: params.policy,
});
if (params.pinDns !== false) {
dispatcher = createPinnedDispatcher(pinned);
}
@@ -164,6 +161,12 @@ export async function fetchWithSsrFGuard(params: GuardedFetchOptions): Promise<G
release: async () => release(dispatcher),
};
} catch (err) {
if (err instanceof SsrFBlockedError) {
const context = params.auditContext ?? "url-fetch";
logWarn(
`security: blocked URL fetch (${context}) target=${parsedUrl.origin}${parsedUrl.pathname} reason=${err.message}`,
);
}
await release(dispatcher);
throw err;
}