fix(feed-discovery): block reserved feed IP targets (#88)

Co-authored-by: jsdavid278-cyber <jsdavid278-cyber@users.noreply.github.com>
This commit is contained in:
RissRIce 2026-07-01 02:12:10 -06:00 committed by GitHub
parent edf32139ae
commit c495041adb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 21 additions and 2 deletions

View file

@ -60,8 +60,17 @@ describe("site probing helpers", () => {
});
it("blocks private SSRF targets", async () => {
await expect(assertSafeHttpUrl("http://0.0.0.0/feed")).rejects.toThrow(/Blocked internal/);
await expect(assertSafeHttpUrl("http://127.0.0.1/feed")).rejects.toThrow(/Blocked internal/);
await expect(assertSafeHttpUrl("http://192.0.2.1/feed")).rejects.toThrow(/Blocked internal/);
await expect(assertSafeHttpUrl("http://198.18.0.1/feed")).rejects.toThrow(/Blocked internal/);
await expect(assertSafeHttpUrl("http://198.51.100.1/feed")).rejects.toThrow(/Blocked internal/);
await expect(assertSafeHttpUrl("http://203.0.113.1/feed")).rejects.toThrow(/Blocked internal/);
await expect(assertSafeHttpUrl("http://224.0.0.1/feed")).rejects.toThrow(/Blocked internal/);
await expect(assertSafeHttpUrl("http://240.0.0.1/feed")).rejects.toThrow(/Blocked internal/);
await expect(assertSafeHttpUrl("http://[::]/feed")).rejects.toThrow(/Blocked internal/);
await expect(assertSafeHttpUrl("http://[2001:db8::1]/feed")).rejects.toThrow(/Blocked internal/);
await expect(assertSafeHttpUrl("http://[ff02::1]/feed")).rejects.toThrow(/Blocked internal/);
await expect(assertSafeHttpUrl("http://[::ffff:192.168.1.10]/feed")).rejects.toThrow(/Blocked internal/);
await expect(assertSafeHttpUrl("http://[::192.168.1.10]/feed")).rejects.toThrow(/Blocked internal/);
await expect(assertSafeHttpUrl("file:///etc/passwd")).rejects.toThrow(/Unsupported URL protocol/);

View file

@ -53,14 +53,20 @@ function isBlockedIp(value: string) {
const kind = isIP(value);
if (kind === 4) {
const parts = value.split(".").map((part) => Number(part));
const [a, b] = parts;
const [a, b, c] = parts;
return (
a === 0 ||
a === 10 ||
a === 127 ||
(a === 169 && b === 254) ||
(a === 172 && b >= 16 && b <= 31) ||
(a === 192 && b === 0 && c === 0) ||
(a === 192 && b === 0 && c === 2) ||
(a === 192 && b === 168) ||
(a === 198 && (b === 18 || b === 19)) ||
(a === 198 && b === 51 && c === 100) ||
(a === 203 && b === 0 && c === 113) ||
a >= 224 ||
(a === 100 && b >= 64 && b <= 127)
);
}
@ -90,9 +96,13 @@ function isBlockedIp(value: string) {
return (
normalized === "::" ||
normalized === "::1" ||
normalized.startsWith("100:") ||
normalized.startsWith("2001:2:") ||
normalized.startsWith("2001:db8:") ||
normalized.startsWith("fc") ||
normalized.startsWith("fd") ||
normalized.startsWith("fe80")
/^fe[89ab][0-9a-f]:/.test(normalized) ||
normalized.startsWith("ff")
);
}