Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ private fun SerializedTypeNameMatcher.matchTypeArgs(
if (args.size != type.typeArguments.size) return false

args.zip(type.typeArguments).all { (m, a) ->
if (a.isAnyTypeArg()) return@all true
m.matchType(a.erasedName(), resolveType = { a }, erasedMatch)
}
}
Expand All @@ -44,6 +45,14 @@ private fun SerializedTypeNameMatcher.matchTypeArgs(
}
}

/**
* `true` for type arguments that denote "any type" — a raw use's declared
* type variable (e.g. `E` of `List<E>`) or an unbounded wildcard `<?>`. Any
* pattern matcher accepts such a slot because the unknown could be whatever
* the pattern requires.
*/
fun JIRType.isAnyTypeArg(): Boolean = this is JIRTypeVariable || this is JIRUnboundWildcard

/**
* Erased class name for matching — drops any generic decoration that
* [JIRType.typeName] may carry (e.g. `Map<String, Object>` → `java.util.Map`)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import org.opentaint.dataflow.configuration.jvm.TypeArgMatcher
import org.opentaint.dataflow.configuration.jvm.TypeMatches
import org.opentaint.dataflow.configuration.jvm.TypeMatchesPattern
import org.opentaint.dataflow.configuration.jvm.erasedName
import org.opentaint.dataflow.configuration.jvm.isAnyTypeArg
import org.opentaint.dataflow.jvm.ap.ifds.CallPositionValue
import org.opentaint.dataflow.jvm.ap.ifds.JIRFactTypeChecker
import org.opentaint.dataflow.jvm.ap.ifds.JIRLocalAliasAnalysis
Expand Down Expand Up @@ -387,9 +388,12 @@ class JIRBasicAtomEvaluator(
is CallPositionValue.VarArgValue -> callVarArgValue(res.value)
}

private fun TypeArgMatcher.matchType(type: JIRType): Boolean = when (this) {
is TypeArgMatcher.Class -> matchType(type)
is TypeArgMatcher.Array -> matchType(type)
private fun TypeArgMatcher.matchType(type: JIRType): Boolean {
if (type.isAnyTypeArg()) return true
return when (this) {
is TypeArgMatcher.Class -> matchType(type)
is TypeArgMatcher.Array -> matchType(type)
}
}

private fun TypeArgMatcher.Class.matchType(type: JIRType): Boolean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import base.RuleSample;
import base.RuleSet;
import base.TaintRuleFalsePositive;
import org.springframework.http.ResponseEntity;

@RuleSet("example/RuleWithGenericMetavarArrayArg.yaml")
Expand All @@ -20,10 +19,6 @@ ResponseEntity<byte[]> methodReturningResponseEntityByteArray(String data) {
return null;
}

/**
* Raw ResponseEntity. Note: pattern is ResponseEntity<$T>, so whether this fires
* depends on whether the engine considers raw as unifiable with a type-arg metavar.
*/
@SuppressWarnings("rawtypes")
ResponseEntity methodReturningRawResponseEntity(String data) {
sink(data);
Expand All @@ -47,9 +42,9 @@ public void entrypoint() {
}

/**
* In opentaint the method-decl pattern's return-type check is effectively ignored
* on raw vs. parameterized, so raw ResponseEntity DOES get matched by
* ResponseEntity&lt;$T&gt;. We treat this as a Positive to pin the current behavior.
* The metavar type-arg pattern {@code ResponseEntity<$T>} matches the same
* set of types as {@code ResponseEntity<?>} and the raw form, so a raw use
* of {@code ResponseEntity} matches.
*/
final static class PositiveRawResponseEntity extends RuleWithGenericMetavarArrayArg {
@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package example;

import base.RuleSample;
import base.RuleSet;
import org.springframework.http.ResponseEntity;

/**
* A28. {@code pattern-not-inside} with a return type that differs from
* {@code pattern-inside} must filter on its own return type.
*
* <p>Rule: {@code pattern-inside ResponseEntity<?> $METHOD(..., String $A, ...)}
* combined with {@code pattern-not-inside ResponseEntity<Integer> $METHOD(...)}.
* The two method-decl signatures share parameter shape but differ on return
* type. The negative predicate must emit a return-type {@code IsType} clause
* for its own signature; otherwise it would drop the return-type constraint
* and exclude every method matching the parameter shape, masking real
* positives.</p>
*/
@RuleSet("example/RuleWithNotInsideDistinctReturnType.yaml")
public abstract class RuleWithNotInsideDistinctReturnType implements RuleSample {

void sink(String data) {}

ResponseEntity<String> methodReturningString(String data) {
sink(data);
return null;
}

ResponseEntity<Object> methodReturningObject(String data) {
sink(data);
return null;
}

ResponseEntity<Integer> methodReturningInteger(String data) {
sink(data);
return null;
}

/**
* {@code <String>} return — the not-inside's return-type {@code <Integer>}
* must NOT match here, so the rule fires.
*/
final static class PositiveStringReturn extends RuleWithNotInsideDistinctReturnType {
@Override
public void entrypoint() {
String data = "tainted";
methodReturningString(data);
}
}

/**
* {@code <Object>} return — same reasoning.
*/
final static class PositiveObjectReturn extends RuleWithNotInsideDistinctReturnType {
@Override
public void entrypoint() {
String data = "tainted";
methodReturningObject(data);
}
}

/**
* {@code <Integer>} return — the not-inside excludes this method.
*/
final static class NegativeIntegerReturn extends RuleWithNotInsideDistinctReturnType {
@Override
public void entrypoint() {
String data = "tainted";
methodReturningInteger(data);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package example;

import base.RuleSample;
import base.RuleSet;
import org.springframework.http.ResponseEntity;

/**
* A25. Concrete-{@code Object} type argument — pattern
* {@code ResponseEntity<Object> $METHOD(...)}.
*
* <p>{@code Object} is the upper bound of the unbounded wildcard {@code ?},
* so a method declaring its return as {@code ResponseEntity<?>} satisfies
* the {@code ResponseEntity<Object>} pattern; the raw form is identical to
* {@code ResponseEntity<?>} and matches as well. Other concrete type
* arguments such as {@code String} or {@code Integer} do NOT match.</p>
*/
@RuleSet("example/RuleWithObjectTypeArg.yaml")
public abstract class RuleWithObjectTypeArg implements RuleSample {

void sink(String data) {}

ResponseEntity<Object> methodReturningResponseEntityObject(String data) {
sink(data);
return null;
}

ResponseEntity<?> methodReturningResponseEntityWildcard(String data) {
sink(data);
return null;
}

@SuppressWarnings("rawtypes")
ResponseEntity methodReturningRawResponseEntity(String data) {
sink(data);
return null;
}

ResponseEntity<String> methodReturningResponseEntityString(String data) {
sink(data);
return null;
}

ResponseEntity<Integer> methodReturningResponseEntityInteger(String data) {
sink(data);
return null;
}

final static class PositiveObjectMatchesObject extends RuleWithObjectTypeArg {
@Override
public void entrypoint() {
String data = "tainted";
methodReturningResponseEntityObject(data);
}
}

/**
* {@code ResponseEntity<?>} matches the {@code <Object>} pattern because
* the unbounded wildcard's upper bound is {@code Object}.
*/
final static class PositiveWildcardMatchesObject extends RuleWithObjectTypeArg {
@Override
public void entrypoint() {
String data = "tainted";
methodReturningResponseEntityWildcard(data);
}
}

/**
* Raw {@code ResponseEntity} is identical to {@code ResponseEntity<?>},
* so it matches the {@code <Object>} pattern too.
*/
final static class PositiveRawMatchesObject extends RuleWithObjectTypeArg {
@Override
public void entrypoint() {
String data = "tainted";
methodReturningRawResponseEntity(data);
}
}

final static class NegativeStringTypeArg extends RuleWithObjectTypeArg {
@Override
public void entrypoint() {
String data = "tainted";
methodReturningResponseEntityString(data);
}
}

final static class NegativeIntegerTypeArg extends RuleWithObjectTypeArg {
@Override
public void entrypoint() {
String data = "tainted";
methodReturningResponseEntityInteger(data);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package example;

import base.RuleSample;
import base.RuleSet;
import org.springframework.http.ResponseEntity;

/**
* A26. Concrete-{@code String} type argument — pattern
* {@code ResponseEntity<String> $METHOD(...)}.
*
* <p>Raw {@code ResponseEntity} and {@code ResponseEntity<?>} both denote
* "any type argument", so a concrete pattern like {@code <String>} matches
* both — the unknown type argument could be {@code String}. Other concrete
* type arguments such as {@code Object} or {@code Integer} do NOT match.</p>
*/
@RuleSet("example/RuleWithStringTypeArgMatchesRawAndWildcard.yaml")
public abstract class RuleWithStringTypeArgMatchesRawAndWildcard implements RuleSample {

void sink(String data) {}

ResponseEntity<String> methodReturningResponseEntityString(String data) {
sink(data);
return null;
}

ResponseEntity<?> methodReturningResponseEntityWildcard(String data) {
sink(data);
return null;
}

@SuppressWarnings("rawtypes")
ResponseEntity methodReturningRawResponseEntity(String data) {
sink(data);
return null;
}

ResponseEntity<Object> methodReturningResponseEntityObject(String data) {
sink(data);
return null;
}

ResponseEntity<Integer> methodReturningResponseEntityInteger(String data) {
sink(data);
return null;
}

final static class PositiveStringMatchesString extends RuleWithStringTypeArgMatchesRawAndWildcard {
@Override
public void entrypoint() {
String data = "tainted";
methodReturningResponseEntityString(data);
}
}

/**
* {@code ResponseEntity<?>} could be parameterized with {@code String}, so
* the {@code <String>} pattern matches it.
*/
final static class PositiveWildcardMatchesString extends RuleWithStringTypeArgMatchesRawAndWildcard {
@Override
public void entrypoint() {
String data = "tainted";
methodReturningResponseEntityWildcard(data);
}
}

/**
* Raw {@code ResponseEntity} is identical to {@code ResponseEntity<?>}, so
* the {@code <String>} pattern matches it for the same reason.
*/
final static class PositiveRawMatchesString extends RuleWithStringTypeArgMatchesRawAndWildcard {
@Override
public void entrypoint() {
String data = "tainted";
methodReturningRawResponseEntity(data);
}
}

final static class NegativeObjectTypeArg extends RuleWithStringTypeArgMatchesRawAndWildcard {
@Override
public void entrypoint() {
String data = "tainted";
methodReturningResponseEntityObject(data);
}
}

final static class NegativeIntegerTypeArg extends RuleWithStringTypeArgMatchesRawAndWildcard {
@Override
public void entrypoint() {
String data = "tainted";
methodReturningResponseEntityInteger(data);
}
}
}
Loading
Loading