mirror of
https://github.com/profullstack/logicsrc.git
synced 2026-08-13 22:37:29 +00:00
fix(feed-discovery): block additional reserved SSRF IP ranges
Closes #88 Expand feed-discovery SSRF guard to reject additional non-public IPv4 and IPv6 address ranges: IPv4: - Documentation TEST-NET-1/2/3 (192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24) - IETF protocol assignments (192.0.0.0/24) - Benchmark (198.18.0.0/15) - Multicast (224.0.0.0/4) and reserved (240.0.0.0/4) IPv6: - Discard-only (100::/64) - Benchmark (2001:2::/48) - Documentation (2001:db8::/32) - Full link-local scope (fe80::/10, not just fe80::) - Multicast (ff00::/8) Adds regression tests for all newly blocked ranges.
This commit is contained in:
parent
edf32139ae
commit
a9521f8482
2 changed files with 53 additions and 2 deletions
|
|
@ -67,6 +67,37 @@ describe("site probing helpers", () => {
|
|||
await expect(assertSafeHttpUrl("file:///etc/passwd")).rejects.toThrow(/Unsupported URL protocol/);
|
||||
});
|
||||
|
||||
it("blocks reserved and documentation IPv4 SSRF targets", async () => {
|
||||
// TEST-NET-1 (192.0.2.0/24)
|
||||
await expect(assertSafeHttpUrl("http://192.0.2.1/feed")).rejects.toThrow(/Blocked internal/);
|
||||
// TEST-NET-2 (198.51.100.0/24)
|
||||
await expect(assertSafeHttpUrl("http://198.51.100.1/feed")).rejects.toThrow(/Blocked internal/);
|
||||
// TEST-NET-3 (203.0.113.0/24)
|
||||
await expect(assertSafeHttpUrl("http://203.0.113.1/feed")).rejects.toThrow(/Blocked internal/);
|
||||
// IETF protocol assignments (192.0.0.0/24)
|
||||
await expect(assertSafeHttpUrl("http://192.0.0.1/feed")).rejects.toThrow(/Blocked internal/);
|
||||
// Benchmark (198.18.0.0/15)
|
||||
await expect(assertSafeHttpUrl("http://198.18.0.1/feed")).rejects.toThrow(/Blocked internal/);
|
||||
await expect(assertSafeHttpUrl("http://198.19.255.255/feed")).rejects.toThrow(/Blocked internal/);
|
||||
// Multicast (224.0.0.0/4)
|
||||
await expect(assertSafeHttpUrl("http://224.0.0.1/feed")).rejects.toThrow(/Blocked internal/);
|
||||
// Reserved (240.0.0.0/4)
|
||||
await expect(assertSafeHttpUrl("http://240.0.0.1/feed")).rejects.toThrow(/Blocked internal/);
|
||||
});
|
||||
|
||||
it("blocks reserved and documentation IPv6 SSRF targets", async () => {
|
||||
// Discard-only (100::/64)
|
||||
await expect(assertSafeHttpUrl("http://[100::]/feed")).rejects.toThrow(/Blocked internal/);
|
||||
// Benchmark (2001:2::/48)
|
||||
await expect(assertSafeHttpUrl("http://[2001:2::1]/feed")).rejects.toThrow(/Blocked internal/);
|
||||
// Documentation (2001:db8::/32)
|
||||
await expect(assertSafeHttpUrl("http://[2001:db8::1]/feed")).rejects.toThrow(/Blocked internal/);
|
||||
// Multicast (ff00::/8)
|
||||
await expect(assertSafeHttpUrl("http://[ff02::1]/feed")).rejects.toThrow(/Blocked internal/);
|
||||
// Link-local (fe80::/10) — already blocked, verify extended range (feb0::)
|
||||
await expect(assertSafeHttpUrl("http://[feb0::1]/feed")).rejects.toThrow(/Blocked internal/);
|
||||
});
|
||||
|
||||
it("ignores malformed configured candidate URLs", async () => {
|
||||
const provider = new WebCandidateFeedProbeProvider({
|
||||
cacheTtlSeconds: 60,
|
||||
|
|
|
|||
|
|
@ -61,7 +61,17 @@ function isBlockedIp(value: string) {
|
|||
(a === 169 && b === 254) ||
|
||||
(a === 172 && b >= 16 && b <= 31) ||
|
||||
(a === 192 && b === 168) ||
|
||||
(a === 100 && b >= 64 && b <= 127)
|
||||
(a === 100 && b >= 64 && b <= 127) ||
|
||||
// Documentation (TEST-NET-1/2/3)
|
||||
(a === 192 && b === 0 && parts[2] === 2) ||
|
||||
(a === 198 && b === 51 && parts[2] === 100) ||
|
||||
(a === 203 && b === 0 && parts[2] === 113) ||
|
||||
// IETF protocol assignments (192.0.0.0/24)
|
||||
(a === 192 && b === 0 && parts[2] === 0) ||
|
||||
// Benchmark (198.18.0.0/15)
|
||||
(a === 198 && (b === 18 || b === 19)) ||
|
||||
// Multicast (224.0.0.0/4) and reserved (240.0.0.0/4)
|
||||
a >= 224
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -92,7 +102,17 @@ function isBlockedIp(value: string) {
|
|||
normalized === "::1" ||
|
||||
normalized.startsWith("fc") ||
|
||||
normalized.startsWith("fd") ||
|
||||
normalized.startsWith("fe80")
|
||||
normalized.startsWith("fe80") ||
|
||||
// Discard-only (100::/64)
|
||||
normalized.startsWith("100::") ||
|
||||
// Benchmark (2001:2::/48)
|
||||
normalized.startsWith("2001:2:") ||
|
||||
// Documentation (2001:db8::/32)
|
||||
normalized.startsWith("2001:db8:") ||
|
||||
// Link-local scoped (fe80::/10 — covers fe80 through febf)
|
||||
/^fe[89ab]/.test(normalized) ||
|
||||
// Multicast (ff00::/8)
|
||||
normalized.startsWith("ff")
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue