8.7 KiB
Security Review
Date: 2026-06-05
Scope reviewed:
- Vue/Vite renderer in
src/ - Electron main/preload in
electron/ - MP4 render server in
server/ - Project import/export paths for
.json,.ruw, backups, assets, fonts, and generated HTML exports - Build/dependency posture at the repository root
Summary
The desktop shell starts from a good Electron baseline: contextIsolation: true, nodeIntegration: false, sandbox: true, and a narrow preload bridge. I did not find direct Node exposure to the Vue renderer.
The highest-risk area is the standalone MP4 render server. It accepts unauthenticated cross-origin render jobs, large request bodies, and attacker-controlled scene data, then renders it in Chromium with --no-sandbox and feeds frames to ffmpeg. That combination should be treated as a network-exposed compute/sandbox boundary until it is tightened.
The second broad theme is input hardening. Project files and .ruw bundles are structurally checked, but not deeply schema-validated or bounded before the app uses canvas sizes, frame counts, assets, and timelines. A crafted file can likely cause heavy CPU/memory use in both browser and server render paths.
Findings
High: Render Server Is Unauthenticated And Cross-Origin
Evidence:
server/server.js:19enables defaultcors(), allowing browser requests from any origin.server/server.js:20accepts JSON bodies up to200mb.server/server.js:27starts render jobs without authentication, CSRF protection, origin checks, rate limits, or concurrency limits.docker-compose.yml:4-5publishes port6000on the host.
Impact:
Any site reachable by a user's browser, or any network client that can reach the service, can submit expensive render jobs. This can exhaust CPU, memory, disk, Chromium processes, or ffmpeg work queues. If the server is ever exposed beyond localhost, it becomes a public unauthenticated render farm.
Recommendations:
- Bind the service to localhost by default, or require an explicit opt-in for network exposure.
- Replace default
cors()with an allowlist for the app origin. - Add authentication or a per-session token for
/render,/events,/file, andDELETE. - Add request rate limits, maximum active jobs, queue limits, and per-job timeouts.
- Lower the default body limit and reject requests before parsing when possible.
High: Chromium Renders Untrusted Data With Sandbox Disabled
Evidence:
server/server.js:126-134launches Chromium with--no-sandboxand--disable-setuid-sandbox.server/server.js:166-167loadsrender.htmland injects caller-provided scene data into the page.server/Dockerfile:1-20uses the default root user; no non-rootUSERis configured.
Impact:
The server renders attacker-controlled image/audio/video/SVG-like asset data in Chromium. If a browser engine, image decoder, font decoder, media decoder, or canvas path is exploited, disabling the Chromium sandbox and running the container as root increases the blast radius.
Recommendations:
- Run the container as a non-root user.
- Prefer enabling the Chromium sandbox. If the container needs extra setup, document and configure it rather than relying on
--no-sandbox. - Treat each render job as untrusted: use process-level timeouts, memory/CPU limits, and container hardening.
- Consider a separate locked-down worker container for renders.
Medium: Project And Bundle Imports Lack Deep Validation And Bounds
Evidence:
src/lib/projectFile.ts:39-67checks only top-levelmagic,canvas,scenes, andassetsshape before returning the imported object asProject.src/lib/zipUtil.ts:121-171parses ZIP entries into memory without explicit caps on entry count, central directory size, total extracted size, or path/name length.server/server.js:115-119derivesfps,duration, andtotalFramesdirectly from request data.server/server.js:135-138uses request-provided canvas dimensions as the Chromium viewport.
Impact:
A crafted .json, .ruw, backup, or server request can specify extreme canvas sizes, frame counts, layer/keyframe arrays, base64 assets, font data, or ZIP metadata. The likely outcome is denial of service: browser tab hangs, Electron renderer memory pressure, server CPU exhaustion, or failed/very slow exports.
Recommendations:
- Add a real project schema validator at file and server boundaries.
- Clamp canvas dimensions, duration, fps, layer counts, keyframe counts, asset count, asset byte size, and total project size.
- Add ZIP reader bounds and clearer errors for malformed/truncated archives.
- Normalize imported projects into a safe internal shape instead of trusting arbitrary object fields.
Medium: Render Server Dependency Installs Are Not Reproducible
Evidence:
server/package.jsonexists, but noserver/package-lock.jsonis present.server/Dockerfile:15-16copies onlypackage.jsonand runsnpm install --omit=dev.npm audit --prefix server --omit=devcannot run without a lockfile.
Impact:
Server builds may pull different transitive dependency versions over time. That makes vulnerability tracking, incident response, and reproducible releases harder.
Recommendations:
- Commit
server/package-lock.json. - Use
npm ci --omit=devin the server Dockerfile. - Add server dependency auditing to CI.
Medium: Development Test Overlay Ships In The Production Bundle
Evidence:
src/App.vue:38statically imports./devtools/TestOverlay.vue.src/App.vue:806always mounts the component.npm run buildoutput showssrc/devtools/tests.tspulled into the production bundle through static/dynamic import relationships.
Impact:
This increases production attack surface and bundle size. Even if the test runner is intended to open only from a development-only Electron menu item, the code is still shipped to every web and desktop user.
Recommendations:
- Gate the test overlay behind
import.meta.env.DEV. - Load it with
defineAsyncComponentor dynamic import only in development. - Ensure production builds do not include
src/devtools/tests.ts.
Low: Electron Navigation And External Link Policy Could Be Stricter
Evidence:
electron/main.cts:40-44opens anyhttp://orhttps://URL externally.electron/main.cts:47-51uses stringstartsWithchecks for navigation allow/deny decisions.
Impact:
This is not a direct Node compromise because the renderer is isolated. Still, if untrusted content ever reaches a clickable/openable path, arbitrary external links can be launched. String-prefix navigation checks are also easier to get wrong than parsed URL origin/path checks.
Recommendations:
- Prefer parsed URL checks over
startsWith. - Allow only expected origins in development.
- Consider allowing only
https:external links, or an explicit domain allowlist for help/about links.
Low: No Content Security Policy Is Defined
Evidence:
index.htmland builtdist/index.htmlhave no CSP meta tag.
Impact:
Vue's default escaping reduces XSS risk, and Electron disables Node integration. A CSP would still provide defense in depth for the web app, generated asset previews, and Electron renderer by limiting script, media, image, and connection destinations.
Recommendations:
- Add a CSP for the app shell.
- Account for required
data:/blob:sources for user assets and exports. - Add Electron-specific CSP validation during packaged smoke tests.
Positive Notes
- Electron uses
contextIsolation, disablesnodeIntegration, enables renderer sandboxing, and exposes only a small preload API. - The preload bridge does not expose arbitrary IPC invocation or Node primitives.
- The Electron app denies
window.openin-app and prevents most unexpected navigation. .ruwuses STORE-only ZIP handling, avoiding decompression bombs from deflated entries.- The root production dependency audit completed with
0 vulnerabilities. - A shallow secret scan found no obvious committed
.env, key, secret, PEM, or certificate files outside dependencies.
Verification Performed
npm run buildpassed.npm audit --omit=devpassed with0 vulnerabilities.npm audit --prefix server --omit=devcould not run becauseserver/has no lockfile.
Suggested Remediation Order
- Lock down the render server: localhost binding, CORS allowlist, auth/token, rate limits, job limits, and timeouts.
- Run render workers as non-root and remove Chromium
--no-sandboxif feasible. - Add schema validation and resource bounds for imported projects,
.ruwbundles, backups, and render requests. - Add
server/package-lock.json, switch Docker tonpm ci --omit=dev, and audit server dependencies in CI. - Remove devtools/test runner code from production bundles.
- Add CSP and stricter Electron URL parsing/allowlists.