← MCP server and skill grades

soundbox

HamSanders/soundbox

B
Type
MCP server
Scanned
Oct 30, 2026
Version
085e0ecef754
Files read
525

Soundbox is a local AI studio, desktop app, and MCP server that generates sound effects, music, and speech on your computer using open-weight models. The biggest risk is that its server has no authentication and disables browser security checks when listening on all network interfaces (such as in Docker), which could let attackers or malicious websites write files to arbitrary folders, steal saved API keys, or send unchecked options to the underlying audio engine.

What it can reach

Files
Reads and writes audio files, model weights, SQLite databases, logs, and settings under ~/.soundbox (or custom folders), installs skill files into coding agent folders (such as ~/.claude and ~/.cursor), and exposes an API to browse host directories.
Network
Runs a local HTTP API, web UI, and MCP server on port 17494, downloads AI models and runtime binaries from Hugging Face and GitHub, and connects to user-configured external AI and OpenTelemetry endpoints.
Shell commands
Spawns local AI inference binaries (audio.cpp, llama-server, sd-server), ffmpeg for audio conversion, nvidia-smi for GPU checks, archive tools (tar, unzip), and OS file managers.
Secrets
Reads Hugging Face tokens from environment variables (HF_TOKEN, HUGGING_FACE_HUB_TOKEN) and stores tokens and external AI API keys in settings.json.
Browser
Accesses microphone input and connected Web MIDI controllers through the web interface when triggered by the user.
System
Queries GPU and VRAM hardware information and interacts with local audio and MIDI devices.

Findings

