Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
0603372
More Java/Kotlin rules
dvvrd Mar 24, 2026
dad82c8
Wire missing -in-servlet-app/-in-spring-app rules and add CodeQL sani…
misonijnik May 16, 2026
11ef7af
Add CodeQL-aligned LDAP/SSRF/XSS sanitizers and exercising negative t…
misonijnik May 16, 2026
c232482
Add minimal repros for analyzer FN gaps + the propagators that fix them
misonijnik May 17, 2026
e99b695
Add JVM-level regression tests for pattern-matcher behaviours touched…
misonijnik May 17, 2026
6822ea1
Add Servlet API propagators + widen servlet entry-point pattern
misonijnik May 17, 2026
9126688
Drive rules/test FN from 281 to 7 with CI-style JVM flags
misonijnik May 17, 2026
fa1e212
Add CodeQL-aligned barriers with test bank for path, ssrf, ldap, log,…
misonijnik May 17, 2026
86a404e
More CodeQL-aligned barriers: XPath, SSTI, deser, log line-breaks, re…
misonijnik May 17, 2026
3cad367
Add SMTP CRLF, unvalidated-redirect, and Spring/XSS barrier sanitizers
misonijnik May 17, 2026
cdc6f4a
Add ValidatingObjectInputStream deserialization barrier test
misonijnik May 17, 2026
d919e60
More XSS encoder barriers: Apache escapeEcmaScript, OWASP ESAPI suite
misonijnik May 17, 2026
6ac9454
Extend response-injection barriers to match XSS encoder set
misonijnik May 17, 2026
b94fff1
Extend spring-response-injection barriers to match XSS encoder set
misonijnik May 17, 2026
182f6f5
Add CodeQL external barrierModel sanitizers: File.getName + ESAPI Val…
misonijnik May 17, 2026
9357b65
Treat URL encoders as CRLF/http-response-splitting barriers
misonijnik May 17, 2026
e763dda
Add Jenkins hudson.Util.escape as XSS / response-injection barrier
misonijnik May 17, 2026
77c75ca
Drop ESAPI Validator.getValidURI — not in current ESAPI surface
misonijnik May 17, 2026
71c5294
Add pixee java-security-toolkit barriers: HtmlEncoder, Newlines
misonijnik May 17, 2026
909c0a7
Add pixee Urls.create as SSRF barrier
misonijnik May 17, 2026
8d4ef8d
Add pixee Urls.create as unvalidated-redirect barrier
misonijnik May 17, 2026
0a9a498
Add pixee deser + reflection barriers
misonijnik May 17, 2026
3a7aed2
Drop runCommandAsString sanitizer (not in pixee 1.2.3 API)
misonijnik May 17, 2026
f61987b
Add pixee jakarta.PathValidator.validateDispatcherPath as url-forward…
misonijnik May 17, 2026
e15af5d
Eliminate remaining 7 FN by fixing sink + propagator gaps
misonijnik May 17, 2026
53e0732
Revert propagators
misonijnik May 18, 2026
dce20dd
Remove rule tests
misonijnik May 18, 2026
5379a09
Polish rules: dedupe, fix typos, consolidate provenance
misonijnik May 18, 2026
3e7091e
Activate rule-test suite end-to-end: rename test IDs, add propagators
misonijnik May 18, 2026
f9ade76
ssrf-sinks: add inline URL/URI wrapper variants for 9 missing sinks
misonijnik May 18, 2026
33fe5ed
ssrf-sinks: refactor URL/URI wrappers into pattern-inside let-bindings
misonijnik May 18, 2026
4cb019f
ssrf-sinks: chain pattern-inside let-bindings via shared metavars
misonijnik May 18, 2026
9ad5fd3
ssrf-sinks: collapse builder-chain steps into a single multi-line pat…
misonijnik May 18, 2026
9f3d5d8
ssrf-sinks: document why `$_` is the .uri/.url receiver
misonijnik May 18, 2026
7184d4a
ssrf-sinks: use literal `new Request.Builder()` for OkHttp, keep `$_`…
misonijnik May 18, 2026
b695075
opentaint-config: split third-party propagators into per-jar files
misonijnik May 18, 2026
02d5c80
ssrf-sinks: bind HttpRequest newBuilder() receiver via $NEW_BUILDER
misonijnik May 20, 2026
66ba21b
Remove new taint sources (revert to origin/main source rules)
misonijnik May 22, 2026
7469955
ci-rules: overlay source propagators into analyzer jar
misonijnik May 22, 2026
54fdf85
ci-rules: overlay config with zip to avoid jar uf duplicate-entry crash
misonijnik May 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .github/workflows/ci-rules.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ on:
push:
paths:
- 'rules/**'
- 'core/opentaint-config/config/**'
- '.github/workflows/ci-rules.yaml'
branches: [ "main" ]
pull_request:
paths:
- 'rules/**'
- 'core/opentaint-config/config/**'
- '.github/workflows/ci-rules.yaml'
branches: [ "main" ]
workflow_dispatch:
Expand Down Expand Up @@ -70,6 +72,26 @@ jobs:
--logs-file autobuild.log \
--verbosity debug

- name: Use source propagators (overlay repo config into analyzer jar)
run: |
# The rule-test runner (--debug-run-rule-tests -> TestProjectAnalyzer)
# loads taint passThrough rules only from the analyzer jar's bundled
# /config resources (loadDefaultConfig -> ConfigLoader); it does NOT read
# --approximations-config. The released analyzer jar is built from main,
# so its bundled config lacks the propagators kept in this repo's source
# tree, which surfaces as false negatives. Overlay the in-repo propagators
# (core/opentaint-config/config/config -> jar entries under config/) so the
# rule tests run against the source config.
#
# Use `zip`, not `jar uf`: the analyzer is a shadow/fat jar with duplicate
# directory entries (e.g. `org/`). `jar uf` rewrites the whole archive via
# ZipOutputStream, which aborts on those pre-existing duplicates
# (java.util.zip.ZipException: duplicate entry: org/). `zip` copies existing
# entries as-is and only replaces/adds the config files.
apt-get update && apt-get install -y zip
JAR="$PWD/opentaint-analyzer/opentaint-project-analyzer.jar"
( cd core/opentaint-config/config && zip -r "$JAR" config )

- name: Run OpenTaint analyzer
run: |
java -Xmx8G -Djdk.util.jar.enableMultiRelease=false -Dorg.opentaint.ir.impl.storage.defaultBatchSize=2000 \
Expand Down
13 changes: 13 additions & 0 deletions core/opentaint-config/config/config/jar-split/ant-1.10.14.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
passThrough:
# Apache Ant: FileSet.setDir(File) / setFile(File) — the file argument
# is stored on the FileSet instance, so a subsequent
# `copy.addFileset(fs)` sink that requires a tainted $FILE detects
# the flow.
- function: org.apache.tools.ant.types.FileSet#setDir
copy:
- from: arg(0)
to: this
- function: org.apache.tools.ant.types.FileSet#setFile
copy:
- from: arg(0)
to: this
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
passThrough:
# Apache Commons Codec — Base64 encode/decode just re-codes bytes
# without disturbing the underlying tainted data, so taint should
# flow from input to output.
- function: org.apache.commons.codec.binary.Base64#encodeBase64String
copy:
- from: arg(0)
to: result
- function: org.apache.commons.codec.binary.Base64#encodeBase64
copy:
- from: arg(0)
to: result
- function: org.apache.commons.codec.binary.Base64#decodeBase64
copy:
- from: arg(0)
to: result
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
passThrough:
# Apache Commons IO: IOUtils.toString(InputStream|Reader|URL, ...) just
# reads bytes/chars from its input and produces a String — taint flows
# from the input source argument to the resulting String.
- function: org.apache.commons.io.IOUtils#toString
copy:
- from: arg(0)
to: result
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
passThrough:
# Groovy compiler: CompilationUnit.addSource(name, source) — the
# source text becomes part of the CompilationUnit instance that's
# later compiled by .compile(), so the source-text argument taints
# the unit.
- function: org.codehaus.groovy.control.CompilationUnit#addSource
copy:
- from: arg(1)
to: this
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
passThrough:
# Apache HttpComponents 5 — String-arg wrapper constructor that the
# SSRF sink rules use as an inline taint carrier.
- function: org.apache.hc.core5.http.io.entity.StringEntity#<init>
copy:
- from: arg(*)
to: this
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
passThrough:
# hudson.FilePath wrapper constructor — taint flows from any
# String/File/URL argument into the constructed FilePath instance.
- function: hudson.FilePath#<init>
copy:
- from: arg(*)
to: this
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
passThrough:
# MVEL compile / executeExpression chain — compileExpression(expr)
# returns a Serializable that's later passed to executeExpression /
# MVELRuntime.execute as a tainted compiled program. The compile
# methods just pass the input expression text through to the result.
- function: org.mvel2.MVEL#compileExpression
copy:
- from: arg(0)
to: result
- function: org.mvel2.MVEL#compileSetExpression
copy:
- from: arg(0)
to: result
- function: org.mvel2.MVEL#compileGetExpression
copy:
- from: arg(0)
to: result
# JSR-223 ScriptEngine compile / compiledScript
- function: org.mvel2.jsr223.MvelScriptEngine#compile
copy:
- from: arg(0)
to: result
- function: org.mvel2.jsr223.MvelScriptEngine#compiledScript
copy:
- from: arg(0)
to: result
16 changes: 16 additions & 0 deletions core/opentaint-config/config/config/jar-split/okhttp-4.12.0.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
passThrough:
# OkHttp Request.Builder — `new Request.Builder().url($X).build()` chain.
# `.url()` mutates the builder and returns it (taint flows arg→this and
# arg→result and this→result so the chain propagates through `.build()`).
- function: okhttp3.Request$Builder#url
copy:
- from: arg(0)
to: result
- from: arg(0)
to: this
- from: this
to: result
- function: okhttp3.Request$Builder#build
copy:
- from: this
to: result
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
passThrough:
# Spring JDBC: NamedParameterUtils.parseSqlStatement(sql) returns a
# ParsedSql wrapping the original SQL, which is then passed to
# (Named)JdbcTemplate query/update sinks. The parse step itself just
# preserves taint into the result.
- function: org.springframework.jdbc.core.namedparam.NamedParameterUtils#parseSqlStatement
copy:
- from: arg(0)
to: result
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
passThrough:
# Spring LDAP query builder chain:
# LdapQueryBuilder.query().base(dn).where(attr).is(val) ... -> LdapQuery
#
# The chain mixes the public LdapQueryBuilder/ConditionCriteria/
# ContainerCriteria interfaces with the package-private
# DefaultConditionCriteria / DefaultContainerCriteria impls. The
# analyzer's chain-split sees the impl-class call sites, so both
# interface and impl entries are needed.
#
# The direct `arg(0) → result` form is what actually propagates taint
# through the chain; the two-step `arg(0)→this` + `this→result` form
# alone wasn't enough (the chain has too many implicit intermediates
# for two-step propagation to reach end-to-end without the direct
# shortcut).
- function: org.springframework.ldap.query.LdapQueryBuilder#base
copy:
- from: arg(0)
to: result
- from: arg(0)
to: this
- from: this
to: result
- function: org.springframework.ldap.query.LdapQueryBuilder#where
copy:
- from: arg(0)
to: result
- from: arg(0)
to: this
- from: this
to: result
- function: org.springframework.ldap.query.LdapQueryBuilder#filter
copy:
- from: arg(0)
to: result
- from: arg(0)
to: this
- from: this
to: result
- function: org.springframework.ldap.query.ConditionCriteria#is
copy:
- from: arg(0)
to: result
- from: arg(0)
to: this
- from: this
to: result
- function: org.springframework.ldap.query.ConditionCriteria#like
copy:
- from: arg(0)
to: result
- from: arg(0)
to: this
- from: this
to: result
- function: org.springframework.ldap.query.ConditionCriteria#whitespaceWildcardsLike
copy:
- from: arg(0)
to: result
- from: arg(0)
to: this
- from: this
to: result
- function: org.springframework.ldap.query.ContainerCriteria#and
copy:
- from: this
to: result
- function: org.springframework.ldap.query.ContainerCriteria#or
copy:
- from: this
to: result
- function: org.springframework.ldap.query.DefaultConditionCriteria#is
copy:
- from: arg(0)
to: this
- from: arg(0)
to: result
- from: this
to: result
- function: org.springframework.ldap.query.DefaultConditionCriteria#like
copy:
- from: arg(0)
to: this
- from: arg(0)
to: result
- from: this
to: result
- function: org.springframework.ldap.query.DefaultConditionCriteria#whitespaceWildcardsLike
copy:
- from: arg(0)
to: this
- from: arg(0)
to: result
- from: this
to: result
- function: org.springframework.ldap.query.DefaultContainerCriteria#and
copy:
- from: this
to: result
- function: org.springframework.ldap.query.DefaultContainerCriteria#or
copy:
- from: this
to: result
- function: org.springframework.ldap.query.DefaultContainerCriteria#append
copy:
- from: arg(0)
to: this
- from: this
to: result
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
passThrough:
# Spring RequestEntity static factories + builder .build() — used by
# the SSRF rule's chained-builder pattern:
# RequestEntity.get(URI.create($X)).build()
- function: org.springframework.http.RequestEntity#get
copy:
- from: arg(0)
to: result
- function: org.springframework.http.RequestEntity$BodyBuilder#build
copy:
- from: this
to: result
- function: org.springframework.http.RequestEntity$HeadersBuilder#build
copy:
- from: this
to: result
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
passThrough:
- function: com.unboundid.ldap.sdk.SearchRequest#<init>
copy:
- from: arg(*)
to: this
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
passThrough:
# Apache Velocity: VelocityContext.put($k, $v) and the AbstractContext
# super-class — taint flows from the value argument into the context
# instance so a tainted value carried into the context reaches a
# subsequent VelocityEngine.evaluate / Template.merge sink.
- function: org.apache.velocity.VelocityContext#put
copy:
- from: arg(1)
to: this
- function: org.apache.velocity.context.AbstractContext#put
copy:
- from: arg(1)
to: this
87 changes: 87 additions & 0 deletions core/opentaint-config/config/config/stdlib.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21358,3 +21358,90 @@ passThrough:
to:
- this
- .java.io.InputStream#<rule-storage>#java.lang.Object

