Files
readest/apps/readest-app/src/__tests__/utils/kosync-ssrf.test.ts
T
Huang Xin 184de9210d fix(security): prevent SSRF in kosync proxy (CWE-918) (#3793)
Validate serverUrl in the kosync API proxy to block requests to
private/internal addresses and non-http(s) schemes. Also fix isLanAddress
to detect 0.0.0.0 and bracket-wrapped IPv6 private addresses.

Closes code-scanning alert #14.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 21:21:51 +02:00

46 lines
1.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { describe, it, expect } from 'vitest';
import { isLanAddress } from '@/utils/network';
/**
* Tests for SSRF protection in the kosync proxy.
* The proxy must reject requests to private/internal addresses.
* See: https://github.com/readest/readest/security/code-scanning/14
*/
describe('isLanAddress SSRF edge cases for proxy', () => {
// 0.0.0.0 routes to localhost on many systems
it('returns true for 0.0.0.0', () => {
expect(isLanAddress('http://0.0.0.0')).toBe(true);
expect(isLanAddress('http://0.0.0.0:8080')).toBe(true);
});
// Cloud metadata endpoint (169.254.x.x is already covered, but test the exact AWS one)
it('returns true for cloud metadata IP 169.254.169.254', () => {
expect(isLanAddress('http://169.254.169.254')).toBe(true);
expect(isLanAddress('http://169.254.169.254/latest/meta-data/')).toBe(true);
});
// IPv6 loopback with brackets (URL standard format)
it('returns true for bracket-wrapped IPv6 loopback [::1]', () => {
expect(isLanAddress('http://[::1]')).toBe(true);
expect(isLanAddress('http://[::1]:8080')).toBe(true);
});
// IPv6 link-local with brackets
it('returns true for bracket-wrapped IPv6 link-local [fe80::1]', () => {
expect(isLanAddress('http://[fe80::1]')).toBe(true);
});
// IPv6 unique local with brackets
it('returns true for bracket-wrapped IPv6 unique local [fc00::1] and [fd00::1]', () => {
expect(isLanAddress('http://[fc00::1]')).toBe(true);
expect(isLanAddress('http://[fd00::abc]')).toBe(true);
});
// Public addresses should still return false
it('returns false for public addresses', () => {
expect(isLanAddress('https://sync.koreader.rocks')).toBe(false);
expect(isLanAddress('https://8.8.8.8')).toBe(false);
expect(isLanAddress('http://[2001:db8::1]')).toBe(false);
});
});