Skip to content

fix(): fix Critical command injection vulnerability + cross-platform breakage in CCTV image proxy - #244

Open
Arielpetit wants to merge 1 commit into
simplifaisoul:masterfrom
Arielpetit:fix/cctv-proxy-command-injection
Open

fix(): fix Critical command injection vulnerability + cross-platform breakage in CCTV image proxy#244
Arielpetit wants to merge 1 commit into
simplifaisoul:masterfrom
Arielpetit:fix/cctv-proxy-command-injection

Conversation

@Arielpetit

Copy link
Copy Markdown

PR: Replace execSync/curl.exe with Native fetch in CCTV Image Proxy

Issue

The CCTV image proxy (src/app/api/cctv/proxy/route.ts:77) contained a critical command injection vulnerability and a cross-platform compatibility issue.

The curlFetch function executed a shell command using execSync:

const data = execSync(`curl.exe -s -k -L --max-time 10 "${url}"`, ...);

This introduced two separate problems:

  1. Cross-platform breakagecurl.exe is the Windows binary name and does not exist on Linux, causing production deployments to fail.
  2. Command injection — The user-controlled url was interpolated directly into a shell command, creating a command injection vulnerability.

Fix

Replaced the execSync/curl.exe implementation with the native asynchronous fetch API.

Changes include:

  • Removed the child_process import.
  • Added an async lenientFetch(url) helper built on fetch.
  • Added request timeout handling via AbortController.
  • Continued validating the request hostname against the existing ALLOWED_HOSTS allowlist before issuing the request, preventing SSRF.

Behavior

Scenario Before After
Valid allowed host (e.g. cdn.skylinewebcams.com) Crashed on Linux (curl.exe not found) ✅ Returns proxied image (200)
Forbidden domain Crashed before validation 403 {"error":"Forbidden domain: ..."}
Missing url parameter N/A 400 {"error":"Missing url parameter"}
Invalid URL N/A 400 {"error":"Invalid URL"}
Upstream timeout Could hang indefinitely ✅ Times out after 12 seconds via AbortController

Files Changed

File Changes Why
src/app/api/cctv/proxy/route.ts Removed execSync/child_process usage and replaced curlFetch() with async lenientFetch() using the native fetch API. Eliminates command injection and enables the proxy to work correctly on Linux.

Verification

  • npm test9 passed, 1 skipped
  • npm run build — Compiled successfully
  • ✅ Manual curl testing — Success path and all error cases verified

@Arielpetit

Copy link
Copy Markdown
Author

@simplifaisoul @sam1am @javierpr0 Can you please review this PR?

Repository owner deleted a comment from vercel Bot Jul 31, 2026
@tjakkaraju

Copy link
Copy Markdown

Independently reached the same fix while hardening this file for a separate issue (#259), and can confirm the approach here is correct.

A few notes that may help a reviewer merge with confidence:

Root cause, not just symptom. Swapping the execSync(\curl.exe … "${url}")for nativefetch removes the shell entirely, so there's no quoting/escaping to get subtly wrong later. That's the right call over trying to sanitize the URL into the shell string.
This is the only shell-exec site. I grepped src/ for execSync / child_process / spawn / exec( — the CCTV proxy is the only real one; every other .exec( is RegExp.prototype.exec. So this PR closes the whole surface, not one instance of it.
The hostname allowlist wasn't sufficient on its own. It runs on new URL(url).hostname, which happily parses a value that carries shell metacharacters after an allowed host — so the allowlist could pass while the shell string still broke out. Worth keeping in mind if the allowlist ever gets reused elsewhere.
One behavioral caveat: curl -k skipped TLS verification; native fetch enforces it. Any camera that only loaded because of a bad/self-signed cert will now fail. That's a functional detail, not a security one — flagging it so it isn't mistaken for a regression in this PR.
The package-lock.json hunk looks like unrelated peer: true churn from a different npm resolution; the security fix is entirely in route.ts.
Verified statically (didn't stand up the stack against live feeds). +1 to merge.

@simplifaisoul simplifaisoul added the invalid This doesn't seem right label Aug 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Important invalid This doesn't seem right To-Review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants