Skip to content

Fix:react shell runtime policy - #3169

Open
Carbene wants to merge 4 commits into
eosphoros-ai:mainfrom
Carbene:fix/react-shell-runtime-policy
Open

Fix:react shell runtime policy#3169
Carbene wants to merge 4 commits into
eosphoros-ai:mainfrom
Carbene:fix/react-shell-runtime-policy

Conversation

@Carbene

@Carbene Carbene commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Description

This PR provides a partial fix for #3167.

In that issue, I demonstrated a proof of concept showing that the current ReAct shell execution path could lead to remote command execution. Fully addressing the issue requires broader changes to authentication, authorization, tool exposure, and user confirmation, which are outside the scope of this PR.

This PR addresses one specific part of the problem: ensuring that the shell interpreter respects the configured runtime policy and the corresponding environment variable.

Previously, shell_interpreter instantiated LocalRuntime directly, bypassing the runtime factory and its safeguards. With this change, shell_interpreter uses the runtime factory to create the execution session. This restores the intended runtime-selection behavior and prevents the shell tool from unconditionally selecting the local runtime.

Fixes part of #3167.

How Has This Been Tested?

New pytest test cases have been added to verify that:

  • shell_interpreter creates sessions through the runtime factory;
  • the configured runtime policy is respected;
  • local runtime execution is not selected when it has not been explicitly
    enabled; and
  • the expected runtime is used when the relevant configuration permits it.

Screenshots

image

Checklist

  • My code follows the style guidelines of this project.
  • I have rebased my branch and ensured that the commit messages follow the
    project's conventions.
  • I have performed a self-review of my changes.
  • I have added comments where the implementation may be difficult to
    understand.
  • I have made the corresponding documentation changes.
  • Any required dependent changes have been merged and published in
    downstream modules.

Possible Future Improvements

This PR does not fully address the broader security issue described in #3167.
Possible follow-up changes include:

  1. Introduce a per-user permission, such as shell_enabled, to control which users are authorized to invoke the shell interpreter.

  2. Introduce an operator-controlled configuration option, such as ENABLE_SHELL_INTERPRETER, so that shell execution is disabled by default unless the deployment administrator explicitly enables it.

  3. Use ConfirmDialog or an equivalent confirmation mechanism to display the command and its potential effects before execution, and require explicit user approval

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

1. Purpose and implementation

Updates ReAct shell execution to honor the configured runtime policy instead of directly selecting LocalRuntime. Shell commands are now security-validated before execution, run through RuntimeFactory.create(), normalized across runtime result formats, and cleaned up safely. Timeout exit code 124 is mapped to TIMEOUT.

Bash sandbox support was also added, including .sh files, sh <filename> execution, and a Docker image mapping.

2. Affected packages and configuration

  • dbgpt-app: ReAct shell validation, runtime selection, status normalization, and task-plan progression logic.
  • dbgpt-sandbox: Bash language configuration, command selection, and Docker file extensions.
  • No exported public API declarations were changed.
  • Runtime behavior now depends on the configured runtime policy and environment settings.

3. Risks

  • Security validation improves protection against dangerous shell operations, but its coverage and bypass resistance should be reviewed for complex shell syntax.
  • Runtime-factory integration may expose differences between local and configured runtimes, including cleanup, timeout, and result-shape behavior.
  • Bash now executes through sh; scripts requiring Bash-specific syntax may not be compatible.
  • The task-plan heuristic changes may alter advancement decisions; the reported non-boolean final JSON path warrants targeted review.
  • No significant performance impact is expected beyond validation and runtime-factory/session-management overhead.

4. Tests and verification

Added tests cover status normalization, safe and dangerous shell validation, runtime-factory usage, Bash command selection, image mapping, and .sh file generation.

Recommended targeted verification:

PYTHONPATH=packages/dbgpt-app/src:packages/dbgpt-sandbox/src:packages/dbgpt-serve/src:packages/dbgpt-core/src \
python -m pytest \
  packages/dbgpt-app/src/dbgpt_app/tests/test_react_shell_runtime.py \
  packages/dbgpt-sandbox/tests/test_bash_runtime_config.py

Additional tests should cover runtime-policy denial/allowance across configured runtime types, session cleanup on creation or execution failures, shell-validation bypass cases, Bash-specific script compatibility, and the revised task-plan advancement branches.

Walkthrough

Changes

Agentic execution and sandbox support

