fix: harden production network and dependency boundaries - #20
Conversation
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Unresolved SSRF bypasses, response buffering, repository validation, and lockfile issues remain.
Get a fresh assessment by requesting another Copilot review.
Review effort: Lite
Findings: 4
Open (6)
Block unspecified IPv4 addresses in SSRF checks · New Reject mapped IPv4 and unspecified IPv6 addresses · New Prevent DNS rebinding between validation and fetch · New Validate redirect destinations against private-address policy · New Regenerate lockfile after removing duplicate plugin dependency · New Stream response bodies to enforce the size limit · New
What changed in this PR
Hardens production network requests and dependency boundaries by adding SSRF protections, response-size limits, repository validation, and removing a duplicate Vite dependency.
Changes:
- Adds DNS/IP and redirect safeguards for URL inspection.
- Bounds remote response sizes.
- Tightens GitHub repository identifiers.
- Removes the duplicate Vite plugin dependency.
| File | Summary |
|---|---|
server.ts |
Adds SSRF checks, response limits, and repository validation. |
package.json |
Removes the duplicate Vite plugin dependency. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| return a === 10 || a === 127 || (a === 169 && b === 254) | ||
| || (a === 172 && b >= 16 && b <= 31) | ||
| || (a === 192 && b === 168); |
| if (net.isIPv4(address)) { | ||
| const [a, b] = address.split('.').map(Number); | ||
| return a === 10 || a === 127 || (a === 169 && b === 254) | ||
| || (a === 172 && b >= 16 && b <= 31) | ||
| || (a === 192 && b === 168); | ||
| } | ||
| if (net.isIPv6(address)) { | ||
| return normalized === '::1' | ||
| || normalized.startsWith('fc') | ||
| || normalized.startsWith('fd') | ||
| || normalized.startsWith('fe8') | ||
| || normalized.startsWith('fe9') | ||
| || normalized.startsWith('fea') | ||
| || normalized.startsWith('feb'); |
| const resolved = await dns.lookup(hostname, { all: true }); | ||
| if (resolved.length === 0 || resolved.some((entry) => isPrivateAddress(entry.address))) { | ||
| return res.status(400).json({ error: 'Private or local network targets are not allowed.' }); | ||
| } |
| const resolved = await dns.lookup(hostname, { all: true }); | ||
| if (resolved.length === 0 || resolved.some((entry) => isPrivateAddress(entry.address))) { | ||
| return res.status(400).json({ error: 'Private or local network targets are not allowed.' }); | ||
| } |
| @@ -15,7 +15,6 @@ | |||
| "dependencies": { | |||
| "@google/genai": "^2.4.0", | |||
| "@tailwindcss/vite": "^4.1.14", | |||
| const htmlText = await response.text(); | ||
| if (Buffer.byteLength(htmlText, 'utf8') > 2 * 1024 * 1024) { | ||
| return res.status(413).json({ success: false, url, httpStatus, latencyMs, contentType, verdict: 'unreachable', error: 'Response body too large.' }); |


Fix duplicate Vite plugin dependency, block private-network SSRF targets in live URL inspection, constrain GitHub repository identifiers, and bound remote HTML response sizes.