koala73/worldmonitor · warning · Error
callbackUrl hostname is a blocked metadata endpoint
Error message
callbackUrl hostname is a blocked metadata endpoint
What it means
Static SSRF-policy check in isBlockedCallbackUrl: if the URL's lowercase hostname is in BLOCKED_METADATA_HOSTNAMES (169.254.169.254, metadata.google.internal, metadata.internal, instance-data, metadata, computemetadata, link-local.s3.amazonaws.com), registration rejects with this message (thrown at webhook-shared.ts:121, surfaced as 400 by registerWebhook). These hostnames expose cloud instance metadata (credentials, IAM tokens) and are the primary SSRF jackpot.
Source
Thrown at server/worldmonitor/shipping/v2/webhook-shared.ts:121
return (data.Answer ?? [])
.filter(answer => answer.type === expectedType && typeof answer.data === 'string')
.map(answer => answer.data!);
};
const records = await Promise.all([resolveRecordType('A'), resolveRecordType('AAAA')]);
return records.flat();
}
/**
* Validate the current DNS answer before storing a webhook. Delivery makes the
* same check immediately before send and pins the resulting socket, which
* keeps this fail-fast check from becoming the only SSRF control.
*/
export async function assertCallbackUrlRegistrationSafe(
callbackUrl: string,
resolveHostname: ResolveHostname = defaultResolveHostname,
): Promise<void> {
const staticError = isBlockedCallbackUrl(callbackUrl);
if (staticError) throw new Error(staticError);
const hostname = new URL(callbackUrl).hostname.toLowerCase();
if (isIpLiteral(hostname)) return;
let resolvedAddresses: string[];
try {
resolvedAddresses = await resolveHostname(hostname);
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
throw new Error(`callbackUrl DNS resolution failed: ${message}`);
}
if (!resolvedAddresses.length) throw new Error('callbackUrl DNS resolution returned no addresses');
const blocked = resolvedAddresses.find(isBlockedResolvedAddress);
if (blocked) throw new Error('callbackUrl resolves to a private/reserved address');
}
export async function generateSecret(): Promise<string> {
const bytes = new Uint8Array(32);
crypto.getRandomValues(bytes);View on GitHub (pinned to eeab0a219f)
Solutions
- Point the callback at your own public https endpoint — metadata hostnames can never be webhook targets
- If this appeared in logs unexpectedly, treat it as a potential SSRF attempt and review the caller/credentials that submitted it
- Audit any integration templates or scripts that auto-generate callbackUrl from environment discovery
Example fix
// before
registerWebhook(ctx, { callbackUrl: 'http://metadata.google.internal/computeMetadata/v1', chokepointIds });
// after
registerWebhook(ctx, { callbackUrl: 'https://status.example.com/webhooks/worldmonitor', chokepointIds }); Defensive patterns
Strategy: validation
Validate before calling
const BLOCKED = new Set(['169.254.169.254','metadata.google.internal','metadata.internal','instance-data','metadata','computemetadata','link-local.s3.amazonaws.com']);
if (BLOCKED.has(new URL(callbackUrl).hostname.toLowerCase())) throw new RangeError('metadata endpoints cannot be webhook targets'); Try / catch
catch (e) { if (e?.details?.[0]?.description?.includes('blocked metadata endpoint')) { replace the callback with your own public https endpoint } else throw e; } Prevention
- Never register cloud metadata hostnames as callbacks
- Treat unexpected occurrences in logs as SSRF attempts and review the submitting credentials
- Audit scripts that auto-discover and register internal endpoints
When it happens
Trigger: POST RegisterWebhook with a callbackUrl pointing at a cloud metadata service, e.g. https://169.254.169.254/latest/meta-data or http://metadata.google.internal/computeMetadata/v1 — either as an attack or as a misconfigured internal integration.
Common situations: Security testing/probing of the webhook feature; a partner accidentally pasting an internal cloud URL; automated tooling that discovers and registers internal endpoints; attempts to make WorldMonitor delivery fetch instance credentials.
Related errors
- callbackUrl resolves to a private/reserved address: ${hostna
- callbackUrl is not allowed
- callbackUrl is not a valid URL
- callbackUrl resolves to a private/reserved address
- callbackUrl is required
AI-assisted analysis of koala73/worldmonitor@eeab0a219f (2026-08-21).
Data as JSON: /api/errors/25964ff07051f0e4.
Report an issue: GitHub.