Layer / File(s) Summary
Bash runtime support
packages/dbgpt-sandbox/src/dbgpt_sandbox/sandbox/config.py, packages/dbgpt-sandbox/src/dbgpt_sandbox/sandbox/execution_layer/docker_runtime.py, packages/dbgpt-sandbox/tests/test_bash_runtime_config.py
Adds Bash image, POSIX shell command, .sh file generation, and related tests.
Shell validation and runtime integration
packages/dbgpt-app/src/dbgpt_app/openapi/api_v1/agentic_data_api.py, packages/dbgpt-app/src/dbgpt_app/tests/test_react_shell_runtime.py
Validates Bash code, executes through RuntimeFactory, normalizes statuses, handles timeouts, and conditionally cleans up sessions.
Skill artifacts and HTML rendering
packages/dbgpt-app/src/dbgpt_app/openapi/api_v1/agentic_data_api.py
Adds uploaded-path injection, source and generated-image handling, data-marker extraction, and template, file, and inline HTML rendering paths.
Todo progression and state updates
packages/dbgpt-app/src/dbgpt_app/openapi/api_v1/agentic_data_api.py
Updates advancement heuristics and makes todowrite return parsed todo state and completion counts.

Estimated code review effort: 4 (Complex) | ~60 minutes

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning The title is related to the change, but it does not follow the required Conventional Commit format. Use a lowercase Conventional Commit title such as fix(react): shell runtime policy with an ASCII colon and concise description.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed The description covers the change summary, issue reference, testing, screenshots, and checklist, matching the template well enough.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Fix failing CI checks
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (1)
packages/dbgpt-core/src/dbgpt/core/interface/prompt.py (1)

30-33: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Make the ImportError chaining explicit.

Ruff B904 flags raising a new exception inside except. Use from None for the intended user-facing message, or from err if the original cause should be preserved.

Source: Linters/SAST tools


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d93d75a3-0872-4b8b-a975-8b06c1132d40

📥 Commits

Reviewing files that changed from the base of the PR and between 36a99b7 and 8768b90.

📒 Files selected for processing (8)
  • packages/dbgpt-app/src/dbgpt_app/openapi/api_v1/agentic_data_api.py
  • packages/dbgpt-app/src/dbgpt_app/tests/test_react_shell_runtime.py
  • packages/dbgpt-core/src/dbgpt/agent/core/base_agent.py
  • packages/dbgpt-core/src/dbgpt/agent/core/role.py
  • packages/dbgpt-core/src/dbgpt/core/interface/prompt.py
  • packages/dbgpt-sandbox/src/dbgpt_sandbox/sandbox/config.py
  • packages/dbgpt-sandbox/src/dbgpt_sandbox/sandbox/execution_layer/docker_runtime.py
  • packages/dbgpt-sandbox/tests/test_bash_runtime_config.py
📜 Review details
🧰 Additional context used
📓 Path-based instructions (5)
**/*.py

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Use Python 3.10 or newer for project development.

Files:

  • packages/dbgpt-sandbox/src/dbgpt_sandbox/sandbox/execution_layer/docker_runtime.py
  • packages/dbgpt-sandbox/src/dbgpt_sandbox/sandbox/config.py
  • packages/dbgpt-sandbox/tests/test_bash_runtime_config.py
  • packages/dbgpt-app/src/dbgpt_app/tests/test_react_shell_runtime.py
  • packages/dbgpt-core/src/dbgpt/agent/core/base_agent.py
  • packages/dbgpt-core/src/dbgpt/agent/core/role.py
  • packages/dbgpt-app/src/dbgpt_app/openapi/api_v1/agentic_data_api.py
  • packages/dbgpt-core/src/dbgpt/core/interface/prompt.py
packages/dbgpt-sandbox/**/*.py

⚙️ CodeRabbit configuration file

packages/dbgpt-sandbox/**/*.py: 将此处视为最高安全等级区域。

  • LocalRuntime 必须保持显式 opt-in。container runtime 不可用时必须 fail closed,
    不得回退到宿主机执行。
  • 将 session 和 task 绑定到已认证用户,防止用户查看、执行或销毁其他用户的 session。
  • 校验 runtime、language、image、dependency、environment、working_dir、
    filename 和 session_id。防止 command injection、path traversal、
    恶意 package argument 和 container name injection。
  • 限制 CPU、memory、process、execution time、output、file size 和 disk usage。
    默认禁止外部网络访问、Docker socket、privileged mode 和过宽的 host mount。
  • 审查 timeout 处理、process tree termination、取消、错误、container 与临时文件清理,
    以及 tar member 的 path、type 和 size。
  • 隔离机制变更必须提供回归测试,覆盖 fail-closed 行为、path 和 command attack、
    timeout、resource limit 以及清理。

