
Irify Sast
- 31 installs
- 4 repo stars
- Updated February 26, 2026
- yaklang/irify-sast-skill
Helps with ai & agent building tasks during AI-assisted development.
About
irify-sast is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- irify-sast
- AI & Agent Building
- AI-coding skill
Irify Sast by the numbers
- 31 all-time installs (skills.sh)
- Ranked #9,202 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Jul 27, 2026 (Skillselion catalog sync)
npx skills add https://github.com/yaklang/irify-sast-skill --skill irify-sastAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 31 |
|---|---|
| repo stars | ★ 4 |
| Last updated | February 26, 2026 |
| Repository | yaklang/irify-sast-skill ↗ |
What it does
Helps with ai & agent building tasks during AI-assisted development.
Files
IRify SAST
Deep static analysis skill powered by IRify's SSA compiler and SyntaxFlow query engine.
Prerequisites
This skill requires the yaklang MCP server. Configure it in your agent's MCP settings:
# Codex: ~/.codex/config.toml
[mcp_servers.yaklang-ssa]
command = "yak"
args = ["mcp", "-t", "ssa"]// Claude Code / Cursor / others
{ "command": "yak", "args": ["mcp", "-t", "ssa"] }Workflow: Engine-First (sf → read → rg)
CRITICAL: Always follow the Engine-First funnel model. The SSA engine sees cross-procedure data flow across all files simultaneously — grep cannot. Do NOT use grep/rg to build a "candidate file pool" before querying. Instead, let the engine be your radar first.
Step 1: Compile (once per project, auto-cached)
ssa_compile(target="/path/to/project", language="java", program_name="MyProject")
→ full compilation, returns program_nameAuto Cache: If the program was already compiled and source files haven't changed, the engine returns [Cache Hit] instantly — no recompilation. Always provide a program_name to enable caching.
Step 2: Query — use SyntaxFlow as the global radar
Directly compose and execute SyntaxFlow rules against the compiled IR. Do NOT pre-scan with grep to find candidates.
ssa_query(program_name="MyProject", rule="<SyntaxFlow rule>")The engine traverses the entire SSA graph in memory, crossing all file boundaries. One query covers what would take dozens of grep commands, with zero false positives on data flow.
Step 3: Read — use Read as the microscope
After ssa_query returns concrete file paths and line numbers, use Read to examine surrounding context (±20 lines). Verify whether the hit is real business code or dead/test code.
Step 4: Grep — use Grep/Glob only for non-code files
Use Grep/Glob only for content that the SSA engine does not process:
- Configuration files (
.yml,.xml,.properties,logback.xml) - Static resources, templates, build scripts
- Quick name/path lookups when you already know the exact string
NEVER use Grep to search for data flow patterns in source code — that is what ssa_query is for.
Incremental Compile (when code changes)
ssa_compile(target="/path/to/project", language="java", base_program_name="MyProject")
→ only changed files recompiled, ProgramOverLay merges base + diff layers
→ returns NEW program_name for subsequent queriesIMPORTANT: Use base_program_name for incremental compilation. re_compile=true is a full recompile that discards all data — only use it to start completely fresh.
Self-Healing Query (auto-retry on syntax error)
When ssa_query returns a SyntaxFlow parsing error: 1. DO NOT apologize to the user or ask for help 2. Read the error message — it contains the exact parse error position and expected tokens 3. Fix the SyntaxFlow rule based on the error 4. Re-invoke ssa_query with the corrected rule 5. Repeat up to 3 times before reporting failure 6. If all retries fail, show the user: the original rule, each attempted fix, and the final error
Critical: Follow User Intent
DO NOT automatically construct source→sink vulnerability rules unless the user explicitly asks for vulnerability detection.
- User asks "find user inputs" → write a source-only rule, list all input endpoints
- User asks "find SQL injection" → write a source→sink taint rule
- User asks "where does this value go" → write a forward trace (
-->) rule - User asks "what calls this function" → write a call-site rule
Source-Only Query Examples (Java)
When the user asks about user inputs, HTTP endpoints, or controllable parameters:
// Find all Spring MVC controller handler methods
*Mapping.__ref__?{opcode: function} as $endpoints;
alert $endpoints;// Find all user-controllable parameters in Spring controllers
*Mapping.__ref__?{opcode: function}<getFormalParams>?{opcode: param && !have: this} as $params;
alert $params;// Find GetMapping vs PostMapping endpoints separately
GetMapping.__ref__?{opcode: function} as $getEndpoints;
PostMapping.__ref__?{opcode: function} as $postEndpoints;
alert $getEndpoints;
alert $postEndpoints;Source→Sink Query Examples (only when user asks for vulnerability detection)
// RCE: trace user input to exec()
Runtime.getRuntime().exec(* #-> * as $source) as $sink;
alert $sink for {title: "RCE", level: "high"};// SQL Injection (MyBatis): detect ${} unsafe interpolation in XML mappers / annotations
// <mybatisSink> is a dedicated NativeCall that finds all MyBatis ${} injection points
<mybatisSink> as $sink;
$sink#{
until: `* & $source`,
}-> as $result;
alert $result for {title: "SQLi-MyBatis", level: "high"};Proactive Security Insights
After running a query and finding results, proactively raise follow-up questions and suggestions. Do NOT just dump results and stop.
When vulnerabilities are found:
1. Suggest fix: "This exec() call receives unsanitized user input. Consider using a whitelist or ProcessBuilder with explicit argument separation." 2. Ask related questions:
- "Should I check if there are other endpoints that also call
Runtime.exec()?" - "Want me to trace whether any input validation/sanitization exists between the source and sink?"
- "Should I look for similar patterns in other controllers?"
3. Cross-reference: If one vulnerability type is found, proactively scan for related types:
- Found RCE → "I also checked for SSRF and found 2 potential issues. Want details?"
When no results are found:
1. Don't just say "no results" — explain WHY:
- "No direct
exec()calls found, but I seeProcessBuilderusage. Want me to check those instead?" - "The query matched 0 sinks. This could mean the code uses a framework abstraction — want me to search for framework-specific patterns?"
2. Suggest alternative queries
When results are ambiguous:
1. Ask for clarification: "I found 8 data flow paths to executeQuery(), but 5 use parameterized queries (safe). Want me to filter to only the 3 using string concatenation?"
Companion Reference Files
When writing SyntaxFlow rules, read these files using the Read tool for syntax help and real-world examples:
| File | When to Read | Path (relative to this file) |
|---|---|---|
| NativeCall Reference | When writing rules that need <nativeCallName()> functions — all 40+ NativeCall functions with syntax and examples | nativecall-reference.md |
| SyntaxFlow Examples | When writing new rules — 20+ production rules covering Java/Go/PHP/C, organized by vulnerability type | syntaxflow-examples.md |
Workflow: 1. Read syntaxflow-examples.md to find a similar rule pattern 2. Need a NativeCall? Read nativecall-reference.md 3. Compose and execute via ssa_query
SyntaxFlow Quick Reference
Search & Match
documentBuilder // variable name
.parse // method name (dot prefix)
documentBuilder.parse // chain
*config* // glob pattern
/(get[A-Z].*)/ // regex patternFunction Call & Parameters
.exec() // match any call
.exec(* as $params) // capture all params
.parse(*<slice(index=1)> as $a1) // capture by indexData Flow Operators
| Operator | Direction | Use |
|---|---|---|
#> | Up 1 level | Direct definition |
#-> | Up recursive | Trace to origin — "where does this COME FROM?" |
-> | Down 1 level | Direct usage |
--> | Down recursive | Trace to final usage — "where does this GO TO?" |
.exec(* #-> * as $source) // trace param origin
$userInput --> as $sinks // trace where value goes
$sink #{depth: 5}-> as $source // depth-limited trace
$val #{
include: `*?{opcode: const}`
}-> as $constSources // filter during trace
$sink #{
until: `* & $source`, // stop when reaching source
}-> as $reachableFilters ?{...}
$vals?{opcode: call} // by opcode: call/const/param/phi/function/return
$vals?{have: 'password'} // by string content
$vals?{!opcode: const} // negation
$vals?{opcode: call && have: 'sql'} // combined
$factory?{!(.setFeature)} // method NOT called on valueVariable, Check & Alert
.exec() as $sink; // assign
check $sink then "found" else "not found"; // assert
alert $sink for { title: "RCE", level: "high" }; // mark finding
$a + $b as $merged; // union
$all - $safe as $vuln; // differenceNativeCall (40+ built-in functions)
Most commonly used — see nativecall-reference.md for full list:
<include('rule-name')> // import lib rule
<typeName()> // get short type name
<fullTypeName()> // get full qualified type name
<getReturns> // function return values
<getFormalParams> // function parameters
<getFunc> // enclosing function
<getCall> // find call sites
<getCallee> // get called function
<getObject> // parent object
<getMembers> // object members
<name> // get name
<slice(index=N)> // extract by index
<mybatisSink> // MyBatis SQL injection sinks
<dataflow(include=`...`)> // filter data flow pathsTips
1. #-> = "where does this come from?", --> = "where does this go?" 2. Use * for params, don't hardcode names 3. SSA resolves assignments: a = getRuntime(); a.exec(cmd) = getRuntime().exec(cmd) 4. Use opcode filters to distinguish constants / parameters / calls 5. Combine check + alert for actionable results 6. After code changes, use base_program_name (not re_compile) for fast incremental updates 7. Before writing a new rule, read `syntaxflow-examples.md` to find similar patterns 8. When unsure about a NativeCall, read `nativecall-reference.md` for usage and examples
SyntaxFlow NativeCall Reference
NativeCall is SyntaxFlow's extension mechanism — built-in functions invoked with <name(args)> syntax for advanced SSA IR analysis.
Syntax
<nativeCallName(arg1, key="value", ...)>Quick Reference Table
| NativeCall | Input | Output | Description |
|---|---|---|---|
<include> | — | Values | Import a reusable lib rule by name |
<typeName> | Any value | Strings | Get short type name (without package path) |
<fullTypeName> | Any value | Strings | Get full qualified type name (with package path + version) |
<name> | Any value | Strings | Get name of function/variable/method/field |
<string> | Any value | Strings | Get string representation |
<getReturns> | Function | Values | Get function return values |
<getFormalParams> | Function | Params | Get function formal parameters |
<getFunc> | Any instruction | Function | Get the function containing this instruction |
<getCall> | Function | Call instructions | Get call sites of a function |
<getCallee> | Call instruction | Function | Get the called function from a call instruction |
<searchFunc> | Any value | Functions | Search all call sites of a function across the program |
<getObject> | Member | Object | Get parent object of a member |
<getMembers> | Object | Members | Get all members of an object/class |
<getMemberByKey> | Object | Member | Get specific member by key name |
<getSiblings> | Member | Members | Get sibling members of the same parent object |
<getUsers> | Any value | Instructions | Get all instructions that USE this value |
<getPredecessors> | Any value | Values | Get predecessor nodes (reverse data flow) |
<getActualParams> | Call instruction | Values | Get actual arguments of a call |
<getActualParamLen> | Call instruction | Number | Get number of actual arguments |
<slice> | Container | Subset | Extract element(s) by index/range |
<regexp> | String | Strings | Regex match with group extraction |
<strlower> | String | String | Convert to lowercase |
<strupper> | String | String | Convert to uppercase |
<const> | — | Values | Search constant values in the program |
<eval> | String | Values | Dynamically execute a SyntaxFlow rule string |
<fuzztag> | Template | String | Evaluate a yaklang fuzztag template |
<show> | Any | Same | Debug: print value without side effects |
<self> | Any | Same | Return self (for chaining) |
<var> | Any | Same | Store value into variable table |
<delete> | — | — | Delete a variable |
<forbid> | Any | — | Mark as forbidden; error if value exists |
<dataflow> | After --> or #-> | Values | Extract data flow information with filter |
<sourceCode> | Any instruction | String | Get source code text (with optional context lines) |
<opcodes> | Any value | Strings | Get all opcode types in the containing function |
<scanNext> | Instruction | Instruction | Get next instruction in sequence |
<scanPrevious> | Instruction | Instruction | Get previous instruction in sequence |
<scanInstruction> | Any value | Instructions | Get all instructions in current basic block |
<len> | Container | Number | Get length of array/list/params |
<root> | Any member/call chain | Value | Get root object of a call chain |
<versionIn> | Version string | Boolean | Check if version is in a range |
<mybatisSink> | — | Values | Find MyBatis unsafe ${} SQL injection sinks |
<freeMarkerSink> | — | Values | Find FreeMarker template injection sinks |
<javaUnescapeOutput> | — | Values | Find unescaped output in JSP/Thymeleaf (XSS) |
<isSanitizeName> | Function/Call | Boolean | Check if name matches sanitizer patterns |
<getCurrentBlueprint> | Function | Blueprint | Get current function's class blueprint |
<getBluePrint> | Any value | Blueprint | Get type blueprint (class structure) |
<getParentsBlueprint> | Class | Blueprints | Get parent class blueprints |
<getInterfaceBlueprint> | Class | Blueprints | Get implemented interface blueprints |
<getRootParentBlueprint> | Class | Blueprint | Get root ancestor class blueprint |
<extendsBy> | Class | Boolean | Check if class extends another |
<FilenameByContent> | Any value | String | Get filename where value is defined |
<getFullFileName> | — | Strings | Find files by glob pattern |
<foreach_function_inst> | Function | Values | Iterate all instructions in a function with hook |
---
Detailed Usage & Examples
include — Import Reusable Rules
The most frequently used NativeCall. Import a lib rule declared with lib: 'rule-name' in its desc.
// Import Spring MVC user input sources
<include('java-spring-mvc-param')> as $source;
// Import common filter/sanitizer functions
<include("java-common-filter")>() as $filter
// Import command execution sinks
<include('java-runtime-exec-sink')> as $sink;
// Import Go HTTP handler input sources
<include('golang-user-input')> as $input;Combining includes for vulnerability detection:
<include('java-spring-param')> as $source;
<include("java-http-sink")> as $sink;
$sink #{
include: `<self> & $source`,
exclude: `<self>?{opcode:call}?{!<self> & $source}?{!<self> & $sink}`,
}->as $mid;
alert $mid for { message: "SSRF detected", level: mid };typeName / fullTypeName — Type Inspection
// Short type name (class name + simple names)
JSON.parse<typeName()> as $name
// Results: ["JSON", "com.alibaba.fastjson.JSON"]
// Full qualified name (with package path, version info)
JSON.parse<fullTypeName()> as $name
// Results: ["com.alibaba.fastjson.JSON"]
// Filter by type in conditions
$vals?{<typeName>?{have:'String'}} as $strings
$vals?{<fullTypeName>?{have:'javax.servlet'}} as $servletTypes
$result?{<typeName>?{!any: Long,Integer,Boolean,Double}} as $nonPrimitivegetReturns — Function Return Values
// Get return values of a function
HHHHH<getReturns> as $returnVals;
// Real-world: URL redirect detection
Controller.__ref__<getMembers>?{.annotation.*Mapping && !.annotation.ResponseBody} as $entryMethods;
$entryMethods<getReturns>?{<typeName>?{have: String}}?{have:'redirect:'} as $sink;
// FreeMarker SSTI detection
*Mapping.__ref__<getFunc><getReturns>?{<typeName>?{have:'String'}}<freeMarkerSink> as $sinkgetFormalParams — Function Parameters
// Get formal parameters (excluding 'this')
$start<getFormalParams>?{opcode: param && !have: this} as $params;
// Servlet parameters
/(do(Get|Post|Delete|Filter|[A-Z]\w+))|(service)/<getFormalParams>?{!have: this && opcode: param} as $req;
// Spring MVC annotated method parameters
*Mapping.__ref__<getFormalParams>?{opcode: param && !have: this} as $refgetFunc — Enclosing Function
// Get the function containing a value
$sink?{<getFunc><getCurrentBlueprint><fullTypeName>?{any: "Controller","controller"}} as $output
// Check function return type
$params?{<getFunc><getReturns><typeName>?{have: ResponseEntity}} as $entry;getCall / getCallee — Call Graph Navigation
// getCall: Find call sites using this function
$entry.Open<getCall> as $db;
// getCallee: Get the called function from a call instruction
$params<getCallee>?{<name>?{have:toString}}<getObject>.append(,* as $appendParams)
// Chain for function name extraction
aArgs<getCall><getCallee><name> as $funcNamegetObject / getMembers / getMemberByKey
// getObject: Navigate to parent object
.b<getObject>.c as $sibling;
.readObject?{<typeName>?{have:'java.beans.XMLDecoder'}}<getObject()> as $decoder;
// getMembers: Get all members of a class
Controller.__ref__<getMembers>?{.annotation.*Mapping} as $entryMethods;
$entry.Open()<getMembers> as $client;
// getMemberByKey: Get specific member
$sink<getMemberByKey(key="password")> as $objslice — Parameter Extraction
// By index
hijackHTTPRequest<slice(index=0)> as $param0
.parse(*<slice(index=1)> as $firstArg)
// From index (inclusive)
*sql*.append(*<slice(start=1)> as $params);
ldap_bind(*<slice(start=2)>?{opcode: const} as $pass)regexp — Regex on Strings
// Extract MyBatis ${} parameter names
.annotation.Select.value<regexp(\$\{\s*(\w+)\s*\}, group=1)> as $entry;
// Extract groups from strings
"abc123def"<regexp(`(\d+)`, group: 1)> as $numbers;const — Search Constants
// Glob match
<const(g="127*")> as $output // matches "127.0.0.1"
// Regex match
<const(r="^\d+\.\d+\.\d+\.\d+$")> as $ips
// Exact match
<const(e="127.0.0.1")> as $exact
// Sugar syntax
"127*" as $glob // glob match
e"127.0.0.1" as $ex // exact matchgetUsers — Who Uses This Value
// Check if return value is actually used
$toCheck?{!<getUsers>} as $unusedReturn;
// Check if null check exists (with depth)
$val?{!<getUsers(depth=2)>?{opcode:if}} as $unchecked
// Lock check: is tryLock() result checked?
.tryLock()?{!<getUsers>} as $weak;getPredecessors — Reverse Data Flow
// Get data predecessor
a.b as $b
$b<getPredecessors> as $origin // traces back to sourcedataflow — Data Flow Filtering
Used after --> or #-> to filter data flow paths:
// Check if filter exists in the data flow path
$result<dataflow(include=`* & $filter`)> as $filtered
$result - $filtered as $unfiltered // truly vulnerable pathssourceCode — Get Source Text
bb1<sourceCode> as $code; // just the statement
bb2<sourceCode(context=3)> as $withCtx; // with 3 lines contextlen — Container Length
// Filter calls by argument count
a?(*<len>?{==2}) as $twoArgCalls
setcookie?(*<len>?{<6}) as $insecureCookieroot — Call Chain Root
// a.b().c.d() → root is "a"
.d<root> as $rootObjversionIn — Dependency Version Check
__dependency__.*fastjson.version as $ver;
$ver?{version_in:(0.1.0,1.3.0]} as $vuln // open-closed range
$ver?{version_in:[1.0,2.0)} as $vuln // closed-open range
$ver?{version_in:(1.0,2.0]||(3.0,4.0]} as $vuln // union rangesforeach_function_inst — Iterate Function Instructions
main<foreach_function_inst(hook=<<<CODE
*?{opcode: const} as $output
CODE)>mybatisSink — MyBatis SQL Injection Sinks
<mybatisSink> as $sink; // finds all ${} usages in MyBatis XML/annotationsjavaUnescapeOutput — XSS Detection in Templates
<javaUnescapeOutput> as $sink; // finds ${expr} in JSP / <%= expr %> / th:utextextendsBy — Inheritance Check
Dog<extendsBy($Animal)> as $isDog; // checks if Dog extends AnimalsearchFunc — Global Function Search
aArgs<getCall><searchFunc> as $allCalls; // find all calls to the same functionFilenameByContent / getFullFileName — File Location
A<FilenameByContent> as $file; // "a.java"
<getFullFileName(filename="*/a*")> as $files; // glob file searchSyntaxFlow Real-World Rule Examples
Production rules extracted from IRify's built-in rule library (sfbuildin). Use these as templates when writing new rules.
Rule Structure
A complete .sf rule has two parts: metadata (desc(...)) and the rule logic. For AI-generated on-the-fly queries, only the rule logic is needed. The desc block is for persisted rules.
// Optional: metadata (only for persisted rules)
desc(
title: "Rule Title"
type: vuln // audit | vuln
level: high // info | low | mid | middle | high | critical
lib: 'rule-name' // makes this rule importable via <include('rule-name')>
)
// Rule logic: search → filter → trace → alert
Runtime.getRuntime().exec(,* as $params);
alert $params for { level: "high", title: "RCE Detected" };---
Java Rules
Source: Spring MVC User Input (lib rule)
This is the foundation rule imported by most Java vulnerability rules.
// Find all Spring MVC controller method parameters as user input sources
*Mapping.__ref__?{opcode: function} as $start;
$start<getFormalParams>?{opcode: param && !have: this} as $params;
$params?{!<typeName>?{have:'javax.servlet.http'}} as $output;
// Also capture HttpServletRequest.get*() calls
$params?{<typeName>?{have:'javax.servlet.http.HttpServletRequest'}} as $request;
$request.get*() as $output;
alert $output;RCE: Runtime.exec() Command Injection
Runtime.getRuntime().exec(,* as $output);
alert $output for { level: "high", title: "Java Command Execution" };RCE: Source → Sink with Data Flow
<include('java-servlet-param')> as $source;
<include('java-spring-param')> as $source;
check $source;
<include('java-runtime-exec-sink')> as $sink;
<include('java-command-exec-sink')> as $sink;
check $sink;
$sink #{
include: `<self> & $source`,
exclude: `<self>?{opcode:call}?{!<self> & $source}`
}->as $high;
alert $high for { message: "Command injection, no filter", level: high };SQL Injection: StringBuilder.append()
*sql*.append(*<slice(start=1)> as $params);
check $params;
$params?{!opcode: const}#{
hook: `*?{opcode: const && have: 'WHERE'}<show> as $flag`,
}->
alert $flag for { level: "low", title: "SQL String Append" };SQL Injection: Statement.executeQuery()
.createStatement().executeQuery(,* as $params);
check $params;
$params<getCallee>?{<name>?{have:toString}}<getObject>.append(,* as $appendParams)
$params<getFunc><getFormalParams> as $limited
$params + $appendParams as $params
$params?{opcode: param} as $directly
$params?{!opcode: param} #{include: `*?{opcode:param && <self> & $limited}`}-> as $indirectly
$directly + $indirectly as $vuln
alert $vuln for { level: "high", title: "Java SQL Injection" };SQL Injection: MyBatis ${} with Source-Sink-Filter
<include('java-spring-mvc-param')> as $source;
<include("java-common-filter")>() as $filter
<mybatisSink> as $sink
// Trace from sink to source
$sink#{
until: `* & $source`,
}-> as $result
// Exclude safe primitive types
$result?{<typeName>?{!any: Long,Integer,Boolean,Double}} as $all
// Separate filtered vs unfiltered paths
$all<dataflow(include=`* & $filter`)> as $mid
alert $mid for { level: "mid", message: "MyBatis SQLi, filter exists" };
$all - $mid as $high
alert $high for { level: "high", message: "MyBatis SQLi, NO filter" };SpEL Expression Injection
<include('java-spring-mvc-param')> as $source;
check $source;
SpelExpressionParser()?{<typeName>?{have:'org.springframework.expression.spel'}} as $context;
$context.parseExpression(*<slice(index=1)> as $sink);
$sink #{
until: `* & $source`,
exclude: `*?{opcode:call}?{!* & $source}?{!* & $sink}`,
}-> as $mid;
alert $mid for { level: "middle", title: "Spring SpEL Injection" };Groovy Shell Code Injection
<include('java-spring-mvc-param')> as $source;
<include('java-groovy-lang-shell-sink')> as $sink;
$sink #{
include: `* & $source`,
exclude: `*?{opcode:call}?{!<self> & $source}?{!<self> & $sink}`,
exclude: `*?{opcode:phi}`,
}-> as $high;
alert $high for { level: "high", title: "Groovy Shell Code Injection" };SSRF: Spring → HTTP Client
<include('java-spring-param')> as $source;
<include("java-http-sink")> as $sink;
$sink #{
include: `<self> & $source`,
exclude: `<self>?{opcode:call}?{!<self> & $source}?{!<self> & $sink}`,
}->as $mid;
alert $mid for { message: "SSRF detected", risk: ssrf, level: mid };XXE: Missing Security Config
DocumentBuilderFactory() as $factory;
$factory?{!(.setFeature)} as $unsafe;
alert $unsafe for { message: "XML parser without security features" };URL Redirect Detection
Controller.__ref__<getMembers>?{.annotation.*Mapping && !.annotation.ResponseBody} as $entryMethods;
$entryMethods<getReturns>?{<typeName>?{have: String}}?{have:'redirect:'} as $sink;
alert $sink for { level: "mid", title: "URL Redirect" };FreeMarker SSTI
*Mapping.__ref__<getFunc><getReturns>?{<typeName>?{have:'String'}}<freeMarkerSink> as $sink
alert $sink;---
Golang Rules
Source: HTTP Handler User Input (lib rule)
// Gin framework
.Query() as $output;
.PostForm() as $output;
.Param() as $output;
.GetHeader() as $output;
// net/http
.FormValue() as $output;
*.URL.Query().Get() as $output;
alert $output;SSRF: User Input → http.Get/Do/Post
<include('golang-user-input')> as $input;
*.Do(<slice(index=0)>* as $client)
*.Get(<slice(index=0)>* as $client)
*.Post(<slice(index=0)>* as $client)
$client?{* #{until: `*<fullTypeName()>?{have: "net/http"}`}->} as $func
$func.Get(* #-> as $param);
$param #{
until: "* & $input"
}-> as $mid
alert $mid for { level: "mid", title: "Golang HTTP SSRF" };XXE: XML Parser Audit
xml?{<fullTypeName>?{have: 'encoding/xml'}} as $entry;
$entry.NewDecoder() as $output;
alert $output for { level: "mid", title: "Golang XML XXE Risk" };SQL Injection: database/sql
<include('golang-user-input')> as $input;
<include('golang-database-net-sql-sink')> as $sink;
$sink #{
until: `* & $input`,
}-> as $result
alert $result for { level: "high", title: "Golang SQL Injection" };---
PHP Rules
Source: User Input Parameters
// Superglobals
$_GET as $output;
$_POST as $output;
$_REQUEST as $output;
$_COOKIE as $output;
$_FILES as $output;
$_SERVER as $output;
alert $output;RCE: Dangerous Functions
/^(eval|exec|assert|system|shell_exec|pcntl_exec|popen|ob_start)$/ as $output
alert $output for { level: "info", title: "PHP Command Execution Functions" };Command Injection: Source → Sink
<include('php-custom-param')> as $source;
<include('php-os-exec')> as $sink;
$sink #{
until: `* & $source`,
}-> as $result
alert $result for { level: "high", title: "PHP Command Injection" };SQL Injection: MySQL
<include('php-custom-param')> as $source;
// MySQL query functions as sinks
mysql_query(* as $sink);
mysqli_query(,* as $sink);
$sink #{
until: `* & $source`,
}-> as $result
alert $result for { level: "high", title: "PHP MySQL Injection" };XXE: DOMDocument
<include('php-custom-param')> as $params;
DOMDocument().loadXML(* as $sink);
DOMDocument().load(* as $sink);
$sink #{
until: `* & $params`,
}-> as $result
alert $result for { level: "high", title: "PHP DOMDocument XXE" };File Inclusion
<include('php-custom-param')> as $source;
include(* as $sink);
require(* as $sink);
include_once(* as $sink);
require_once(* as $sink);
$sink #{
until: `* & $source`,
}-> as $result
alert $result for { level: "high", title: "PHP File Inclusion" };---
C Rules
Buffer Overflow
// Dangerous functions without bounds checking
strcpy(,* as $sink);
strcat(,* as $sink);
gets(* as $sink);
sprintf(,* as $sink);
alert $sink for { level: "high", title: "C Buffer Overflow Risk" };---
Common Patterns & Techniques
Pattern 1: Source-Sink with Filter Detection
The standard three-layer pattern: find source, find sink, check if filter exists.
<include('java-spring-mvc-param')> as $source;
<include("java-common-filter")>() as $filter;
<some-sink> as $sink;
$sink #{until: `* & $source`}-> as $all
$all<dataflow(include=`* & $filter`)> as $filtered
$all - $filtered as $unfiltered
alert $filtered for { level: "mid", message: "Vuln found but filter exists" };
alert $unfiltered for { level: "high", message: "Vuln found, NO filter" };Pattern 2: Include + Exclude Traversal
$sink #{
include: `<self> & $source`, // must reach source
exclude: `<self>?{opcode:call}?{!<self> & $source}?{!<self> & $sink}`, // skip unrelated calls
}-> as $result;Pattern 3: Variable Arithmetic
$a + $b as $merged; // union: combine results from multiple sources
$all - $safe as $vuln; // difference: remove safe results to get vulnerable onesPattern 4: Type-Based Filtering
// Exclude safe primitive types from SQL injection results
$result?{<typeName>?{!any: Long,Integer,Boolean,Double}} as $nonPrimitive
// Check if value belongs to specific framework
$val?{<fullTypeName>?{have:'javax.servlet'}} as $servletTypesPattern 5: SCA (Software Composition Analysis)
__dependency__.*fastjson.version as $ver;
$ver?{version_in:(0.1.0,1.2.83]} as $vuln
alert $vuln for { level: "high", title: "Vulnerable fastjson version" };Pattern 6: Cookie Security Check
// Check if setcookie() has enough parameters for security flags
setcookie?(*<len>?{<6}) as $insecureCookie
alert $insecureCookie for { level: "mid", title: "Insecure Cookie" };---
Built-in Include Libraries
For the complete list of all 71 <include('name')> rules with descriptions, see builtin-rules.md.