Skip to content

[security] fix(prompts,installer): require explicit prompt opt-in and disable plugin lifecycle scripts#1546

Open
Hinotoi-agent wants to merge 3 commits intovolcengine:mainfrom
Hinotoi-agent:fix/prompt-sandbox-and-safe-plugin-install
Open

[security] fix(prompts,installer): require explicit prompt opt-in and disable plugin lifecycle scripts#1546
Hinotoi-agent wants to merge 3 commits intovolcengine:mainfrom
Hinotoi-agent:fix/prompt-sandbox-and-safe-plugin-install

Conversation

@Hinotoi-agent
Copy link
Copy Markdown
Contributor

@Hinotoi-agent Hinotoi-agent commented Apr 17, 2026

Summary

This PR hardens two execution-capable trust boundaries in OpenViking.

  • disable config- and environment-driven custom prompt template loading by default behind prompts.enable_custom_templates
  • sandbox prompt rendering so enabled custom templates cannot execute unsafe Jinja attribute traversal during PromptManager.render()
  • disable npm lifecycle scripts in the OpenClaw plugin installer and local install helper so attacker-controlled plugin content cannot execute during dependency installation
  • add focused regression coverage for the prompt-template opt-in gate and sandboxing behavior, and update prompt customization docs to match the new secure default

Security issues covered

Issue Impact Severity
Config/env-driven custom prompt templates loaded by default Writable/imported template content could become executable Jinja when operators pointed prompts.templates_dir at writable storage-backed paths High
Unsandboxed custom prompt template rendering Enabled custom templates could execute unsafe Jinja expressions during PromptManager.render() High
Plugin installer lifecycle-script execution Attacker-controlled plugin content could execute during plugin install/update when npm install ran lifecycle hooks High

Before this PR

  • prompts.templates_dir and OPENVIKING_PROMPT_TEMPLATES_DIR were honored automatically once present.
  • PromptManager.render() used raw Jinja rendering, so custom prompt content was treated as executable template code.
  • OpenClaw installer/update flows ran npm install inside plugin directories without disabling lifecycle scripts.
  • There was no focused regression coverage proving that custom prompt templates are disabled by default.

After this PR

  • OpenViking uses bundled prompt templates by default.
  • Operators must explicitly set prompts.enable_custom_templates: true before config- or environment-based custom prompt directories are used.
  • Prompt rendering uses a sandboxed Jinja environment.
  • OpenClaw installer/update flows and the local plugin install helper disable npm lifecycle scripts during dependency installation.
  • Focused tests lock in both the prompt opt-in gate and the sandboxed rendering behavior.

Explicit operator choice

Secure default configuration:

{
  "prompts": {
    "enable_custom_templates": false
  }
}

Explicit opt-in configuration:

{
  "prompts": {
    "enable_custom_templates": true,
    "templates_dir": "/path/to/custom-prompts"
  }
}

Disabled state:

  • bundled templates are used
  • prompts.templates_dir is ignored
  • OPENVIKING_PROMPT_TEMPLATES_DIR is ignored

Enabled state:

  • trusted operators can load custom templates from a configured directory
  • environment variable override works again
  • rendering still stays inside a sandboxed Jinja environment

Why this matters

These issues cross a content-to-execution boundary.

In the prompt path, writable template content could move from configuration/storage into executable Jinja at render time. In the installer path, plugin package metadata could move from plugin content into code execution through npm lifecycle hooks. Both should require an explicit trust boundary instead of happening implicitly.

Attack flow

attacker-controlled imported prompt template
    -> operator points prompts.templates_dir at writable/imported storage
        -> PromptManager loads the imported YAML template
            -> vulnerable code executes attacker-controlled Jinja during render
attacker-controlled plugin package content
    -> installer copies plugin into staging/plugin directory
        -> npm install runs in that attacker-controlled directory
            -> lifecycle scripts execute during install/update

Affected code

Issue Files
Prompt custom-template opt-in gate + sandboxing openviking/prompts/manager.py, openviking_cli/utils/config/prompts_config.py, tests/test_prompt_manager.py, tests/misc/test_prompt_manager_security.py, docs/en/guides/10-prompt-guide.md, docs/zh/guides/10-prompt-guide.md
Plugin installer lifecycle-script execution examples/openclaw-plugin/setup-helper/install.js, examples/openclaw-plugin/install.sh, bot/scripts/install_local_openclaw_plugin.sh