Files:

  • packages/dbgpt-sandbox/src/dbgpt_sandbox/sandbox/execution_layer/docker_runtime.py
  • packages/dbgpt-sandbox/src/dbgpt_sandbox/sandbox/config.py
  • packages/dbgpt-sandbox/tests/test_bash_runtime_config.py
**/{tests/**/*.py,test_*.py,*_test.py}

⚙️ CodeRabbit configuration file

**/{tests/**/*.py,test_*.py,*_test.py}: 检查 Python 测试、fixture 和测试辅助代码是否提供了有意义的回归覆盖。

  • 应断言可观察行为和对外相关契约,而不是只断言 mock 调用次数或实现细节。将 mock
    保持在真实 I/O 或进程边界,使被测行为本身仍会执行。
  • 对于 bug 修复,聚焦的回归测试应在旧行为上失败。对于安全修复,在能够安全测试时,
    应包含一个具体的绕过方式或恶意输入场景。
  • 当这些场景与变更的生产路径相关时,覆盖错误、空输入、边界、清理,以及异步取消
    或 timeout 行为;不要要求与变更无关的穷尽式覆盖。
  • 保持单元测试确定且隔离,不依赖真实网络、外部模型、数据库、GPU、Docker、
    wall-clock time 或开发者机器状态。确实需要这些资源的测试应位于 integration test
    中或明确标记为 integration test,说明其前置条件,并清理创建的资源。
  • 如果可以使用 event、mock clock、同步原语或有界不变量验证行为,应避免使用 sleep
    和脆弱的性能阈值。
  • 对于聚焦验证,建议运行 uv run pytest 并指定相关测试路径。不要假设 make test 会运行
    dbgpt_app、dbgpt_serve、dbgpt_ext、dbgpt_client 或 dbgpt_sandbox 的测试;其当前
    target 运行的是 dbgpt package 测试。不要将 make fmt-check 描述为只读命令,因为
    当前 target 会调用 ruff check --fix。

Files:

  • packages/dbgpt-sandbox/tests/test_bash_runtime_config.py
  • packages/dbgpt-app/src/dbgpt_app/tests/test_react_shell_runtime.py
packages/dbgpt-app/src/dbgpt_app/**/*.py

⚙️ CodeRabbit configuration file

packages/dbgpt-app/src/dbgpt_app/**/*.py: - dbgpt-app 是组合和启动层。共享 interface、storage abstraction 和
connector implementation 应位于更底层的 package 中。

  • 审查 SystemApp 注册、初始化与关闭顺序、全局状态、配置默认值以及启动失败后的清理。
  • OpenAPI endpoint、文件上传、GitHub import、skill extraction 和 artifact
    download 必须防止绝对路径、父目录遍历、symlink escape、Zip Slip 和 Tar Slip。
    必须限制文件数量、单文件大小和总大小。
  • 不可信的 Python、shell 和任意代码执行必须使用受限 sandbox。不得回退到宿主机
    exec、eval、shell=True 或不受限的文件系统访问。SQL 执行应使用 datasource
    connector 层,并强制实施参数化、最小权限、适用情况下的只读访问、行数限制和 timeout。
  • SSE 和 asynchronous generator 必须处理断开连接、取消、timeout、错误、
    terminal event、task 清理和用户数据隔离。
  • 保持 conversation、message、streaming event 和前端消费协议的兼容性,并为
    用户可见行为提供回归测试。

Files:

  • packages/dbgpt-app/src/dbgpt_app/tests/test_react_shell_runtime.py
  • packages/dbgpt-app/src/dbgpt_app/openapi/api_v1/agentic_data_api.py
packages/dbgpt-core/src/dbgpt/**/*.py

⚙️ CodeRabbit configuration file

packages/dbgpt-core/src/dbgpt/**/*.py: 这是以 dbgpt 名义发布的核心库。

  • 严格审查公共 API、构造函数参数、返回类型、Pydantic 字段、序列化格式和异常语义的向后兼容性。
  • 不得引入或扩大 core 对 dbgpt_ext、dbgpt_serve、dbgpt_app、dbgpt_client 或
    dbgpt_sandbox 的反向依赖。只报告当前 diff 新增或扩大的依赖违规问题。
  • 对于 AWEL 变更,应同时审查同步、异步和流式执行路径。检查 DAG 关系、
    ContextVar 传播、背压、顺序、结束信号、异常传播和取消处理。
  • 不得在 async 函数中直接执行阻塞式数据库、文件、网络或模型操作。检查连接、
    task、thread、generator 和临时资源的清理。
  • 公共行为变更必须提供邻近的回归测试,并覆盖相关的正常、错误、空输入、并发、
    取消或流式场景。

