fix(builder): add approval context propagation for sub-tool execution#1125
fix(builder): add approval context propagation for sub-tool execution#1125ilblackdragon merged 11 commits intonearai:stagingfrom
Conversation
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request addresses a critical security vulnerability where the Highlights
Changelog
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
f324120 to
4f61a9a
Compare
There was a problem hiding this comment.
Code Review
The pull request addresses an issue where the build_software tool bypassed tool approval checks in autonomous contexts by introducing a two-level approval system. This was achieved by adding an approval_context field to the JobContext struct, along with a with_approval_context method and a check_approval_in_context helper function. The build_software tool was updated to create a job-specific approval context that explicitly allows certain sub-tools (like shell and file operations) and uses the new helper function to enforce these permissions. Additionally, the worker's job execution logic was modified to prioritize job-level approval contexts over worker-level ones, and the scheduler was updated to propagate the approval context to jobs. New tests were added to validate this enhanced approval mechanism.
zmanian
left a comment
There was a problem hiding this comment.
Security Review
This PR modifies the tool approval system and needs careful attention.
Critical concerns:
1. JobContext::default() behavioral change (security regression risk)
Default::default() now includes ApprovalContext::autonomous(), which changes behavior for UnlessAutoApproved tools:
- Before: default() had approval_context: None -> worker blocks UnlessAutoApproved
- After: default() has Autonomous { allowed_tools: {} } -> allows UnlessAutoApproved
Any code path using JobContext::default() outside the builder will silently unblock UnlessAutoApproved tools. Recommend reverting Default to approval_context: None and only setting it explicitly in the builder.
2. Job-level context bypasses worker-level entirely
When job_ctx.approval_context is Some, the worker-level approval check is completely skipped. Consider making the checks additive (both must pass) rather than job-level-takes-precedence, to maintain defense in depth.
3. check_approval_in_context is more permissive than worker check
When approval_context is None, this function returns Ok(()) (allow all), while the worker blocks non-Never. This inconsistency could lead to privilege escalation if callers use this helper thinking it provides equivalent security.
4. Builder allows http -- justify this
Building WASM tools requires shell, file ops, apply_patch -- but why http? Please justify or remove.
Required changes:
- Revert JobContext::default() to approval_context: None
- Either make worker + job checks additive, or document why job-level override is safe
- Fix check_approval_in_context to block when context is None (match worker behavior)
- Justify or remove http from builder's allowed tools
- No real CI has run -- fork PR, only classify/scope
|
Focused on tool-calling blocker impact. I agree with the requested security changes before merge:
Once those are addressed, this should materially unblock sub-tool execution in autonomous flows. |
…hecks This addresses the remaining security review concern from PR nearai#1125. Previously, the worker used "precedence" semantics where job-level approval context would completely bypass worker-level checks. This meant a tool's job-level context could potentially override worker-level restrictions. Changes: - Worker now checks BOTH job-level AND worker-level approval contexts - Tool is blocked if EITHER level blocks it (additive/intersection semantics) - Maintains defense in depth: job-level cannot bypass worker-level restrictions Tests added: - test_additive_approval_semantics_both_levels_must_approve: verifies job-level blocks take effect even when worker-level allows - test_additive_approval_worker_block_overrides_job_allow: verifies worker-level blocks take effect even when job-level allows - test_additive_approval_both_levels_allow: verifies tool is allowed only when both levels approve Security review reference: - Issue nearai#3 from @G7CNF: "document or enforce additive semantics for job + worker approval checks" - Issue nearai#2 from @zmanian: "Job-level context bypasses worker-level entirely" Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ilblackdragon
left a comment
There was a problem hiding this comment.
Additional Review Findings
Beyond zmanian's existing feedback (no CI has run, scope classification):
Must-fix
-
Merge conflicts — PR is
CONFLICTINGon staging.execute_build_toolon staging callsprepare_tool_paramswhich the PR's version skips. After rebase, ensureprepare_tool_paramsis preserved. -
Error type regression — the PR changes the error from
ToolError::AutonomousUnavailable(with a rich message listing available tools) toToolError::AuthRequired { name }(generic). This breaks the existing testtest_approval_context_returns_structured_autonomous_unavailable_error(line 2077) and degrades the user experience. -
PR description inaccuracy — lists
httpas an allowed builder tool, but the code only includes 5 tools (nohttp). The code comment even says "we don't need to grant direct http tool access."
Should-fix
-
Duplicated approval context propagation in scheduler — identical
if let Some(ref approval) = approval_context { ctx.approval_context = Some(approval.clone()); }blocks in bothmetadataandelsebranches. Move after the if/else. -
ApprovalRequirement::Nevertools blocked by allowlist —is_blockedignores the_requirementparameter. Tools likeechoortimewithApprovalRequirement::Nevershould be usable without being explicitly listed. -
Job restored from DB loses
approval_context—#[serde(skip)]means it'sNoneafter restart. Tools that worked will be blocked. Add a TODO comment.
Testing
- No test for
Nevertools in additive model — what happens when aNevertool has no approval_context? - No test for builder's
execute_build_toolwith unlisted tool — the integration test creates a matching context but doesn't call the builder method.
Add approval_context to JobContext so tools can propagate approval information when executing sub-tools. This enables tools like build_software to properly check approvals for shell, write_file, etc. - Add approval_context: Option<ApprovalContext> field to JobContext - Add with_approval_context() builder method - Add check_approval_in_context() helper for tools to verify permissions - Default JobContext now includes autonomous approval context Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move job context fetch before approval check and add job-level approval context checking. Job-level context takes precedence over worker-level, allowing tools like build_software to set specific allowed sub-tools while maintaining the fallback to worker-level approval for normal operations. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Store approval_context from dispatch into JobContext so it's available to tools during execution. This completes the chain: scheduler -> job context -> tools -> sub-tools. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Update build_software to create a JobContext with build-specific approval permissions and check approval before executing sub-tools. This allows the builder to work in autonomous contexts (web UI, routines) while maintaining security by only allowing specific build-related tools. Allowed tools: shell, read_file, write_file, list_dir, apply_patch, http Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When restoring jobs from database, set approval_context to None. The context will be populated by the scheduler on next dispatch if needed. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add tests for: - JobContext default includes approval_context - with_approval_context() builder method - Autonomous context blocks Always-approved tools unless explicitly allowed - autonomous_with_tools allows specific tools - Builder tool approval context configuration Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit addresses all security concerns raised in PR review: 1. Revert JobContext::default() to approval_context: None - Previously set ApprovalContext::autonomous() which was too permissive - Secure default requires explicit opt-in for autonomous execution - Any code using JobContext::default() now correctly blocks non-Never tools 2. Fix check_approval_in_context() to match worker behavior - Previously returned Ok(()) when approval_context was None (insecure) - Now uses ApprovalContext::is_blocked_or_default() for consistency - Prevents privilege escalation through sub-tool execution paths 3. Remove "http" from builder's allowed tools - Building software doesn't require direct http tool access - Shell commands (cargo, npm, pip) handle dependency fetching - Reduces attack surface for builder tool execution 4. Update tests to reflect new secure defaults - Tests now verify JobContext::default() blocks non-Never tools - New test added for secure default behavior Security review references: - Issue nearai#1: JobContext::default() behavioral change - Issue nearai#3: check_approval_in_context more permissive than worker check - Issue nearai#4: Builder allows http without justification Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…hecks This addresses the remaining security review concern from PR nearai#1125. Previously, the worker used "precedence" semantics where job-level approval context would completely bypass worker-level checks. This meant a tool's job-level context could potentially override worker-level restrictions. Changes: - Worker now checks BOTH job-level AND worker-level approval contexts - Tool is blocked if EITHER level blocks it (additive/intersection semantics) - Maintains defense in depth: job-level cannot bypass worker-level restrictions Tests added: - test_additive_approval_semantics_both_levels_must_approve: verifies job-level blocks take effect even when worker-level allows - test_additive_approval_worker_block_overrides_job_allow: verifies worker-level blocks take effect even when job-level allows - test_additive_approval_both_levels_allow: verifies tool is allowed only when both levels approve Security review reference: - Issue nearai#3 from @G7CNF: "document or enforce additive semantics for job + worker approval checks" - Issue nearai#2 from @zmanian: "Job-level context bypasses worker-level entirely" Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Restore requirement-aware is_blocked() semantics: Never and UnlessAutoApproved tools pass in autonomous context, Only Always tools require explicit allowlist entry - Use AutonomousUnavailable error (with descriptive reason) instead of generic AuthRequired for approval blocking in worker - Deduplicate approval_context propagation in scheduler dispatch (single update_context_and_get call instead of duplicated blocks) - Remove http from builder tool allowlist (shell handles network) - Add TODO comments for serde(skip) losing approval_context on DB restore in both libsql and postgres backends - Add tests: Never tools in additive model, builder unlisted tool blocking Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
4c2d10f to
4ffea34
Compare
zmanian
left a comment
There was a problem hiding this comment.
Review -- REQUEST_CHANGES
Critical: is_blocked() semantic change silently broadens autonomous permissions
The PR changes is_blocked so that UnlessAutoApproved tools are automatically allowed in ANY autonomous context, regardless of the allowlist. This is a significant policy escalation affecting all autonomous execution paths, not just the builder.
Before: autonomous_with_tools(["shell"]) blocks write_file, read_file, http, etc.
After: autonomous_with_tools(["shell"]) allows ALL UnlessAutoApproved tools (write_file, read_file, shell non-destructive, http, list_dir, apply_patch, etc.) -- the allowlist only gates Always-requirement tools.
This means any routine, background job, or web UI dispatch scoped to ["memory_search"] now implicitly gets write_file, shell, http, etc. The allowed_tools list becomes a gate for destructive commands only, not a restrictive boundary.
Question for discussion: Is this the intended security model? If so, it needs explicit documentation and an audit of all autonomous_with_tools call sites. If the allowlist is meant to be restrictive, revert the UnlessAutoApproved line to !allowed_tools.contains(tool_name).
Medium
- M1: Builder's allowlist is effectively redundant for 4 of 5 tools under the new semantics (only
shellwith destructive commands is actually gated) - M2: PR description says
httpis in the allowlist but code excludes it - M3: Postgres DB restore path not updated (only libSQL and history/store shown)
- Merge conflicts with staging -- needs rebase
Low
#[serde(skip)]means approval context lost on restart (documented with TODO)needs_updateguard is always true
Addressing Review FeedbackAll items from the three reviews have been addressed. Here's the status of each: zmanian's Security Review (review 1) — all 4 items resolved
ilblackdragon's Review — all 8 items resolved
zmanian's Review 2 —
|
| # | Issue | Status |
|---|---|---|
| M1 | Builder allowlist redundant | Not applicable — builder tools use Always requirement, so the allowlist is effective |
| M2 | PR description says http |
✅ Fixed |
| M3 | Postgres DB restore not updated | Already covered — postgres delegates to history/store.rs which has the TODO |
| Low | needs_update always true |
Correct observation — dispatch_job() always provides Some(approval_context) via autonomous_approval_context(). The else branch is defensive but unreachable in current callers. |
All changes in commit 0e5f1b12. CI passes: cargo fmt ✅, cargo clippy ✅ (zero warnings), 3921 lib tests + 9 integration tests ✅.
Code Review — Post-rebaseBug: Duplicate approval check in worker/job.rsSeverity: High — The PR adds a new additive approval check (lines 517-555) but the pre-existing approval check at lines 561-567 is still present, creating a double-check: This means:
If a tool passes the first check, it hits the second check which is worker-level only (no additive semantics). The second check is redundant for the success path but could produce a different error type on the failure path. The first check also uses raw Fix: Remove the pre-existing check at lines 561-567 and use Minor Issues
|
- Remove pre-existing worker-level-only approval check (lines 561-567) that duplicated the new additive check, using a different error type and missing job-level context - Use normalized_params (not raw params) for requires_approval() so parameter-dependent approval (e.g. shell destructive detection) works correctly with coerced values - Remove unused autonomous_unavailable_error import - Add comment documenting unreachable else branch in scheduler Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Code reviewFound 10 issues:
Lines 496 to 504 in a8c05b8 The enum defined in
This will fail to compile because the variant doesn't exist.
ironclaw/src/tools/builder/core.rs Lines 808 to 814 in a8c05b8
ironclaw/src/agent/scheduler.rs Line 218 in a8c05b8
Line 207 in a8c05b8
ironclaw/src/agent/scheduler.rs Lines 220 to 224 in a8c05b8
Summary: The PR implements sound approval context propagation with good atomicity guarantees. However, there are 2 blocking compilation errors ( |
…nearai#1125) * feat(context): add approval_context field to JobContext Add approval_context to JobContext so tools can propagate approval information when executing sub-tools. This enables tools like build_software to properly check approvals for shell, write_file, etc. - Add approval_context: Option<ApprovalContext> field to JobContext - Add with_approval_context() builder method - Add check_approval_in_context() helper for tools to verify permissions - Default JobContext now includes autonomous approval context Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(worker): check job-level approval context before executing tools Move job context fetch before approval check and add job-level approval context checking. Job-level context takes precedence over worker-level, allowing tools like build_software to set specific allowed sub-tools while maintaining the fallback to worker-level approval for normal operations. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(scheduler): propagate approval_context to JobContext Store approval_context from dispatch into JobContext so it's available to tools during execution. This completes the chain: scheduler -> job context -> tools -> sub-tools. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(builder): use approval context for sub-tool execution Update build_software to create a JobContext with build-specific approval permissions and check approval before executing sub-tools. This allows the builder to work in autonomous contexts (web UI, routines) while maintaining security by only allowing specific build-related tools. Allowed tools: shell, read_file, write_file, list_dir, apply_patch, http Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(db): initialize approval_context as None in job restoration When restoring jobs from database, set approval_context to None. The context will be populated by the scheduler on next dispatch if needed. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test: add comprehensive approval context tests Add tests for: - JobContext default includes approval_context - with_approval_context() builder method - Autonomous context blocks Always-approved tools unless explicitly allowed - autonomous_with_tools allows specific tools - Builder tool approval context configuration Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(security): address critical approval context security issues This commit addresses all security concerns raised in PR review: 1. Revert JobContext::default() to approval_context: None - Previously set ApprovalContext::autonomous() which was too permissive - Secure default requires explicit opt-in for autonomous execution - Any code using JobContext::default() now correctly blocks non-Never tools 2. Fix check_approval_in_context() to match worker behavior - Previously returned Ok(()) when approval_context was None (insecure) - Now uses ApprovalContext::is_blocked_or_default() for consistency - Prevents privilege escalation through sub-tool execution paths 3. Remove "http" from builder's allowed tools - Building software doesn't require direct http tool access - Shell commands (cargo, npm, pip) handle dependency fetching - Reduces attack surface for builder tool execution 4. Update tests to reflect new secure defaults - Tests now verify JobContext::default() blocks non-Never tools - New test added for secure default behavior Security review references: - Issue nearai#1: JobContext::default() behavioral change - Issue nearai#3: check_approval_in_context more permissive than worker check - Issue nearai#4: Builder allows http without justification Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(worker): implement additive approval semantics for job + worker checks This addresses the remaining security review concern from PR nearai#1125. Previously, the worker used "precedence" semantics where job-level approval context would completely bypass worker-level checks. This meant a tool's job-level context could potentially override worker-level restrictions. Changes: - Worker now checks BOTH job-level AND worker-level approval contexts - Tool is blocked if EITHER level blocks it (additive/intersection semantics) - Maintains defense in depth: job-level cannot bypass worker-level restrictions Tests added: - test_additive_approval_semantics_both_levels_must_approve: verifies job-level blocks take effect even when worker-level allows - test_additive_approval_worker_block_overrides_job_allow: verifies worker-level blocks take effect even when job-level allows - test_additive_approval_both_levels_allow: verifies tool is allowed only when both levels approve Security review reference: - Issue nearai#3 from @G7CNF: "document or enforce additive semantics for job + worker approval checks" - Issue nearai#2 from @zmanian: "Job-level context bypasses worker-level entirely" Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(security): address PR nearai#1125 review feedback - Restore requirement-aware is_blocked() semantics: Never and UnlessAutoApproved tools pass in autonomous context, Only Always tools require explicit allowlist entry - Use AutonomousUnavailable error (with descriptive reason) instead of generic AuthRequired for approval blocking in worker - Deduplicate approval_context propagation in scheduler dispatch (single update_context_and_get call instead of duplicated blocks) - Remove http from builder tool allowlist (shell handles network) - Add TODO comments for serde(skip) losing approval_context on DB restore in both libsql and postgres backends - Add tests: Never tools in additive model, builder unlisted tool blocking Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(worker): remove duplicate approval check and use normalized params - Remove pre-existing worker-level-only approval check (lines 561-567) that duplicated the new additive check, using a different error type and missing job-level context - Use normalized_params (not raw params) for requires_approval() so parameter-dependent approval (e.g. shell destructive detection) works correctly with coerced values - Remove unused autonomous_unavailable_error import - Add comment documenting unreachable else branch in scheduler Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: ilblackdragon@gmail.com <ilblackdragon@gmail.com>
…#1125) * feat(context): add approval_context field to JobContext Add approval_context to JobContext so tools can propagate approval information when executing sub-tools. This enables tools like build_software to properly check approvals for shell, write_file, etc. - Add approval_context: Option<ApprovalContext> field to JobContext - Add with_approval_context() builder method - Add check_approval_in_context() helper for tools to verify permissions - Default JobContext now includes autonomous approval context Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(worker): check job-level approval context before executing tools Move job context fetch before approval check and add job-level approval context checking. Job-level context takes precedence over worker-level, allowing tools like build_software to set specific allowed sub-tools while maintaining the fallback to worker-level approval for normal operations. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(scheduler): propagate approval_context to JobContext Store approval_context from dispatch into JobContext so it's available to tools during execution. This completes the chain: scheduler -> job context -> tools -> sub-tools. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(builder): use approval context for sub-tool execution Update build_software to create a JobContext with build-specific approval permissions and check approval before executing sub-tools. This allows the builder to work in autonomous contexts (web UI, routines) while maintaining security by only allowing specific build-related tools. Allowed tools: shell, read_file, write_file, list_dir, apply_patch, http Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(db): initialize approval_context as None in job restoration When restoring jobs from database, set approval_context to None. The context will be populated by the scheduler on next dispatch if needed. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test: add comprehensive approval context tests Add tests for: - JobContext default includes approval_context - with_approval_context() builder method - Autonomous context blocks Always-approved tools unless explicitly allowed - autonomous_with_tools allows specific tools - Builder tool approval context configuration Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(security): address critical approval context security issues This commit addresses all security concerns raised in PR review: 1. Revert JobContext::default() to approval_context: None - Previously set ApprovalContext::autonomous() which was too permissive - Secure default requires explicit opt-in for autonomous execution - Any code using JobContext::default() now correctly blocks non-Never tools 2. Fix check_approval_in_context() to match worker behavior - Previously returned Ok(()) when approval_context was None (insecure) - Now uses ApprovalContext::is_blocked_or_default() for consistency - Prevents privilege escalation through sub-tool execution paths 3. Remove "http" from builder's allowed tools - Building software doesn't require direct http tool access - Shell commands (cargo, npm, pip) handle dependency fetching - Reduces attack surface for builder tool execution 4. Update tests to reflect new secure defaults - Tests now verify JobContext::default() blocks non-Never tools - New test added for secure default behavior Security review references: - Issue #1: JobContext::default() behavioral change - Issue #3: check_approval_in_context more permissive than worker check - Issue #4: Builder allows http without justification Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(worker): implement additive approval semantics for job + worker checks This addresses the remaining security review concern from PR #1125. Previously, the worker used "precedence" semantics where job-level approval context would completely bypass worker-level checks. This meant a tool's job-level context could potentially override worker-level restrictions. Changes: - Worker now checks BOTH job-level AND worker-level approval contexts - Tool is blocked if EITHER level blocks it (additive/intersection semantics) - Maintains defense in depth: job-level cannot bypass worker-level restrictions Tests added: - test_additive_approval_semantics_both_levels_must_approve: verifies job-level blocks take effect even when worker-level allows - test_additive_approval_worker_block_overrides_job_allow: verifies worker-level blocks take effect even when job-level allows - test_additive_approval_both_levels_allow: verifies tool is allowed only when both levels approve Security review reference: - Issue #3 from @G7CNF: "document or enforce additive semantics for job + worker approval checks" - Issue #2 from @zmanian: "Job-level context bypasses worker-level entirely" Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(security): address PR #1125 review feedback - Restore requirement-aware is_blocked() semantics: Never and UnlessAutoApproved tools pass in autonomous context, Only Always tools require explicit allowlist entry - Use AutonomousUnavailable error (with descriptive reason) instead of generic AuthRequired for approval blocking in worker - Deduplicate approval_context propagation in scheduler dispatch (single update_context_and_get call instead of duplicated blocks) - Remove http from builder tool allowlist (shell handles network) - Add TODO comments for serde(skip) losing approval_context on DB restore in both libsql and postgres backends - Add tests: Never tools in additive model, builder unlisted tool blocking Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(worker): remove duplicate approval check and use normalized params - Remove pre-existing worker-level-only approval check (lines 561-567) that duplicated the new additive check, using a different error type and missing job-level context - Use normalized_params (not raw params) for requires_approval() so parameter-dependent approval (e.g. shell destructive detection) works correctly with coerced values - Remove unused autonomous_unavailable_error import - Add comment documenting unreachable else branch in scheduler Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: ilblackdragon@gmail.com <ilblackdragon@gmail.com>
…nearai#1125) * feat(context): add approval_context field to JobContext Add approval_context to JobContext so tools can propagate approval information when executing sub-tools. This enables tools like build_software to properly check approvals for shell, write_file, etc. - Add approval_context: Option<ApprovalContext> field to JobContext - Add with_approval_context() builder method - Add check_approval_in_context() helper for tools to verify permissions - Default JobContext now includes autonomous approval context Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(worker): check job-level approval context before executing tools Move job context fetch before approval check and add job-level approval context checking. Job-level context takes precedence over worker-level, allowing tools like build_software to set specific allowed sub-tools while maintaining the fallback to worker-level approval for normal operations. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(scheduler): propagate approval_context to JobContext Store approval_context from dispatch into JobContext so it's available to tools during execution. This completes the chain: scheduler -> job context -> tools -> sub-tools. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(builder): use approval context for sub-tool execution Update build_software to create a JobContext with build-specific approval permissions and check approval before executing sub-tools. This allows the builder to work in autonomous contexts (web UI, routines) while maintaining security by only allowing specific build-related tools. Allowed tools: shell, read_file, write_file, list_dir, apply_patch, http Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(db): initialize approval_context as None in job restoration When restoring jobs from database, set approval_context to None. The context will be populated by the scheduler on next dispatch if needed. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test: add comprehensive approval context tests Add tests for: - JobContext default includes approval_context - with_approval_context() builder method - Autonomous context blocks Always-approved tools unless explicitly allowed - autonomous_with_tools allows specific tools - Builder tool approval context configuration Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(security): address critical approval context security issues This commit addresses all security concerns raised in PR review: 1. Revert JobContext::default() to approval_context: None - Previously set ApprovalContext::autonomous() which was too permissive - Secure default requires explicit opt-in for autonomous execution - Any code using JobContext::default() now correctly blocks non-Never tools 2. Fix check_approval_in_context() to match worker behavior - Previously returned Ok(()) when approval_context was None (insecure) - Now uses ApprovalContext::is_blocked_or_default() for consistency - Prevents privilege escalation through sub-tool execution paths 3. Remove "http" from builder's allowed tools - Building software doesn't require direct http tool access - Shell commands (cargo, npm, pip) handle dependency fetching - Reduces attack surface for builder tool execution 4. Update tests to reflect new secure defaults - Tests now verify JobContext::default() blocks non-Never tools - New test added for secure default behavior Security review references: - Issue nearai#1: JobContext::default() behavioral change - Issue nearai#3: check_approval_in_context more permissive than worker check - Issue nearai#4: Builder allows http without justification Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(worker): implement additive approval semantics for job + worker checks This addresses the remaining security review concern from PR nearai#1125. Previously, the worker used "precedence" semantics where job-level approval context would completely bypass worker-level checks. This meant a tool's job-level context could potentially override worker-level restrictions. Changes: - Worker now checks BOTH job-level AND worker-level approval contexts - Tool is blocked if EITHER level blocks it (additive/intersection semantics) - Maintains defense in depth: job-level cannot bypass worker-level restrictions Tests added: - test_additive_approval_semantics_both_levels_must_approve: verifies job-level blocks take effect even when worker-level allows - test_additive_approval_worker_block_overrides_job_allow: verifies worker-level blocks take effect even when job-level allows - test_additive_approval_both_levels_allow: verifies tool is allowed only when both levels approve Security review reference: - Issue nearai#3 from @G7CNF: "document or enforce additive semantics for job + worker approval checks" - Issue nearai#2 from @zmanian: "Job-level context bypasses worker-level entirely" Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(security): address PR nearai#1125 review feedback - Restore requirement-aware is_blocked() semantics: Never and UnlessAutoApproved tools pass in autonomous context, Only Always tools require explicit allowlist entry - Use AutonomousUnavailable error (with descriptive reason) instead of generic AuthRequired for approval blocking in worker - Deduplicate approval_context propagation in scheduler dispatch (single update_context_and_get call instead of duplicated blocks) - Remove http from builder tool allowlist (shell handles network) - Add TODO comments for serde(skip) losing approval_context on DB restore in both libsql and postgres backends - Add tests: Never tools in additive model, builder unlisted tool blocking Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(worker): remove duplicate approval check and use normalized params - Remove pre-existing worker-level-only approval check (lines 561-567) that duplicated the new additive check, using a different error type and missing job-level context - Use normalized_params (not raw params) for requires_approval() so parameter-dependent approval (e.g. shell destructive detection) works correctly with coerced values - Remove unused autonomous_unavailable_error import - Add comment documenting unreachable else branch in scheduler Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: ilblackdragon@gmail.com <ilblackdragon@gmail.com>
Problem
The
build_softwaretool bypassed tool approval checks when executing sub-tools (shell, write_file, etc.) because it calledtool.execute()directly with a dummyJobContext::default(). This prevented the builder from working in autonomous contexts (web UI, routines).Solution
Add
approval_contextfield toJobContextand propagate it through the execution chain (scheduler → job context → tools → sub-tools). Tools likebuild_softwarecan now set specific allowed sub-tools while maintaining security.Changes
approval_context: Option<ApprovalContext>field toJobContextwith builder methodcheck_approval_in_context()helper withis_blocked_or_defaultsemantics (blocks non-Nevertools when context isNone)AutonomousUnavailableerror with descriptive reason.approval_contextfrom dispatch toJobContext(single atomicupdate_context_and_getcall)prepare_tool_paramsfor parameter normalizationNoneon job restoration (with TODO for future persistence)Nevertools in additive model and builder unlisted tool blockingAllowed Builder Tools
The builder explicitly allows:
shell,read_file,write_file,list_dir,apply_patchApproval Semantics
ApprovalRequirement::Never— always allowed (no approval needed)ApprovalRequirement::UnlessAutoApproved— allowed in autonomous contexts (autonomous = auto-approved)ApprovalRequirement::Always— only allowed if explicitly listed inallowed_toolsapproval_contextisNone(no autonomous context): all non-Nevertools are blocked (legacy/secure default)Verification
References
🤖 Generated with Claude Code