Root cause

Issue 1: Config/env-driven custom prompt template loading by default

  • PromptManager trusted config and environment overrides for external template directories with no explicit opt-in.
  • The trust boundary failed because external template sources were treated as normal runtime configuration instead of privileged operator-controlled behavior.

Issue 2: Unsandboxed custom prompt template rendering

  • PromptManager rendered template content with raw Jinja rendering.
  • The trust boundary failed because custom prompt files were treated as safe executable templates rather than untrusted content.

Issue 3: Plugin installer lifecycle-script execution

  • Installer/update flows ran npm install directly inside attacker-controlled plugin directories.
  • The trust boundary failed because plugin package lifecycle hooks were treated as trusted during installation.

CVSS assessment

Issue CVSS v3.1 Vector
Config/env-driven custom prompt template loading by default 8.1 High CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
Unsandboxed custom prompt template rendering 8.1 High CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
Plugin installer lifecycle-script execution 7.8 High CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:H

Rationale:

  • The prompt issue becomes process-level code execution when operators enable custom template loading from writable/imported storage-backed paths; this PR now requires explicit opt-in and keeps rendering sandboxed even after opt-in.
  • The installer issue requires an operator to install/update attacker-controlled plugin content, but code executes immediately once that installation occurs.

Safe reproduction steps

1. Prompt custom-template execution

  1. Configure a deployment to use a writable/imported directory for prompts.templates_dir.
  2. Import a custom prompt template into that directory through the existing import path.
  3. Put a safe Jinja payload in the template body that only writes a harmless marker file.
  4. Trigger PromptManager.render() for that template.
  5. Observe marker-file creation on vulnerable code.
  6. After this PR, the same config-driven path is ignored unless prompts.enable_custom_templates: true, and unsafe Jinja attribute traversal is blocked even when opt-in is enabled.

2. OpenClaw plugin installer lifecycle execution

  1. Create a local OpenViking repo copy with a plugin package.json containing a safe postinstall payload that only writes a harmless marker file.
  2. Run the documented OpenClaw OpenViking installer against that local repo.
  3. Observe that vulnerable code runs npm install in the plugin staging/deploy directory.
  4. Observe marker-file creation during install and again during update.
  5. After this PR, the same install path uses npm install --ignore-scripts, so the marker file is not created.

Expected vulnerable behavior

  • External prompt template directories configured through settings or environment are honored implicitly.
  • Enabled custom templates can execute unsafe Jinja expressions during render.
  • Plugin package lifecycle hooks execute during installer dependency installation.

Changes in this PR

  • add prompts.enable_custom_templates with a secure default of false
  • make PromptManager ignore config- and environment-driven external template directories unless that setting is enabled
  • replace raw Jinja rendering in PromptManager with a sandboxed Jinja environment
  • add regression tests for the secure-default gate, env/config override behavior after opt-in, and blocked unsafe Jinja attribute traversal
  • document the new prompt customization opt-in flow in the English and Chinese prompt guides
  • add --ignore-scripts to the OpenClaw plugin installer and local install helper npm install flows

Files changed

Category Files What changed
Prompt config hardening openviking_cli/utils/config/prompts_config.py Add enable_custom_templates with a default-secure opt-in model
Prompt resolution hardening openviking/prompts/manager.py Gate config/env custom template loading behind the new setting and keep Jinja sandboxed
Prompt regression tests tests/test_prompt_manager.py, tests/misc/test_prompt_manager_security.py Verify disabled-by-default behavior, enabled override behavior, and blocked unsafe rendering
Prompt docs docs/en/guides/10-prompt-guide.md, docs/zh/guides/10-prompt-guide.md Document the secure default and explicit opt-in configuration
Plugin installer hardening examples/openclaw-plugin/setup-helper/install.js, examples/openclaw-plugin/install.sh, bot/scripts/install_local_openclaw_plugin.sh Disable npm lifecycle scripts during dependency installation

Maintainer impact

  • This patch is intentionally narrow and does not redesign prompt loading or plugin installation.
  • Bundled prompt behavior remains the default.
  • Trusted operators still have a supported way to use custom prompt templates, but it is now explicit and sandboxed.
  • Plugin dependency installation still works, but installer-time lifecycle hooks no longer execute.
  • No unrelated server/router/storage behavior is changed by this patch.

