Description
Same defect class as #5900's CompositeExecutor gap, found while auditing ScopedToolExecutor for the checkpoint-forwarding issue (#5905).
ScopedToolExecutor<E> (crates/zeph-tools/src/scope.rs:435-614) forwards set_skill_env, set_effective_trust, is_tool_retryable, and is_tool_speculatable to self.inner, but has no override for requires_confirmation (crates/zeph-tools/src/executor.rs:813-822). Grepping scope.rs for requires_confirmation returns zero matches.
Because it isn't overridden, the blanket impl<T: ToolExecutor> ErasedToolExecutor for T routes requires_confirmation_erased calls to the base trait default, which unconditionally returns false — discarding whatever the wrapped chain (e.g. TrustGateExecutor, which does override it at crates/zeph-tools/src/trust_gate.rs:304) would actually report.
ScopedToolExecutor is documented as the outermost wrapper in production wiring (its own module doc, confirmed in src/runner.rs:2680-2727), so whenever capability_scopes is configured, the speculative-dispatch engine (crates/zeph-core/src/agent/speculative/mod.rs) queries requires_confirmation on the fully composed top-level executor and always gets false back for tools wrapped by ScopedToolExecutor, regardless of the real policy underneath.
By contrast, ShadowProbeExecutor (audited in the same pass) correctly forwards requires_confirmation to self.inner (crates/zeph-tools/src/shadow_probe.rs:388-390) — the gap is specific to ScopedToolExecutor.
Impact / Current Exploitability
Per #5900's own note, no leaf executor currently sets is_tool_speculatable(tool_id) == true outside test code, so the speculative-dispatch path this feeds is not yet live for any real tool — this is currently dormant, not an active incident. However, capability_scopes is a documented, spec-driven feature (spec 050) meant for production use, and the trait's own doc for requires_confirmation states callers "must not treat None/default as authoritative bypass" in spirit — silently losing the confirmation requirement at the outermost layer defeats the purpose of the metadata query as soon as any tool opts into speculative dispatch.
Expected Behavior
ScopedToolExecutor::requires_confirmation should delegate: self.inner.requires_confirmation(call).
Actual Behavior
Always returns false (the trait default) regardless of what the wrapped chain reports.
Environment
- HEAD: 2616f7b (zeph-tools rotation sweep, CI cycle 1285)
- Crate:
zeph-tools — scope.rs
Suggested Fix
fn requires_confirmation(&self, call: &ToolCall) -> bool {
self.inner.requires_confirmation(call)
}
Add a regression test mirroring the pattern used for #5900, asserting the call reaches self.inner through ScopedToolExecutor.
Related: #5900 (same class, CompositeExecutor/AdversarialPolicyGateExecutor), #5905 (checkpoint-forwarding gap found in the same audit pass on scope.rs/shadow_probe.rs), #3869 (original defect class).
Description
Same defect class as #5900's
CompositeExecutorgap, found while auditingScopedToolExecutorfor the checkpoint-forwarding issue (#5905).ScopedToolExecutor<E>(crates/zeph-tools/src/scope.rs:435-614) forwardsset_skill_env,set_effective_trust,is_tool_retryable, andis_tool_speculatabletoself.inner, but has no override forrequires_confirmation(crates/zeph-tools/src/executor.rs:813-822). Greppingscope.rsforrequires_confirmationreturns zero matches.Because it isn't overridden, the blanket
impl<T: ToolExecutor> ErasedToolExecutor for Troutesrequires_confirmation_erasedcalls to the base trait default, which unconditionally returnsfalse— discarding whatever the wrapped chain (e.g.TrustGateExecutor, which does override it atcrates/zeph-tools/src/trust_gate.rs:304) would actually report.ScopedToolExecutoris documented as the outermost wrapper in production wiring (its own module doc, confirmed insrc/runner.rs:2680-2727), so whenevercapability_scopesis configured, the speculative-dispatch engine (crates/zeph-core/src/agent/speculative/mod.rs) queriesrequires_confirmationon the fully composed top-level executor and always getsfalseback for tools wrapped byScopedToolExecutor, regardless of the real policy underneath.By contrast,
ShadowProbeExecutor(audited in the same pass) correctly forwardsrequires_confirmationtoself.inner(crates/zeph-tools/src/shadow_probe.rs:388-390) — the gap is specific toScopedToolExecutor.Impact / Current Exploitability
Per #5900's own note, no leaf executor currently sets
is_tool_speculatable(tool_id) == trueoutside test code, so the speculative-dispatch path this feeds is not yet live for any real tool — this is currently dormant, not an active incident. However,capability_scopesis a documented, spec-driven feature (spec 050) meant for production use, and the trait's own doc forrequires_confirmationstates callers "must not treatNone/default as authoritative bypass" in spirit — silently losing the confirmation requirement at the outermost layer defeats the purpose of the metadata query as soon as any tool opts into speculative dispatch.Expected Behavior
ScopedToolExecutor::requires_confirmationshould delegate:self.inner.requires_confirmation(call).Actual Behavior
Always returns
false(the trait default) regardless of what the wrapped chain reports.Environment
zeph-tools—scope.rsSuggested Fix
Add a regression test mirroring the pattern used for #5900, asserting the call reaches
self.innerthroughScopedToolExecutor.Related: #5900 (same class,
CompositeExecutor/AdversarialPolicyGateExecutor), #5905 (checkpoint-forwarding gap found in the same audit pass onscope.rs/shadow_probe.rs), #3869 (original defect class).