Skip to content

feat(rules): support redirects and pipe variables in when clauses#203

Merged
fohte merged 7 commits intomainfrom
fohte/add-redirects-pipe-when-variables
Mar 14, 2026
Merged

feat(rules): support redirects and pipe variables in when clauses#203
fohte merged 7 commits intomainfrom
fohte/add-redirects-pipe-when-variables

Conversation

@fohte
Copy link
Owner

@fohte fohte commented Mar 13, 2026

Why

  • when clauses cannot reference redirect (>, >>, 2>&1) or pipe (|) information for rule evaluation
    • runok strips redirects and pipes before rule evaluation, making the shell's connection structure invisible to when expressions
    • Use cases: enforcing output redirects on specific commands, blocking piped stdin execution (curl | sh)

What

  • Add two new context variables redirects and pipe available in when expressions
    • redirects: list of redirect operators attached to the command (type, operator, target, descriptor)
    • pipe: pipeline position of the command (stdin, stdout)
  • Extract redirect/pipe metadata from the tree-sitter AST and pass it through to the CEL expression evaluation context

Open with Devin

The `when` clause could only reference env, flags, args, and paths,
making it impossible to write rules based on shell redirect operators
or pipeline position. This prevented use cases like requiring output
redirects for noisy commands or blocking `curl | sh` patterns.

Add two new context variables available in CEL `when` expressions:
- `redirects`: list of redirect operators (type, operator, target,
  descriptor) attached to the command
- `pipe`: object with stdin/stdout bools indicating pipeline position

Extract redirect/pipe metadata from the tree-sitter AST during command
parsing via `extract_commands_with_metadata`, thread it through
evaluate_compound -> evaluate_command_inner -> build_expr_context,
and register as CEL variables.

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

Summary of Changes

Hello, 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 extends the capabilities of rule evaluation by exposing shell-level constructs like redirects and pipes to the when clauses. This allows for the creation of more sophisticated and security-focused rules, such as preventing dangerous piped commands or ensuring proper output handling. The changes involve updates to the command parsing, expression evaluation, and rule engine components to correctly extract and utilize this new metadata, along with comprehensive documentation and integration tests.

Highlights

  • Enhanced Rule Evaluation: Introduced the ability for when clauses in rules to reference shell redirect (>, >>, 2>&1) and pipe (|) information, enabling more granular control over command execution.
  • New Context Variables: Added two new context variables, redirects and pipe, available within when expressions. redirects provides a list of redirect operators and their details, while pipe indicates the command's position within a pipeline (stdin/stdout).
  • AST Metadata Extraction: Implemented logic to extract redirect and pipe metadata directly from the tree-sitter Abstract Syntax Tree (AST) during command parsing, making this information available for Common Expression Language (CEL) evaluation.
  • Updated Documentation and Examples: Expanded the when-clause documentation to include detailed explanations and practical examples for using the new redirects and pipe variables, demonstrating use cases like blocking curl | sh or enforcing output redirects.
Changelog
  • docs/src/content/docs/rule-evaluation/when-clause.md
    • Updated the count of available context variables from four to six.
    • Added a new section detailing the redirects context variable, including its structure, types, and usage examples.
    • Introduced a new section for the pipe context variable, explaining its boolean fields (stdin, stdout) and providing examples.
    • Included new examples for redirect-based gating and pipe safety in the 'Examples' section.
  • src/rules/command_parser.rs
    • Defined new structs RedirectInfo, PipeInfo, and ExtractedCommand to encapsulate redirect and pipe metadata.
    • Refactored extract_commands to use a new extract_commands_with_metadata function, which now returns ExtractedCommand objects.
    • Modified the collect_commands function to accept and propagate PipeInfo and RedirectInfo through the AST traversal.
    • Implemented extract_redirect_info and classify_redirect helper functions to parse redirect details from tree-sitter nodes.
    • Added extensive unit tests for extract_commands_with_metadata covering various redirect and pipeline scenarios.
  • src/rules/expr_evaluator.rs
    • Extended the ExprContext struct to include redirects (vector of RedirectInfo) and pipe (PipeInfo) fields.
    • Updated the evaluate function to register redirects and pipe variables with the CEL interpreter, converting them into appropriate cel_interpreter::Value types.
    • Added new unit tests to verify correct access and evaluation of the redirects and pipe variables within CEL expressions.
  • src/rules/rule_engine.rs
    • Modified evaluate_command_inner and evaluate_simple_command functions to accept redirects and pipe arguments.
    • Updated evaluate_compound to utilize extract_commands_with_metadata for parsing commands and passing the extracted metadata to nested evaluations.
    • Adjusted the build_expr_context function to populate the redirects and pipe fields in the ExprContext for rule evaluation.
  • tests/integration/when_clause_rules.rs
    • Added new integration tests for redirect_output_exists to check rules based on the presence of output redirects.
    • Included tests for redirect_target_starts_with to validate rules that inspect redirect destinations.
    • Implemented tests for pipe_stdin_prevents_shell_execution to ensure rules can block commands receiving piped input (e.g., curl | sh).
    • Added tests for redirect_descriptor_check to verify rules can evaluate based on specific file descriptors in redirects.
    • Introduced tests for redirect_count_check to confirm rules can check the total number of redirects.
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.

