mirror of
https://github.com/github/codeql.git
synced 2026-08-03 17:03:02 +02:00
Add javascript/ssrf-ipv6-transition-incomplete-guard, an experimental @kind problem query that flags hand-rolled SSRF host guards which reject private/loopback IPv4 ranges but never unwrap IPv6-transition forms (IPv4-mapped ::ffff:, NAT64 64:ff9b::, 6to4 2002::). Such guards can be bypassed by wrapping an internal IPv4 address in a transition literal. Includes a .qhelp with good/bad examples, a change note, and a test pack with two true-positive fixtures (private-ip package guard and a hand-written RFC 1918 denylist) and two negative-control fixtures (ipaddr.js range classifier and an explicit ::ffff: unwrap). Signed-off-by: tonghuaroot <23011166+tonghuaroot@users.noreply.github.com>
33 lines
858 B
JavaScript
33 lines
858 B
JavaScript
const http = require('http');
|
|
|
|
const IPV4_MAPPED_PREFIX = '::ffff:';
|
|
|
|
// OK: this guard uses a hand-rolled denylist, but it first unwraps the
|
|
// IPv6-transition form, so the embedded IPv4 is normalized before the check.
|
|
function unwrapMapped(host) {
|
|
// strip an IPv4-mapped `::ffff:` prefix down to the embedded dotted quad
|
|
if (host.toLowerCase().startsWith(IPV4_MAPPED_PREFIX)) {
|
|
return host.slice(IPV4_MAPPED_PREFIX.length);
|
|
}
|
|
return host;
|
|
}
|
|
|
|
function isPrivateAddress(host) { // OK
|
|
const h = unwrapMapped(host);
|
|
return (
|
|
h === '127.0.0.1' ||
|
|
h === '169.254.169.254' ||
|
|
h.startsWith('10.') ||
|
|
h.startsWith('192.168')
|
|
);
|
|
}
|
|
|
|
function validateHost(host) { // OK
|
|
if (isPrivateAddress(host)) {
|
|
throw new Error('blocked internal host');
|
|
}
|
|
return http.get('http://' + host + '/');
|
|
}
|
|
|
|
module.exports = { validateHost };
|