Skip to content

refactor(policy): centralize sandbox enforcement and validate config#297

Merged
liujuanjuan1984 merged 8 commits intomainfrom
eval/issue-278
Mar 23, 2026
Merged

refactor(policy): centralize sandbox enforcement and validate config#297
liujuanjuan1984 merged 8 commits intomainfrom
eval/issue-278

Conversation

@liujuanjuan1984
Copy link
Collaborator

@liujuanjuan1984 liujuanjuan1984 commented Mar 23, 2026

关联

改动概览

本 PR 将沙箱相关判断收敛到统一策略对象,并把目录解析、shell 能力暴露、配置一致性校验接到同一套 policy 语义上;同时继续清理执行链路中没有语义增益的薄壳方法。

按模块说明

src/opencode_a2a/sandbox_policy.py

  • 新增统一 SandboxPolicy,集中解释 workspace 边界、sandbox mode、write access scope 与 declared writable roots。
  • 提供目录解析与配置一致性校验的统一入口。

src/opencode_a2a/execution/executor.py

  • 删除旧的 execution/policy.py 包装层,改为直接持有 SandboxPolicy。
  • 内联 session control 相关转发方法,执行层直接调用 SessionManager。
  • 删除剩余的目录解析薄壳,JSON-RPC hook 直接接到统一策略与 session manager。

src/opencode_a2a/server/application.py

  • JSON-RPC control hook 直接绑定到实际执行对象,不再经过 executor 包装。
  • A2AClientManager 内联 _entry_expired / _entry_in_use 这类单行状态 helper,减少额外跳转。

src/opencode_a2a/profile/runtime.py

  • session shell 暴露改为同时受功能开关与 sandbox policy 控制。

src/opencode_a2a/config.py

  • 使用 BeforeValidator(_parse_declared_list) 直接解析 declared list,移除额外 validator 薄壳。
  • 在 settings 校验阶段直接执行 sandbox policy consistency validation。
  • 保留 Settings.from_env() 作为应用入口与类型适配点;测试侧普通场景直接使用 Settings()

src/opencode_a2a/contracts/extensions.py

  • 删除 DeploymentConditionalMethod.availability 这类仅包一层布尔表达式的 property,直接在 retention 文档中写入值。

测试

  • 更新 settings、runtime profile、agent card、JSON-RPC transport contract、session ownership 等测试以匹配新的策略接线。
  • settings 基础用例直接使用 Settings(),保留仅对应用入口需要的 from_env() 依赖。