Files:

  • packages/dbgpt-core/src/dbgpt/agent/core/base_agent.py
  • packages/dbgpt-core/src/dbgpt/agent/core/role.py
  • packages/dbgpt-core/src/dbgpt/core/interface/prompt.py
🪛 Ruff (0.16.0)
packages/dbgpt-app/src/dbgpt_app/openapi/api_v1/agentic_data_api.py

[error] 2275-2276: try-except-pass detected, consider logging the exception

(S110)


[warning] 2275-2275: Do not catch blind exception: Exception

(BLE001)

packages/dbgpt-core/src/dbgpt/core/interface/prompt.py

[warning] 30-33: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

🔇 Additional comments (2)
packages/dbgpt-core/src/dbgpt/agent/core/base_agent.py (1)

13-13: LGTM!

packages/dbgpt-core/src/dbgpt/agent/core/role.py (1)

11-11: LGTM!

Comment on lines +992 to +995
if isinstance(status, ExecutionStatus):
return status
if getattr(result, "exit_code", None) == 124:
return ExecutionStatus.TIMEOUT

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Prioritize exit code 124 before enum status normalization.

An enum-shaped result with status=ExecutionStatus.ERROR and exit_code=124 returns ERROR at Line 993, so the timeout-specific response path is skipped.

  • packages/dbgpt-app/src/dbgpt_app/openapi/api_v1/agentic_data_api.py#L992-L995: check exit_code == 124 before returning an existing enum status.
  • packages/dbgpt-app/src/dbgpt_app/tests/test_react_shell_runtime.py#L14-L40: add ExecutionStatus.ERROR plus exit code 124 and expect ExecutionStatus.TIMEOUT.
📍 Affects 2 files
  • packages/dbgpt-app/src/dbgpt_app/openapi/api_v1/agentic_data_api.py#L992-L995 (this comment)
  • packages/dbgpt-app/src/dbgpt_app/tests/test_react_shell_runtime.py#L14-L40

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is because the difference between local runtime and docker runtime. The docker (also other sandboxed runtime) returns with an object instead of the enum. To ensure the minimal fix, I pass the result directly if it runs in the local runtime, which is the first branch of this.

Comment on lines +54 to +77
def test_shell_interpreter_uses_runtime_factory_and_entry_validation():
source_path = (
Path(__file__).resolve().parents[1]
/ "openapi"
/ "api_v1"
/ "agentic_data_api.py"
)
tree = ast.parse(source_path.read_text(encoding="utf-8"))
shell_interpreter = next(
node
for node in ast.walk(tree)
if isinstance(node, ast.AsyncFunctionDef)
and node.name == "shell_interpreter"
)

call_names = {
ast.unparse(node.func)
for node in ast.walk(shell_interpreter)
if isinstance(node, ast.Call)
}

assert "RuntimeFactory.create" in call_names
assert "LocalRuntime" not in call_names
assert "_shell_validation_error" in call_names

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

Test runtime policy through observable execution behavior.

This AST-only check passes even if validation occurs after session creation or runtime-policy selection is bypassed at runtime. Add a focused test that invokes the tool with mocked factory/runtime/session objects, asserting unsafe shell input creates no runtime/session and an allowed command uses the factory-selected runtime.

As per path instructions, “应断言可观察行为和对外相关契约” and “对于安全修复,在能够安全测试时,应包含一个具体的绕过方式或恶意输入场景。”

Source: Path instructions

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the real-world deployment, the runtime policy is defaultly set as local. So the fallback is right. No need to test it.

