Skip to content

fix(security): resolve 6 confirmed vulnerabilities (#273) - #274

Open
rahmaniramin550-ai wants to merge 1 commit into
MiniMax-AI:mainfrom
rahmaniramin550-ai:fix/security-remediations-273
Open

rahmaniramin550-ai wants to merge 1 commit into
MiniMax-AI:mainfrom
rahmaniramin550-ai:fix/security-remediations-273

Conversation

@rahmaniramin550-ai

@rahmaniramin550-ai rahmaniramin550-ai commented Oct 2, 2026 •

Copy link
Copy Markdown

Summary of Security Remediations

Closes #273

This PR resolves all 6 confirmed security vulnerabilities reported in #273:

  1. Vision Image SSRF & Excessive Buffering (src/utils/image.ts)

    • Added validateSafeUrl to reject private IPv4/IPv6, loopback (127.0.0.0/8, ::1), link-local / cloud metadata (169.254.169.254), and internal hostnames.
    • Replaced unbounded res.arrayBuffer() buffering with streaming chunk reading that aborts and throws CLIError immediately if size exceeds MAX_IMAGE_SIZE_BYTES (50 MB), preventing denial of service / memory exhaustion.
  2. Speech Subtitle SSRF (src/commands/speech/synthesize.ts)

    • Validates response.data.subtitle_file with validateSafeUrl before attempting download, preventing internal network access if an API response is spoofed or compromised.
  3. Media Download SSRF & Safe File Creation (src/files/download.ts)

    • Added destination URL validation rejecting private/loopback/cloud metadata destinations before starting download attempts.
    • Configured temporary download file writer with flags: 'wx' and mode: 0o600 to prevent symlink following and TOCTOU file hijacking.
  4. Config Temp-File Symlink Race (src/config/loader.ts)

    • Replaced predictable static config.json.tmp path with unique, cryptographically random temporary filename.
    • Wrote temporary config using flag: 'wx' (O_CREAT | O_EXCL) and mode 0o600 so pre-existing symlinks cannot be overwritten.
    • Added automatic cleanup of temporary files if rename fails.
  5. Self-Update Temporary Symlink Overwrite (src/update/self-update.ts)

    • Isolated self-update binary downloads inside a dedicated temporary directory created via mkdtempSync with 0o700 user-only permissions.
    • Set download writer flag to wx with mode: 0o700.
    • Prevented unlinking pre-existing files when opening fails with EEXIST.
    • Guaranteed cleanup of temporary directory in finally block.
  6. Authenticated Query & Path Parameter Injection (src/client/endpoints.ts)

    • Applied encodeURIComponent to taskId in videoTaskEndpoint and videoTaskV2Endpoint.
    • Applied encodeURIComponent to fileId in fileRetrieveEndpoint.

Verification & Tests

  • Added 6 dedicated regression test suites:
    • test/utils/network.test.ts (16 passing tests)
    • test/utils/image-security.test.ts (7 passing tests)
    • test/files/download-ssrf.test.ts (5 passing tests)
    • test/commands/speech/synthesize-security.test.ts (1 passing test)
    • test/config/loader-security.test.ts (2 passing tests)
    • test/update/self-update-security.test.ts (1 passing test)
    • test/client/endpoints.test.ts (9 passing tests)
  • tsc --noEmit passes with 0 type errors.
  • eslint passes with 0 errors.

Payout Address (USDC / Solana):
BuMURy7R5mkCUtasaAWwvXZReZYmCCANMyUXrRy6CZzo


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

- src/utils/image.ts: prevent SSRF on remote image URLs and enforce bounded chunk streaming against memory exhaustion

- src/commands/speech/synthesize.ts: validate subtitle_file URL against SSRF before download

- src/files/download.ts: add SSRF validation rejecting private/loopback/cloud metadata destinations and secure temp file creation

- src/config/loader.ts: generate unpredictable temp files and write with mode 0o600 and flag 'wx' to prevent symlink overwrites

- src/update/self-update.ts: isolate updates in private mkdtemp directory and write with flag 'wx' without unlinking existing files

- src/client/endpoints.ts: URL-encode taskId and fileId parameters to prevent authenticated query/path parameter injection

- add comprehensive unit and regression tests for all 6 vulnerabilities
@x4evexnol

Copy link
Copy Markdown

Thanks for putting work into this — the private-range checks and the wx/mkdtemp changes are solid, and match the direction I ended up going locally.

I'm the author of #273, and there are a few gaps here that keep the SSRF findings reachable, so I'd rather they get closed before this is considered a full fix:

Redirects. fetch follows 30x by default, so everything validateSafeUrl rejects at the URL level can come back on the second hop: https://public-host.example/image.png passes validation, and that server just answers with Location: http://127.0.0.1/... or 169.254.169.254, bypassing all of the literal IP checks. Rejecting redirects outright (redirect: 'error') is the straightforward fix, and it fits how these URLs behave in practice — a subtitle or generated-media URL has no legitimate reason to bounce around.

DNS rebinding. validateSafeUrl resolves the host with dns.lookup(), then fetch resolves the same name a second time on its own. A rebinding setup just answers the first lookup with a public IP and the second with a loopback or metadata address, so the check passes and the request still lands internally. The comment in the code says the lookup guards against rebinding, but check-then-fetch doesn't — the checked address has to be pinned to the actual request (connect to the resolved IP with the Host header / SNI taken from the original name, or a custom undici lookup). Related: when dns.lookup fails, the catch block lets the fetch go through anyway, which soft-fails in the wrong direction.

src/commands/video/generate.ts is still uncovered. The task ID flows into join(os.tmpdir(), 'mmx-video', taskId + '.mp4'), so a malicious or misconfigured API can make the CLI write outside that directory with a ../ in the ID. The endpoint encoding in src/client/endpoints.ts doesn't touch this half — it needs a separator/path check before the local path is built.

No timeouts. The hardened fetches never get an abort signal, so a deliberately slow target can still hang the CLI for as long as it likes, even with the streaming size cap in place.

I have a local patch covering all seven affected files plus regression tests, which I've kept private for the disclosure — happy to hand it to the maintainers directly if that's useful. Not trying to gatekeep the fix, I just don't want #273 closed with the SSRFs half-addressed.

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.

[Security] Six confirmed vulnerabilities remain in latest main

3 participants