Skip to content

fix(local-runtime): stop inheriting daemon environment in plugin subprocesses - #799

Open
SashaMIT wants to merge 1 commit into
langgenius:mainfrom
SashaMIT:fix/plugin-subprocess-env-allowlist
Open

fix(local-runtime): stop inheriting daemon environment in plugin subprocesses#799
SashaMIT wants to merge 1 commit into
langgenius:mainfrom
SashaMIT:fix/plugin-subprocess-env-allowlist

Conversation

@SashaMIT

@SashaMIT SashaMIT commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Plugin subprocesses no longer receive a copy of the daemon's own environment variables.

What

getInstanceCmd built plugin processes with cmd.Environ(), which copies the daemon process's entire environment into every plugin subprocess. In every real deployment that environment carries the daemon's full credential set: DB_PASSWORD / DB_USERNAME (direct Postgres access to all tenant plugin data), SERVER_KEY (the shared key protecting the daemon's management and dispatch routes), DIFY_INNER_API_KEY (the daemon's identity for Dify's internal API), Redis and cloud storage credentials, ADMIN_API_KEY, and anything else the operator places in the container env. Any installed plugin could read all of it from os.environ and exfiltrate it over the network, since plugins make arbitrary outbound calls by design. The slim CLI's local mode had the same pattern (os.Environ()), running marketplace-downloaded plugin code with the developer's full shell environment.

Why

The local runtime's design (separate process per plugin, dedicated working directory, stdio-only IPC, heartbeat watchdog) exists to run third-party marketplace code at reduced trust. Inheriting the daemon environment defeats the confidentiality half of that isolation, and plugins need none of these variables to function: daemon to plugin traffic is stdio-framed, and plugin configuration arrives per-invocation rather than through the environment. The repo already recognized this pattern for the short-lived uv installer child process (buildUVCommandEnv, with a test asserting UNRELATED_SECRET is not inherited); the long-lived plugin process was missed.

How

Replace inheritance with an explicit allowlist builder, BuildPluginCommandEnv, mirroring buildUVCommandEnv. It passes through what plugins legitimately need: PATH, HOME, locale variables (LANG, LC_ALL, LC_CTYPE), temp directories (TMPDIR, TEMP, TMP), TZ, custom CA bundles (SSL_CERT_FILE, REQUESTS_CA_BUNDLE), and proxy variables in both cases, with the daemon config's proxy settings (HTTP_PROXY / HTTPS_PROXY / NO_PROXY, themselves loaded from env or config) taking precedence. INSTALL_METHOD=local is set exactly as before. Everything else, including all *_KEY / *_SECRET / *_PASSWORD / *_TOKEN style credentials, never reaches plugin code. The slim CLI local mode now uses the same builder.

How verified

  • New internal/core/local_runtime/subprocess_test.go: TestBuildPluginCommandEnv asserts allowlisted variables pass through, config proxy settings win over inherited ones, and DB_PASSWORD / SERVER_KEY / DIFY_INNER_API_KEY / AWS_* / REDIS_PASSWORD / ADMIN_API_KEY are absent. TestGetInstanceCmdDoesNotInheritDaemonEnv builds the real plugin command against a fake venv and asserts the same on cmd.Env, proving the spawn site is wired to the allowlist.
  • go build passes for the touched packages, and go test ./internal/core/local_runtime/... ./pkg/slim/... passes in full. (A whole-repo go build ./... additionally requires the gitignored pkg/license/private_key/PRIVATE_KEY.pem, unrelated to this change.)

Same disclosure class as our merged #796: a one-line omission visible in the public source, fixed directly via public PR.

Made with Cursor

…rocesses

getInstanceCmd built plugin processes with cmd.Environ(), copying the
daemon's full environment (DB_PASSWORD, SERVER_KEY, DIFY_INNER_API_KEY,
Redis and cloud storage credentials) into every plugin subprocess, where
any installed plugin could read and exfiltrate it over the network.

Replace inheritance with an explicit allowlist builder,
BuildPluginCommandEnv, mirroring the existing buildUVCommandEnv pattern
used for the uv installer child process. The allowlist passes through
what plugins legitimately need (PATH, HOME, locale variables, temp
directories, TZ, CA bundle and proxy variables), daemon config proxy
settings take precedence over inherited ones, and INSTALL_METHOD=local
is set as before. The slim CLI local mode used the same os.Environ()
pattern for marketplace-downloaded plugins and now shares the builder.
@dosubot dosubot Bot added size:M This PR changes 30-99 lines, ignoring generated files. bug Something isn't working go Pull requests that update go code labels Aug 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working go Pull requests that update go code size:M This PR changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant