Skip to content

fix(scanner): Make per-language enabled_rules allowlists take effect - #141

Open
lelia wants to merge 3 commits into
mainfrom
lelia/language-_enabled_rules-has-no-effect-bare-ids-p
Open

lelia wants to merge 3 commits into
mainfrom
lelia/language-_enabled_rules-has-no-effect-bare-ids-p

Conversation

@lelia

@lelia lelia commented Nov 14, 2026 •

Copy link
Copy Markdown
Contributor

Summary

<language>_enabled_rules never narrowed a SAST scan. OpenGrep prefixes the ID of every rule it loads from a --config path with that path's directories (js-react-dangerous-html becomes socket-basics.socket_basics.rules.js-react-dangerous-html), so the --exclude-rule flags built from bare rule IDs never matched and every bundled rule ran. This PR makes the allowlist work, and keeps default scans unchanged by defaulting to every rule.

Behavior change: anyone who sets <language>_enabled_rules in a workflow, on the command line, or in the Socket Basics dashboard will see that language's scan narrow to the listed rules for the first time, so expect fewer SAST findings. The CHANGELOG upgrade note explains how to keep running every rule.

Changes

  • Filter fix: the scanner passes OpenGrep a temporary copy of each allowlisted rules file that holds only the enabled rules, instead of --exclude-rule flags, which OpenGrep matches against the path-prefixed rule IDs.
  • Fingerprints and triage preserved: Socket keys an injected SAST alert on its full props, including the OpenGrep fingerprint (computeAlertKey in depscan), and stores triage against that key. OpenGrep derives the fingerprint from a rule ID prefixed with the rules file's directory, not its file name. The filtered copy is therefore written next to the original, under a hidden .yaml name the *.yml rule globs skip, so kept findings report the same fingerprint, alert key and triage as an unfiltered scan. If the rules directory is not writable, the copy falls back to a temporary directory with a warning. The image runs as root, so Action users take the same-directory path.
  • Shared rules files: C and C++ share c_cpp.yml, and C# and .NET share dotnet.yml. When both languages in a pair are enabled and either has an empty list, the shared file now runs every rule instead of applying the other language's list.
  • Warnings: enabled IDs that match no rule are logged. An allowlist that matches nothing runs every rule for the language (with a warning), matching how custom rules already behaved.
  • Defaults: <language>_enabled_rules defaults are now empty in action.yml and connectors.yaml for all 15 languages, so every bundled rule runs, which is what scans already did. The Action defaults had drifted from the CLI defaults and named seven rule IDs that no longer exist; with the filter working they would have cut JavaScript scans from 59 rules to 6.
  • Unsupported languages: all-languages scans skip erlang.yml and objective-c.yml in every expansion path (two of the three were missing the exclusion). OpenGrep supports neither language and reported an UnknownLanguageError for each.
  • Temp files: the temporary custom-rule and filtered-rule directories are removed when the scan ends. Custom-rule directories were never cleaned up before, and the unused CustomRulesBuilder.cleanup() is removed.
  • Docs: docs/parameters.md and docs/github-action.md describe the new default, and their examples now use real rule IDs instead of names like sql-injection.

Testing

  • tests/test_opengrep_rule_filter_e2e.py runs the real OpenGrep binary on the bundled JavaScript rules and checks which rules report findings. It fails on main and passes here, on both OpenGrep 1.30.0 and the pinned v1.33.1. CI already installs OpenGrep and sets SOCKET_BASICS_REQUIRE_OPENGREP, so it runs there.
  • The end-to-end tests also check that findings kept by an allowlist report the same fingerprints as an unfiltered scan, on repeated runs. That test fails if the filtered copy is written to another directory.
  • Unit tests now check which rules are in the files OpenGrep receives, not the command line, and cover unknown IDs, an allowlist that matches nothing, all_rules_enabled, skipping unsupported rule files, temp-file cleanup after a normal scan, a missing OpenGrep binary, a crashing scan, and a failed custom-rule build, the fallback when the rules directory is read-only, and shared-file lists for C/C++ and C#/.NET (two of these fail on the previous code).
  • tests/test_action_inputs_consistency.py rejects any default rule list that names an unknown rule ID. It fails against the old action.yml.
  • Writing a filtered copy re-saves the rules YAML, so every bundled rules file was round-tripped and scanned against the same files with v1.33.1: findings were identical (110 across Java, Python, and JavaScript) with no new errors.
  • Full suite: 529 passed, 1 skipped.

Open question for review: if the dashboard pre-fills the <language>_enabled_rules fields with the old curated lists, those orgs will see their scans narrow on upgrade. That needs confirming with the dashboard owners before release.

Fixes #135
Fixes CE-496

