Skip to content

feat(rules): support compound command evaluation with result aggregation#49

Merged
fohte merged 2 commits intomainfrom
fohte/impl-runok-init-compound-eval
Feb 19, 2026
Merged

feat(rules): support compound command evaluation with result aggregation#49
fohte merged 2 commits intomainfrom
fohte/impl-runok-init-compound-eval

Conversation

@fohte
Copy link
Owner

@fohte fohte commented Feb 18, 2026

Why

  • Compound commands (pipelines |, &&, ||, ;) passed to evaluate_command() are evaluated as a single command, so individual sub-commands are not checked against permission rules
    • e.g. ls -la | rm -rf / does not trigger the deny rule for rm -rf /

What

  • Add evaluate_compound() that splits compound commands into individual sub-commands, evaluates each, and aggregates results
    • Action aggregation: Explicit Deny Wins (deny > ask > allow > default)
    • Sandbox policy aggregation: Strictest Wins (writable intersection, deny union, network.allow intersection)
    • Escalate action to Ask when writable intersection becomes empty (contradiction)
  • Add SandboxPreset::merge_strictest() to aggregate multiple sandbox presets

Open with Devin

evaluate_command() only handled single commands and wrapper recursion.
Compound commands (pipelines, &&, ||, ;) need each sub-command evaluated
independently with results aggregated to enforce security policies across
the full command line.

Add evaluate_compound() that splits compound commands via
extract_commands(), evaluates each with evaluate_command(), and
aggregates results:
- Action: Explicit Deny Wins (deny > ask > allow > default)
- Sandbox policy: Strictest Wins via SandboxPreset::merge_strictest()
  (writable roots intersection, deny paths union, network intersection)
- Writable roots contradiction (empty intersection) escalates to Ask

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@gemini-code-assist
Copy link

Summary of Changes

Hello @fohte, 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 significantly enhances the rule engine's capability to handle complex shell commands by introducing a robust mechanism for evaluating compound commands. It ensures that security policies, including actions and sandbox restrictions, are applied consistently and securely across all components of a multi-part command, preventing unintended bypasses and providing clearer enforcement outcomes.

Highlights

  • Compound Command Evaluation: Implemented a new evaluate_compound function that correctly parses and evaluates compound shell commands (e.g., using |, &&, ||, ;). This ensures that individual sub-commands within a compound command are checked against permission rules, closing a potential security loophole.
  • Action Aggregation: Introduced an 'Explicit Deny Wins' policy for aggregating actions from multiple sub-commands. This means if any sub-command results in a 'Deny', the entire compound command will be denied, and 'Ask' takes precedence over 'Allow' and 'Default'.
  • Sandbox Policy Aggregation: Developed a 'Strictest Wins' policy for merging sandbox presets across sub-commands. This involves intersecting writable roots, uniting denied paths, and intersecting network allow rules to ensure the most restrictive combined policy is applied.
  • Writable Roots Contradiction Handling: Added logic to detect contradictions in writable roots during sandbox policy aggregation. If the intersection of writable roots becomes empty while individual presets defined writable paths, the action is escalated to 'Ask' to prompt user confirmation.
  • New MergedSandboxPolicy Structure: Defined a new MergedSandboxPolicy struct to represent the resolved and aggregated sandbox policy, providing concrete values for enforcement rather than optional fields.
Changelog
  • src/config/model.rs
    • Added MergedSandboxPolicy struct for aggregated sandbox policies.
    • Implemented SandboxPreset::merge_strictest function to combine multiple sandbox presets using 'Strictest Wins' logic.
    • Added comprehensive unit tests for SandboxPreset::merge_strictest covering various merging scenarios.
  • src/rules/rule_engine.rs
    • Added CompoundEvalResult struct to encapsulate the action and merged sandbox policy for compound commands.
    • Introduced evaluate_compound function to parse, evaluate, and aggregate results for compound commands.
    • Implemented has_writable_contradiction helper function to detect conflicting writable roots in merged sandbox policies.
    • Added escalate_to_ask helper function to promote 'Allow' or 'Default' actions to 'Ask' on sandbox contradictions.
    • Refactored merge_results and added merge_actions to centralize action aggregation logic based on 'Explicit Deny Wins' priority.
    • Added extensive unit tests for evaluate_compound covering action and sandbox policy aggregation, as well as contradiction handling.
Using Gemini Code Assist

The 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 /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

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 .gemini/ folder in the base of the repository. Detailed instructions can be found here.

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

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces support for evaluating compound commands by splitting them into sub-commands and aggregating the results, which is a great feature for improving security rule granularity. The implementation for merging sandbox policies and aggregating actions seems robust. My review focuses on improving the test suite's structure to align with the repository's style guide by parameterizing similar test cases, and a small efficiency improvement in the new evaluate_compound logic.

Copy link
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 4 additional findings.

Open in Devin Review

Gemini Code Assist review pointed out redundant iteration in
has_writable_contradiction and test cases that should be parameterized
per the repository style guide.

Pass deduplicated unique_names to has_writable_contradiction instead of
the raw preset_names list. Parameterize similar test cases using rstest
in both model.rs (merge_strictest writable/network) and rule_engine.rs
(compound all_allow/deny_wins across operators).

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@codecov
Copy link

codecov bot commented Feb 18, 2026

Codecov Report

❌ Patch coverage is 98.01980% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 89.78%. Comparing base (c6fd568) to head (c073856).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
src/rules/rule_engine.rs 96.96% 3 Missing ⚠️
src/config/model.rs 99.02% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main      #49      +/-   ##
==========================================
+ Coverage   89.32%   89.78%   +0.45%     
==========================================
  Files          18       18              
  Lines        3803     4002     +199     
==========================================
+ Hits         3397     3593     +196     
- Misses        406      409       +3     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@fohte fohte merged commit 4395c0b into main Feb 19, 2026
5 checks passed
@fohte fohte deleted the fohte/impl-runok-init-compound-eval branch February 19, 2026 05:59
@fohte-bot fohte-bot bot mentioned this pull request Feb 18, 2026
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