gemini-code-assist[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

@codecov
Copy link

codecov bot commented Mar 13, 2026

Codecov Report

❌ Patch coverage is 92.27941% with 21 lines in your changes missing coverage. Please review.
✅ Project coverage is 89.57%. Comparing base (ffee2ec) to head (6bb4220).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
src/rules/command_parser.rs 93.38% 9 Missing ⚠️
src/adapter/mod.rs 66.66% 6 Missing ⚠️
src/rules/rule_engine.rs 92.59% 6 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #203      +/-   ##
==========================================
+ Coverage   89.53%   89.57%   +0.04%     
==========================================
  Files          49       49              
  Lines        9574     9808     +234     
==========================================
+ Hits         8572     8786     +214     
- Misses       1002     1022      +20     
Flag Coverage Δ
Linux 89.48% <92.27%> (+0.04%) ⬆️
macOS 90.80% <91.91%> (+0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ 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.

The `>|` operator in the docs markdown table contained an unescaped
pipe character, breaking the table rendering. Pipeline unit tests
were separate functions instead of parameterized cases, violating the
repository's rstest style guide.

Escape `|` in the operator table and consolidate pipeline tests into
a single `#[rstest]` parameterized test.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
devin-ai-integration[bot]

This comment was marked as resolved.

…tadata

evaluate_command() always passed empty redirects and default PipeInfo
to evaluate_command_inner(), so `when` clauses referencing `redirects`
or `pipe` always saw empty values for single commands. This meant
rules like `when: 'redirects.exists(r, r.type == "output")'` never
matched when evaluated through the evaluate_command path.

Fix evaluate_command() to extract metadata via
extract_commands_with_metadata() for single commands, and add
evaluate_command_with_metadata() for callers (like the adapter) that
already have pre-extracted metadata. Update the adapter to use
extract_commands_with_metadata() and pass the metadata through.
devin-ai-integration[bot]

This comment was marked as resolved.

When extract_commands_with_metadata() failed (e.g., SyntaxError from
malformed input), the fallback was changed from vec![command.clone()]
to vec![], which hit the commands.is_empty() branch and returned
default_action() without evaluating any rules. This could silently
allow commands that should be denied.

Restore the original behavior: on parse failure, fall back to an
ExtractedCommand containing the raw command with empty metadata.
devin-ai-integration[bot]

This comment was marked as resolved.

The note stated that redirects are only populated through
evaluate_compound, but evaluate_command now also extracts and passes
redirect/pipe metadata for single commands.
…ipe-when-variables

# Conflicts:
#	docs/src/content/docs/rule-evaluation/when-clause.md
#	src/rules/expr_evaluator.rs
#	src/rules/rule_engine.rs
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 found 1 new potential issue.

View 15 additional findings in Devin Review.

Open in Devin Review

The pipeline handler computed PipeInfo based solely on the child's
index position, ignoring the parent pipe context. For nested
pipelines like `cmd1 | (cmd2 | cmd3)`, cmd2 would get
stdin=false instead of stdin=true, causing when clauses like
`pipe.stdin` to incorrectly evaluate to false.

Compose parent and local pipe context with logical OR so that
nested pipeline commands inherit the outer pipeline's position.
@fohte fohte merged commit a513e1b into main Mar 14, 2026
10 checks passed
@fohte fohte deleted the fohte/add-redirects-pipe-when-variables branch March 14, 2026 03:59
@fohte-bot fohte-bot bot mentioned this pull request Mar 14, 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