Suggested fix rationale

  • External prompt template loading is a privileged control-plane behavior and should require explicit operator intent.
  • Sandboxing prompt rendering is the narrowest durable fix for the validated PromptManager.render() execution path.
  • Disabling npm lifecycle scripts is the narrowest durable fix for installer-time execution from attacker-controlled plugin content.
  • The focused tests make the secure default, opt-in path, and sandbox behavior hard to regress.

Reference patterns from other software

  • GitHub Actions requires explicit maintainer approval before untrusted fork content can reach privileged workflow execution.
  • GitLab protects sensitive runners/variables behind explicit authorization boundaries.
  • This PR follows the same principle: untrusted content should not reach execution-capable paths without an explicit operator decision.

Type of change

  • Security fix
  • Tests
  • Documentation update
  • Refactor with no behavior change

Test plan

  • cd /tmp/OpenViking-pr && python3 -m pytest -o addopts='' tests/test_prompt_manager.py tests/misc/test_prompt_manager_security.py -q
  • cd /tmp/OpenViking-pr && ruff check openviking/prompts/manager.py openviking_cli/utils/config/prompts_config.py tests/test_prompt_manager.py tests/misc/test_prompt_manager_security.py
  • cd /tmp/OpenViking-pr && node --check examples/openclaw-plugin/setup-helper/install.js
  • cd /tmp/OpenViking-pr && bash -n examples/openclaw-plugin/install.sh
  • cd /tmp/OpenViking-pr && bash -n bot/scripts/install_local_openclaw_plugin.sh
  • safe local validation that patched PromptManager blocks unsafe Jinja attribute traversal without creating the marker file
  • safe local validation that patched installer logic no longer executes a malicious postinstall payload during install

Executed with:

  • cd /tmp/OpenViking-pr && python3 -m pytest -o addopts='' tests/test_prompt_manager.py tests/misc/test_prompt_manager_security.py -q
  • cd /tmp/OpenViking-pr && ruff check openviking/prompts/manager.py openviking_cli/utils/config/prompts_config.py tests/test_prompt_manager.py tests/misc/test_prompt_manager_security.py
  • cd /tmp/OpenViking-pr && node --check examples/openclaw-plugin/setup-helper/install.js
  • cd /tmp/OpenViking-pr && bash -n examples/openclaw-plugin/install.sh
  • cd /tmp/OpenViking-pr && bash -n bot/scripts/install_local_openclaw_plugin.sh
  • safe local PromptManager marker-file validation after patch
  • safe local installer marker-file validation after patch

Disclosure notes

  • This PR is intentionally bounded to the validated prompt-template and installer execution-capable surfaces above.
  • It does not claim a new unauthenticated remote HTTP RCE path.
  • Prompt-template exploitability depends on operator opt-in to custom template loading; the secure default in this PR removes the implicit config/env exposure.
  • The plugin-installer issue is installer/supply-chain scoped: exploitability depends on attacker-controlled plugin content reaching the installer.
  • No unrelated repo files were changed.

@github-actions
Copy link
Copy Markdown

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 2 🔵🔵⚪⚪⚪
🏅 Score: 92
🧪 PR contains tests
🔒 No security concerns identified
✅ No TODO sections
🔀 Multiple PR themes

Sub-PR theme: Sandbox custom prompt template rendering

Relevant files:

  • openviking/prompts/manager.py
  • tests/misc/test_prompt_manager_security.py

Sub-PR theme: Disable npm lifecycle scripts in plugin installers

Relevant files:

  • bot/scripts/install_local_openclaw_plugin.sh
  • examples/openclaw-plugin/install.sh
  • examples/openclaw-plugin/setup-helper/install.js

⚡ No major issues detected

@github-actions
Copy link
Copy Markdown

PR Code Suggestions ✨

No code suggestions found for the PR.

@Hinotoi-agent Hinotoi-agent changed the title [security] fix(prompts,installer): sandbox templates and disable plugin lifecycle scripts [security] fix(prompts,installer): require explicit prompt opt-in and disable plugin lifecycle scripts Apr 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

1 participant