9 medium8 low
  1. MediumDocker container binds unauthenticated server to 0.0.0.0 by default

    Dockerfile:110

    The Docker entrypoint defaults to binding the Soundbox HTTP and MCP server to 0.0.0.0 on port 17494, and the server has no authentication. If a user publishes the port without explicitly restricting it to 127.0.0.1 on the host, anyone on the network can call the API and MCP tools, consume GPU resources, and access or delete library audio.

    Fix: Document and configure host port bindings to 127.0.0.1:17494:17494 by default, or require an authentication token when binding to non-loopback interfaces.

  2. MediumWildcard host binding disables DNS-rebinding and CORS origin checks (including in Docker)

    server/src/cli.ts:520

    When the server is started with --host 0.0.0.0 (which is the default CMD in the Dockerfile), cli.ts passes allowedHosts: "any" to buildApp(). In server/src/app.ts, allowedHosts === "any" completely skips the Host header check, even if SOUNDBOX_ALLOWED_HOSTS is set. Meanwhile, isTrustedOrigin() in server/src/origins.ts assumes the Host header was already validated by app.ts and trusts any Origin header whose host matches the Host header (host === hostHeader.trim().toLowerCase()). As a result, even if a user publishes the Docker container port only to 127.0.0.1:17494 on their host, any malicious website can use DNS rebinding (sending matching Host and Origin headers for the attacker's domain) to bypass both the Host check and CORS preflight protection and make arbitrary unauthenticated requests to the Soundbox API.

    Fix: Do not disable the Host header check when bound to 0.0.0.0 or ::. Instead, always enforce the LOOPBACK_HOSTS and SOUNDBOX_ALLOWED_HOSTS allowlist in app.ts unless the user explicitly opts out via a dedicated flag or wildcard entry in SOUNDBOX_ALLOWED_HOSTS.

  3. MediumUnfiltered model load and request options passed to audio.cpp

    server/src/schemas.ts:635

    Both speakRequestSchema and the story segment schemas accept arbitrary key-value pairs in request_options (exposed to MCP tools as delivery) and load_options, which are forwarded as --request-option and --load-option flags to audio.cpp. Some model family parameters (such as voice_samples in VibeVoice) accept filesystem paths, so an untrusted caller or prompt-injected agent could supply unexpected runtime flags or reference local files outside the sound library.

    Fix: Validate request_options and load_options against an allowlist of safe parameter names and types defined for each model family in models/params.ts, and reject any parameter that accepts raw filesystem paths.

  4. MediumMissing UNC and path validation on audiocpp_models_dir setting

    server/src/service.ts:2726

    While updateSettings validates sounds_dir with assertSoundsDirUsable (blocking Windows UNC network paths via isUncPath and preventing nested directory moves), audiocpp_models_dir is applied directly without UNC or path validation and immediately probed on disk by adoptDownloadedModels(). On Windows, setting audiocpp_models_dir to a UNC path can force the server to connect to a remote SMB share and leak NetNTLM credentials, or point model storage and deletion at unintended directories.

    Fix: Apply the same isUncPath and path safety checks used in assertSoundsDirUsable to audiocpp_models_dir before updating settings or scanning the directory.

  5. MediumArbitrary directory creation and file write in skill installer

    server/src/skills.ts:139

    When target is set to "dir", resolveTarget and install accept any filesystem path (exposed via skillInstallRequestSchema to the HTTP API) without path or UNC restrictions. Calling install creates the target directory recursively and removes/overwrites subdirectories matching bundled skill names at any location writable by the server process.

    Fix: Restrict skill installation via the HTTP API to the predefined agent targets (claude, cursor, codex), or reject UNC paths and restrict custom target directories to safe user workspace locations.

  6. MediumSaved endpoint API keys can be exfiltrated by changing the URL in a test or update request

    server/src/endpoints/service.ts:94

    When testing (POST /api/endpoints/test) or updating (PUT /api/endpoints/:id) a custom audio endpoint, EndpointService.build() reuses the previously saved api_key whenever api_key is omitted from the request, even if the url field is changed to a different host. Because the HTTP API has no authentication, any local process or attacker able to reach the API can pass an existing endpoint id with an attacker-controlled url and cause the server to send the stored Authorization: Bearer <api_key> header to that URL.

    Fix: Only reuse the stored api_key when the endpoint's url (or at least its scheme, host, and port) is unchanged; require the key to be re-entered if the target host changes.

  7. MediumDocker image binds to 0.0.0.0 by default, disabling Host-header DNS rebinding protection

    Dockerfile:110

    The Docker container starts the server with --host 0.0.0.0 so that port forwarding from the host can reach it. However, in Soundbox's HTTP server, a wildcard bind (0.0.0.0) disables the Host header allowlist check on /api/* and /files/* routes. Even when the port is published only on 127.0.0.1:17494 on the host, a malicious website visited in the user's browser can use DNS rebinding to read the library, browse host/container directories via GET /api/fs/dirs, or trigger same-origin API actions.

    Fix: Keep the loopback Host header allowlist active even when the socket is bound to 0.0.0.0, requiring non-loopback Host headers to be explicitly listed in SOUNDBOX_ALLOWED_HOSTS.

  8. MediumUnfiltered delivery options from MCP tools are passed directly to audio.cpp as request options

    server/src/models/audiocpp.ts:1461

    The MCP speech tools (soundbox.speak, soundbox.create_story, soundbox.add_lines, soundbox.update_line) accept an arbitrary delivery key-value map (Record<string, string>) and forward it into AudioCppModel.speak() as requestOptions without filtering keys against safe delivery parameters. In audio.cpp, request options for several families include local file path parameters (such as voice_samples in VibeVoice or voice_model_path and retrieval_index_path in RVC), allowing an untrusted prompt injection in the agent to pass arbitrary local file paths into the native runtime.

    Fix: Validate requestOptions keys against an allowlist of safe numeric, boolean, and choice parameters defined for the model family in paramsFor(s.family), and reject file-path options.

  9. MediumDocker container binds unauthenticated server to 0.0.0.0 and disables API Host checks

    Dockerfile:110

    The Docker image starts the server with --host 0.0.0.0 and has no authentication. As noted in docs/INSTALL.md, binding to 0.0.0.0 also turns off the Host header allowlist for the web UI and /api/* routes. If a user runs the container with -p 17494:17494 instead of restricting the host mapping to 127.0.0.1, anyone on the network (or a malicious webpage via DNS rebinding) can read or delete the audio library, trigger GPU jobs, or reconfigure external endpoints.

    Fix: Keep the Host header allowlist active even when listening on 0.0.0.0, or require an API token when bound to a non-loopback address.

  10. LowUnpinned moving tag used for third-party runtime base image

    Dockerfile:22

    The Dockerfile pulls ghcr.io/0xshug0/audio.cpp using the moving tag full-cpu by default. Because this image supplies the native audiocpp_cli binary executed by the server, an upstream compromise or breaking daily build could introduce untrusted code into newly built containers.

    Fix: Pin AUDIOCPP_TAG to a specific immutable digest or dated release tag by default.

  11. LowDocker image uses a daily-moving upstream base image tag by default

    Dockerfile:22

    The Dockerfile defaults AUDIOCPP_TAG to 'full-cpu', which the comments note is a moving tag updated daily on ghcr.io/0xshug0/audio.cpp. Building the container without overriding this build argument pulls unpinned upstream binaries that could change unexpectedly or be compromised.

    Fix: Pin AUDIOCPP_TAG to a specific dated release tag or an immutable SHA-256 image digest by default.

  12. LowContainer listens on 0.0.0.0 without built-in authentication

    Dockerfile:110

    The container starts the Soundbox server bound to 0.0.0.0 on port 17494 so Docker port publishing can reach it, and the server has no authentication. If a user runs the container with -p 17494:17494 instead of binding to 127.0.0.1:17494:17494, any device on the local network can call the API and MCP tools.

    Fix: Ensure docker run and compose configurations bind the host port to 127.0.0.1, or add optional token authentication to the server.

  13. LowUnpinned mutable base image tag in Dockerfile

    Dockerfile:22

    The Dockerfile uses ghcr.io/0xshug0/audio.cpp:full-cpu by default without pinning an immutable digest or dated release tag. As noted in the Dockerfile comments, these upstream tags move daily, meaning a rebuild could pull modified or compromised upstream binaries without notice.

    Fix: Pin AUDIOCPP_TAG to a specific dated release tag and/or pin the base image by its SHA-256 digest.

  14. LowUnpinned moving base image tag in Dockerfile

    Dockerfile:22

    The Dockerfile uses a mutable daily tag (full-cpu) for the third-party base image ghcr.io/0xshug0/audio.cpp by default. If the upstream tag is compromised or modified unexpectedly, container builds could pull untrusted binaries.

    Fix: Pin AUDIOCPP_TAG to a specific immutable release tag and image SHA256 digest by default.

  15. LowPredictable log file path in shared temporary directory

    src-tauri/src/backend.rs:358

    The Tauri desktop shell writes startup logs to a fixed filename (soundbox-desktop.log) in the system temporary directory (/tmp on Unix and macOS) without checking file ownership or disabling symlink following. On a multi-user machine, another local user could pre-create a symlink at /tmp/soundbox-desktop.log to append log lines to files owned by the user running Soundbox.

    Fix: Write the desktop launcher log inside the user's private Soundbox data directory (~/.soundbox) or user-scoped application log directory instead of the shared system temp directory.

  16. LowUnpinned daily-moving base image tag in Dockerfile

    Dockerfile:22

    The Dockerfile defaults to the floating full-cpu tag of ghcr.io/0xshug0/audio.cpp, which the comments note moves daily. Building the image without overriding this argument pulls mutable upstream binaries without digest or version pinning.

    Fix: Pin AUDIOCPP_TAG to a specific dated release tag and SHA256 image digest by default.

  17. LowDockerfile uses a mutable daily base image tag by default

    Dockerfile:22

    The Dockerfile builds on ghcr.io/0xshug0/audio.cpp:full-cpu by default. As the comments note, this tag moves daily and contains the prebuilt audiocpp_cli binary that runs inside the container, meaning builds are not reproducible and automatically pull unpinned upstream binaries.

    Fix: Pin AUDIOCPP_TAG to a specific dated release tag and immutable SHA-256 image digest by default.