# ── Collection / Iterator / Iterable / Enumeration ─────────────────────
- function: java.util.Collection#iterator
copy:
- from: this
to: result
- function: java.lang.Iterable#iterator
copy:
- from: this
to: result
- function: java.util.Iterator#next
copy:
- from: this
to: result
- function: java.util.Enumeration#nextElement
copy:
- from: this
to: result

# ── java.lang.String#getBytes (String → byte[]) ────────────────────────
- function: java.lang.String#getBytes
copy:
- from: this
to: result

# ── java.util.Base64$Encoder ──────────────────────────────────────────
- function: java.util.Base64$Encoder#encodeToString
copy:
- from: arg(0)
to: result
- function: java.util.Base64$Encoder#encode
copy:
- from: arg(0)
to: result

# ── java.net.URL (String) constructor (direct arg→this; the existing
# URL#<init>(String) entry uses arg(*) which doesn't apply consistently
# enough for tests like UnsafeStaplerServeFileServlet) ────────────────
- function: java.net.URL#<init>
signature: (java.lang.String) void
copy:
- from: arg(0)
to: this

# ── java.net.URI ──────────────────────────────────────────────────────
- function: java.net.URI#create
copy:
- from: arg(0)
to: result

# ── javax.management JMX (stdlib management API) ───────────────────────
- function: javax.management.remote.JMXServiceURL#<init>
copy:
- from: arg(*)
to: this
- function: javax.management.remote.JMXConnectorFactory#newJMXConnector
copy:
- from: arg(0)
to: result

# ── javax.xml.transform.stream.StreamSource ────────────────────────────
- function: javax.xml.transform.stream.StreamSource#<init>
copy:
- from: arg(*)
to: this

# ── java.net.http.HttpRequest$Builder (Java 11+ HttpClient) ───────────
- function: java.net.http.HttpRequest#newBuilder
copy:
- from: arg(0)
to: result
- function: java.net.http.HttpRequest$Builder#uri
copy:
- from: arg(0)
to: result
- from: arg(0)
to: this
- from: this
to: result
- function: java.net.http.HttpRequest$Builder#build
copy:
- from: this
to: result
- function: java.net.http.HttpRequest$Builder#GET
copy:
- from: this
to: result
Loading
Loading