How OfflineCreator MCP secures local image uploads
Review the published local upload path, its root and image checks, and the symlink and runtime gaps that remain.
Get practical MCP creation notesStart with what the local upload gate actually checks
The published @offlinecreator/mcp 0.1.2 package applies five separate checks before a local file is sent: it resolves the supplied name against an upload root, rejects a lexical path that lands outside that root, allows only .png, .jpg, .jpeg, .webp, or .gif names, requires a nonempty regular file no larger than 10 MiB, and recognizes the bytes as PNG, JPEG, GIF, or WebP. The configured root comes from OFFLINECREATOR_UPLOAD_ROOT; when that variable is absent, the package uses the process working directory.
These controls belong to the local package path, not to every way an image can enter Studio. The package client uses the local reader only when filePath is supplied and a readLocalImage function has been injected. Its stdio entry point injects that reader. The separate imageBase64 branch decodes and checks bytes without reading a named local file, so an upload-root statement does not describe that branch.
- Root
- OFFLINECREATOR_UPLOAD_ROOT or the process working directoryRelative names resolve under that root; absolute names are accepted only when the lexical containment check keeps them under it.
- Filename
- .png, .jpg, .jpeg, .webp, or .gifThe extension is lowercased before the allowlist check.
- File and bytes
- Regular, nonempty, at most 10 MiB, recognized signatureThe content type sent to Studio comes from byte sniffing rather than from the filename.
- OfflineCreator Studio on npm: @offlinecreator/mcp 0.1.2 package README and registry record
- OfflineCreator Studio via npm and UNPKG: @offlinecreator/mcp 0.1.2 published safe filesystem implementation
- OfflineCreator Studio via npm and UNPKG: @offlinecreator/mcp 0.1.2 published safe image implementation
- OfflineCreator Studio via npm and UNPKG: @offlinecreator/mcp 0.1.2 published Studio API client
- OfflineCreator Studio via npm and UNPKG: @offlinecreator/mcp 0.1.2 published stdio entry point
Trace a filePath from tool input to the Studio request
For a filePath upload, the package first validates that the generation identifier is a UUID. It then calls the injected local-image reader with the fixed 10 MiB maximum and the configured upload root. The reader rejects an empty name or a name containing a null byte, resolves the root and candidate path, checks lexical containment with path.relative, checks the filename extension, opens the candidate for reading, and inspects the opened handle.
After opening, it requires the handle to describe a regular file, rejects zero bytes and an over-limit stat size, allocates a buffer of that size, reads from the same handle, and sniffs the buffer. If those checks pass, the client sends the bytes to /api/v1/generations/{generationId}/input with content-type set to the sniffed image type and content-length set to the buffer length. The MCP upload_input handler then calls the submit endpoint; a successful local read is therefore one stage in the image-to-video reservation workflow, not proof that provider submission or generation completed.
- Before open
- Validate name, lexical root containment, and extensionA path equal to the root itself is rejected because the input must identify a file.
- After open
- Validate handle type, nonzero size, limit, and signatureThe handle is closed in a finally block whether validation succeeds or fails.
- Network boundary
- Send checked bytes, then request submissionPassing the local checks does not itself establish successful submission or generation completion.
- OfflineCreator Studio via npm and UNPKG: @offlinecreator/mcp 0.1.2 published safe filesystem implementation
- OfflineCreator Studio via npm and UNPKG: @offlinecreator/mcp 0.1.2 published safe image implementation
- OfflineCreator Studio via npm and UNPKG: @offlinecreator/mcp 0.1.2 published Studio API client
- OfflineCreator Studio via npm and UNPKG: @offlinecreator/mcp 0.1.2 published stdio entry point
- OfflineCreator Studio via npm and UNPKG: @offlinecreator/mcp 0.1.2 published MCP tool server
Do not confuse an allowed extension with an asserted media type
The filename allowlist and the byte-signature test are independent gates in the published implementation. The extension must be one of five image suffixes, but the code does not require that suffix to agree with the recognized bytes. A file named example.jpg whose header is recognized as PNG can pass both gates: .jpg is allowed, and the PNG signature is recognized. The client then labels the outbound body image/png because the signature result, not the suffix, becomes the request content type.
The signature checks are intentionally narrow. PNG checks its first four identifying bytes, JPEG checks the three-byte FF D8 FF prefix, GIF accepts GIF87a or GIF89a, and WebP requires RIFF plus WEBP in the expected positions. This confirms a supported header pattern; it is not a full image decode, malware scan, decompression-bomb analysis, pixel-count limit, metadata scrub, or guarantee that a downstream decoder will accept the complete file.
- Extension decision
- Is the lowercased suffix on the five-entry allowlist?This limits names but does not establish the bytes' format.
- Signature decision
- Do the leading bytes match one of four supported patterns?The sniffed result determines the request content type.
- Not demonstrated
- Complete decode safety or file sanitizationNo decoder, pixel-budget check, metadata removal, or malware scanner appears in the reviewed package path.
- OfflineCreator Studio via npm and UNPKG: @offlinecreator/mcp 0.1.2 published safe image implementation
- OfflineCreator Studio via npm and UNPKG: @offlinecreator/mcp 0.1.2 published safe filesystem implementation
- OfflineCreator Studio via npm and UNPKG: @offlinecreator/mcp 0.1.2 published Studio API client
Treat the upload root as a lexical guard, not a proven symlink jail
The published reader normalizes path strings with path.resolve and decides containment from path.relative before calling open with the ordinary "r" flag. Node documents path.resolve as producing a normalized absolute path and path.relative as a relative path calculation. In the reviewed reader, there is no call to realpath and no O_NOFOLLOW open flag. The implementation evidence therefore supports rejection of direct .. traversal and absolute paths outside the configured root, but it does not support the stronger statement that every filesystem alias resolves inside the root.
The public implementation and test support a narrower boundary: normalized path strings and an ordinary ../outside.png case. They do not establish canonical containment through a symbolic link, junction, mount, hard link, or retargeted path because the reader does not canonicalize the candidate or request a no-follow open, and the test contains none of those cases. Configure the root to contain only intended upload candidates; stronger filesystem containment needs separate evidence.
- Supported
- Lexical traversal and outside-absolute-path rejectionThe package test covers an ordinary ../outside.png attempt.
- Not established
- Canonical containment through links or mountsThe reviewed reader does not canonicalize the candidate or use a no-follow open.
- Operational choice
- Use a dedicated, least-content upload directoryThe package resolves the candidate and compares its relative path against the configured root.
Evidence boundary for OfflineCreator local image uploads
The strongest supported conclusion is narrow: the published local stdio package places lexical root, extension, regular-file, nonempty, 10 MiB, and recognized-header checks before it sends filePath bytes to Studio. The published client makes filePath conditional on a configured local reader, and the package's stdio entry point injects that reader. The imageBase64 branch instead decodes supplied bytes and has no named local path to evaluate against the upload root.
The evidence also fixes the limits of that conclusion. The reader implements lexical rather than canonical containment, the image-type function recognizes selected header bytes rather than fully decoding an image, and the public test covers only four named cases. Because this pass reviewed published 0.1.2 artifacts rather than running a production reservation, version-specific conclusions should follow the installed package artifact and should not be read as proof of deployed upload success, provider acceptance, or generation completion.
- OfflineCreator Studio on npm: @offlinecreator/mcp 0.1.2 package README and registry record
- OfflineCreator Studio via npm and UNPKG: @offlinecreator/mcp 0.1.2 published safe filesystem implementation
- OfflineCreator Studio via npm and UNPKG: @offlinecreator/mcp 0.1.2 published safe image implementation
- OfflineCreator Studio via npm and UNPKG: @offlinecreator/mcp 0.1.2 published Studio API client
- OfflineCreator Studio via npm and UNPKG: @offlinecreator/mcp 0.1.2 published stdio entry point
- OpenJS Foundation: Node.js Path documentation
- OpenJS Foundation: Node.js File system documentation
- OfflineCreator Studio on GitHub: OfflineCreator MCP safe filesystem tests at revision c29bc9a
- OfflineCreator Studio on GitHub: OfflineCreator MCP package manifest at revision c29bc9a