Skip to content

fix(rules): match FlagWithValue patterns against =-joined tokens#180

Merged
fohte merged 4 commits intomainfrom
fohte/fix-flag-equals-matching
Mar 10, 2026
Merged

fix(rules): match FlagWithValue patterns against =-joined tokens#180
fohte merged 4 commits intomainfrom
fohte/fix-flag-equals-matching

Conversation

@fohte
Copy link
Owner

@fohte fohte commented Mar 10, 2026

Why

  • FlagWithValue patterns (e.g. [--sort *]) did not match =-joined tokens (e.g. --sort=-committerdate)
    • Space-separated form (--sort value) matched, but =-joined form (--sort=value) failed because the flag was compared by exact match
    • The =-joined handling already existed for flag-only negation (match_flag_token_with_equals) but was not applied to FlagWithValue

What

  • Split =-joined tokens into flag and value parts in FlagWithValue matching
    • Also recognize =-joined form in optional_flags_absent to prevent false "flag absent" detection

Open with Devin

`FlagWithValue` patterns like `[--sort *]` only matched space-separated
forms (`--sort value`) but not `=`-joined forms (`--sort=value`). This
caused commands like `git branch -a --sort=-committerdate` to fall
through to `ask` instead of matching the allow rule.

The `=`-joined splitting was already implemented for flag-only negation
(`match_flag_token_with_equals`) but was missing from `FlagWithValue`
matching and `optional_flags_absent` detection.

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

codecov bot commented Mar 10, 2026

Codecov Report

❌ Patch coverage is 88.23529% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 89.90%. Comparing base (1d6c757) to head (15e18ae).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
src/rules/pattern_matcher.rs 88.23% 6 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #180      +/-   ##
==========================================
- Coverage   89.92%   89.90%   -0.02%     
==========================================
  Files          38       38              
  Lines        7225     7273      +48     
==========================================
+ Hits         6497     6539      +42     
- Misses        728      734       +6     
Flag Coverage Δ
Linux 89.77% <88.23%> (-0.02%) ⬇️
macOS 91.62% <88.23%> (-0.03%) ⬇️

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.

devin-ai-integration[bot]

This comment was marked as resolved.

fohte and others added 2 commits March 10, 2026 21:40
The `extract_placeholder_all` function (used for wrapper command
placeholder extraction) had the same missing `=`-joined support as
`match_tokens_core`. Wrapper patterns with `FlagWithValue` (e.g.
`run -m|--mode debug <cmd>`) would fail to extract the placeholder
when the flag was `=`-joined (e.g. `run -m=debug echo hello`).

Co-Authored-By: Claude Opus 4.6 <[email protected]>
The `=`-joined matching behavior for `FlagWithValue` patterns was not
documented. Users would not know that `--sort=-committerdate` is
matched by a `[--sort *]` pattern without reading the source code.

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

This comment was marked as resolved.

…test

Per CLAUDE.md rules, tests that differ only in input should use
`#[rstest]` with `#[case]` instead of separate functions.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@fohte fohte merged commit 6ebb296 into main Mar 10, 2026
10 checks passed
@fohte fohte deleted the fohte/fix-flag-equals-matching branch March 10, 2026 13:01
@fohte-bot fohte-bot bot mentioned this pull request Mar 10, 2026
@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 addresses a critical bug in the pattern matching system where FlagWithValue patterns failed to recognize flags and their associated values when joined by an equals sign (e.g., --flag=value). The changes enhance the core matching logic to correctly parse these =-joined forms, ensuring consistent and robust pattern matching behavior regardless of the flag-value syntax used. This significantly improves the reliability of rules that depend on FlagWithValue patterns.

Highlights

  • Enhanced Flag Matching: FlagWithValue patterns now correctly match =-joined tokens (e.g., --sort=-committerdate), resolving a previous limitation where only space-separated forms were recognized.
  • Improved Optional Flag Detection: The optional_flags_absent logic was updated to accurately detect the presence of =-joined flags, preventing false "flag absent" detections.
  • Documentation Updates: The pattern matching documentation has been expanded to clarify the support for =-joined flag values in FlagWithValue and optional groups.
  • Comprehensive Testing: New unit and integration tests were added to validate the correct behavior of =-joined flag matching across various scenarios.
