diff --git a/core/opentaint-java-querylang/samples/src/main/java/example/AllowlistNotInsideDoc.java b/core/opentaint-java-querylang/samples/src/main/java/example/AllowlistNotInsideDoc.java new file mode 100644 index 000000000..46a50dc41 --- /dev/null +++ b/core/opentaint-java-querylang/samples/src/main/java/example/AllowlistNotInsideDoc.java @@ -0,0 +1,35 @@ +package example; + +import base.RuleSample; +import base.RuleSet; + +/** + * Doc validation: pattern-not-inside excludes when the positively produced + * client is the receiver of the safe-configuration call. + */ +@RuleSet("example/AllowlistNotInsideDoc.yaml") +public abstract class AllowlistNotInsideDoc implements RuleSample { + + static class Client { + static Client builder() { return new Client(); } + void allowHost(String host) {} + void connect(String url) {} + } + + static class Positive extends AllowlistNotInsideDoc { + @Override + public void entrypoint() { + Client c = Client.builder(); + c.connect("http://example.com"); + } + } + + static class Negative extends AllowlistNotInsideDoc { + @Override + public void entrypoint() { + Client c = Client.builder(); + c.allowHost("trusted.example"); + c.connect("http://example.com"); + } + } +} diff --git a/core/opentaint-java-querylang/samples/src/main/java/example/ArgEventSanityDoc.java b/core/opentaint-java-querylang/samples/src/main/java/example/ArgEventSanityDoc.java new file mode 100644 index 000000000..e3bbe8f8d --- /dev/null +++ b/core/opentaint-java-querylang/samples/src/main/java/example/ArgEventSanityDoc.java @@ -0,0 +1,37 @@ +package example; + +import base.RuleSample; +import base.RuleSet; + +/** + * Doc validation sanity probe: the same argument-position check event used in + * the negative probes, but required POSITIVELY. If this matches, the event + * exists as an automaton step and the negative failures are specific to + * negative clauses. + */ +@RuleSet("example/ArgEventSanityDoc.yaml") +public abstract class ArgEventSanityDoc implements RuleSample { + + static Object decode(Object o) { return o; } + static int checksum; + + static void check(Object o) { checksum += o.hashCode(); } + static void consume(Object o) {} + + static class Positive extends ArgEventSanityDoc { + @Override + public void entrypoint() { + Object r = decode("x"); + check(r); + consume(r); + } + } + + static class Negative extends ArgEventSanityDoc { + @Override + public void entrypoint() { + Object r = decode("x"); + consume(r); + } + } +} diff --git a/core/opentaint-java-querylang/samples/src/main/java/example/ArgFullPatternNotDoc.java b/core/opentaint-java-querylang/samples/src/main/java/example/ArgFullPatternNotDoc.java new file mode 100644 index 000000000..abc02d2af --- /dev/null +++ b/core/opentaint-java-querylang/samples/src/main/java/example/ArgFullPatternNotDoc.java @@ -0,0 +1,36 @@ +package example; + +import base.RuleSample; +import base.RuleSet; + +/** + * Doc validation: full-form pattern-not whose added event uses the produced + * value in argument position does not anchor the exclusion — while the + * identical event matches positively (see ArgEventSanityDoc). + */ +@RuleSet("example/ArgFullPatternNotDoc.yaml") +public abstract class ArgFullPatternNotDoc implements RuleSample { + + static String decode(Object o) { return String.valueOf(o); } + static int checksum; + + static void check(String o) { checksum += o.hashCode(); } + static void consume(String o) {} + + static class Positive extends ArgFullPatternNotDoc { + @Override + public void entrypoint() { + String r = decode("x"); + consume(r); + } + } + + static class Negative extends ArgFullPatternNotDoc { + @Override + public void entrypoint() { + String r = decode("x"); + check(r); + consume(r); + } + } +} diff --git a/core/opentaint-java-querylang/samples/src/main/java/example/ArgNotInsideAnchoredDoc.java b/core/opentaint-java-querylang/samples/src/main/java/example/ArgNotInsideAnchoredDoc.java new file mode 100644 index 000000000..2ce2fbe4d --- /dev/null +++ b/core/opentaint-java-querylang/samples/src/main/java/example/ArgNotInsideAnchoredDoc.java @@ -0,0 +1,39 @@ +package example; + +import base.RuleSample; +import base.RuleSet; + +/** + * Doc validation: argument-position pattern-not-inside does not anchor even + * with a satisfiable containment — the producer lives in pattern-inside and + * the main pattern is the single consume event, so the excluded context can + * enclose the match, yet the exclusion still has no effect. Contrast with + * ReceiverNotInsideSpanDoc, where a receiver-position exclusion works even + * when the context cannot contain the producing event. + */ +@RuleSet("example/ArgNotInsideAnchoredDoc.yaml") +public abstract class ArgNotInsideAnchoredDoc implements RuleSample { + + static String decode(Object o) { return String.valueOf(o); } + static int checksum; + + static void check(String o) { checksum += o.hashCode(); } + static void consume(String o) {} + + static class Positive extends ArgNotInsideAnchoredDoc { + @Override + public void entrypoint() { + String r = decode("x"); + consume(r); + } + } + + static class Negative extends ArgNotInsideAnchoredDoc { + @Override + public void entrypoint() { + String r = decode("x"); + check(r); + consume(r); + } + } +} diff --git a/core/opentaint-java-querylang/samples/src/main/java/example/ArgObserverPatternNotDoc.java b/core/opentaint-java-querylang/samples/src/main/java/example/ArgObserverPatternNotDoc.java new file mode 100644 index 000000000..393805242 --- /dev/null +++ b/core/opentaint-java-querylang/samples/src/main/java/example/ArgObserverPatternNotDoc.java @@ -0,0 +1,34 @@ +package example; + +import base.RuleSample; +import base.RuleSet; + +/** + * Doc validation: documents the current argument-position restriction: an + * excluded event that uses the produced value only as a call argument does + * not yet anchor the exclusion. + */ +@RuleSet("example/ArgObserverPatternNotDoc.yaml") +public abstract class ArgObserverPatternNotDoc implements RuleSample { + + static String decode(Object o) { return String.valueOf(o); } + static void check(String o) {} + static void consume(String o) {} + + static class Positive extends ArgObserverPatternNotDoc { + @Override + public void entrypoint() { + String r = decode("x"); + consume(r); + } + } + + static class Negative extends ArgObserverPatternNotDoc { + @Override + public void entrypoint() { + String r = decode("x"); + check(r); + consume(r); + } + } +} diff --git a/core/opentaint-java-querylang/samples/src/main/java/example/ObjectParameterControlDoc.java b/core/opentaint-java-querylang/samples/src/main/java/example/ObjectParameterControlDoc.java new file mode 100644 index 000000000..03d055c0a --- /dev/null +++ b/core/opentaint-java-querylang/samples/src/main/java/example/ObjectParameterControlDoc.java @@ -0,0 +1,30 @@ +package example; + +import base.RuleSample; +import base.RuleSet; + +@RuleSet("example/ObjectParameterControlDoc.yaml") +public abstract class ObjectParameterControlDoc implements RuleSample { + static class Payload {} + + Payload src() { return new Payload(); } + void clean(Object data) {} // parameter widened to Object + void sink(Payload data) {} + + final static class PositiveSimple extends ObjectParameterControlDoc { + @Override + public void entrypoint() { + Payload data = src(); + sink(data); + } + } + + final static class NegativeSimple extends ObjectParameterControlDoc { + @Override + public void entrypoint() { + Payload data = src(); + clean(data); + sink(data); + } + } +} diff --git a/core/opentaint-java-querylang/samples/src/main/java/example/ObjectTypedValueCastDoc.java b/core/opentaint-java-querylang/samples/src/main/java/example/ObjectTypedValueCastDoc.java new file mode 100644 index 000000000..48b91bd9a --- /dev/null +++ b/core/opentaint-java-querylang/samples/src/main/java/example/ObjectTypedValueCastDoc.java @@ -0,0 +1,30 @@ +package example; + +import base.RuleSample; +import base.RuleSet; + +@RuleSet("example/ObjectTypedValueCastDoc.yaml") +public abstract class ObjectTypedValueCastDoc implements RuleSample { + static class Payload {} + + Object src() { return new Payload(); } // value's static type is Object + void clean(Payload data) {} + void sink(Object data) {} + + final static class PositiveSimple extends ObjectTypedValueCastDoc { + @Override + public void entrypoint() { + Object data = src(); + sink(data); + } + } + + final static class NegativeSimple extends ObjectTypedValueCastDoc { + @Override + public void entrypoint() { + Object data = src(); + clean((Payload) data); + sink(data); + } + } +} diff --git a/core/opentaint-java-querylang/samples/src/main/java/example/ObjectTypedValueDoc.java b/core/opentaint-java-querylang/samples/src/main/java/example/ObjectTypedValueDoc.java new file mode 100644 index 000000000..1cb98a2b9 --- /dev/null +++ b/core/opentaint-java-querylang/samples/src/main/java/example/ObjectTypedValueDoc.java @@ -0,0 +1,28 @@ +package example; + +import base.RuleSample; +import base.RuleSet; + +@RuleSet("example/ObjectTypedValueDoc.yaml") +public abstract class ObjectTypedValueDoc implements RuleSample { + Object src() { return null; } + void clean(Object data) {} + void sink(Object data) {} + + final static class PositiveSimple extends ObjectTypedValueDoc { + @Override + public void entrypoint() { + Object data = src(); + sink(data); + } + } + + final static class NegativeSimple extends ObjectTypedValueDoc { + @Override + public void entrypoint() { + Object data = src(); + clean(data); + sink(data); + } + } +} diff --git a/core/opentaint-java-querylang/samples/src/main/java/example/ObjectTypedValueReceiverDoc.java b/core/opentaint-java-querylang/samples/src/main/java/example/ObjectTypedValueReceiverDoc.java new file mode 100644 index 000000000..eb263975c --- /dev/null +++ b/core/opentaint-java-querylang/samples/src/main/java/example/ObjectTypedValueReceiverDoc.java @@ -0,0 +1,27 @@ +package example; + +import base.RuleSample; +import base.RuleSet; + +@RuleSet("example/ObjectTypedValueReceiverDoc.yaml") +public abstract class ObjectTypedValueReceiverDoc implements RuleSample { + Object src() { return null; } + void sink(Object data) {} + + final static class PositiveSimple extends ObjectTypedValueReceiverDoc { + @Override + public void entrypoint() { + Object data = src(); + sink(data); + } + } + + final static class NegativeSimple extends ObjectTypedValueReceiverDoc { + @Override + public void entrypoint() { + Object data = src(); + data = data.toString(); + sink(data); + } + } +} diff --git a/core/opentaint-java-querylang/samples/src/main/java/example/ReceiverSanitizePatternNotDoc.java b/core/opentaint-java-querylang/samples/src/main/java/example/ReceiverSanitizePatternNotDoc.java new file mode 100644 index 000000000..1595e459e --- /dev/null +++ b/core/opentaint-java-querylang/samples/src/main/java/example/ReceiverSanitizePatternNotDoc.java @@ -0,0 +1,36 @@ +package example; + +import base.RuleSample; +import base.RuleSet; + +/** + * Doc validation: multi-event pattern-not where the excluded on-path event is + * a receiver call on the produced value. + */ +@RuleSet("example/ReceiverSanitizePatternNotDoc.yaml") +public abstract class ReceiverSanitizePatternNotDoc implements RuleSample { + + static class Value { + Value sanitized() { return this; } + } + + static Value decode(Object o) { return new Value(); } + static void consume(Value v) {} + + static class Positive extends ReceiverSanitizePatternNotDoc { + @Override + public void entrypoint() { + Value r = decode("x"); + consume(r); + } + } + + static class Negative extends ReceiverSanitizePatternNotDoc { + @Override + public void entrypoint() { + Value r = decode("x"); + r = r.sanitized(); + consume(r); + } + } +} diff --git a/core/opentaint-java-querylang/samples/src/main/java/example/SanitizeEventSanityDoc.java b/core/opentaint-java-querylang/samples/src/main/java/example/SanitizeEventSanityDoc.java new file mode 100644 index 000000000..066732757 --- /dev/null +++ b/core/opentaint-java-querylang/samples/src/main/java/example/SanitizeEventSanityDoc.java @@ -0,0 +1,35 @@ +package example; + +import base.RuleSample; +import base.RuleSet; + +/** + * Doc validation sanity probe: the self-sanitizing reassignment event + * required POSITIVELY. If this matches, the event exists as an automaton + * step and the negative failures for the same event are specific to + * negative clauses. + */ +@RuleSet("example/SanitizeEventSanityDoc.yaml") +public abstract class SanitizeEventSanityDoc implements RuleSample { + + static Object decode(Object o) { return o; } + static Object sanitize(Object o) { return o; } + static void consume(Object o) {} + + static class Positive extends SanitizeEventSanityDoc { + @Override + public void entrypoint() { + Object r = decode("x"); + r = sanitize(r); + consume(r); + } + } + + static class Negative extends SanitizeEventSanityDoc { + @Override + public void entrypoint() { + Object r = decode("x"); + consume(r); + } + } +} diff --git a/core/opentaint-java-querylang/samples/src/main/java/example/SanitizeNotInsideDoc.java b/core/opentaint-java-querylang/samples/src/main/java/example/SanitizeNotInsideDoc.java new file mode 100644 index 000000000..f5c0ef446 --- /dev/null +++ b/core/opentaint-java-querylang/samples/src/main/java/example/SanitizeNotInsideDoc.java @@ -0,0 +1,37 @@ +package example; + +import base.RuleSample; +import base.RuleSet; + +/** + * Doc validation: pattern-not-inside whose excluded event is the + * self-sanitizing reassignment, in the anchored shape: producer in + * pattern-inside, single-event main pattern, so the excluded context can + * enclose the match. The identical event matches positively + * (SanitizeEventSanityDoc); the receiver-position counterpart excludes + * (ReceiverSanitizePatternNotDoc). + */ +@RuleSet("example/SanitizeNotInsideDoc.yaml") +public abstract class SanitizeNotInsideDoc implements RuleSample { + + static String decode(Object o) { return String.valueOf(o); } + static String sanitize(String o) { return o; } + static void consume(String o) {} + + static class Positive extends SanitizeNotInsideDoc { + @Override + public void entrypoint() { + String r = decode("x"); + consume(r); + } + } + + static class Negative extends SanitizeNotInsideDoc { + @Override + public void entrypoint() { + String r = decode("x"); + r = sanitize(r); + consume(r); + } + } +} diff --git a/core/opentaint-java-querylang/samples/src/main/java/example/SanitizePatternNotDoc.java b/core/opentaint-java-querylang/samples/src/main/java/example/SanitizePatternNotDoc.java new file mode 100644 index 000000000..509584511 --- /dev/null +++ b/core/opentaint-java-querylang/samples/src/main/java/example/SanitizePatternNotDoc.java @@ -0,0 +1,35 @@ +package example; + +import base.RuleSample; +import base.RuleSet; + +/** + * Doc validation: documents the current argument-position restriction: a + * pattern-not event that uses the tracked value only as a call argument + * (static-call sanitize) does not yet anchor the exclusion. See + * ReceiverSanitizePatternNotDoc for the working receiver-shaped form. + */ +@RuleSet("example/SanitizePatternNotDoc.yaml") +public abstract class SanitizePatternNotDoc implements RuleSample { + + static String decode(Object o) { return String.valueOf(o); } + static String sanitize(String o) { return o; } + static void consume(String o) {} + + static class Positive extends SanitizePatternNotDoc { + @Override + public void entrypoint() { + String r = decode("x"); + consume(r); + } + } + + static class Negative extends SanitizePatternNotDoc { + @Override + public void entrypoint() { + String r = decode("x"); + r = sanitize(r); + consume(r); + } + } +} diff --git a/core/opentaint-java-querylang/samples/src/main/java/example/TypedValueControlDoc.java b/core/opentaint-java-querylang/samples/src/main/java/example/TypedValueControlDoc.java new file mode 100644 index 000000000..547809ffe --- /dev/null +++ b/core/opentaint-java-querylang/samples/src/main/java/example/TypedValueControlDoc.java @@ -0,0 +1,30 @@ +package example; + +import base.RuleSample; +import base.RuleSet; + +@RuleSet("example/TypedValueControlDoc.yaml") +public abstract class TypedValueControlDoc implements RuleSample { + static class Payload {} + + Payload src() { return new Payload(); } + void clean(Payload data) {} + void sink(Payload data) {} + + final static class PositiveSimple extends TypedValueControlDoc { + @Override + public void entrypoint() { + Payload data = src(); + sink(data); + } + } + + final static class NegativeSimple extends TypedValueControlDoc { + @Override + public void entrypoint() { + Payload data = src(); + clean(data); + sink(data); + } + } +} diff --git a/core/opentaint-java-querylang/samples/src/main/resources/example/AllowlistNotInsideDoc.yaml b/core/opentaint-java-querylang/samples/src/main/resources/example/AllowlistNotInsideDoc.yaml new file mode 100644 index 000000000..c170d3ec2 --- /dev/null +++ b/core/opentaint-java-querylang/samples/src/main/resources/example/AllowlistNotInsideDoc.yaml @@ -0,0 +1,14 @@ +rules: + - id: example-AllowlistNotInsideDoc + languages: + - java + severity: ERROR + message: match example/AllowlistNotInsideDoc + patterns: + - pattern-inside: | + $CLIENT = Client.builder(); + ... + - pattern: $CLIENT.connect($URL) + - pattern-not-inside: | + $CLIENT.allowHost("trusted.example"); + ... diff --git a/core/opentaint-java-querylang/samples/src/main/resources/example/ArgEventSanityDoc.yaml b/core/opentaint-java-querylang/samples/src/main/resources/example/ArgEventSanityDoc.yaml new file mode 100644 index 000000000..8675a7a7f --- /dev/null +++ b/core/opentaint-java-querylang/samples/src/main/resources/example/ArgEventSanityDoc.yaml @@ -0,0 +1,13 @@ +rules: + - id: example-ArgEventSanityDoc + languages: + - java + severity: ERROR + message: match example/ArgEventSanityDoc + patterns: + - pattern: | + $RESULT = decode($INPUT); + ... + check($RESULT); + ... + consume($RESULT); diff --git a/core/opentaint-java-querylang/samples/src/main/resources/example/ArgFullPatternNotDoc.yaml b/core/opentaint-java-querylang/samples/src/main/resources/example/ArgFullPatternNotDoc.yaml new file mode 100644 index 000000000..c159ada67 --- /dev/null +++ b/core/opentaint-java-querylang/samples/src/main/resources/example/ArgFullPatternNotDoc.yaml @@ -0,0 +1,17 @@ +rules: + - id: example-ArgFullPatternNotDoc + languages: + - java + severity: ERROR + message: match example/ArgFullPatternNotDoc + patterns: + - pattern: | + $RESULT = decode($INPUT); + ... + consume($RESULT); + - pattern-not: | + $RESULT = decode($INPUT); + ... + check($RESULT); + ... + consume($RESULT); diff --git a/core/opentaint-java-querylang/samples/src/main/resources/example/ArgNotInsideAnchoredDoc.yaml b/core/opentaint-java-querylang/samples/src/main/resources/example/ArgNotInsideAnchoredDoc.yaml new file mode 100644 index 000000000..4bd099f2f --- /dev/null +++ b/core/opentaint-java-querylang/samples/src/main/resources/example/ArgNotInsideAnchoredDoc.yaml @@ -0,0 +1,14 @@ +rules: + - id: example-ArgNotInsideAnchoredDoc + languages: + - java + severity: ERROR + message: match example/ArgNotInsideAnchoredDoc + patterns: + - pattern-inside: | + $RESULT = decode($INPUT); + ... + - pattern: consume($RESULT) + - pattern-not-inside: | + check($RESULT); + ... diff --git a/core/opentaint-java-querylang/samples/src/main/resources/example/ArgObserverPatternNotDoc.yaml b/core/opentaint-java-querylang/samples/src/main/resources/example/ArgObserverPatternNotDoc.yaml new file mode 100644 index 000000000..3142c391a --- /dev/null +++ b/core/opentaint-java-querylang/samples/src/main/resources/example/ArgObserverPatternNotDoc.yaml @@ -0,0 +1,16 @@ +rules: + - id: example-ArgObserverPatternNotDoc + languages: + - java + severity: ERROR + message: match example/ArgObserverPatternNotDoc + patterns: + - pattern: | + $RESULT = decode($INPUT); + ... + consume($RESULT); + - pattern-not: | + ... + check($RESULT); + ... + consume($RESULT); diff --git a/core/opentaint-java-querylang/samples/src/main/resources/example/ObjectParameterControlDoc.yaml b/core/opentaint-java-querylang/samples/src/main/resources/example/ObjectParameterControlDoc.yaml new file mode 100644 index 000000000..60b09bee3 --- /dev/null +++ b/core/opentaint-java-querylang/samples/src/main/resources/example/ObjectParameterControlDoc.yaml @@ -0,0 +1,17 @@ +rules: + - id: example-ObjectParameterControlDoc + languages: + - java + severity: ERROR + message: match example/ObjectParameterControlDoc + patterns: + - pattern: |- + $A = src(); + ... + sink($A); + - pattern-not: |- + $A = src(); + ... + clean($A); + ... + sink($A); diff --git a/core/opentaint-java-querylang/samples/src/main/resources/example/ObjectTypedValueCastDoc.yaml b/core/opentaint-java-querylang/samples/src/main/resources/example/ObjectTypedValueCastDoc.yaml new file mode 100644 index 000000000..17520c269 --- /dev/null +++ b/core/opentaint-java-querylang/samples/src/main/resources/example/ObjectTypedValueCastDoc.yaml @@ -0,0 +1,17 @@ +rules: + - id: example-ObjectTypedValueCastDoc + languages: + - java + severity: ERROR + message: match example/ObjectTypedValueCastDoc + patterns: + - pattern: |- + $A = src(); + ... + sink($A); + - pattern-not: |- + $A = src(); + ... + clean($A); + ... + sink($A); diff --git a/core/opentaint-java-querylang/samples/src/main/resources/example/ObjectTypedValueDoc.yaml b/core/opentaint-java-querylang/samples/src/main/resources/example/ObjectTypedValueDoc.yaml new file mode 100644 index 000000000..3b93c7573 --- /dev/null +++ b/core/opentaint-java-querylang/samples/src/main/resources/example/ObjectTypedValueDoc.yaml @@ -0,0 +1,17 @@ +rules: + - id: example-ObjectTypedValueDoc + languages: + - java + severity: ERROR + message: match example/ObjectTypedValueDoc + patterns: + - pattern: |- + $A = src(); + ... + sink($A); + - pattern-not: |- + $A = src(); + ... + clean($A); + ... + sink($A); diff --git a/core/opentaint-java-querylang/samples/src/main/resources/example/ObjectTypedValueReceiverDoc.yaml b/core/opentaint-java-querylang/samples/src/main/resources/example/ObjectTypedValueReceiverDoc.yaml new file mode 100644 index 000000000..818bcf1eb --- /dev/null +++ b/core/opentaint-java-querylang/samples/src/main/resources/example/ObjectTypedValueReceiverDoc.yaml @@ -0,0 +1,17 @@ +rules: + - id: example-ObjectTypedValueReceiverDoc + languages: + - java + severity: ERROR + message: match example/ObjectTypedValueReceiverDoc + patterns: + - pattern: |- + $A = src(); + ... + sink($A); + - pattern-not: |- + $A = src(); + ... + $A = $A.toString(); + ... + sink($A); diff --git a/core/opentaint-java-querylang/samples/src/main/resources/example/ReceiverSanitizePatternNotDoc.yaml b/core/opentaint-java-querylang/samples/src/main/resources/example/ReceiverSanitizePatternNotDoc.yaml new file mode 100644 index 000000000..9c3547595 --- /dev/null +++ b/core/opentaint-java-querylang/samples/src/main/resources/example/ReceiverSanitizePatternNotDoc.yaml @@ -0,0 +1,17 @@ +rules: + - id: example-ReceiverSanitizePatternNotDoc + languages: + - java + severity: ERROR + message: match example/ReceiverSanitizePatternNotDoc + patterns: + - pattern: | + $RESULT = decode($INPUT); + ... + consume($RESULT); + - pattern-not: | + $RESULT = decode($INPUT); + ... + $RESULT = $RESULT.sanitized(); + ... + consume($RESULT); diff --git a/core/opentaint-java-querylang/samples/src/main/resources/example/SanitizeEventSanityDoc.yaml b/core/opentaint-java-querylang/samples/src/main/resources/example/SanitizeEventSanityDoc.yaml new file mode 100644 index 000000000..b48272f83 --- /dev/null +++ b/core/opentaint-java-querylang/samples/src/main/resources/example/SanitizeEventSanityDoc.yaml @@ -0,0 +1,13 @@ +rules: + - id: example-SanitizeEventSanityDoc + languages: + - java + severity: ERROR + message: match example/SanitizeEventSanityDoc + patterns: + - pattern: | + $RESULT = decode($INPUT); + ... + $RESULT = sanitize($RESULT); + ... + consume($RESULT); diff --git a/core/opentaint-java-querylang/samples/src/main/resources/example/SanitizeNotInsideDoc.yaml b/core/opentaint-java-querylang/samples/src/main/resources/example/SanitizeNotInsideDoc.yaml new file mode 100644 index 000000000..0a5e46b09 --- /dev/null +++ b/core/opentaint-java-querylang/samples/src/main/resources/example/SanitizeNotInsideDoc.yaml @@ -0,0 +1,14 @@ +rules: + - id: example-SanitizeNotInsideDoc + languages: + - java + severity: ERROR + message: match example/SanitizeNotInsideDoc + patterns: + - pattern-inside: | + $RESULT = decode($INPUT); + ... + - pattern: consume($RESULT) + - pattern-not-inside: | + $RESULT = sanitize($RESULT); + ... diff --git a/core/opentaint-java-querylang/samples/src/main/resources/example/SanitizePatternNotDoc.yaml b/core/opentaint-java-querylang/samples/src/main/resources/example/SanitizePatternNotDoc.yaml new file mode 100644 index 000000000..b21982541 --- /dev/null +++ b/core/opentaint-java-querylang/samples/src/main/resources/example/SanitizePatternNotDoc.yaml @@ -0,0 +1,17 @@ +rules: + - id: example-SanitizePatternNotDoc + languages: + - java + severity: ERROR + message: match example/SanitizePatternNotDoc + patterns: + - pattern: | + $RESULT = decode($INPUT); + ... + consume($RESULT); + - pattern-not: | + $RESULT = decode($INPUT); + ... + $RESULT = sanitize($RESULT); + ... + consume($RESULT); diff --git a/core/opentaint-java-querylang/samples/src/main/resources/example/TypedValueControlDoc.yaml b/core/opentaint-java-querylang/samples/src/main/resources/example/TypedValueControlDoc.yaml new file mode 100644 index 000000000..c9ec1f705 --- /dev/null +++ b/core/opentaint-java-querylang/samples/src/main/resources/example/TypedValueControlDoc.yaml @@ -0,0 +1,17 @@ +rules: + - id: example-TypedValueControlDoc + languages: + - java + severity: ERROR + message: match example/TypedValueControlDoc + patterns: + - pattern: |- + $A = src(); + ... + sink($A); + - pattern-not: |- + $A = src(); + ... + clean($A); + ... + sink($A); diff --git a/core/opentaint-java-querylang/src/test/kotlin/org/opentaint/semgrep/DocNegativeShapesTest.kt b/core/opentaint-java-querylang/src/test/kotlin/org/opentaint/semgrep/DocNegativeShapesTest.kt new file mode 100644 index 000000000..7c486cc14 --- /dev/null +++ b/core/opentaint-java-querylang/src/test/kotlin/org/opentaint/semgrep/DocNegativeShapesTest.kt @@ -0,0 +1,90 @@ +package org.opentaint.semgrep + +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.TestInstance +import org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS +import org.opentaint.semgrep.util.SampleBasedTest +import kotlin.test.Test + +/** + * Pins negative-clause exclusion behavior. + * + * The open defect: when the tracked value's declared type is + * `java.lang.Object`, a structural negative that observes the value does not + * exclude the match. `ObjectTypedValueDoc` and `ObjectTypedValueCastDoc` + * assert the desired semantics and stay red until it is fixed. + * + * Everything else passes and isolates the defect: the same rule shapes with a + * declared type (String, a custom class) exclude correctly, the excluded + * call's own parameter type is irrelevant, and an Object-typed value is + * excluded correctly when the negative rebinds it through a receiver call. + */ +@TestInstance(PER_CLASS) +class DocNegativeShapesTest : SampleBasedTest() { + + // --- the defect --- + + @Test + fun `test Object-typed value is not excluded`() = runTest() + + @Test + fun `test Object-typed value is not excluded through a cast`() = + runTest() + + // --- controls isolating it --- + + @Test + fun `test declared-type value is excluded`() = runTest() + + @Test + fun `test excluded call parameter type is irrelevant`() = + runTest() + + @Test + fun `test Object-typed value is excluded when the negative rebinds it`() = + runTest() + + // --- clause shapes, all with declared-type values --- + + @Test + fun `test full-form pattern-not with an observing event`() = + runTest() + + @Test + fun `test leading-ellipsis pattern-not with an observing event`() = + runTest() + + @Test + fun `test pattern-not with a self-sanitizing reassignment`() = + runTest() + + @Test + fun `test pattern-not with a receiver-call reassignment`() = + runTest() + + @Test + fun `test pattern-not-inside with an observing event`() = + runTest() + + @Test + fun `test pattern-not-inside with a self-sanitizing reassignment`() = + runTest() + + @Test + fun `test pattern-not-inside excluding a configured receiver`() = + runTest() + + // --- positive controls: the excluded events match when required --- + + @Test + fun `test observing event matches positively`() = runTest() + + @Test + fun `test reassignment event matches positively`() = + runTest() + + @AfterAll + fun close() { + closeRunner() + } +}