Skip to content

fix(server): disable code-executing plugins by default, add opt-in flag - #337

Open
akushonkamen wants to merge 2 commits into
algorithmicsuperintelligence:mainfrom
akushonkamen:fix/302-executecode-opt-in
Open

akushonkamen wants to merge 2 commits into
algorithmicsuperintelligence:mainfrom
akushonkamen:fix/302-executecode-opt-in

Conversation

@akushonkamen

Copy link
Copy Markdown
Contributor

Fixes #302

Root cause

load_plugins() in optillm/server.py registers every module that exposes a SLUG and a run() unconditionally, so the executecode plugin is active in all deployments by default. That plugin executes Python blocks taken straight from user requests (and LLM responses) in a live Jupyter ExecutePreprocessor kernel with no sandboxing: with optillm_api_key unset (the default), any unauthenticated request reaching executecode-<model> gets arbitrary code execution on the host; with a key, any authorized client has the same power (env vars, ~/.ssh, cloud IMDS, lateral movement).

This applies the same "secure by default + explicit opt-out" pattern just merged in #334 for --readurls-allow-internal to the one plugin that unconditionally executes caller-supplied code. The change is exactly the short-term remediation proposed in #302 by the reporter: disable the executecode plugin by default; require explicit opt-in, documented as intended only for air-gapped single-user environments.

Change (3 files, +142/-1)

  • optillm/server.py
    • New module constant UNSAFE_DEFAULT_DISABLED_PLUGINS = {"executecode"} (a set, so future code-executing plugins can be added).
    • load_plugins() skips such plugins unless explicitly enabled, with a warning naming the exact flag:
      Plugin 'executecode' is disabled by default because it executes arbitrary code. Start optillm with --enable-unsafe-plugins=executecode (env: OPTILLM_ENABLE_UNSAFE_PLUGINS) to allow it.
    • New --enable-unsafe-plugins / OPTILLM_ENABLE_UNSAFE_PLUGINS / server_config['enable_unsafe_plugins'] option (comma-separated slugs) — the same three-part CLI/env/server_config template as --readurls-allow-internal.
    • main() reloads plugins after parse_args() populates server_config, so the opt-in (and --plugins-dir) applies to the loaded set; the existing pre-parse load stays because --approach choices are built from the registry. With the env var set, even that first load includes the plugin, so --approach executecode... keeps validating. Disabled plugins also disappear from request dispatch (executecode-<model> slugs) since that reads the same registry.
  • tests/test_plugin_gating.py (new, fully offline): default-off, opt-in via server_config / env / comma list, warning text, plus pure-function tests for extract_python_code() and should_execute_request_code().
  • README.md: executecode row marked disabled by default, plus a "Code Execution Security" section mirroring the readurls one, carrying the issue's warning that this is only for single-user, air-gapped environments.

Default behavior change (intentional)

Deployments relying on executecode must start optillm with --enable-unsafe-plugins=executecode (or OPTILLM_ENABLE_UNSAFE_PLUGINS=executecode). This break is the point of the fix; #334 set the precedent that optillm blocks dangerous defaults by default.

Red → green evidence

  • Before (test file applied, optillm/server.py fix stashed):
    • pytest tests/test_plugin_gating.py → ImportError: cannot import name 'UNSAFE_DEFAULT_DISABLED_PLUGINS' from 'optillm.server'
    • probe: load_plugins(); 'executecode' in plugin_approaches → True (vulnerable default)
  • After: pytest tests/test_plugin_gating.py -v → 11 passed; CI subset pytest tests/test_plugins.py tests/test_proxy_plugin.py tests/test_approaches.py → 37 passed; OPTILLM_API_KEY=optillm python tests/test_ci_quick.py → all checks pass; compileall clean.

Scope / follow-ups

  • No sandboxing in this change (nsjail/gVisor-style isolation is out of scope, per the issue's long-term suggestion); this only removes the unsafe default.
  • Other code-execution surfaces (e.g. coc executing LLM-generated code) are untouched; they can be added to UNSAFE_DEFAULT_DISABLED_PLUGINS in a follow-up.
  • Pure stdlib; no new dependencies.

@CrepuscularIRIS this implements the short-term mitigation you proposed in #302. @codelion marking as security hardening — happy to adjust the flag name/set membership.

AI disclosure: implementation and tests were prepared with AI assistance, reviewed and submitted by @akushonkamen.

akushonkamen and others added 2 commits October 10, 2026 16:51
load_plugins() registered every module with a SLUG and a run() unconditionally,
so the executecode plugin - which runs Python from user requests and LLM
responses in a live Jupyter kernel with no sandboxing - was active in all
deployments by default. With no optillm_api_key set, any unauthenticated
request could reach full code execution on the host (issue algorithmicsuperintelligence#302).

- Add UNSAFE_DEFAULT_DISABLED_PLUGINS and skip such plugins during
  load_plugins() with a warning that names the exact flag to re-enable
- Opt back in via --enable-unsafe-plugins (CLI),
  OPTILLM_ENABLE_UNSAFE_PLUGINS (env) or server_config; comma-separated
  slugs, same three-part pattern as --readurls-allow-internal
- Reload plugins after parse_args() in main() so the opt-in (and
  --plugins-dir) apply to the loaded set; the pre-parse load is kept for
  --approach choices. With the env var set even the first load includes
  the plugin, so --approach keeps working
- README: mark the executecode row as disabled by default and add a
  code-execution security section mirroring the readurls one
- No sandboxing in this change (nsjail/gVisor-style isolation is out of
  scope); pure stdlib, no new dependencies

Note: this is an intentional default behavior change - deployments that
rely on executecode must now set the flag or env var.

Fixes algorithmicsuperintelligence#302

This branch has not been deployed

No deployments
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: Unauthenticated RCE via executecode plugin — unsandboxed Python execution

2 participants