fix(rules/java): match non-literal args in role/permission checks#12
fix(rules/java): match non-literal args in role/permission checks#12
Conversation
Five method-call rules required the role/permission/feature argument to
be a string literal. In idiomatic Java that argument is usually a
constant (`Role.ADMIN`, `Permissions.USER_DELETE`) or a variable, both
parsed as `field_access` or `identifier` — not `string_literal`. As a
result, calls like `acct.hasRole(Role.ADMIN)` and
`account.hasRole(role)` were silently missed, even though the same call
with a string literal was caught.
Expand the argument pattern to a tree-sitter `[...]` choice over
`string_literal`, `field_access`, and `identifier` for:
- has-role-call
- is-user-in-role
- role-equals-check
- shiro-is-permitted
- feature-gate-check
The Rego stub for non-literal cases substitutes the captured expression
text verbatim (e.g. `in {"Role.ADMIN"}`) — imperfect, but the finding is
recorded and the snippet shows the original call site for manual review.
Verified against the endorsed_resume tarball from #7: scan now reports
all 7 previously-missed `hasRole(Role.X)` / `hasRole(role)` call sites
across `AdminApiController`, `SecureControllerBase`, and `AdminService`.
Adds 8 cargo-test cases (one per non-literal form per affected rule)
plus 8 toml `rule.tests` fixtures.
Fixes #7.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthroughFive Java rules broaden Tree-sitter queries to match method/field arguments as string literals, field accesses, or bare identifiers; corresponding tests and Rust matcher unit tests were added to assert detection of these non-literal argument patterns. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
Comment |
There was a problem hiding this comment.
This PR successfully expands Java authorization rule patterns to detect role/permission checks with non-literal arguments (field accesses and identifiers), addressing the issue where calls like acct.hasRole(Role.ADMIN) were previously missed.
The implementation is sound across all five affected rule files, with consistent pattern expansion from single string_literal nodes to tree-sitter choice syntax accepting string_literal | field_access | identifier. The addition of 8 comprehensive test cases validates the new matching behavior.
The Rego stub limitation (substituting non-literal expressions verbatim) is appropriately acknowledged and doesn't block functionality - findings are correctly captured for manual review.
Approval: No blocking issues found. The changes are well-structured, thoroughly tested, and ready to merge.
You can now have the agent implement changes and create commits directly on your pull request's source branch. Simply comment with /q followed by your request in natural language to ask the agent to make changes.
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@rules/java/is-user-in-role.toml`:
- Around line 43-52: Add a positive test case covering identifier/variable role
arguments so variable-based calls don't regress: in the [[rule.tests]] section
add a new input snippet where request.isUserInRole(roleVar) (e.g., roleVar set
to Role.ADMIN or assigned from Role.ADMIN) is used and set expect_match = true;
reference the existing request.isUserInRole usage and name the new fixture
roleVar to parallel the existing Role.ADMIN test.
In `@rules/java/shiro-is-permitted.toml`:
- Around line 53-62: The test suite is missing a positive test for the
identifier-argument branch introduced by the pattern `(identifier) `@perm_value``;
add a new [[rule.tests]] case that defines a variable (e.g., String permVar =
Permissions.USER_DELETE or var permVar = Permissions.USER_DELETE) and calls
subject.isPermitted(permVar) so the query binds `perm_value` via identifier;
ensure expect_match = true and the input source includes the identifier usage
(subject.isPermitted(permVar)) to verify the `(identifier) `@perm_value`` branch
matches.
In `@src/scanner/matcher.rs`:
- Around line 381-391: The tests like
java_is_user_in_role_non_literal_arg_matches currently only cover field-access
args; add parallel identifier-based test cases that call parse_and_match_java
with a call using a bare identifier (e.g., request.isUserInRole(role) or
isUserInRole(roleVar)) against the same rule file (is-user-in-role.toml), assert
findings is not empty, and do the same pattern for the two other Java tests at
the other locations; use the same test helpers (parse_and_match_java) and keep
test names indicating "identifier_arg_matches" to mirror the existing
field-access tests for future parity and safety.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 57323497-aa70-4a3a-be04-61974a4644b6
📒 Files selected for processing (6)
rules/java/feature-gate-check.tomlrules/java/has-role-call.tomlrules/java/is-user-in-role.tomlrules/java/role-equals-check.tomlrules/java/shiro-is-permitted.tomlsrc/scanner/matcher.rs
CodeRabbit flagged that the new non-literal-arg branches matched `(identifier)` in the tree-sitter query, but tests only exercised the `(field_access)` form. Add identifier-arg coverage so a regression on the bare-variable path can't slip through unnoticed. - rules/java/is-user-in-role.toml: positive fixture for `roleVar` - rules/java/shiro-is-permitted.toml: positive fixture for `permVar` - src/scanner/matcher.rs: three sibling `_identifier_arg_matches` cargo tests for is-user-in-role, shiro-is-permitted, feature-gate (has-role-call and role-equals-check already had both forms).
Summary
Five Java method-call rules required the role/permission/feature argument to be a string literal. In idiomatic Java that argument is usually a constant (
Role.ADMIN,Permissions.USER_DELETE) or a variable — parsed by tree-sitter asfield_accessoridentifier, notstring_literal. As a result, calls likeacct.hasRole(Role.ADMIN)andaccount.hasRole(role)were silently missed even though the same call with a string literal was caught.Verified against the
endorsed_resume_src.tar.gzattached to #7:Before this PR — 4 findings, no
hasRolecalls detected.After this PR — 11 findings, including all 7 previously-missed
hasRolecall sites:Changes
For each affected rule, expand the argument pattern from a single
string_literalto a tree-sitter[...]choice overstring_literal | field_access | identifier, all captured under the same name:rules/java/has-role-call.toml—hasRole/hasAnyRole/hasAuthority/hasAnyAuthorityrules/java/is-user-in-role.toml—request.isUserInRole(...)rules/java/role-equals-check.toml—getRole().equals(...)rules/java/shiro-is-permitted.toml—subject.isPermitted(...)/checkPermission(...)rules/java/feature-gate-check.toml—hasFeature/isFeatureEnabled/hasPlan/...Annotation rules (
@Secured,@PreAuthorize,@RolesAllowed,@RequiresPermissions,@RequiresRoles) are intentionally not changed — annotation arguments are nearly always string literals in practice and the existing pattern is correct for the common case.Rego stub behaviour for non-literal args
The Rego stub substitutes the captured expression text verbatim:
These are imperfect — they need manual review before they're useful — but the finding is now recorded with the original snippet, which was the user's main complaint in #7. Improving the stub for non-literal cases (e.g., emitting a
# TODO: resolve constant valuecomment) is a separate, smaller change that would benefit from--deepLLM-assisted resolution down the road.Tests
cargo testcases insrc/scanner/matcher.rs(one per non-literal form per affected rule)[[rule.tests]]toml fixtures (run viazift rules test)The 5
rules testfailures are pre-existing and unrelated to this PR:java-security-interface-impl— fixed in fix(rules/java): repair invalid query in security-interface-impl #11 (already merged), so this branch will rebase clean.java-access-decision-voter[1]— flagged in fix(rules/java): repair invalid query in security-interface-impl #11 as a separate follow-up.Fixes #7.
Test plan
cargo fmt -- --checkcargo clippy --tests -- -D warningscargo testendorsed_resume_src.tar.gzfrom Zift did not discover all instances of authorization, even though they follow patterns similar to the ones that it did find #7 — all 7 missedhasRolecalls now detectedvbench-server-src.tar.gzfrom Zift did not find the major authorization logic in this app #6 — same baseline findings (no false positives introduced)Summary by CodeRabbit