Files
cleveragents-core/features/security_readonly.feature
T
khyari hamza 493e5cf8a1
CI / benchmark-publish (pull_request) Has been skipped
CI / quality (pull_request) Successful in 20s
CI / lint (pull_request) Successful in 20s
CI / build (pull_request) Successful in 28s
CI / security (pull_request) Successful in 33s
CI / typecheck (pull_request) Successful in 34s
CI / integration_tests (pull_request) Successful in 3m38s
CI / benchmark-regression (pull_request) Successful in 26m8s
CI / unit_tests (pull_request) Successful in 29m12s
CI / docker (pull_request) Successful in 1m2s
CI / coverage (pull_request) Successful in 50m23s
fix(security): wire read_only through ExecuteStubActor and replace stubs with real tests
- Add read_only kwarg to ExecuteStubActor.execute(), propagated to
  ChangeSetCapture; PlanExecutor._run_execute_with_stub passes
  plan.read_only through the execution path (P1 #2 fix)
- Replace CLI fail-fast and Action-Skill stub scenarios with real
  ExecuteStubActor spy test and SkillContext.enforce_write_guard
  integration tests (P2 #1 fix)
- Update docs: remove unimplemented Layer 5 (Action-Skill), add
  ExecuteStubActor wiring description, fix test command (P2 #2 fix)

ISSUES CLOSED: #322
2026-02-26 20:33:20 +00:00

129 lines
6.7 KiB
Gherkin

@unit
Feature: Read-only action enforcement
As a plan operator
I need the system to enforce read-only constraints at every layer
So that read-only plans never produce side-effects
Background:
Given the read-only enforcement modules are available
# ── ToolRuntime._enforce_capabilities ────────────────────────────────
Scenario: Read-only plan blocks a write-capable tool with tool name in error
Given a tool "builtin/file-write" with writes=True and read_only=False
And a tool execution context with plan_id "plan-ro-1" and read_only True
When I enforce capabilities on the tool
Then a ToolAccessDeniedError should be raised for the tool
And the access-denied message should contain "builtin/file-write"
And the access-denied message should contain "read-only"
Scenario: Read-only plan allows a read-only tool
Given a tool "builtin/search" with writes=False and read_only=True
And a tool execution context with plan_id "plan-ro-2" and read_only True
When I enforce capabilities on the tool
Then no enforcement error should be raised
Scenario: Non-read-only plan allows a write-capable tool
Given a tool "builtin/file-write" with writes=True and read_only=False
And a tool execution context with plan_id "plan-rw-1" and read_only False
When I enforce capabilities on the tool
Then no enforcement error should be raised
Scenario: Tool with writes=True is blocked even when cap.read_only is unset
Given a tool "builtin/hybrid-tool" with writes=True and read_only=False
And a tool execution context with plan_id "plan-ro-3" and read_only True
When I enforce capabilities on the tool
Then a ToolAccessDeniedError should be raised for the tool
And the access-denied message should contain "builtin/hybrid-tool"
Scenario: Tool with neither read_only nor writes passes on read-only plan
Given a tool "builtin/noop" with writes=False and read_only=False
And a tool execution context with plan_id "plan-ro-4" and read_only True
When I enforce capabilities on the tool
Then no enforcement error should be raised
# ── SkillContext.enforce_write_guard ──────────────────────────────────
Scenario: SkillContext enforce_write_guard blocks write tool with tool name
Given a read-only SkillContext for plan "plan-sk-1"
When I call enforce_write_guard with tool_name "skill/writer"
Then a SkillExecutionError should be raised for the guard
And the write-guard error message should contain "skill/writer"
And the write-guard error type should be PERMISSION_DENIED
Scenario: SkillContext enforce_write_guard allows on writable context
Given a writable SkillContext for plan "plan-sk-2"
When I call enforce_write_guard with tool_name "skill/writer"
Then no write-guard error should be raised
# ── ChangeSetCapture read-only enforcement ───────────────────────────
Scenario: ChangeSet builder rejects write entry when plan is read-only
Given a ChangeSetCapture with plan_id "plan-cs-1" and read_only True
When I try to wrap a write-capable tool spec
Then a ReadOnlyViolationError should be raised for the capture
And the capture error message should contain "read-only"
Scenario: ChangeSet builder allows wrapping when not read-only
Given a ChangeSetCapture with plan_id "plan-cs-2" and read_only False
When I try to wrap a write-capable tool spec
Then no capture error should be raised
Scenario: ChangeSet builder passes read-only tool through unchanged
Given a ChangeSetCapture with plan_id "plan-cs-3" and read_only True
When I wrap a read-only tool spec
Then the wrapped tool spec should be returned unchanged
# ── ExecuteStubActor read-only propagation ─────────────────────────────
Scenario: ExecuteStubActor propagates read_only to ChangeSetCapture
When I execute via ExecuteStubActor with read_only True
Then the stub actor should create a read-only ChangeSetCapture
Scenario: ExecuteStubActor allows writes when not read-only
When I execute via ExecuteStubActor with read_only False
Then the stub actor should create a writable ChangeSetCapture
# ── File write tool blocked ──────────────────────────────────────────
Scenario: File write tool blocked under read-only plan via ToolRuntime
Given a tool "builtin/file-write" with writes=True and read_only=False
And a tool execution context with plan_id "plan-fw-1" and read_only True
When I enforce capabilities on the tool
Then a ToolAccessDeniedError should be raised for the tool
And the access-denied message should contain "builtin/file-write"
# ── Git push tool blocked ────────────────────────────────────────────
Scenario: Git push tool blocked under read-only plan via ToolRuntime
Given a tool "git/push" with writes=True and read_only=False
And a tool execution context with plan_id "plan-gp-1" and read_only True
When I enforce capabilities on the tool
Then a ToolAccessDeniedError should be raised for the tool
And the access-denied message should contain "git/push"
# ── Read-only SkillContext blocks write tool ───────────────────────────
Scenario: Read-only SkillContext blocks write tool via enforce_write_guard
Given a read-only SkillContext for plan "plan-compat-1"
When I call enforce_write_guard with tool_name "test/write-tool"
Then a SkillExecutionError should be raised for the guard
And the write-guard error message should contain "test/write-tool"
Scenario: Writable SkillContext allows write tool
Given a writable SkillContext for plan "plan-compat-2"
When I call enforce_write_guard with tool_name "test/write-tool"
Then no write-guard error should be raised
# ── Plan -> ToolExecutionContext propagation ──────────────────────────
Scenario: ToolExecutionContext carries plan_read_only from plan
Given a Plan domain model with read_only=True and plan_id "plan-ex-1"
When I create a ToolExecutionContext from the plan
Then the propagated context plan_read_only should be True
Scenario: ToolExecutionContext writable for writable plan
Given a Plan domain model with read_only=False and plan_id "plan-ex-2"
When I create a ToolExecutionContext from the plan
Then the propagated context plan_read_only should be False