koala73/worldmonitor · error · Error
Webhook URL must not point to a metadata endpoint
Error message
Webhook URL must not point to a metadata endpoint
What it means
Returned when the webhook URL's hostname matches BLOCKED_METADATA_HOSTNAMES (localhost, 169.254.169.254, metadata.google.internal, metadata.internal, instance-data, metadata, computemetadata, link-local.s3.amazonaws.com). This is the cloud-metadata SSRF guard: it stops a webhook from exfiltrating instance credentials from AWS/GCP/Azure IMDS or similar metadata services.
Source
Thrown at api/_notification-webhook-ssrf.ts:223
async function defaultResolveHostname(hostname: string): Promise<string[]> {
const records = await Promise.all([
resolveDnsJson(hostname, 'A'),
resolveDnsJson(hostname, 'AAAA'),
]);
return records.flat();
}
/**
* Fail fast at registration when the webhook hostname currently resolves to a
* private or reserved address. Delivery repeats this check (and pins its
* connection) because DNS can change after registration.
*/
export async function assertNotificationWebhookRegistrationUrlSafe(
rawUrl: string,
resolveHostname: ResolveHostname = defaultResolveHostname,
): Promise<void> {
const staticError = blockedNotificationWebhookUrlReason(rawUrl);
if (staticError) throw new Error(staticError);
const hostname = new URL(rawUrl).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(`Webhook URL DNS resolution failed: ${message}`);
}
if (!resolvedAddresses.length) throw new Error('Webhook URL DNS resolution returned no addresses');
if (resolvedAddresses.some(isBlockedNotificationResolvedAddress)) {
throw new Error('Webhook URL must not point to a private/local address');
}
}
View on GitHub (pinned to ffec79ac33)
Solutions
- Point the webhook at a real, public, https endpoint that you control.
- Remove any localhost/metadata hostnames from your test fixtures and configuration.
- Treat the rejection as expected behavior — do not attempt to bypass it; the block is intentional defense-in-depth.
Example fix
// before
registerWebhook('https://169.254.169.254/latest/meta-data/')
// after
registerWebhook('https://your-actual-service.example.com/webhook') Defensive patterns
Strategy: validation
Validate before calling
const BLOCKED_METADATA_HOSTNAMES = new Set([
'localhost','169.254.169.254','metadata.google.internal','metadata.internal',
'instance-data','metadata','computemetadata','link-local.s3.amazonaws.com',
]);
function isMetadataHost(rawUrl: string): boolean {
try { return BLOCKED_METADATA_HOSTNAMES.has(new URL(rawUrl).hostname.toLowerCase()); }
catch { return false; }
}
if (isMetadataHost(input)) return res.status(400).json({ error: 'Metadata/localhost hosts are not permitted.' }); Type guard
function isBlockedMetadataHostname(value: unknown): boolean {
if (typeof value !== 'string') return false;
try { return BLOCKED_METADATA_HOSTNAMES.has(new URL(value).hostname.toLowerCase()); } catch { return false; }
} Try / catch
try {
await assertNotificationWebhookRegistrationUrlSafe(rawUrl);
} catch (err) {
if (err.message === 'Webhook URL must not point to a metadata endpoint') {
return res.status(400).json({ error: 'Cloud-metadata and localhost hosts are blocked.' });
}
throw err;
} Prevention
- Maintain the BLOCKED_METADATA_HOSTNAMES list alongside any new cloud-provider metadata endpoint your infra uses.
- Treat rejections of metadata hosts as expected behavior in security tests; do not file bug reports for them.
- Keep webhook test fixtures pointed at public test sinks (webhook.site, requestbin) over https.
When it happens
Trigger: Webhook registration with a URL whose hostname is literally one of the blocked metadata endpoints, e.g. `https://169.254.169.254/latest/meta-data/` or `https://metadata.google.internal/computeMetadata/`.
Common situations: A security researcher probing the webhook feature for SSRF; an accidental localhost URL left in a test fixture; a misconfigured integration that was pointed at a metadata-like hostname.
Related errors
- Webhook URL must not point to a private/local address
- Webhook URL is not a valid URL
- Webhook URL must use HTTPS
- Webhook URL DNS resolution returned no addresses
- serverUrl hostname is blocked: ${hostname}
AI-assisted analysis of koala73/worldmonitor@ffec79ac33 (2026-08-12).
Data as JSON: /api/errors/ae3665b5f36f3f19.
Report an issue: GitHub.