Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions packages/provider-utils/src/validate-download-url.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,18 @@ describe('validateDownloadUrl', () => {
DownloadError,
);
});

it('should block .internal domains (cloud metadata)', () => {
expect(() =>
validateDownloadUrl('http://metadata.google.internal/computeMetadata/v1'),
).toThrow(DownloadError);
});

it('should block nested .internal domains', () => {
expect(() =>
validateDownloadUrl('http://service.namespace.svc.cluster.internal/api'),
).toThrow(DownloadError);
});
});

describe('blocked IPv4 addresses', () => {
Expand Down Expand Up @@ -137,6 +149,60 @@ describe('validateDownloadUrl', () => {
DownloadError,
);
});

it('should block 100.64.0.0/10 (Shared/CGNAT - RFC 6598)', () => {
expect(() => validateDownloadUrl('http://100.64.0.1/file')).toThrow(
DownloadError,
);
expect(() => validateDownloadUrl('http://100.100.100.1/file')).toThrow(
DownloadError,
);
expect(() => validateDownloadUrl('http://100.127.255.255/file')).toThrow(
DownloadError,
);
});

it('should allow 100.63.x.x and 100.128.x.x (outside CGNAT)', () => {
expect(() =>
validateDownloadUrl('http://100.63.255.255/file'),
).not.toThrow();
expect(() =>
validateDownloadUrl('http://100.128.0.1/file'),
).not.toThrow();
});

it('should block 198.18.0.0/15 (Benchmarking - RFC 2544)', () => {
expect(() => validateDownloadUrl('http://198.18.0.1/file')).toThrow(
DownloadError,
);
expect(() => validateDownloadUrl('http://198.19.255.255/file')).toThrow(
DownloadError,
);
});

it('should allow 198.17.x.x and 198.20.x.x (outside benchmarking)', () => {
expect(() =>
validateDownloadUrl('http://198.17.0.1/file'),
).not.toThrow();
expect(() =>
validateDownloadUrl('http://198.20.0.1/file'),
).not.toThrow();
});

it('should block 192.0.0.0/24 (IETF Protocol Assignments - RFC 6890)', () => {
expect(() => validateDownloadUrl('http://192.0.0.1/file')).toThrow(
DownloadError,
);
});

it('should block 240.0.0.0/4 (Reserved - RFC 1112)', () => {
expect(() => validateDownloadUrl('http://240.0.0.1/file')).toThrow(
DownloadError,
);
expect(() => validateDownloadUrl('http://255.255.255.255/file')).toThrow(
DownloadError,
);
});
});

describe('blocked IPv6 addresses', () => {
Expand Down Expand Up @@ -192,5 +258,11 @@ describe('validateDownloadUrl', () => {
validateDownloadUrl('http://[::ffff:203.0.113.1]/file'),
).not.toThrow();
});

it('should block ::ffff: mapped 100.64.x.x (CGNAT via IPv6)', () => {
expect(() =>
validateDownloadUrl('http://[::ffff:100.64.0.1]/file'),
).toThrow(DownloadError);
});
});
});
19 changes: 17 additions & 2 deletions packages/provider-utils/src/validate-download-url.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,12 @@ export function validateDownloadUrl(url: string): void {
});
}

// Block localhost and .local domains
// Block localhost, .local, .localhost, and .internal domains
if (
hostname === 'localhost' ||
hostname.endsWith('.local') ||
hostname.endsWith('.localhost')
hostname.endsWith('.localhost') ||
hostname.endsWith('.internal')
) {
throw new DownloadError({
url,
Expand Down Expand Up @@ -96,14 +97,28 @@ function isPrivateIPv4(ip: string): boolean {
if (a === 0) return true;
// 10.0.0.0/8
if (a === 10) return true;
// 100.64.0.0/10 - Shared Address Space (RFC 6598, used in cloud CGNAT/VPCs)
if (a === 100 && b >= 64 && b <= 127) return true;
// 127.0.0.0/8
if (a === 127) return true;
// 169.254.0.0/16
if (a === 169 && b === 254) return true;
// 172.16.0.0/12
if (a === 172 && b >= 16 && b <= 31) return true;
// 192.0.0.0/24 - IETF Protocol Assignments (RFC 6890)
if (a === 192 && b === 0 && parts[2] === 0) return true;
// 192.0.2.0/24 - Documentation (TEST-NET-1, RFC 5737)
if (a === 192 && b === 0 && parts[2] === 2) return true;
// 192.168.0.0/16
if (a === 192 && b === 168) return true;
// 198.18.0.0/15 - Benchmarking (RFC 2544)
if (a === 198 && (b === 18 || b === 19)) return true;
// 198.51.100.0/24 - Documentation (TEST-NET-2, RFC 5737)
if (a === 198 && b === 51 && parts[2] === 100) return true;
// 203.0.113.0/24 - Documentation (TEST-NET-3, RFC 5737)
if (a === 203 && b === 0 && parts[2] === 113) return true;
// 240.0.0.0/4 - Reserved for future use (RFC 1112)
if (a >= 240) return true;

return false;
}
Expand Down