Skip to content

feat(osv-advisory-source,validate-url): add SSRF defense for --osv-url flag#751

Open
luojiyin1987 wants to merge 10 commits into
OWASP:mainfrom
luojiyin1987:feat/ssrf-defense-osv-url
Open

feat(osv-advisory-source,validate-url): add SSRF defense for --osv-url flag#751
luojiyin1987 wants to merge 10 commits into
OWASP:mainfrom
luojiyin1987:feat/ssrf-defense-osv-url

Conversation

@luojiyin1987

@luojiyin1987 luojiyin1987 commented Jun 24, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds Server-Side Request Forgery (SSRF) protection for the --osv-url flag to prevent abuse when users specify custom OSV endpoints.

Fixes #750

Problem

The --osv-url flag currently accepts any URL that passes new URL() parseability. An attacker who controls this flag could redirect outbound HTTP traffic to internal services, exfiltrating package data from lockfiles.

Solution

Implement multi-layered SSRF defense:

  1. HTTPS only - HTTP URLs are rejected
  2. Public IPs only - private/reserved IP ranges (RFC 1918, loopback, link-local) are blocked by default
  3. Explicit opt-in - --allow-private-osv-url flag to bypass protection for trusted internal mirrors
  4. No redirects - redirect: 'error' on all fetch calls to prevent redirect-based attacks

Changes

Core Implementation

  • src/utils/validate-url.ts (new) - URL validation with HTTPS check, DNS resolution, private IP detection
  • src/types.ts - Added allowPrivateOsvUrl?: boolean to ParsedOptions
  • src/cli/args.ts - Parse --allow-private-osv-url flag
  • src/cli/validate.ts - Call validateOsvUrl() in validation
  • src/index.ts - Updated to async validateOptions() with await
  • src/advisory/osv-advisory-source.ts - Added redirect: 'error' to both fetch calls

Tests

  • tests/validate-url.test.ts (new) - 22 test cases covering HTTPS validation, DNS resolution, private IP detection
  • tests/validate.test.ts - Updated to async pattern
  • tests/osv-advisory-source.test.ts - Updated fetch mock expectations

Documentation

  • README.md - Added usage examples
  • website/docs/cli-reference.md - Added flag documentation with security note
  • website/docs/corporate-proxy.md - Added SSRF protection guide for internal mirrors
  • website/docs/offline-advisory-db.md - Added flag documentation
  • website/docs/security-assurance-case.md - Updated A10 SSRF status

Security Considerations

  • Default: Only official OSV (api.osv.dev) allowed without --osv-url
  • --osv-url: Requires HTTPS and public IPs only
  • --allow-private-osv-url: Explicitly bypasses protection for trusted internal mirrors
  • Redirects: All blocked to prevent redirect-based SSRF attacks

Testing

Test Suites: 3 passed, 3 total
Tests:       36 passed, 36 total

Example Usage

# Official OSV (default) - no SSRF risk
cve-lite .

# Custom public endpoint
cve-lite . --osv-url https://security.example.com/osv

# Internal mirror (requires explicit bypass)
cve-lite . --osv-url https://internal-mirror.local/osv --allow-private-osv-url

This PR addresses Finding 2 from the security audit report.

- Use dns.lookup with { all: true } to check ALL resolved addresses
- Reject if ANY resolved address is private/reserved
- Expand PRIVATE_IP_PATTERNS to cover:
  - CGNAT (100.64.0.0/10)
  - IETF Protocol (192.0.0.0/24)
  - Documentation addresses (TEST-NET-1/2/3)
  - Benchmark (198.18.0.0/15)
  - Reserved (240.0.0.0/4)
  - Broadcast (255.255.255.255)
  - IPv6 unspecified (::)
  - IPv6 documentation (2001:db8::/32)
  - IPv6 NAT64 (64:ff9b::/96)
  - IPv6 discard (100::/64)
  - IPv6 Teredo (2001::/32)
  - IPv6 benchmark (2001:2::/48)
- Add IPv4-mapped IPv6 address detection (::ffff:127.0.0.1)
- Add comprehensive DNS resolution tests
@luojiyin1987 luojiyin1987 force-pushed the feat/ssrf-defense-osv-url branch from bd2a1d0 to d4774c4 Compare June 25, 2026 02:30

@sonukapoor sonukapoor left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good work on the layered approach — HTTPS-only plus DNS resolution plus explicit bypass is the right model for this. Two bugs in the IP pattern table need fixing before this can merge, plus a few smaller things.

Also: --allow-private-osv-url without --osv-url is silently accepted right now. A quick guard like if (options.allowPrivateOsvUrl && !options.osvUrl) with a clear error message would make the pairing explicit.

In website/docs/security-assurance-case.md, the A10 row opens with "The CLI only makes outbound HTTP requests to fixed, hardcoded endpoints" but then describes user-controlled URL behavior — those two halves contradict each other. The old wording was accurate before --osv-url existed. Worth leading with the user-controlled case and describing the mitigations directly, dropping the "fixed, hardcoded" claim.

Comment thread src/utils/validate-url.ts Outdated
Comment thread src/utils/validate-url.ts Outdated
Comment thread src/utils/validate-url.ts Outdated
- Fix CGNAT regex gap: second-octets 108,109,118,119 now blocked
- Add IPv4 multicast pattern (224.0.0.0/4) to private IP check
- Guard --allow-private-osv-url without --osv-url with clear error
- Replace console.warn with return-value warning to avoid stderr bleed in --json mode
- Add test cases for CGNAT edge cases, multicast, and allowPrivate guard
…ation

- Store ssrfWarning instead of returning early, so all subsequent
  guards (--fix --json, --create-pr, --report --json, --ca-cert)
  still run when --allow-private-osv-url is set
- Add 5 regression tests covering invalid flag combos with private --osv-url
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: Add SSRF defense for --osv-url flag

2 participants