OpenGrep prefixes the ID of every rule it loads from a --config path
with the path's directories, so the --exclude-rule flags built from bare
rule IDs never matched and every bundled rule ran regardless of
<language>_enabled_rules. The scanner now passes OpenGrep a temporary
copy of the rules file holding only the enabled rules. Enabled IDs that
match no rule are logged as a warning, and an allowlist that matches
nothing runs every rule for the language.

The <language>_enabled_rules defaults are now empty in action.yml and
connectors.yaml, so every bundled rule runs by default, as scans already
did. The Action defaults had drifted from the CLI defaults and named
seven rule IDs that no longer exist. A test now rejects default lists
that name unknown rule IDs.

Also:
- Skip erlang.yml and objective-c.yml in every all-languages expansion;
  OpenGrep supports neither language.
- Remove the temporary custom-rule and filtered-rule directories when
  the scan ends.
- Add an end-to-end test that runs the real OpenGrep binary.

Fixes #135
@lelia
lelia marked this pull request as ready for review November 14, 2026 03:32
@lelia
lelia requested a review from a team as a code owner November 14, 2026 03:32
@lelia

lelia commented Nov 14, 2026

Copy link
Copy Markdown
Contributor Author

bugbot run

@cursor

cursor Bot commented Nov 14, 2026 •

Copy link
Copy Markdown

PR Summary

Medium Risk
Enabling or dashboard-prefilled <language>_enabled_rules will narrow scans and reduce findings for the first time; default-empty configs preserve prior coverage but orgs with explicit allowlists need to verify expectations.

Overview
Fixes \<language\>_enabled_rules so it actually narrows SAST scans. The scanner no longer relies on --exclude-rule (which could not match OpenGrep’s path-prefixed rule IDs) and instead feeds OpenGrep temporary rule YAML containing only the allowlisted rules, with warnings for unknown IDs and a fallback to all rules when the list matches nothing.

Defaults and docs: <language>_enabled_rules defaults are now empty in action.yml and connectors.yaml, so unset configs still run every bundled rule (matching pre-fix behavior). Changelog, parameters, and GitHub Action docs are updated accordingly.

Other scan behavior: All-languages runs skip erlang.yml and objective-c.yml; custom and filtered rule temp dirs are removed after each scan. Tests add OpenGrep e2e coverage and guard default rule lists against stale IDs.

Reviewed by Cursor Bugbot for commit e901ffd. Configure here.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit e901ffd. Configure here.

with open(filtered_path, 'w', encoding='utf-8') as fh:
yaml.safe_dump({**data, 'rules': kept}, fh, sort_keys=False, allow_unicode=True)
logger.info("Enabled %d of %d rules for %s", len(kept), len(rules), rf)
config_args.extend(['--config', str(filtered_path)])

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Shared-file allowlist drops sibling rules

Medium Severity

c_cpp.yml and dotnet.yml are each keyed by filename, so one non-empty sibling allowlist becomes the filter for the whole file. --all-languages enables both c and cpp (and both csharp and dotnet). An empty sibling list is documented as “run every rule,” but those IDs are never added, so C++-only rules (and extra .NET rules) are dropped from the scan.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit e901ffd. Configure here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 3a11c65. When both languages that share c_cpp.yml or dotnet.yml are enabled and either one has an empty enabled-rules list, the shared file now runs every rule. Tests cover both pairs.

OpenGrep computes a finding's fingerprint from its rule ID, and by
default it prefixes that ID with the directories of the rule file's
--config path. Filtered and custom rule files live in a new temporary
directory on every run, so unchanged findings got a new fingerprint
each scan, which breaks deduplication in the JSON and Microsoft
Sentinel outputs. Pass --no-rewrite-rule-ids so the fingerprint depends
only on the rule and the code.

Also test that temporary rule directories are removed when OpenGrep is
missing, when the scan raises, and when building custom rules fails.
@lelia lelia changed the title fix(scanner): Make per-language enabled-rule allowlists take effect fix(scanner): Make per-language enabled_rules allowlists take effect Nov 14, 2026
Socket keys an injected SAST alert on its full props, including the
OpenGrep fingerprint, and stores triage against that key. The fingerprint
comes from a rule ID that OpenGrep prefixes with the rules file's
directories, not its file name. Passing --no-rewrite-rule-ids would have
given every SAST alert a new key once, dropping its triage.

Drop that flag and write each filtered copy next to the file it was made
from, under a hidden .yaml name that the *.yml rule globs skip. Kept
findings report the same fingerprint as an unfiltered scan. When the
rules directory is not writable, the copy falls back to a temporary
directory and the log warns that fingerprints will differ.

C and C++ share c_cpp.yml, and C# and .NET share dotnet.yml. When both
languages in a pair are enabled and either has an empty list, the shared
file now runs every rule instead of applying the other language's list.

This branch has not been deployed

No deployments
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.

<language>_enabled_rules has no effect (bare IDs passed to --exclude-rule), and 7 Action default rule IDs no longer exist

1 participant