[rule]
id = "java-ownership-check"
languages = ["java"]
category = "ownership"
confidence = "medium"
description = "Resource ownership comparison where at least one side names a principal-flavored getter (e.g., getUserId().equals(resource.getOwnerId()))"
query = """
(method_invocation
object: (method_invocation
name: (identifier) @getter)
name: (identifier) @method_name
arguments: (argument_list
(method_invocation
name: (identifier) @other_getter))
) @match
"""
[rule.predicates.getter]
match = "(?i)^(getId|getUserId|getOwnerId|getCreatedBy|getAuthorId)$"
[rule.predicates.method_name]
eq = "equals"
[rule.predicates.other_getter]
match = "(?i)^(getId|getUserId|getOwnerId|getCreatedBy|getAuthorId)$"
[[rule.cross_predicates]]
kind = "any_match"
captures = ["getter", "other_getter"]
match = "(?i)^(getUserId|getOwnerId|getCreatedBy|getAuthorId)$"
[[rule.tests]]
input = """
public class Service {
public void check() {
if (user.getUserId().equals(resource.getOwnerId())) { allow(); }
}
}
"""
expect_match = true
[[rule.tests]]
input = """
public class Service {
public void check() {
if (user.getName().equals(other.getName())) { compare(); }
}
}
"""
expect_match = false
[[rule.tests]]
input = """
public class Service {
public void check() {
if (record.getId().equals(other.getOwnerId())) { allow(); }
}
}
"""
expect_match = true
[[rule.tests]]
input = """
public class AuditFileQueueSpool {
public void check() {
if (record.getId().equals(indexRecord.getId())) { skip(); }
}
}
"""
expect_match = false
[[rule.tests]]
input = """
public class XUserPermissionServiceBase {
public void check() {
if (xUserPerm.getId().equals(mObj.getId())) { skip(); }
}
}
"""
expect_match = false