Changelog
  • docs/src/content/docs/architecture/pattern-matching.md
    • Updated the description of FlagWithValue to explicitly mention support for =-joined forms.
  • docs/src/content/docs/pattern-syntax/matching-behavior.md
    • Added a new section detailing how =-joined flag values are matched, including examples.
  • docs/src/content/docs/pattern-syntax/optional-groups.md
    • Included new examples to illustrate the matching of =-joined flag values within optional groups.
  • src/rules/pattern_matcher.rs
    • Modified match_tokens_core and extract_placeholder_all functions to correctly parse and match =-joined flag values for FlagWithValue patterns.
    • The optional_flags_absent function was also updated to recognize =-joined flags when checking for their presence.
  • tests/integration/optional_notation_and_path_ref.rs
    • Added new integration test cases to verify the correct handling of =-joined flag values in optional notation and FlagWithValue patterns.
Activity
  • The pull request was authored by fohte.
  • An automated review was performed by Devin.
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 addresses an issue where FlagWithValue patterns didn't match =-joined tokens. However, the current fix is incomplete, introducing a potential security bypass in wrapper command extraction when =-joined flag syntax is used, which violates established rules for security pattern matching and literal token parsing. Additionally, there's an opportunity to reduce code duplication in match_tokens_core to improve maintainability.

Comment on lines +222 to +245
// Case 2: `=`-joined flag and value (e.g. `--sort=value`)
if let Some(eq_pos) = cmd_tokens[i].find('=') {
let flag_part = &cmd_tokens[i][..eq_pos];
let value_part = &cmd_tokens[i][eq_pos + 1..];
if flag_part.starts_with('-')
&& aliases.iter().any(|a| a.as_str() == flag_part)
&& match_single_token(value, value_part, definitions)
{
let remaining = remove_indices(cmd_tokens, &[i]);
if let Some(ref mut caps) = captures {
let saved_len = caps.len();
if matches!(value.as_ref(), PatternToken::Wildcard) {
caps.push(value_part);
}
if match_tokens_core(rest, &remaining, definitions, steps, Some(*caps))
{
return true;
}
caps.truncate(saved_len);
} else if match_tokens_core(rest, &remaining, definitions, steps, None) {
return true;
}
}
}

Choose a reason for hiding this comment

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

security-high high

The =-joined flag matching for FlagWithValue patterns is incomplete in extract_placeholder_all, leading to a security bypass. This allows wrapper patterns to be circumvented if flags are provided in =-joined form, potentially bypassing security rules. This violates the principle that pattern matching for security rules must check all occurrences of flags in the raw input, and that rules must match tokens literally, especially for flags with attached values. The same =-joined matching logic applied elsewhere should also be implemented in extract_placeholder_all for PatternToken::FlagWithValue. Additionally, consider refactoring the duplicated logic for space-separated and =-joined FlagWithValue matching in match_tokens_core to improve maintainability.

References
  1. For command parsing, pattern matching for security rules should be performed on the raw, tokenized input (raw_tokens) to ensure all occurrences of flags are checked. Incomplete matching for =-joined flags can lead to security bypasses, violating this rule.
  2. The command parser should follow a 'What You See Is How It Parses' principle. Rules must match tokens literally, especially for flags with attached values (e.g., -XPOST or --flag=value), and incomplete matching for these forms violates this principle.

fohte added a commit that referenced this pull request Mar 10, 2026
…into single match_engine

Two nearly-identical recursive matching functions (match_tokens_core for
boolean matching, extract_placeholder_all for placeholder extraction)
duplicated all PatternToken variant handling. Bug fixes applied to one
function were frequently missed in the other (PR #175, #179, #180).

Merge both into a single `match_engine` function parameterized by an
optional `extract` tuple. Each PatternToken variant's logic now exists
in exactly one place.

Also introduce a flag normalization layer (`split_flag_equals`,
`flag_aliases_match_token`) to centralize `--flag=value` splitting,
replacing the ad-hoc `=`-joined handling scattered across
match_flag_token_with_equals, FlagWithValue, and optional_flags_absent.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
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