Comment on lines +1261 to +1264
_env = SandboxedEnvironment()
system_prompt = _env.from_string(self.bind_prompt.template).render(
prompt_param
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== locate files =="
git ls-files | rg 'packages/dbgpt-core/src/dbgpt/(agent/core/base_agent.py|agent/core/role.py|core/interface/prompt.py)$|requirements|pyproject|poetry.lock'

echo
echo "== relevant snippets =="
for f in packages/dbgpt-core/src/dbgpt/agent/core/base_agent.py packages/dbgpt-core/src/dbgpt/agent/core/role.py packages/dbgpt-core/src/dbgpt/core/interface/prompt.py; do
  echo "--- $f"
  sed -n '1,180p' "$f"
done

echo
echo "== imports/usages of template_env/SandboxedEnvironment/render in agent core =="
rg -n "SandboxedEnvironment|ImmutableSandboxedEnvironment|from_string|render\(|template_env|bind_prompt|class .*Prompt|class .*Role" packages/dbgpt-core/src/dbgpt/agent -S
rg -n "SandboxedEnvironment|ImmutableSandboxedEnvironment|from_string|render\(|template_env" packages/dbgpt-core/src/dbgpt/core/interface/prompt.py packages/dbgpt-core/src/dbgpt/core -S

Repository: eosphoros-ai/DB-GPT

Length of output: 25738


🌐 Web query:

Jinja2 sandboxed environment unsafe callables mutable collections ImmutableSandboxedEnvironment documentation

💡 Result:

The Jinja2 sandbox is designed to render untrusted templates by intercepting and restricting access to attributes, methods, and operations that could have security side effects [1][2]. SandboxedEnvironment The core class for sandboxing is jinja2.sandbox.SandboxedEnvironment [1][2]. It allows you to define what is "safe" through several mechanisms: Unsafe Callables: By default, the sandbox considers callables safe unless they are decorated with @jinja2.sandbox.unsafe or have certain attributes set (e.g., func.alters_data = True or func.unsafe_callable = True) [1][3][4]. You can also override the is_safe_callable(obj) method to implement custom logic [1][2]. Attribute Access: The environment checks if attribute access is safe via is_safe_attribute(obj, attr, value) [3][4]. By default, it blocks private and internal attributes (those starting with an underscore) [1][3]. You can override this method to further restrict access [1][2]. ImmutableSandboxedEnvironment The jinja2.sandbox.ImmutableSandboxedEnvironment is a subclass of SandboxedEnvironment that specifically prevents the modification of built-in mutable objects such as lists, dictionaries, sets, and deques [1][3]. It achieves this by overriding is_safe_attribute to call modifies_known_mutable(obj, attr) [3][4]. This helper function identifies if an attribute on a known mutable type (like list.append or dict.clear) would perform a modification, and blocks access to it if so [1][3]. Security Recommendations The official documentation emphasizes that the sandbox is not a replacement for running code in a separate, secure process [1]. Recommendations include: - Pass only essential data to the template rather than broad, global data [1][2]. - Avoid passing objects that have methods with side effects [1][2]. - Upgrade Jinja2 to the latest version to benefit from ongoing security patches, such as those that handle string formatting vulnerabilities [5].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== agent profile base renderer =="
sed -n '200,360p' packages/dbgpt-core/src/dbgpt/agent/core/profile/base.py

echo
echo "== role rest of template usage =="
sed -n '180,270p' packages/dbgpt-core/src/dbgpt/agent/core/role.py

echo
echo "== core prompt render callsites =="
rg -n "_jinja2_formatter|template_format == \"jinja2\"|prompt_template_format" packages/dbgpt-core/src/dbgpt packages/dbgpt-app/src packages/dbgpt-serve/src packages/dbgpt-client/src -S || true

echo
echo "== pyproject jinja2 version pin =="
rg -n 'jinja2|SandboxedEnvironment' pyproject.toml packages/dbgpt-core/pyproject.toml requirements -S || true

Repository: eosphoros-ai/DB-GPT

Length of output: 10141


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Behavioral source probe: demonstrate Jinja2 sandbox default callable acceptability
# without executing repository code or requiring jinja2/runtime artifacts in the repo.
python3 - <<'PY'
def print_fn(value):
    pass

class SafeCallable:
    def __init__(self, value):
        self.value = value

class UnsafeCallable:
    func = type("f", (), {"alters_data": True})
    def __call__(self, value):
        raise RuntimeError("unsafe")

print("default callable accept examples:")
for obj in [print_fn, SafeCallable(1), UnsafeCallable]:
    safe = not getattr(obj, "unsafe_callable", False) and (
        getattr(getattr(obj, "func", None), "alters_data", False) is not True
        and getattr(getattr(obj, "func", None), "unsafe_callable", False) is not True
    )
    # This mirrors Jinja2.sandbox.is_safe_callable: callables are unsafe only
    # when decorated `@unsafe` or func.alters_data/func.unsafe_callable is true.
    print(type(obj).__name__, safe)
PY

Repository: eosphoros-ai/DB-GPT

Length of output: 234


Use one shared deny-by-default Jinja sandbox across prompt renderers.

Default SandboxedEnvironment allows ordinary callables and does not block mutations to known mutable values such as lists/dicts; Jinja recommends ImmutableSandboxedEnvironment and explicit unsafe-calling configuration for templates backed by user-controlled or arbitrary prompt data. Apply one hardening policy/shared restricted env across base_agent.py, role.py, and _jinja2_formatter; profile.py also defaults to SandboxedEnvironment() and should align.

📍 Affects 3 files
  • packages/dbgpt-core/src/dbgpt/agent/core/base_agent.py#L1261-L1264 (this comment)
  • packages/dbgpt-core/src/dbgpt/agent/core/role.py#L121-L123
  • packages/dbgpt-core/src/dbgpt/core/interface/prompt.py#L35-L37

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The system_prompt is hardcoded. There's no necessity to worry about this.

Comment on lines +26 to +37
"""Format a template using jinja2 in a secure sandbox."""
try:
from jinja2 import Template
from jinja2.sandbox import SandboxedEnvironment
except ImportError:
raise ImportError(
"jinja2 not installed, which is needed to use the jinja2_formatter. "
"Please install it with `pip install jinja2`."
)

return Template(template).render(**kwargs)
env = SandboxedEnvironment()

return env.from_string(template).render(**kwargs)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== files =="
git ls-files | rg 'prompt\.py$|jinja|sandbox|tests?/.*/prompt|prompt.*test' | sed -n '1,120p'

echo "== prompt.py excerpt =="
cat -n packages/dbgpt-core/src/dbgpt/core/interface/prompt.py | sed -n '1,80p'

echo "== references to jinja2_formatter =="
rg -n "jinja2_formatter|jinja2|SandboxedEnvironment" packages/dbgpt-core/src packages/dbgpt-core/test packages/tests 2>/dev/null | sed -n '1,200p'

Repository: eosphoros-ai/DB-GPT

Length of output: 9769


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== all jinja2 references in core interface and tests =="
rg -n "jinja2|template_format=.+jinja2|_jinja2_formatter|format_jinja2|SandboxedEnvironment|assertRaises|forbidden_attribute|test_.*jinja|blocked|missing|empty|immutability|immutable" packages/dbgpt-core/src/dbgpt/core/interface packages/dbgpt-core -g '*.py' --glob '!**/__pycache__/**' | sed -n '1,260p'

echo "== prompt tests outline/size =="
wc -l packages/dbgpt-core/src/dbgpt/core/interface/tests/test_prompt.py
cat -n packages/dbgpt-core/src/dbgpt/core/interface/tests/test_prompt.py | sed -n '1,220p'

echo "== prompt public formatter usage/definition =="
cat -n packages/dbgpt-core/src/dbgpt/core/interface/prompt.py | sed -n '720,810p'

Repository: eosphoros-ai/DB-GPT

Length of output: 44117


Add regression coverage for the Jinja2 formatter contract.

The current test_format_jinja2 only covers ordinary parameter rendering. Add jinja2-specific regression tests for missing/empty values, blocked attributes, callable rejection, and input immutability; these should fail if rendered with an unsandboxed jinja2.Environment.

🧰 Tools
🪛 Ruff (0.16.0)

[warning] 30-33: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling

(B904)

Source: Path instructions

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is introduced in other pr rather than this.

"go": ".go",
"rust": ".rs",
"python-vnc": ".py",
"bash": ".sh",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Enforce the configured execution timeout for Bash sessions.

Bash now reaches DockerSandboxSession.execute(), where container.exec_run() has no deadline and blocks synchronously. A command such as while :; do :; done ignores the caller’s 30-second SessionConfig.timeout, can block the event loop, and leaves the process running. Use an exec-level timeout that terminates the process tree/container, not only asyncio.wait_for() around the caller.

As per path instructions, “限制 CPU、memory、process、execution time、output、file size 和 disk usage。”

Source: Path instructions

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with that. But this is somehow too heavy for a runtime policy fix.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (8)
packages/dbgpt-app/src/dbgpt_app/openapi/api_v1/agentic_data_api.py (8)

554-562: 🔒 Security & Privacy | 🔴 Critical | ⚡ Quick win

Reject path-bearing upload filenames before writing.

file.filename can be absolute or contain ../, so upload_dir / filename can write outside pilot/tmp; the same value is later reused for the installed file. Validate/reject separators and traversal before either write.

Proposed fix
 filename = file.filename
+if (
+    filename != Path(filename).name
+    or "\\" in filename
+    or "\x00" in filename
+):
+    return Result.failed(code="E4002", msg="Invalid filename")
+
 suffix = Path(filename).suffix.lower()

As per path instructions, OpenAPI file uploads must prevent absolute paths and parent-directory traversal.

Source: Path instructions


1861-1893: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Keep a single shared todo-list object.

make_todowrite() closes over this list, but Line 2814 later rebinds _todo_list. The stream loop then reads a different list, so todowrite updates are not reflected in automatic advancement, termination completion, or persisted plans. Remove the legacy reinitialization/implementation or create the tool after the final list is established.


3179-3189: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Cancel agent work when the SSE client disconnects.

The generator creates an independent agent_task, and this newly exposed dispatcher can start child agents. Cancellation/disconnect exits the generator without cancelling and awaiting the lead task, so sandbox/tool work can continue after the client is gone. Add a generator-level finally that cancels and awaits agent_task; ensure dispatch children receive that cancellation.

As per path instructions, SSE and asynchronous generators must handle disconnects, cancellation, and task cleanup.

Source: Path instructions


4222-4237: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Do not publish every tool result as a knowledge citation.

_cited_chunks is populated from shell, SQL, connector, skill, and other tool observations, then emitted as a “Knowledge Base” references payload. This can expose unrelated or sensitive tool output in the final response. Collect citations only for kb_*/knowledge retrieval actions and retain source metadata.

As per path instructions, streaming events must preserve user-data isolation.

Source: Path instructions


3333-3340: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Make knowledge mode a strict knowledge-tool allowlist.

Knowledge mode currently exposes business_tools and connector tools in both branches; the skill branch additionally omits kb_tool_list entirely. This breaks the knowledge-only contract and can expose non-knowledge capabilities.

  • packages/dbgpt-app/src/dbgpt_app/openapi/api_v1/agentic_data_api.py#L3333-L3340: include kb_tool_list and exclude business/connector tool extras.
  • packages/dbgpt-app/src/dbgpt_app/openapi/api_v1/agentic_data_api.py#L3529-L3539: exclude business/connector tool extras.

4639-4652: 🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

Authorize question replies against the owning user/conversation.

Both endpoints accept any authenticated caller and only key the operation by request_id; neither verifies that the caller owns the pending question’s conversation. Bind pending questions to the originating user and reject mismatched callers.

  • packages/dbgpt-app/src/dbgpt_app/openapi/api_v1/agentic_data_api.py#L4639-L4652: verify ownership before replying.
  • packages/dbgpt-app/src/dbgpt_app/openapi/api_v1/agentic_data_api.py#L4655-L4667: verify ownership before rejecting.

As per path instructions, SSE and agent flows must preserve user-data isolation.

Source: Path instructions


4108-4117: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Handle structured tool-chunk content.

Several tools return dict-valued content (for example JSON and table chunks). Calling .strip() unconditionally raises AttributeError and aborts the stream. Preserve the original content for history; only strip and cite string content.

Proposed fix
- content = (item.get("content") or "").strip()
+ content = item.get("content")
  current_history_step["outputs"].append(
      {
          "output_type": item.get("output_type", "text"),
          "content": content,
      }
  )
- clean = _strip_html_tags(content)
- if len(clean) >= 10:
-     _cited_chunks.append({"content": clean})
+ if isinstance(content, str):
+     clean = _strip_html_tags(content)
+     if len(clean) >= 10:
+         _cited_chunks.append({"content": clean})

4342-4347: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Encode apostrophes in the references attribute.

A retrieved chunk containing ' terminates references='...' early, causing the frontend’s JSON extraction to fail. Encode apostrophes as JSON Unicode escapes before embedding the payload.

- payload = json.dumps(payload_obj, ensure_ascii=False)
+ payload = json.dumps(payload_obj, ensure_ascii=False).replace("'", r"\u0027")

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: af7031f7-d85a-41f8-8cb1-c77699efa6eb

📥 Commits

Reviewing files that changed from the base of the PR and between 8768b90 and 63fa724.

📒 Files selected for processing (1)
  • packages/dbgpt-app/src/dbgpt_app/openapi/api_v1/agentic_data_api.py
📜 Review details
⚠️ CI failures not shown inline (2)

GitHub Actions: Python Code Quality Checks / build: Fix:react shell runtime policy

Conclusion: failure

View job details

##[group]Run make fmt-check
 �[36;1mmake fmt-check�[0m
 shell: /usr/bin/bash -e {0}
 ##[endgroup]
 .venv.make/bin/ruff format --check packages
 unformatted: File would be reformatted
   --> packages/dbgpt-app/src/dbgpt_app/tests/test_react_shell_runtime.py:1:1
    |
 64 |         for node in ast.walk(tree)
    -         if isinstance(node, ast.AsyncFunctionDef)
    -         and node.name == "shell_interpreter"
 65 +         if isinstance(node, ast.AsyncFunctionDef) and node.name == "shell_interpreter"
 66 |     )
    |
 1 file would be reformatted, 1332 files already formatted
 make: *** [Makefile:75: fmt-check] Error 1
 ##[error]Process completed with exit code 2.

GitHub Actions: Python Code Quality Checks / 0_build.txt: Fix:react shell runtime policy

Conclusion: failure

View job details

##[group]Run make fmt-check
 �[36;1mmake fmt-check�[0m
 shell: /usr/bin/bash -e {0}
 ##[endgroup]
 .venv.make/bin/ruff format --check packages
 unformatted: File would be reformatted
   --> packages/dbgpt-app/src/dbgpt_app/tests/test_react_shell_runtime.py:1:1
    |
 64 |         for node in ast.walk(tree)
    -         if isinstance(node, ast.AsyncFunctionDef)
    -         and node.name == "shell_interpreter"
 65 +         if isinstance(node, ast.AsyncFunctionDef) and node.name == "shell_interpreter"
 66 |     )
    |
 1 file would be reformatted, 1332 files already formatted
 make: *** [Makefile:75: fmt-check] Error 1
 ##[error]Process completed with exit code 2.
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Use Python 3.10 or newer for project development.

Files:

  • packages/dbgpt-app/src/dbgpt_app/openapi/api_v1/agentic_data_api.py
packages/dbgpt-app/src/dbgpt_app/**/*.py

⚙️ CodeRabbit configuration file

packages/dbgpt-app/src/dbgpt_app/**/*.py: - dbgpt-app 是组合和启动层。共享 interface、storage abstraction 和
connector implementation 应位于更底层的 package 中。

  • 审查 SystemApp 注册、初始化与关闭顺序、全局状态、配置默认值以及启动失败后的清理。
  • OpenAPI endpoint、文件上传、GitHub import、skill extraction 和 artifact
    download 必须防止绝对路径、父目录遍历、symlink escape、Zip Slip 和 Tar Slip。
    必须限制文件数量、单文件大小和总大小。
  • 不可信的 Python、shell 和任意代码执行必须使用受限 sandbox。不得回退到宿主机
    exec、eval、shell=True 或不受限的文件系统访问。SQL 执行应使用 datasource
    connector 层,并强制实施参数化、最小权限、适用情况下的只读访问、行数限制和 timeout。
  • SSE 和 asynchronous generator 必须处理断开连接、取消、timeout、错误、
    terminal event、task 清理和用户数据隔离。
  • 保持 conversation、message、streaming event 和前端消费协议的兼容性,并为
    用户可见行为提供回归测试。

Files:

  • packages/dbgpt-app/src/dbgpt_app/openapi/api_v1/agentic_data_api.py
🪛 Ruff (0.16.0)
packages/dbgpt-app/src/dbgpt_app/openapi/api_v1/agentic_data_api.py

[warning] 1070-1070: Do not catch blind exception: Exception

(BLE001)


[warning] 3107-3107: String contains ambiguous (FULLWIDTH LEFT PARENTHESIS). Did you mean ( (LEFT PARENTHESIS)?

(RUF001)


[warning] 3107-3107: String contains ambiguous (FULLWIDTH RIGHT PARENTHESIS). Did you mean ) (RIGHT PARENTHESIS)?

(RUF001)


[warning] 3108-3108: String contains ambiguous (FULLWIDTH COMMA). Did you mean , (COMMA)?

(RUF001)


[warning] 3108-3108: String contains ambiguous (FULLWIDTH COLON). Did you mean : (COLON)?

(RUF001)


[warning] 3114-3114: String contains ambiguous (FULLWIDTH COLON). Did you mean : (COLON)?

(RUF001)


[warning] 3116-3116: String contains ambiguous (FULLWIDTH COMMA). Did you mean , (COMMA)?

(RUF001)


[warning] 3117-3117: String contains ambiguous (FULLWIDTH COMMA). Did you mean , (COMMA)?

(RUF001)


[warning] 3173-3173: Do not catch blind exception: Exception

(BLE001)


[warning] 3337-3339: Consider iterable unpacking instead of concatenation

Replace with iterable unpacking

(RUF005)


[warning] 3343-3355: Consider iterable unpacking instead of concatenation

Replace with iterable unpacking

(RUF005)


[warning] 3535-3538: Consider iterable unpacking instead of concatenation

Replace with iterable unpacking

(RUF005)


[warning] 3542-3565: Consider iterable unpacking instead of concatenation

Replace with iterable unpacking

(RUF005)


[warning] 4593-4593: Do not perform function call Body in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)


[warning] 4594-4594: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)


[warning] 4643-4643: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)


[warning] 4658-4658: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable

(B008)

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.

1 participant