Matchers
Matchers identify specific patterns in your code. Learn how to use calls() andvariable() to find function calls and variable references.
calls() Matcher
The calls() matcher finds function and method calls in your code. It's the most commonly used matcher for security rules.
Match a Single Function
Find all calls to a specific function:
from codepathfinder import rule, calls
@rule(id="detect-eval", severity="critical")
def detect_eval():
"""Find dangerous eval() calls"""
return calls("eval")This matches:
eval("some code") # Matched!
exec("other code") # Not matchedMatch Multiple Functions
Pass multiple function names to match any of them:
@rule(id="code-exec", severity="critical")
def detect_code_execution():
"""Find code execution functions"""
return calls("eval", "exec", "compile")This matches:
eval("x = 1") # Matched!
exec("print('hi')") # Matched!
compile("code", "", "exec") # Matched!Match Methods
Use dot notation to match methods on specific classes:
@rule(id="subprocess-shell", severity="high")
def detect_subprocess_shell():
"""Find subprocess.Popen with shell=True"""
return calls("subprocess.Popen", match_name={"shell": True})This matches:
subprocess.Popen("ls", shell=True) # Matched!
subprocess.Popen("ls", shell=False) # Not matchedWildcard Patterns
Use * wildcards to match multiple variations of function names. This is incredibly powerful for catching entire families of functions.
Module Wildcards
Match any method on a module:
@rule(id="requests-calls", severity="info")
def detect_http_requests():
"""Find all HTTP requests"""
return calls("requests.*")This matches:
requests.get("https://example.com") # Matched!
requests.post("https://api.com/data") # Matched!
requests.put("https://api.com/update") # Matched!
requests.delete("https://api.com/del") # Matched!Any Module Pattern
Match a method on any class or module:
@rule(id="sql-execute", severity="critical")
def detect_sql_execution():
"""Find SQL execute calls on any database connection"""
return calls("*.execute", "*.executemany")This matches:
cursor.execute("SELECT * FROM users") # Matched!
connection.execute("INSERT INTO logs") # Matched!
db.execute("UPDATE settings") # Matched!
session.executemany("DELETE FROM old") # Matched!Complete Wildcards
Match any function (use sparingly, can be slow):
@rule(id="any-password", severity="medium")
def detect_password_params():
"""Find any function call with a password parameter"""
return calls("*", match_name={"password": "*"})Performance Tip: Complete wildcards (*) check every function call in your codebase. Use them only when combined with argument matching or other constraints.
variable() Matcher
The variable() matcher finds variable references in your code. Useful for detecting usage of specific variable names.
Match Variable Names
from codepathfinder import rule, variable
@rule(id="debug-mode", severity="medium")
def detect_debug_variables():
"""Find DEBUG variable usage"""
return variable("DEBUG")This matches:
DEBUG = True # Matched!
if DEBUG: # Matched!
print(DEBUG) # Matched!Variable Wildcards
Use wildcards to match variable name patterns:
@rule(id="sensitive-vars", severity="high")
def detect_sensitive_variables():
"""Find variables with sensitive names"""
return variable("*PASSWORD*", "*SECRET*", "*API_KEY*")This matches:
DB_PASSWORD = "secret" # Matched!
SECRET_KEY = "xyz" # Matched!
API_KEY_PRODUCTION = "abc" # Matched!Combining Matchers
Use And, Or, and Not to create complex patterns.
Or - Match Any Pattern
from codepathfinder import rule, calls, Or
@rule(id="deserialization", severity="critical")
def detect_insecure_deserialization():
"""Find insecure deserialization"""
return Or(
calls("pickle.loads"),
calls("pickle.load"),
calls("yaml.unsafe_load"),
calls("yaml.load")
)And - All Must Match
from codepathfinder import rule, calls, variable, And
@rule(id="eval-with-input", severity="critical")
def detect_eval_with_user_input():
"""Find eval() called with user input variable"""
return And(
calls("eval"),
variable("user_input")
)Not - Exclude Patterns
from codepathfinder import rule, calls, Not
@rule(id="production-debug", severity="high")
def detect_debug_outside_tests():
"""Find debug calls outside test files"""
return And(
calls("print", "pprint"),
Not(calls("test_*"))
)Real-World Example:
@rule(id="comprehensive-sqli", severity="critical")
def detect_sql_injection_patterns():
"""Comprehensive SQL injection detection"""
return Or(
# Direct SQL execution
calls("*.execute", "*.executemany"),
# ORM raw queries
calls("*.raw"),
# String formatting into SQL
And(
calls("*.format"),
variable("*SQL*", "*QUERY*")
)
)