Skip to content

add word boundaries to regex patterns to reduce false positives#1125

Open
Shaktisinhchavda wants to merge 2 commits intomandiant:masterfrom
Shaktisinhchavda:fix/add-word-boundaries
Open

add word boundaries to regex patterns to reduce false positives#1125
Shaktisinhchavda wants to merge 2 commits intomandiant:masterfrom
Shaktisinhchavda:fix/add-word-boundaries

Conversation

@Shaktisinhchavda
Copy link

closes #1109

@Shaktisinhchavda
Copy link
Author

@mr-tz please review.

Copy link
Collaborator

@mr-tz mr-tz left a comment

Choose a reason for hiding this comment

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

have you also looked at the substring features?

Comment on lines 27 to 28
- string: /(?<!\w)ida[gqtuw]?(\.exe)?$/i
- string: /ida[gqtuw]?64(\.exe)?$/i
Copy link
Collaborator

Choose a reason for hiding this comment

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

what about these?

Copy link
Author

Choose a reason for hiding this comment

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

Thanks for the review, @mr-tz!

-- IDA Regexes (Lines 27-28): Good catch. I missed these because they had slightly different structures (specifically the lookbehind and end-of-string anchor). I've updated them to "/\bida[gqtuw]?(.exe)?\b/i" and "/\bida[gqtuw]?64(.exe)?\b/i" for consistency with the rest of the file.

-- Substring Features: I primarily focused on existing regex string patterns in this pass. However, searching the ruleset, I see several substring features (like ifconfig, TouchSocket, and CHttpModule) that would definitely benefit from word boundaries to reduce false positives. I'll start converting these to regex string features with \b boundaries as suggested.

I've pushed an update with the IDA fixes. Should I include the substring conversions in this PR, or would you prefer a separate one focused on that?

- or:
- api: EnumDeviceDrivers
- string: /driverquery(\.exe)?/i
- string: /\bdriverquery(\.exe)?\b/i
Copy link
Collaborator

Choose a reason for hiding this comment

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

how did you decide when to have a trailing \b and when not to?

Copy link
Author

Choose a reason for hiding this comment

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

The \b was intended to prevent partial matches like driverqueryhost. I agree it’s messy, so I propose switching to dual substring entries instead (e.g., driverquery and driverquery.exe). It provides the same protection with much better readability. What do you think? @williballenthin

Copy link
Collaborator

Choose a reason for hiding this comment

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

why do you have one \b for some strings and two \b in other strings?

Copy link
Author

Choose a reason for hiding this comment

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

The inconsistency was an oversight during the update. Switching to the proposed dual substring approach will fix this entirely and ensure consistent whole-word matching across all rules.

- string: /\brekall(\.exe)?\b/i
- string: /\btcpdump(\.exe)?\b/i
- string: /\bwindasm(\.exe)?\b/i
- string: /\bx32dbgn(\.exe)?\b/i
Copy link
Collaborator

Choose a reason for hiding this comment

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

with this change, the patterns are much harder to read as a human, which is unfortunate.

this isn't a comment on the quality of the PR, just about what we're considering.

it seems many of these regexes are regexes versus substrings so that we can match the optional extension in a single pass. we could replace these with something like:

- substring: foo
- substring: foo.exe

this would be much easier to read (the \b would happen behind the scenes), though presumably runtime would be longer, because there are twice as many regexes to run.

with a little bit of benchmarking we could show this is or is not acceptable. thoughts @mr-tz @mike-hunhoff @Shaktisinhchavda ?

Copy link
Author

Choose a reason for hiding this comment

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

Agreed, the regexes are hard to audit. I propose refactoring them into simple substring pairs for the tool names and extensions. This prioritizes clarity while maintaining the same logic. I’m happy to update the PR this way if you agree.

Copy link
Collaborator

Choose a reason for hiding this comment

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

yes, this is what i proposed above. please suggest a plan that includes benchmarking capa before/after to show that the runtime doesn't change substantially when introducing additional substring features.

Copy link
Collaborator

Choose a reason for hiding this comment

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

in the scenario that runtime is worse, we can imagine an optimization to capa that translates an or(substring, substring, substring) into a single regex that matches in a single pass. so i don't think this is insurmountable. but we should still check the data before committing to the changes.

Copy link
Author

Choose a reason for hiding this comment

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

agreed that data should drive the decision. Here is my plan for benchmarking the performance impact:

--Test Set: Run capa against the complete capa-testfiles repository.
Evaluation: Compare average wall-clock runtime across three passes for the current regex rules versus the proposed substring refactor.
--Verification: Confirm that match results and hit counts remain identical for all samples.

I’ll share the results here once complete. I also agree that if we do see a performance hit, an engine-level optimization to merge OR-ed substrings would be an excellent long-term solution.

Copy link
Author

Choose a reason for hiding this comment

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

I've completed the benchmarking using a 3-pass average against capa-testfiles. Results confirmed identical hits but showed a ~55% increase in average runtime (from 1.04s up to 1.62s per file).

While the delta is ~0.6s, I believe the significant gain in readability and auditability justifies the overhead. We could potentially recover this performance later via the engine-level optimization mentioned. Is this trade-off acceptable to the team? @williballenthin

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.

use word boundaries to reduce FPs when detecting strings using regular expressions

3 participants