A curated collection of security rules for OpenTaint, a static analysis engine for Java and Kotlin that combines Semgrep-style pattern matching with dataflow/taint analysis.
The repository provides:
- A logically structured set of executable security rules for real-world Java/Kotlin applications
- A shared library of reusable rule components (sources, sinks, propagators, etc.)
- A test suite that validates rule behavior and enforces coverage for all enabled rules
.
├─ ruleset/java/
│ ├─ security/ # Executable rules run against user code (one file per vulnerability class)
│ └─ lib/ # Reusable rule fragments, not executed directly (marked as lib: true)
└─ test/
└─ src/main/java/
└─ security/ # Rule tests with @PositiveRuleSample / @NegativeRuleSample
All rules that are intended to run on user code live under ruleset/. Each file groups a class of vulnerability.
Example:
ruleset/java/security/
command-injection.yaml
sqli.yaml
xss.yaml
xxe.yaml
Characteristics:
- Rules are written in Semgrep-compatible YAML.
- Each rule entry has an
id,severity,message,metadata,languages, and pattern/mode fields (mode: taint,pattern,patterns,pattern-either,pattern-sources,pattern-sinks, etc.). - Rules in
ruleset/are considered executable unless:options.disabled: <reason>— the rule is disabledoptions.lib: true— the rule is a library component (should normally reside inlib/)
The lib/ directory contains rule fragments that are not executed standalone. They are building blocks (sources, sinks, propagators, etc.) that other rules compose via mode: join or standard taint rules.
Structure is by technology, example:
lib/
java/
generic/
command-injection-sinks.yaml
servlet-sqli-sinks.yaml
servlet-untrusted-data-source.yaml
servlet-response-injection-sinks.yaml
xxe-sinks.yaml
spring/
jdbc-sqli-sinks.yaml
spring-response-injection-sinks.yaml
untrusted-data-source.yaml
All library rules are marked:
rules:
- id: java-servlet-untrusted-data-source
options:
lib: true
...Key points:
lib: trueexplicitly marks a rule as non-executable; it will not be run by OpenTaint as a top-level rule.- Library rules are typically:
- Source definitions (
*untrusted-data-source*) - Sink definitions (
*sinks*) - Propagation or helper patterns shared across multiple vulnerabilities
- Source definitions (
Many rules under ruleset/ combine multiple library rules using mode: join.
Example (from ruleset/java/security/ssrf.yaml):
- id: ssrf-in-servlet-app
languages:
- java
mode: join
join:
refs:
- rule: java/lib/generic/servlet-untrusted-data-source.yaml#java-servlet-untrusted-data-source
as: untrusted-data
- rule: java/lib/generic/ssrf-sinks.yaml#java-ssrf-sink
as: sink
on:
- 'untrusted-data.$UNTRUSTED -> sink.$UNTRUSTED'Semantics:
mode: joinderives a composite rule from other rules referenced injoin.refs.refsdefines:rule: path to the library rule file plus#<rule-id>inside that YAMLas: local alias for referencing captures/variables from that rule
ondescribes how to correlate matches from referenced rules:untrusted-data.$UNTRUSTED -> sink.$UNTRUSTEDexpresses a dataflow relationship between the$UNTRUSTEDcaptured in the source rule and the same$UNTRUSTEDcaptured in the sink rule.
This join mode is based on Semgrep's join mode, but OpenTaint extends it with custom features (such as the -> notation in the on section) to express taint-style flows across multiple rule components.
Rules follow Semgrep syntax and concepts:
- Pattern-based rules:
pattern,patterns,pattern-either,pattern-inside,pattern-not-inside,metavariable-regex, etc.
- Taint-style rules:
mode: taintpattern-sources,pattern-propagators,pattern-sanitizers,pattern-sinks- Dataflow through methods, fields, and variables
- Metadata:
cwe,short-description,full-description(where provided)- External references (OWASP, CWE, upstream rule sources)
- Optional
licenseandprovenance
Rule behavior is validated via Java test snippets under:
test/src/main/java/security/
Each test class declares inline code samples annotated with:
@PositiveRuleSample(...)— code that must trigger a specific rule@NegativeRuleSample(...)— code that must not trigger that rule (not shown above but typically paired with positives)
Annotation usage (conceptually):
@PositiveRuleSample(
value = "java/security/xss.yaml",
id = "xss-in-servlet-app"
)
class SomeServletXssSample {
// vulnerable code here
}The CI helper RuleCoverageCheck (in test/src/main/java/rules/RuleCoverageCheck.java) enforces:
- YAML validity for every file in
ruleset/:- Root is a map and contains a
ruleslist. - Each rule has a non-blank
id.
- Root is a map and contains a
- Test coverage for all active rules:
- Active rules are those in
ruleset/where:options.disabledis nottrue, andoptions.libis nottrue
- Each such rule must have at least one
@PositiveRuleSamplereferencing:value = "<relative-path-to-rule-yaml>"(e.g.java/security/xss.yaml)id = "<rule-id>"(the rule'sidvalue)
- Active rules are those in
If any active rule is not covered by a positive sample, or if any YAML is invalid, the checker:
- Prints detailed errors (uncounted rules, invalid YAML, etc.)
- Exits with a non-zero status (breaking the build/CI)
This repository exposes a Gradle verification task:
verification/checkRulesCoverage
Behavior:
- Runs the
RuleCoverageCheckhelper - Ensures:
- All rule YAMLs in
ruleset/are syntactically valid - Every enabled, non-lib rule has at least one positive test sample
- All rule YAMLs in
Usage (from the test/root subdirectory):
cd test/root
../gradlew verification/checkRulesCoverageOn success:
"Rule coverage check passed: all rules valid and covered."is printed.
On failure:
- It prints all problems (invalid YAML, uncovered rules) and fails the task.
When introducing or changing rules, follow these guidelines:
-
Choose the correct location
- Executable vulnerability rules →
java/security/<vuln-class>.yaml - Shared sources, sinks, or helpers →
java/lib/generic/orjava/lib/spring/
- Executable vulnerability rules →
-
Mark library-only rules
- Add
options.lib: truefor library fragments inlib/(or exceptionally inruleset/if they are not meant to be executed directly).
- Add
-
Avoid duplicates
- Reuse existing library rules from
lib/and compose them viamode: joinwhere applicable.
- Reuse existing library rules from
-
Update tests
- Add at least one
@PositiveRuleSample(and typically@NegativeRuleSample) undertest/src/main/java/security/. - Reference the rule by:
value = "<relative YAML path under project root>"id = "<rule id>"
- Add at least one
-
Run coverage checks
- From the
test/rootsubdirectory execute../gradlew verification/checkRulesCoverageto ensure:- No YAML errors
- All executable rules are covered by tests
- From the
This project is released under the MIT License.
The core analysis engine is released under the Apache 2.0 License.
Rule content may incorporate or adapt patterns originally published under various open-source licenses (for example, from community rule sets). Where applicable, original provenance and license information is recorded in rule metadata.