相关提交

  • 4572436 refactor(policy): centralize sandbox decisions for directory and shell (#278)
  • 9fd7531 fix(config): validate sandbox policy consistency (#278)
  • 0f47531 refactor(policy): remove wrapper helpers from sandbox policy (#278)
  • 26385b9 refactor(execution): remove policy wrapper layer (#278)
  • df356dd refactor(execution): inline sandbox and session control hooks (#278)
  • 4b94d81 refactor(execution): remove remaining resolve_directory wrapper (#278)
  • 5e3026f refactor(config): remove validator and accessor wrappers (#278)
  • e26b4b1 refactor(policy): inline remaining thin wrappers in sandbox paths (#278)

验证

  • uv run pre-commit run --all-files
  • uv run pytest
  • 345 passed
  • coverage: 91.01%

@liujuanjuan1984
Copy link
Collaborator Author

本轮对 PR 代码变动的审查结论如下。

结论

  • 未发现阻塞问题。
  • 本次改动整体合理,且比 issue 原始方案更符合当前主干架构边界:不是伪造一个无法真正拦截上游 syscall 的 FileSystemGuard,而是把当前仓库实际能负责的部分收敛为统一策略与入口级 enforcement。

代码变动评估

1. 策略中心化方向是正确的

  • sandbox_policy.py 统一收敛了目录解析、shell 可用性、配置一致性校验。
  • execution/policy.py 改为复用统一策略,避免目录规则在执行层与 profile 层各写一套。
  • 这符合 #278 要解决的问题本质:从“声明式 metadata”推进到“系统化执行逻辑”。

2. shell 暴露与运行时策略已对齐

  • 现在 session_shell 不再只看 A2A_ENABLE_SESSION_SHELL,而是同时受 A2A_SANDBOX_MODE / A2A_WRITE_ACCESS_SCOPE 约束。
  • 这使 agent card、capability snapshot、supported methods 与真实运行时行为保持一致,避免 capability 泄露比运行时策略更宽松。

3. settings 层一致性校验是必要补强

  • A2A_WRITE_ACCESS_SCOPE=none 仍声明 writable roots,本质上是矛盾配置,提前在 settings 阶段拒绝是对的。
  • workspace_only 却声明 workspace 外 writable roots,同样应该在启动时失败,而不是留到运行中出现含混行为。

当前残余风险 / 后续空间

1. 仍然是入口级 enforcement,不是 syscall 级沙箱

  • 这是当前架构边界决定的,不是本 PR 缺陷。
  • 如果未来要做到真正的文件读写隔离,仍需要上游 OpenCode runtime 或 OS / container sandbox 配合。

2. 当前策略收紧点优先覆盖了 directory + shell + settings

  • #278 来说已经是合理完成态。
  • 但若后续出现更多 write-capable session/control 方法,建议继续复用 SandboxPolicy,不要回到分散判断。

issue 关系判断

  • Closes #278:准确。
  • Relates to #166:准确,因高风险能力边界与单实例 authz 模型需要保持一致。
  • Relates to #274:准确,未来如继续扩展策略拒绝类型,仍需统一错误翻译。

@liujuanjuan1984
Copy link
Collaborator Author

已基于当前分支补做一轮清理,聚焦本 PR 自己引入的死代码/不必要套壳:

  1. 移除了 sandbox_policy.py 中仅用于包一层布尔结果的 SessionShellAvailability
  2. 移除了 validate_sandbox_settings_consistency(...) 这类单次转发 helper,改为在 config.py 直接调用 SandboxPolicy.from_settings(...).validate_configuration()
  3. 移除了 SandboxPolicy 中当前未被实际消费的冗余字段,避免策略对象看起来比真实执行范围更大。

本轮不改变外部行为,只收紧实现边界,让策略层更直接。

已重新验证:

  • uv run pre-commit run --all-files
  • uv run pytest
  • 345 passed
  • coverage: 91.14%

@liujuanjuan1984
Copy link
Collaborator Author

已继续基于当前分支修正 reviewer 指出的残留套壳:

  1. 删除 src/opencode_a2a/execution/policy.py
  • 该文件在目录规则迁移到 SandboxPolicy 后只剩转发职责,不再保留。
  1. executor 直接持有 SandboxPolicy
  • 目录解析现在直接调用统一策略,不再经过 PolicyEnforcer
  • 这样 directory 的运行时入口与控制面入口都收敛到同一实现。

本轮验证:

  • uv run pre-commit run --all-files
  • uv run pytest
  • 345 passed
  • coverage: 91.12%

@liujuanjuan1984
Copy link
Collaborator Author

已继续基于当前分支清理 executor.py 中剩余的纯转发方法,并同步收敛 app 接线:

  1. executor 内部
  • resolve_directory(...) 保留为唯一目录入口。
  • _ExecutionCoordinator 已直接调用 SessionManager
  • 删除 _get_or_create_session_get_session_lock_finalize_preferred_session_binding_claim_preferred_session_finalize_session_claim_release_preferred_session_claim 等纯转发方法。
  1. server/application.py
  • JSON-RPC control hooks 现在直接接 executor.resolve_directoryexecutor._session_manager 的真实方法。
  • 仍保持 hook 缺失时统一抛 Control methods require guard hooks 的原契约。

本轮验证:

  • uv run pre-commit run --all-files
  • uv run pytest
  • 345 passed
  • coverage: 91.08%

@liujuanjuan1984
Copy link
Collaborator Author

已继续基于当前分支清理你指出的最后一层 resolve_directory 套壳:

  1. 删除 executor.resolve_directory(...)
  • 执行路径现在直接调用 SandboxPolicy.resolve_directory(...)
  1. server/application.py 接线改为直接传策略方法
  • JSON-RPC directory hook 现在使用 partial(executor._sandbox_policy.resolve_directory, default_directory=...)
  • 不再通过 executor 方法再包一层。
  1. 同步修正测试
  • tests/execution/test_directory_validation.py 改为直接验证策略对象的目录解析。
  • tests/server/test_transport_contract.py 改为提供 _sandbox_policy 测试桩。

本轮验证:

  • uv run pre-commit run --all-files
  • uv run pytest
  • 345 passed
  • coverage: 91.08%

@liujuanjuan1984
Copy link
Collaborator Author

已继续基于当前分支收口一批额外薄壳函数/属性:

  1. config.py
  • 删除 _normalize_declared_lists(...)
  • 改为 BeforeValidator(_parse_declared_list) 的声明式写法,保留同样行为但去掉 Pydantic classmethod 薄壳。
  1. profile/runtime.py + contracts/extensions.py
  • 删除 RuntimeProfile.session_shell_enabled
  • 调用方直接使用 runtime_profile.session_shell.enabled
  1. server/application.py
  • 删除 A2AClientManager.cache_ttl_seconds/cache_maxsize 这类 accessor wrapper。
  • 测试改为直接断言真实内部字段。

本轮验证:

  • uv run pre-commit run --all-files
  • uv run pytest
  • 345 passed
  • coverage: 91.05%

@liujuanjuan1984
Copy link
Collaborator Author

本轮继续做了一次薄壳收口,已提交并推送:e26b4b1 refactor(policy): inline remaining thin wrappers in sandbox paths (#278)

本次主要处理:

  • 删除 DeploymentConditionalMethod.availability 这类无语义增益的 property
  • settings 基础测试直接改为 Settings(),减少对 from_env() 的不必要依赖
  • 内联 A2AClientManager_entry_expired / _entry_in_use 这类单行状态 helper

保留项说明:

  • Settings.from_env() 仍然保留。它现在承担应用入口与类型适配职责,不再视为普通业务薄壳。

验证结果:

  • uv run pre-commit run --all-files
  • uv run pytest
  • 345 passed
  • coverage: 91.01%

@liujuanjuan1984
Copy link
Collaborator Author

补一条干净的收口说明。

本轮新增提交:e26b4b1 refactor(policy): inline remaining thin wrappers in sandbox paths (#278)

本次继续清理的薄壳:

  • 删除 DeploymentConditionalMethod.availability property
  • settings 基础测试直接改为 Settings()
  • 内联 A2AClientManager_entry_expired / _entry_in_use 这类单行状态 helper

保留项:

  • Settings.from_env() 继续保留,作为应用入口与类型适配点,不再按普通业务薄壳处理

验证结果:

  • uv run pre-commit run --all-files
  • uv run pytest
  • 345 passed
  • coverage: 91.01%

@liujuanjuan1984 liujuanjuan1984 marked this pull request as ready for review March 23, 2026 10:39
@liujuanjuan1984 liujuanjuan1984 merged commit d965941 into main Mar 23, 2026
3 checks passed
@liujuanjuan1984 liujuanjuan1984 deleted the eval/issue-278 branch March 23, 2026 10:39
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.

[Priority: High] [Security] 强化沙箱策略的系统化执行逻辑

1 participant