diff --git a/java/ql/automodel/src/AutomodelApplicationModeCharacteristics.qll b/java/ql/automodel/src/AutomodelApplicationModeCharacteristics.qll index b124e453fbc..44d74a5565f 100644 --- a/java/ql/automodel/src/AutomodelApplicationModeCharacteristics.qll +++ b/java/ql/automodel/src/AutomodelApplicationModeCharacteristics.qll @@ -15,7 +15,6 @@ private import semmle.code.java.security.QueryInjection private import semmle.code.java.dataflow.internal.ModelExclusions as ModelExclusions private import AutomodelJavaUtil as AutomodelJavaUtil private import semmle.code.java.security.PathSanitizer as PathSanitizer -private import AutomodelSharedGetCallable as AutomodelSharedGetCallable import AutomodelSharedCharacteristics as SharedCharacteristics import AutomodelEndpointTypes as AutomodelEndpointTypes @@ -64,8 +63,6 @@ abstract private class ApplicationModeEndpoint extends TApplicationModeEndpoint */ abstract Callable getCallable(); - abstract Call getCall(); - /** * Gets the input (if any) for this endpoint, eg.: `Argument[0]`. * @@ -99,50 +96,50 @@ abstract private class ApplicationModeEndpoint extends TApplicationModeEndpoint abstract string toString(); } +class TCallArgument = TExplicitArgument or TInstanceArgument or TImplicitVarargsArray; + /** - * A class representing nodes that are arguments to calls. + * An endpoint that represents an "argument" to a call in a broad sense, including + * both explicit arguments and the instance argument. */ -class ExplicitArgument extends ApplicationModeEndpoint, TExplicitArgument { +abstract class CallArgument extends ApplicationModeEndpoint, TCallArgument { Call call; DataFlow::Node arg; - ExplicitArgument() { this = TExplicitArgument(call, arg) } - override Callable getCallable() { result = call.getCallee().getSourceDeclaration() } - override Call getCall() { result = call } + override string getMaDOutput() { none() } + + override DataFlow::Node asNode() { result = arg } + + Call getCall() { result = call } + + override string toString() { result = arg.toString() } +} + +/** + * An endpoint that represents an explicit argument to a call. + */ +class ExplicitArgument extends CallArgument, TExplicitArgument { + ExplicitArgument() { this = TExplicitArgument(call, arg) } private int getArgIndex() { this.asTop() = call.getArgument(result) } override string getMaDInput() { result = "Argument[" + this.getArgIndex() + "]" } - override string getMaDOutput() { none() } - override Top asTop() { result = arg.asExpr() } - - override DataFlow::Node asNode() { result = arg } - - override string toString() { result = arg.toString() } } -class InstanceArgument extends ApplicationModeEndpoint, TInstanceArgument { - Call call; - DataFlow::Node arg; - +/** + * An endpoint that represents the instance argument to a call. + */ +class InstanceArgument extends CallArgument, TInstanceArgument { InstanceArgument() { this = TInstanceArgument(call, arg) } - override Callable getCallable() { result = call.getCallee().getSourceDeclaration() } - - override Call getCall() { result = call } - override string getMaDInput() { result = "Argument[this]" } - override string getMaDOutput() { none() } - override Top asTop() { if exists(arg.asExpr()) then result = arg.asExpr() else result = call } - override DataFlow::Node asNode() { result = arg } - override string toString() { result = arg.toString() } } @@ -155,26 +152,14 @@ class InstanceArgument extends ApplicationModeEndpoint, TInstanceArgument { * In order to be able to distinguish between varargs endpoints and regular endpoints, we export the `isVarargsArray` * meta data field in the extraction queries. */ -class ImplicitVarargsArray extends ApplicationModeEndpoint, TImplicitVarargsArray { - Call call; - DataFlow::Node vararg; +class ImplicitVarargsArray extends CallArgument, TImplicitVarargsArray { int idx; - ImplicitVarargsArray() { this = TImplicitVarargsArray(call, vararg, idx) } - - override Callable getCallable() { result = call.getCallee().getSourceDeclaration() } - - override Call getCall() { result = call } + ImplicitVarargsArray() { this = TImplicitVarargsArray(call, arg, idx) } override string getMaDInput() { result = "Argument[" + idx + "]" } - override string getMaDOutput() { none() } - override Top asTop() { result = call } - - override DataFlow::Node asNode() { result = vararg } - - override string toString() { result = vararg.toString() } } /** @@ -188,8 +173,6 @@ class MethodReturnValue extends ApplicationModeEndpoint, TMethodReturnValue { override Callable getCallable() { result = call.getCallee().getSourceDeclaration() } - override Call getCall() { result = call } - override string getMaDInput() { none() } override string getMaDOutput() { result = "ReturnValue" } @@ -219,8 +202,6 @@ class OverriddenParameter extends ApplicationModeEndpoint, TOverriddenParameter result = overriddenMethod.getSourceDeclaration() } - override Call getCall() { none() } - private int getArgIndex() { p.getCallable().getParameter(result) = p } override string getMaDInput() { none() } @@ -247,7 +228,9 @@ module ApplicationCandidatesImpl implements SharedCharacteristics::CandidateSig class EndpointType = AutomodelEndpointTypes::EndpointType; - class NegativeEndpointType = AutomodelEndpointTypes::NegativeSinkType; + class SinkType = AutomodelEndpointTypes::SinkType; + + class SourceType = AutomodelEndpointTypes::SourceType; class RelatedLocation = Location::Top; @@ -324,7 +307,7 @@ module ApplicationCandidatesImpl implements SharedCharacteristics::CandidateSig */ RelatedLocation getRelatedLocation(Endpoint e, RelatedLocationType type) { type = CallContext() and - result = e.getCall() + result = e.(CallArgument).getCall() or type = MethodDoc() and result = e.getCallable().(Documentable).getJavadoc() @@ -334,22 +317,6 @@ module ApplicationCandidatesImpl implements SharedCharacteristics::CandidateSig } } -private class JavaCallable = Callable; - -private module ApplicationModeGetCallable implements AutomodelSharedGetCallable::GetCallableSig { - class Callable = JavaCallable; - - class Endpoint = ApplicationCandidatesImpl::Endpoint; - - /** - * Returns the API callable being modeled. - * - * We usually want to use `.getSourceDeclaration()` instead of just 'the' callable, - * because the source declaration callable has erased generic type parameters. - */ - Callable getCallable(Endpoint e) { result = e.getCall().getCallee() } -} - /** * Contains endpoints that are defined in QL code rather than as a MaD model. Ideally this predicate * should be empty. @@ -377,10 +344,10 @@ class ApplicationModeMetadataExtractor extends string { predicate hasMetadata( Endpoint e, string package, string type, string subtypes, string name, string signature, - string input, string output, string isVarargsArray + string input, string output, string isVarargsArray, string alreadyAiModeled, + string extensibleType ) { - exists(Callable callable | - e.getCallable() = callable and + exists(Callable callable | e.getCallable() = callable | (if exists(e.getMaDInput()) then input = e.getMaDInput() else input = "") and (if exists(e.getMaDOutput()) then output = e.getMaDOutput() else output = "") and package = callable.getDeclaringType().getPackage().getName() and @@ -390,9 +357,17 @@ class ApplicationModeMetadataExtractor extends string { subtypes = AutomodelJavaUtil::considerSubtypes(callable).toString() and name = callable.getName() and signature = ExternalFlow::paramsString(callable) and - if e instanceof ImplicitVarargsArray - then isVarargsArray = "true" - else isVarargsArray = "false" + ( + if e instanceof ImplicitVarargsArray + then isVarargsArray = "true" + else isVarargsArray = "false" + ) and + extensibleType = e.getExtensibleType() + ) and + ( + not CharacteristicsImpl::isModeled(e, _, extensibleType, _) and alreadyAiModeled = "" + or + CharacteristicsImpl::isModeled(e, _, extensibleType, alreadyAiModeled) ) } } @@ -402,7 +377,8 @@ class ApplicationModeMetadataExtractor extends string { */ /** - * A negative characteristic that indicates that an is-style boolean method is unexploitable even if it is a sink. + * A negative characteristic that indicates that parameters of an is-style boolean method should not be considered sinks, + * and its return value should not be considered a source. * * A sink is highly unlikely to be exploitable if its callable's name starts with `is` and the callable has a boolean return * type (e.g. `isDirectory`). These kinds of calls normally do only checks, and appear before the proper call that does @@ -410,46 +386,70 @@ class ApplicationModeMetadataExtractor extends string { * * TODO: this might filter too much, it's possible that methods with more than one parameter contain interesting sinks */ -private class UnexploitableIsCharacteristic extends CharacteristicsImpl::NotASinkCharacteristic { +private class UnexploitableIsCharacteristic extends CharacteristicsImpl::NeitherSourceNorSinkCharacteristic +{ UnexploitableIsCharacteristic() { this = "unexploitable (is-style boolean method)" } override predicate appliesToEndpoint(Endpoint e) { - not ApplicationCandidatesImpl::isSink(e, _, _) and e.getCallable().getName().matches("is%") and - e.getCallable().getReturnType() instanceof BooleanType - } -} - -/** - * A negative characteristic that indicates that an existence-checking boolean method is unexploitable even if it is a - * sink. - * - * A sink is highly unlikely to be exploitable if its callable's name is `exists` or `notExists` and the callable has a - * boolean return type. These kinds of calls normally do only checks, and appear before the proper call that does the - * dangerous/interesting thing, so we want the latter to be modeled as the sink. - */ -private class UnexploitableExistsCharacteristic extends CharacteristicsImpl::NotASinkCharacteristic { - UnexploitableExistsCharacteristic() { this = "unexploitable (existence-checking boolean method)" } - - override predicate appliesToEndpoint(Endpoint e) { - not ApplicationCandidatesImpl::isSink(e, _, _) and - exists(Callable callable | - callable = ApplicationModeGetCallable::getCallable(e) and - callable.getName().toLowerCase() = ["exists", "notexists"] and - callable.getReturnType() instanceof BooleanType + e.getCallable().getReturnType() instanceof BooleanType and + ( + e.getExtensibleType() = "sinkModel" and + not ApplicationCandidatesImpl::isSink(e, _, _) + or + e.getExtensibleType() = "sourceModel" and + not ApplicationCandidatesImpl::isSource(e, _, _) and + e.getMaDOutput() = "ReturnValue" ) } } /** - * A negative characteristic that indicates that an endpoint is an argument to an exception, which is not a sink. + * A negative characteristic that indicates that parameters of an existence-checking boolean method should not be + * considered sinks, and its return value should not be considered a source. + * + * A sink is highly unlikely to be exploitable if its callable's name is `exists` or `notExists` and the callable has a + * boolean return type. These kinds of calls normally do only checks, and appear before the proper call that does the + * dangerous/interesting thing, so we want the latter to be modeled as the sink. */ -private class ExceptionCharacteristic extends CharacteristicsImpl::NotASinkCharacteristic { +private class UnexploitableExistsCharacteristic extends CharacteristicsImpl::NeitherSourceNorSinkCharacteristic +{ + UnexploitableExistsCharacteristic() { this = "unexploitable (existence-checking boolean method)" } + + override predicate appliesToEndpoint(Endpoint e) { + exists(Callable callable | + callable = e.getCallable() and + callable.getName().toLowerCase() = ["exists", "notexists"] and + callable.getReturnType() instanceof BooleanType + | + e.getExtensibleType() = "sinkModel" and + not ApplicationCandidatesImpl::isSink(e, _, _) + or + e.getExtensibleType() = "sourceModel" and + not ApplicationCandidatesImpl::isSource(e, _, _) and + e.getMaDOutput() = "ReturnValue" + ) + } +} + +/** + * A negative characteristic that indicates that parameters of an exception method or constructor should not be considered sinks, + * and its return value should not be considered a source. + */ +private class ExceptionCharacteristic extends CharacteristicsImpl::NeitherSourceNorSinkCharacteristic +{ ExceptionCharacteristic() { this = "exception" } override predicate appliesToEndpoint(Endpoint e) { - ApplicationModeGetCallable::getCallable(e).getDeclaringType().getASupertype*() instanceof - TypeThrowable + e.getCallable().getDeclaringType().getASupertype*() instanceof TypeThrowable and + ( + e.getExtensibleType() = "sinkModel" and + not ApplicationCandidatesImpl::isSink(e, _, _) + or + e.getExtensibleType() = "sourceModel" and + not ApplicationCandidatesImpl::isSource(e, _, _) and + e.getMaDOutput() = "ReturnValue" + ) } } @@ -462,7 +462,6 @@ private class IsMaDTaintStepCharacteristic extends CharacteristicsImpl::NotASink IsMaDTaintStepCharacteristic() { this = "taint step" } override predicate appliesToEndpoint(Endpoint e) { - e.getExtensibleType() = "sinkModel" and FlowSummaryImpl::Private::Steps::summaryThroughStepValue(e.asNode(), _, _) or FlowSummaryImpl::Private::Steps::summaryThroughStepTaint(e.asNode(), _, _) @@ -483,18 +482,20 @@ private class LocalCall extends CharacteristicsImpl::UninterestingToModelCharact LocalCall() { this = "local call" } override predicate appliesToEndpoint(Endpoint e) { - ApplicationModeGetCallable::getCallable(e).fromSource() + e.(CallArgument).getCallable().fromSource() + or + e.(MethodReturnValue).getCallable().fromSource() } } /** - * A Characteristic that marks endpoints as uninteresting to model, according to the Java ModelExclusions module. + * A characteristic that marks endpoints as uninteresting to model, according to the Java ModelExclusions module. */ private class ExcludedFromModeling extends CharacteristicsImpl::UninterestingToModelCharacteristic { ExcludedFromModeling() { this = "excluded from modeling" } override predicate appliesToEndpoint(Endpoint e) { - ModelExclusions::isUninterestingForModels(ApplicationModeGetCallable::getCallable(e)) + ModelExclusions::isUninterestingForModels(e.getCallable()) } } @@ -507,8 +508,7 @@ private class NonPublicMethodCharacteristic extends CharacteristicsImpl::Uninter NonPublicMethodCharacteristic() { this = "non-public method" } override predicate appliesToEndpoint(Endpoint e) { - e.getExtensibleType() = "sinkModel" and - not ApplicationModeGetCallable::getCallable(e).isPublic() + exists(Callable c | c = e.getCallable() | not c.isPublic()) } } @@ -530,11 +530,10 @@ private class OtherArgumentToModeledMethodCharacteristic extends Characteristics } override predicate appliesToEndpoint(Endpoint e) { - e.getExtensibleType() = "sinkModel" and not ApplicationCandidatesImpl::isSink(e, _, _) and - exists(Endpoint otherSink | + exists(CallArgument otherSink | ApplicationCandidatesImpl::isSink(otherSink, _, "manual") and - e.getCall() = otherSink.getCall() and + e.(CallArgument).getCall() = otherSink.getCall() and e != otherSink ) } @@ -548,10 +547,7 @@ private class OtherArgumentToModeledMethodCharacteristic extends Characteristics private class FunctionValueCharacteristic extends CharacteristicsImpl::LikelyNotASinkCharacteristic { FunctionValueCharacteristic() { this = "function value" } - override predicate appliesToEndpoint(Endpoint e) { - e.getExtensibleType() = "sinkModel" and - e.asNode().asExpr() instanceof FunctionalExpr - } + override predicate appliesToEndpoint(Endpoint e) { e.asNode().asExpr() instanceof FunctionalExpr } } /** @@ -565,10 +561,7 @@ private class CannotBeTaintedCharacteristic extends CharacteristicsImpl::LikelyN { CannotBeTaintedCharacteristic() { this = "cannot be tainted" } - override predicate appliesToEndpoint(Endpoint e) { - e.getExtensibleType() = "sinkModel" and - not this.isKnownOutNodeForStep(e) - } + override predicate appliesToEndpoint(Endpoint e) { not this.isKnownOutNodeForStep(e) } /** * Holds if the node `n` is known as the predecessor in a modeled flow step. diff --git a/java/ql/automodel/src/AutomodelApplicationModeExtractCandidates.ql b/java/ql/automodel/src/AutomodelApplicationModeExtractCandidates.ql index 0d6814dbd01..e3384a52bb8 100644 --- a/java/ql/automodel/src/AutomodelApplicationModeExtractCandidates.ql +++ b/java/ql/automodel/src/AutomodelApplicationModeExtractCandidates.ql @@ -25,20 +25,20 @@ private import AutomodelJavaUtil bindingset[limit] private Endpoint getSampleForSignature( int limit, string package, string type, string subtypes, string name, string signature, - string input, string output, string isVarargs, string extensibleType + string input, string output, string isVarargs, string extensibleType, string alreadyAiModeled ) { exists(int n, int num_endpoints, ApplicationModeMetadataExtractor meta | num_endpoints = count(Endpoint e | - e.getExtensibleType() = extensibleType and - meta.hasMetadata(e, package, type, subtypes, name, signature, input, output, isVarargs) + meta.hasMetadata(e, package, type, subtypes, name, signature, input, output, isVarargs, + alreadyAiModeled, extensibleType) ) | result = rank[n](Endpoint e, Location loc | loc = e.asTop().getLocation() and - e.getExtensibleType() = extensibleType and - meta.hasMetadata(e, package, type, subtypes, name, signature, input, output, isVarargs) + meta.hasMetadata(e, package, type, subtypes, name, signature, input, output, isVarargs, + alreadyAiModeled, extensibleType) | e order by @@ -63,22 +63,18 @@ where not exists(CharacteristicsImpl::UninterestingToModelCharacteristic u | u.appliesToEndpoint(endpoint) ) and - CharacteristicsImpl::isSinkCandidate(endpoint, _) and + CharacteristicsImpl::isCandidate(endpoint, _) and endpoint = getSampleForSignature(9, package, type, subtypes, name, signature, input, output, - isVarargsArray, extensibleType) and + isVarargsArray, extensibleType, alreadyAiModeled) and + meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input, output, + isVarargsArray, alreadyAiModeled, extensibleType) and // If a node is already modeled in MaD, we don't include it as a candidate. Otherwise, we might include it as a // candidate for query A, but the model will label it as a sink for one of the sink types of query B, for which it's // already a known sink. This would result in overlap between our detected sinks and the pre-existing modeling. We // assume that, if a sink has already been modeled in a MaD model, then it doesn't belong to any additional sink // types, and we don't need to reexamine it. - ( - not CharacteristicsImpl::isModeled(endpoint, _, _, _) and alreadyAiModeled = "" - or - alreadyAiModeled.matches("%ai-%") and - CharacteristicsImpl::isModeled(endpoint, _, _, alreadyAiModeled) - ) and - meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input, output, isVarargsArray) and + alreadyAiModeled.matches(["", "%ai-%"]) and includeAutomodelCandidate(package, type, name, signature) select endpoint.asNode(), "Related locations: $@, $@, $@." + "\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@.", // diff --git a/java/ql/automodel/src/AutomodelApplicationModeExtractNegativeExamples.ql b/java/ql/automodel/src/AutomodelApplicationModeExtractNegativeExamples.ql index 6097e2e22f9..f4ee90331f6 100644 --- a/java/ql/automodel/src/AutomodelApplicationModeExtractNegativeExamples.ql +++ b/java/ql/automodel/src/AutomodelApplicationModeExtractNegativeExamples.ql @@ -40,27 +40,45 @@ Endpoint getSampleForCharacteristic(EndpointCharacteristic c, int limit) { ) } -from - Endpoint endpoint, EndpointCharacteristic characteristic, float confidence, string message, - ApplicationModeMetadataExtractor meta, DollarAtString package, DollarAtString type, - DollarAtString subtypes, DollarAtString name, DollarAtString signature, DollarAtString input, - DollarAtString output, DollarAtString isVarargsArray, DollarAtString extensibleType -where - endpoint = getSampleForCharacteristic(characteristic, 100) and - extensibleType = endpoint.getExtensibleType() and +predicate candidate( + Endpoint endpoint, EndpointCharacteristic characteristic, float confidence, string package, + string type, string subtypes, string name, string signature, string input, string output, + string isVarargsArray, string extensibleType +) { + // the node is known not to be an endpoint of any appropriate type + forall(EndpointType tp | tp = CharacteristicsImpl::getAPotentialType(endpoint) | + characteristic.hasImplications(tp, false, _) + ) and + // the lowest confidence across all endpoint types should be at least highConfidence + confidence = + min(float c | + characteristic.hasImplications(CharacteristicsImpl::getAPotentialType(endpoint), false, c) + ) and confidence >= SharedCharacteristics::highConfidence() and - characteristic.hasImplications(any(NegativeSinkType negative), true, confidence) and - meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input, output, isVarargsArray) and - // It's valid for a node to satisfy the logic for both `isSink` and `isSanitizer`, but in that case it will be - // treated by the actual query as a sanitizer, since the final logic is something like - // `isSink(n) and not isSanitizer(n)`. We don't want to include such nodes as negative examples in the prompt, because - // they're ambiguous and might confuse the model, so we explicitly exclude all known sinks from the negative examples. - not exists(EndpointCharacteristic characteristic2, float confidence2, SinkType positiveType | - not positiveType instanceof NegativeSinkType and + any(ApplicationModeMetadataExtractor meta) + .hasMetadata(endpoint, package, type, subtypes, name, signature, input, output, + isVarargsArray, _, extensibleType) and + // It's valid for a node to be both a potential source/sanitizer and a sink. We don't want to include such nodes + // as negative examples in the prompt, because they're ambiguous and might confuse the model, so we explicitly exclude them here. + not exists(EndpointCharacteristic characteristic2, float confidence2 | + characteristic2 != characteristic + | characteristic2.appliesToEndpoint(endpoint) and confidence2 >= SharedCharacteristics::maximalConfidence() and - characteristic2.hasImplications(positiveType, true, confidence2) - ) and + characteristic2 + .hasImplications(CharacteristicsImpl::getAPotentialType(endpoint), true, confidence2) + ) +} + +from + Endpoint endpoint, EndpointCharacteristic characteristic, float confidence, string message, + DollarAtString package, DollarAtString type, DollarAtString subtypes, DollarAtString name, + DollarAtString signature, DollarAtString input, DollarAtString output, + DollarAtString isVarargsArray, DollarAtString extensibleType +where + endpoint = getSampleForCharacteristic(characteristic, 100) and + candidate(endpoint, characteristic, confidence, package, type, subtypes, name, signature, input, + output, isVarargsArray, extensibleType) and message = characteristic select endpoint.asNode(), message + "\nrelated locations: $@, $@, $@." + "\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@.", // diff --git a/java/ql/automodel/src/AutomodelApplicationModeExtractPositiveExamples.ql b/java/ql/automodel/src/AutomodelApplicationModeExtractPositiveExamples.ql index e933900aecc..8702e6808a4 100644 --- a/java/ql/automodel/src/AutomodelApplicationModeExtractPositiveExamples.ql +++ b/java/ql/automodel/src/AutomodelApplicationModeExtractPositiveExamples.ql @@ -18,9 +18,8 @@ from DollarAtString signature, DollarAtString input, DollarAtString output, DollarAtString isVarargsArray, DollarAtString extensibleType where - extensibleType = endpoint.getExtensibleType() and - meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input, output, isVarargsArray) and - // Extract positive examples of sinks belonging to the existing ATM query configurations. + meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input, output, + isVarargsArray, _, extensibleType) and CharacteristicsImpl::isKnownAs(endpoint, endpointType, _) and exists(CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, CallContext())) select endpoint.asNode(), diff --git a/java/ql/automodel/src/AutomodelEndpointTypes.qll b/java/ql/automodel/src/AutomodelEndpointTypes.qll index 8b08722abe2..e37cc80099f 100644 --- a/java/ql/automodel/src/AutomodelEndpointTypes.qll +++ b/java/ql/automodel/src/AutomodelEndpointTypes.qll @@ -30,11 +30,6 @@ abstract class SinkType extends EndpointType { SinkType() { any() } } -/** The `Negative` class for non-sinks. */ -class NegativeSinkType extends SinkType { - NegativeSinkType() { this = "non-sink" } -} - /** A sink relevant to the SQL injection query */ class SqlInjectionSinkType extends SinkType { SqlInjectionSinkType() { this = "sql-injection" } diff --git a/java/ql/automodel/src/AutomodelFrameworkModeCharacteristics.qll b/java/ql/automodel/src/AutomodelFrameworkModeCharacteristics.qll index c6030874def..a47e470b4a1 100644 --- a/java/ql/automodel/src/AutomodelFrameworkModeCharacteristics.qll +++ b/java/ql/automodel/src/AutomodelFrameworkModeCharacteristics.qll @@ -15,7 +15,6 @@ private import semmle.code.java.security.QueryInjection private import semmle.code.java.security.RequestForgery private import semmle.code.java.dataflow.internal.ModelExclusions as ModelExclusions private import AutomodelJavaUtil as AutomodelJavaUtil -private import AutomodelSharedGetCallable as AutomodelSharedGetCallable import AutomodelSharedCharacteristics as SharedCharacteristics import AutomodelEndpointTypes as AutomodelEndpointTypes @@ -84,7 +83,7 @@ abstract class FrameworkModeEndpoint extends TFrameworkModeEndpoint { /** * Returns the callable that contains the endpoint. */ - abstract Callable getEnclosingCallable(); + abstract Callable getCallable(); abstract Top asTop(); @@ -106,7 +105,7 @@ class ExplicitParameterEndpoint extends FrameworkModeEndpoint, TExplicitParamete override string getParamName() { result = param.getName() } - override Callable getEnclosingCallable() { result = param.getCallable() } + override Callable getCallable() { result = param.getCallable() } override Top asTop() { result = param } @@ -126,7 +125,7 @@ class QualifierEndpoint extends FrameworkModeEndpoint, TQualifier { override string getParamName() { result = "this" } - override Callable getEnclosingCallable() { result = callable } + override Callable getCallable() { result = callable } override Top asTop() { result = callable } @@ -144,7 +143,7 @@ class ReturnValue extends FrameworkModeEndpoint, TReturnValue { override string getParamName() { none() } - override Callable getEnclosingCallable() { result = callable } + override Callable getCallable() { result = callable } override Top asTop() { result = callable } @@ -163,7 +162,7 @@ class OverridableParameter extends FrameworkModeEndpoint, TOverridableParameter override string getParamName() { result = param.getName() } - override Callable getEnclosingCallable() { result = method } + override Callable getCallable() { result = method } override Top asTop() { result = param } @@ -181,7 +180,7 @@ class OverridableQualifier extends FrameworkModeEndpoint, TOverridableQualifier override string getParamName() { result = "this" } - override Callable getEnclosingCallable() { result = m } + override Callable getCallable() { result = m } override Top asTop() { result = m } @@ -202,7 +201,9 @@ module FrameworkCandidatesImpl implements SharedCharacteristics::CandidateSig { class EndpointType = AutomodelEndpointTypes::EndpointType; - class NegativeEndpointType = AutomodelEndpointTypes::NegativeSinkType; + class SinkType = AutomodelEndpointTypes::SinkType; + + class SourceType = AutomodelEndpointTypes::SourceType; class RelatedLocation = Location::Top; @@ -244,8 +245,8 @@ module FrameworkCandidatesImpl implements SharedCharacteristics::CandidateSig { additional predicate sinkSpec( Endpoint e, string package, string type, string name, string signature, string ext, string input ) { - e.getEnclosingCallable().hasQualifiedName(package, type, name) and - signature = ExternalFlow::paramsString(e.getEnclosingCallable()) and + e.getCallable().hasQualifiedName(package, type, name) and + signature = ExternalFlow::paramsString(e.getCallable()) and ext = "" and input = e.getMaDInput() } @@ -254,8 +255,8 @@ module FrameworkCandidatesImpl implements SharedCharacteristics::CandidateSig { Endpoint e, string package, string type, string name, string signature, string ext, string output ) { - e.getEnclosingCallable().hasQualifiedName(package, type, name) and - signature = ExternalFlow::paramsString(e.getEnclosingCallable()) and + e.getCallable().hasQualifiedName(package, type, name) and + signature = ExternalFlow::paramsString(e.getCallable()) and ext = "" and output = e.getMaDOutput() } @@ -267,10 +268,10 @@ module FrameworkCandidatesImpl implements SharedCharacteristics::CandidateSig { */ RelatedLocation getRelatedLocation(Endpoint e, RelatedLocationType type) { type = MethodDoc() and - result = e.getEnclosingCallable().(Documentable).getJavadoc() + result = e.getCallable().(Documentable).getJavadoc() or type = ClassDoc() and - result = e.getEnclosingCallable().getDeclaringType().(Documentable).getJavadoc() + result = e.getCallable().getDeclaringType().(Documentable).getJavadoc() } } @@ -292,16 +293,27 @@ class FrameworkModeMetadataExtractor extends string { predicate hasMetadata( Endpoint e, string package, string type, string subtypes, string name, string signature, - string input, string output, string parameterName + string input, string output, string parameterName, string alreadyAiModeled, + string extensibleType ) { - (if exists(e.getParamName()) then parameterName = e.getParamName() else parameterName = "") and - name = e.getEnclosingCallable().getName() and - (if exists(e.getMaDInput()) then input = e.getMaDInput() else input = "") and - (if exists(e.getMaDOutput()) then output = e.getMaDOutput() else output = "") and - package = e.getEnclosingCallable().getDeclaringType().getPackage().getName() and - type = e.getEnclosingCallable().getDeclaringType().getErasure().(RefType).nestedName() and - subtypes = AutomodelJavaUtil::considerSubtypes(e.getEnclosingCallable()).toString() and - signature = ExternalFlow::paramsString(e.getEnclosingCallable()) + exists(Callable callable | e.getCallable() = callable | + (if exists(e.getMaDInput()) then input = e.getMaDInput() else input = "") and + (if exists(e.getMaDOutput()) then output = e.getMaDOutput() else output = "") and + package = callable.getDeclaringType().getPackage().getName() and + // we're using the erased types because the MaD convention is to not specify type parameters. + // Whether something is or isn't a sink doesn't usually depend on the type parameters. + type = callable.getDeclaringType().getErasure().(RefType).nestedName() and + subtypes = AutomodelJavaUtil::considerSubtypes(callable).toString() and + name = callable.getName() and + signature = ExternalFlow::paramsString(callable) and + (if exists(e.getParamName()) then parameterName = e.getParamName() else parameterName = "") and + e.getExtensibleType() = extensibleType + ) and + ( + not CharacteristicsImpl::isModeled(e, _, extensibleType, _) and alreadyAiModeled = "" + or + CharacteristicsImpl::isModeled(e, _, extensibleType, alreadyAiModeled) + ) } } @@ -310,7 +322,8 @@ class FrameworkModeMetadataExtractor extends string { */ /** - * A negative characteristic that indicates that an is-style boolean method is unexploitable even if it is a sink. + * A negative characteristic that indicates that parameters of an is-style boolean method should not be considered sinks, + * and its return value should not be considered a source. * * A sink is highly unlikely to be exploitable if its callable's name starts with `is` and the callable has a boolean return * type (e.g. `isDirectory`). These kinds of calls normally do only checks, and appear before the proper call that does @@ -318,45 +331,70 @@ class FrameworkModeMetadataExtractor extends string { * * TODO: this might filter too much, it's possible that methods with more than one parameter contain interesting sinks */ -private class UnexploitableIsCharacteristic extends CharacteristicsImpl::NotASinkCharacteristic { +private class UnexploitableIsCharacteristic extends CharacteristicsImpl::NeitherSourceNorSinkCharacteristic +{ UnexploitableIsCharacteristic() { this = "unexploitable (is-style boolean method)" } override predicate appliesToEndpoint(Endpoint e) { - not FrameworkCandidatesImpl::isSink(e, _, _) and - e.getEnclosingCallable().getName().matches("is%") and - e.getEnclosingCallable().getReturnType() instanceof BooleanType - } -} - -/** - * A negative characteristic that indicates that an existence-checking boolean method is unexploitable even if it is a - * sink. - * - * A sink is highly unlikely to be exploitable if its callable's name is `exists` or `notExists` and the callable has a - * boolean return type. These kinds of calls normally do only checks, and appear before the proper call that does the - * dangerous/interesting thing, so we want the latter to be modeled as the sink. - */ -private class UnexploitableExistsCharacteristic extends CharacteristicsImpl::NotASinkCharacteristic { - UnexploitableExistsCharacteristic() { this = "unexploitable (existence-checking boolean method)" } - - override predicate appliesToEndpoint(Endpoint e) { - not FrameworkCandidatesImpl::isSink(e, _, _) and - exists(Callable callable | - callable = e.getEnclosingCallable() and - callable.getName().toLowerCase() = ["exists", "notexists"] and - callable.getReturnType() instanceof BooleanType + e.getCallable().getName().matches("is%") and + e.getCallable().getReturnType() instanceof BooleanType and + ( + e.getExtensibleType() = "sinkModel" and + not FrameworkCandidatesImpl::isSink(e, _, _) + or + e.getExtensibleType() = "sourceModel" and + not FrameworkCandidatesImpl::isSource(e, _, _) and + e.getMaDOutput() = "ReturnValue" ) } } /** - * A negative characteristic that indicates that an endpoint is an argument to an exception, which is not a sink. + * A negative characteristic that indicates that parameters of an existence-checking boolean method should not be + * considered sinks, and its return value should not be considered a source. + * + * A sink is highly unlikely to be exploitable if its callable's name is `exists` or `notExists` and the callable has a + * boolean return type. These kinds of calls normally do only checks, and appear before the proper call that does the + * dangerous/interesting thing, so we want the latter to be modeled as the sink. */ -private class ExceptionCharacteristic extends CharacteristicsImpl::NotASinkCharacteristic { +private class UnexploitableExistsCharacteristic extends CharacteristicsImpl::NeitherSourceNorSinkCharacteristic +{ + UnexploitableExistsCharacteristic() { this = "unexploitable (existence-checking boolean method)" } + + override predicate appliesToEndpoint(Endpoint e) { + exists(Callable callable | + callable = e.getCallable() and + callable.getName().toLowerCase() = ["exists", "notexists"] and + callable.getReturnType() instanceof BooleanType + | + e.getExtensibleType() = "sinkModel" and + not FrameworkCandidatesImpl::isSink(e, _, _) + or + e.getExtensibleType() = "sourceModel" and + not FrameworkCandidatesImpl::isSource(e, _, _) and + e.getMaDOutput() = "ReturnValue" + ) + } +} + +/** + * A negative characteristic that indicates that parameters of an exception method or constructor should not be considered sinks, + * and its return value should not be considered a source. + */ +private class ExceptionCharacteristic extends CharacteristicsImpl::NeitherSourceNorSinkCharacteristic +{ ExceptionCharacteristic() { this = "exception" } override predicate appliesToEndpoint(Endpoint e) { - e.getEnclosingCallable().getDeclaringType().getASupertype*() instanceof TypeThrowable + e.getCallable().getDeclaringType().getASupertype*() instanceof TypeThrowable and + ( + e.getExtensibleType() = "sinkModel" and + not FrameworkCandidatesImpl::isSink(e, _, _) + or + e.getExtensibleType() = "sourceModel" and + not FrameworkCandidatesImpl::isSource(e, _, _) and + e.getMaDOutput() = "ReturnValue" + ) } } @@ -368,6 +406,6 @@ private class NotAModelApi extends CharacteristicsImpl::UninterestingToModelChar NotAModelApi() { this = "not a model API" } override predicate appliesToEndpoint(Endpoint e) { - not e.getEnclosingCallable() instanceof ModelExclusions::ModelApi + not e.getCallable() instanceof ModelExclusions::ModelApi } } diff --git a/java/ql/automodel/src/AutomodelFrameworkModeExtractCandidates.ql b/java/ql/automodel/src/AutomodelFrameworkModeExtractCandidates.ql index c2ab6f33ee7..80f3d3089c0 100644 --- a/java/ql/automodel/src/AutomodelFrameworkModeExtractCandidates.ql +++ b/java/ql/automodel/src/AutomodelFrameworkModeExtractCandidates.ql @@ -21,23 +21,18 @@ from DollarAtString input, DollarAtString output, DollarAtString parameterName, DollarAtString alreadyAiModeled, DollarAtString extensibleType where - endpoint.getExtensibleType() = extensibleType and not exists(CharacteristicsImpl::UninterestingToModelCharacteristic u | u.appliesToEndpoint(endpoint) ) and - CharacteristicsImpl::isSinkCandidate(endpoint, _) and - // If a node is already a known sink for any of our existing ATM queries and is already modeled as a MaD sink, we - // don't include it as a candidate. Otherwise, we might include it as a candidate for query A, but the model will - // label it as a sink for one of the sink types of query B, for which it's already a known sink. This would result in - // overlap between our detected sinks and the pre-existing modeling. We assume that, if a sink has already been - // modeled in a MaD model, then it doesn't belong to any additional sink types, and we don't need to reexamine it. - ( - not CharacteristicsImpl::isSink(endpoint, _, _) and alreadyAiModeled = "" - or - alreadyAiModeled.matches("%ai-%") and - CharacteristicsImpl::isSink(endpoint, _, alreadyAiModeled) - ) and - meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input, output, parameterName) and + CharacteristicsImpl::isCandidate(endpoint, _) and + meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input, output, parameterName, + alreadyAiModeled, extensibleType) and + // If a node is already modeled in MaD, we don't include it as a candidate. Otherwise, we might include it as a + // candidate for query A, but the model will label it as a sink for one of the sink types of query B, for which it's + // already a known sink. This would result in overlap between our detected sinks and the pre-existing modeling. We + // assume that, if a sink has already been modeled in a MaD model, then it doesn't belong to any additional sink + // types, and we don't need to reexamine it. + alreadyAiModeled.matches(["", "%ai-%"]) and includeAutomodelCandidate(package, type, name, signature) select endpoint, "Related locations: $@, $@." + "\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@.", // diff --git a/java/ql/automodel/src/AutomodelFrameworkModeExtractNegativeExamples.ql b/java/ql/automodel/src/AutomodelFrameworkModeExtractNegativeExamples.ql index 3cb23096015..dc9f61ab49c 100644 --- a/java/ql/automodel/src/AutomodelFrameworkModeExtractNegativeExamples.ql +++ b/java/ql/automodel/src/AutomodelFrameworkModeExtractNegativeExamples.ql @@ -19,20 +19,28 @@ from DollarAtString input, DollarAtString output, DollarAtString parameterName, DollarAtString extensibleType where - endpoint.getExtensibleType() = extensibleType and characteristic.appliesToEndpoint(endpoint) and + // the node is known not to be an endpoint of any appropriate type + forall(EndpointType tp | tp = CharacteristicsImpl::getAPotentialType(endpoint) | + characteristic.hasImplications(tp, false, _) + ) and + // the lowest confidence across all endpoint types should be at least highConfidence + confidence = + min(float c | + characteristic.hasImplications(CharacteristicsImpl::getAPotentialType(endpoint), false, c) + ) and confidence >= SharedCharacteristics::highConfidence() and - characteristic.hasImplications(any(NegativeSinkType negative), true, confidence) and - meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input, output, parameterName) and - // It's valid for a node to satisfy the logic for both `isSink` and `isSanitizer`, but in that case it will be - // treated by the actual query as a sanitizer, since the final logic is something like - // `isSink(n) and not isSanitizer(n)`. We don't want to include such nodes as negative examples in the prompt, because - // they're ambiguous and might confuse the model, so we explicitly exclude all known sinks from the negative examples. - not exists(EndpointCharacteristic characteristic2, float confidence2, SinkType positiveType | - not positiveType instanceof NegativeSinkType and + meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input, output, parameterName, + _, extensibleType) and + // It's valid for a node to be both a potential source/sanitizer and a sink. We don't want to include such nodes + // as negative examples in the prompt, because they're ambiguous and might confuse the model, so we explicitly exclude them here. + not exists(EndpointCharacteristic characteristic2, float confidence2 | + characteristic2 != characteristic + | characteristic2.appliesToEndpoint(endpoint) and confidence2 >= SharedCharacteristics::maximalConfidence() and - characteristic2.hasImplications(positiveType, true, confidence2) + characteristic2 + .hasImplications(CharacteristicsImpl::getAPotentialType(endpoint), true, confidence2) ) and message = characteristic select endpoint, diff --git a/java/ql/automodel/src/AutomodelFrameworkModeExtractPositiveExamples.ql b/java/ql/automodel/src/AutomodelFrameworkModeExtractPositiveExamples.ql index dbc3d760d9a..d83ac04c582 100644 --- a/java/ql/automodel/src/AutomodelFrameworkModeExtractPositiveExamples.ql +++ b/java/ql/automodel/src/AutomodelFrameworkModeExtractPositiveExamples.ql @@ -18,9 +18,8 @@ from DollarAtString signature, DollarAtString input, DollarAtString output, DollarAtString parameterName, DollarAtString extensibleType where - endpoint.getExtensibleType() = extensibleType and - meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input, output, parameterName) and - // Extract positive examples of sinks belonging to the existing ATM query configurations. + meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input, output, parameterName, + _, extensibleType) and CharacteristicsImpl::isKnownAs(endpoint, endpointType, _) select endpoint, endpointType + "\nrelated locations: $@, $@." + "\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@.", // diff --git a/java/ql/automodel/src/AutomodelSharedCharacteristics.qll b/java/ql/automodel/src/AutomodelSharedCharacteristics.qll index 3704845fe17..5641a534660 100644 --- a/java/ql/automodel/src/AutomodelSharedCharacteristics.qll +++ b/java/ql/automodel/src/AutomodelSharedCharacteristics.qll @@ -16,7 +16,12 @@ signature module CandidateSig { * An endpoint is a potential candidate for modeling. This will typically be bound to the language's * DataFlow node class, or a subtype thereof. */ - class Endpoint; + class Endpoint { + /** + * Gets the kind of this endpoint, either "sourceModel" or "sinkModel". + */ + string getExtensibleType(); + } /** * A related location for an endpoint. This will typically be bound to the supertype of all AST nodes (eg., `Top`). @@ -31,14 +36,19 @@ signature module CandidateSig { class RelatedLocationType; /** - * A class kind for an endpoint. + * An endpoint type considered by this specification. */ class EndpointType extends string; /** - * An EndpointType that denotes the absence of any sink. + * A sink endpoint type considered by this specification. */ - class NegativeEndpointType extends EndpointType; + class SinkType extends EndpointType; + + /** + * A source endpoint type considered by this specification. + */ + class SourceType extends EndpointType; /** * Gets the endpoint as a location. @@ -103,7 +113,7 @@ module SharedCharacteristics { } /** - * Holds if `endpoint` is modeled as `endpointType` (endpoint type must not be negative). + * Holds if `endpoint` is modeled as `endpointType`. */ predicate isKnownAs( Candidate::Endpoint endpoint, Candidate::EndpointType endpointType, @@ -111,19 +121,31 @@ module SharedCharacteristics { ) { // If the list of characteristics includes positive indicators with maximal confidence for this class, then it's a // known sink for the class. - not endpointType instanceof Candidate::NegativeEndpointType and characteristic.appliesToEndpoint(endpoint) and characteristic.hasImplications(endpointType, true, maximalConfidence()) } /** - * Holds if the candidate sink `candidateSink` should be considered as a possible sink of type `sinkType`, and - * classified by the ML model. A candidate sink is a node that cannot be excluded from `sinkType` based on its - * characteristics. + * Gets a potential type of this endpoint to make sure that sources are + * associated with source types and sinks with sink types. */ - predicate isSinkCandidate(Candidate::Endpoint candidateSink, Candidate::EndpointType sinkType) { - not sinkType instanceof Candidate::NegativeEndpointType and - not exists(getAReasonSinkExcluded(candidateSink, sinkType)) + Candidate::EndpointType getAPotentialType(Candidate::Endpoint endpoint) { + endpoint.getExtensibleType() = "sourceModel" and + result instanceof Candidate::SourceType + or + endpoint.getExtensibleType() = "sinkModel" and + result instanceof Candidate::SinkType + } + + /** + * Holds if the given `endpoint` should be considered as a candidate for type `endpointType`, + * and classified by the ML model. + * + * A candidate is an endpoint that cannot be excluded from `endpointType` based on its characteristics. + */ + predicate isCandidate(Candidate::Endpoint endpoint, Candidate::EndpointType endpointType) { + endpointType = getAPotentialType(endpoint) and + not exists(getAnExcludingCharacteristic(endpoint, endpointType)) } /** @@ -139,27 +161,16 @@ module SharedCharacteristics { } /** - * Gets the list of characteristics that cause `candidateSink` to be excluded as an effective sink for a given sink - * type. + * Gets a characteristics that disbar `endpoint` from being a candidate for `endpointType` + * with at least medium confidence. */ - EndpointCharacteristic getAReasonSinkExcluded( - Candidate::Endpoint candidateSink, Candidate::EndpointType sinkType + EndpointCharacteristic getAnExcludingCharacteristic( + Candidate::Endpoint endpoint, Candidate::EndpointType endpointType ) { - // An endpoint is a sink candidate if none of its characteristics give much indication whether or not it is a sink. - not sinkType instanceof Candidate::NegativeEndpointType and - result.appliesToEndpoint(candidateSink) and - ( - // Exclude endpoints that have a characteristic that implies they're not sinks for _any_ sink type. - exists(float confidence | - confidence >= mediumConfidence() and - result.hasImplications(any(Candidate::NegativeEndpointType t), true, confidence) - ) - or - // Exclude endpoints that have a characteristic that implies they're not sinks for _this particular_ sink type. - exists(float confidence | - confidence >= mediumConfidence() and - result.hasImplications(sinkType, false, confidence) - ) + result.appliesToEndpoint(endpoint) and + exists(float confidence | + confidence >= mediumConfidence() and + result.hasImplications(endpointType, false, confidence) ) } @@ -250,12 +261,46 @@ module SharedCharacteristics { override predicate hasImplications( Candidate::EndpointType endpointType, boolean isPositiveIndicator, float confidence ) { - endpointType instanceof Candidate::NegativeEndpointType and - isPositiveIndicator = true and + endpointType instanceof Candidate::SinkType and + isPositiveIndicator = false and confidence = highConfidence() } } + /** + * A high-confidence characteristic that indicates that an endpoint is not a source of any type. These endpoints can be + * used as negative samples for training or for a few-shot prompt. + */ + abstract class NotASourceCharacteristic extends EndpointCharacteristic { + bindingset[this] + NotASourceCharacteristic() { any() } + + override predicate hasImplications( + Candidate::EndpointType endpointType, boolean isPositiveIndicator, float confidence + ) { + endpointType instanceof Candidate::SourceType and + isPositiveIndicator = false and + confidence = highConfidence() + } + } + + /** + * A high-confidence characteristic that indicates that an endpoint is neither a source nor a sink of any type. + */ + abstract class NeitherSourceNorSinkCharacteristic extends NotASinkCharacteristic, + NotASourceCharacteristic + { + bindingset[this] + NeitherSourceNorSinkCharacteristic() { any() } + + final override predicate hasImplications( + Candidate::EndpointType endpointType, boolean isPositiveIndicator, float confidence + ) { + NotASinkCharacteristic.super.hasImplications(endpointType, isPositiveIndicator, confidence) or + NotASourceCharacteristic.super.hasImplications(endpointType, isPositiveIndicator, confidence) + } + } + /** * A medium-confidence characteristic that indicates that an endpoint is unlikely to be a sink of any type. These * endpoints can be excluded from scoring at inference time, both to save time and to avoid false positives. They should @@ -269,8 +314,8 @@ module SharedCharacteristics { override predicate hasImplications( Candidate::EndpointType endpointType, boolean isPositiveIndicator, float confidence ) { - endpointType instanceof Candidate::NegativeEndpointType and - isPositiveIndicator = true and + endpointType instanceof Candidate::SinkType and + isPositiveIndicator = false and confidence = mediumConfidence() } } @@ -290,8 +335,8 @@ module SharedCharacteristics { override predicate hasImplications( Candidate::EndpointType endpointType, boolean isPositiveIndicator, float confidence ) { - endpointType instanceof Candidate::NegativeEndpointType and - isPositiveIndicator = true and + endpointType instanceof Candidate::SinkType and + isPositiveIndicator = false and confidence = mediumConfidence() } } @@ -344,17 +389,16 @@ module SharedCharacteristics { /** * A negative characteristic that indicates that an endpoint was manually modeled as a neutral model. */ - private class NeutralModelCharacteristic extends NotASinkCharacteristic { + private class NeutralModelCharacteristic extends NeitherSourceNorSinkCharacteristic { NeutralModelCharacteristic() { this = "known non-sink" } override predicate appliesToEndpoint(Candidate::Endpoint e) { Candidate::isNeutral(e) } } /** - * A negative characteristic that indicates that an endpoint is not part of the source code for the project being - * analyzed. + * A negative characteristic that indicates that an endpoint is a sanitizer, and thus not a source. */ - private class IsSanitizerCharacteristic extends NotASinkCharacteristic { + private class IsSanitizerCharacteristic extends NotASourceCharacteristic { IsSanitizerCharacteristic() { this = "known sanitizer" } override predicate appliesToEndpoint(Candidate::Endpoint e) { Candidate::isSanitizer(e, _) } diff --git a/java/ql/automodel/src/AutomodelSharedGetCallable.qll b/java/ql/automodel/src/AutomodelSharedGetCallable.qll deleted file mode 100644 index 87e20969381..00000000000 --- a/java/ql/automodel/src/AutomodelSharedGetCallable.qll +++ /dev/null @@ -1,21 +0,0 @@ -/** - * An automodel extraction mode instantiates this interface to define how to access - * the callable that's associated with an endpoint. - */ -signature module GetCallableSig { - /** - * A callable is the definition of a method, function, etc. - something that can be called. - */ - class Callable; - - /** - * An endpoint is a potential candidate for modeling. This will typically be bound to the language's - * DataFlow node class, or a subtype thereof. - */ - class Endpoint; - - /** - * Gets the callable that's associated with the given endpoint. - */ - Callable getCallable(Endpoint endpoint); -} diff --git a/java/ql/automodel/test/AutomodelApplicationModeExtraction/AutomodelApplicationModeExtractCandidates.expected b/java/ql/automodel/test/AutomodelApplicationModeExtraction/AutomodelApplicationModeExtractCandidates.expected index a7be708f9da..41598f8858c 100644 --- a/java/ql/automodel/test/AutomodelApplicationModeExtraction/AutomodelApplicationModeExtractCandidates.expected +++ b/java/ql/automodel/test/AutomodelApplicationModeExtraction/AutomodelApplicationModeExtractCandidates.expected @@ -11,3 +11,5 @@ | Test.java:56:4:56:4 | o | Related locations: $@, $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:54:3:59:3 | walk(...) | CallContext | Test.java:54:3:59:3 | walk(...) | MethodDoc | Test.java:54:3:59:3 | walk(...) | ClassDoc | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://walk:1:1:1:1 | walk | name | file://(Path,FileVisitOption[]):1:1:1:1 | (Path,FileVisitOption[]) | signature | file://Argument[1]:1:1:1:1 | Argument[1] | input | file://:1:1:1:1 | | output | file://true:1:1:1:1 | true | isVarargsArray | file://:1:1:1:1 | | alreadyAiModeled | file://sinkModel:1:1:1:1 | sinkModel | extensibleType | | Test.java:63:3:63:3 | c | Related locations: $@, $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:63:3:63:20 | getInputStream(...) | CallContext | Test.java:63:3:63:3 | c | MethodDoc | Test.java:63:3:63:3 | c | ClassDoc | file://java.net:1:1:1:1 | java.net | package | file://URLConnection:1:1:1:1 | URLConnection | type | file://true:1:1:1:1 | true | subtypes | file://getInputStream:1:1:1:1 | getInputStream | name | file://():1:1:1:1 | () | signature | file://Argument[this]:1:1:1:1 | Argument[this] | input | file://:1:1:1:1 | | output | file://false:1:1:1:1 | false | isVarargsArray | file://:1:1:1:1 | | alreadyAiModeled | file://sinkModel:1:1:1:1 | sinkModel | extensibleType | | Test.java:68:30:68:47 | writer | Related locations: $@, $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:68:30:68:47 | writer | CallContext | Test.java:68:30:68:47 | writer | MethodDoc | Test.java:68:30:68:47 | writer | ClassDoc | file://java.lang:1:1:1:1 | java.lang | package | file://Throwable:1:1:1:1 | Throwable | type | file://true:1:1:1:1 | true | subtypes | file://printStackTrace:1:1:1:1 | printStackTrace | name | file://(PrintWriter):1:1:1:1 | (PrintWriter) | signature | file://:1:1:1:1 | | input | file://Parameter[0]:1:1:1:1 | Parameter[0] | output | file://false:1:1:1:1 | false | isVarargsArray | file://:1:1:1:1 | | alreadyAiModeled | file://sourceModel:1:1:1:1 | sourceModel | extensibleType | +| Test.java:86:3:88:3 | list(...) | Related locations: $@, $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:86:3:88:3 | list(...) | CallContext | Test.java:86:3:88:3 | list(...) | MethodDoc | Test.java:86:3:88:3 | list(...) | ClassDoc | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://list:1:1:1:1 | list | name | file://(Path):1:1:1:1 | (Path) | signature | file://:1:1:1:1 | | input | file://ReturnValue:1:1:1:1 | ReturnValue | output | file://false:1:1:1:1 | false | isVarargsArray | file://:1:1:1:1 | | alreadyAiModeled | file://sourceModel:1:1:1:1 | sourceModel | extensibleType | +| Test.java:87:4:87:29 | createDirectories(...) | Related locations: $@, $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:87:4:87:29 | createDirectories(...) | CallContext | Test.java:87:4:87:29 | createDirectories(...) | MethodDoc | Test.java:87:4:87:29 | createDirectories(...) | ClassDoc | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://createDirectories:1:1:1:1 | createDirectories | name | file://(Path,FileAttribute[]):1:1:1:1 | (Path,FileAttribute[]) | signature | file://:1:1:1:1 | | input | file://ReturnValue:1:1:1:1 | ReturnValue | output | file://false:1:1:1:1 | false | isVarargsArray | file://:1:1:1:1 | | alreadyAiModeled | file://sourceModel:1:1:1:1 | sourceModel | extensibleType | diff --git a/java/ql/automodel/test/AutomodelApplicationModeExtraction/AutomodelApplicationModeExtractPositiveExamples.expected b/java/ql/automodel/test/AutomodelApplicationModeExtraction/AutomodelApplicationModeExtractPositiveExamples.expected index 60db0852024..bdc5667935b 100644 --- a/java/ql/automodel/test/AutomodelApplicationModeExtraction/AutomodelApplicationModeExtractPositiveExamples.expected +++ b/java/ql/automodel/test/AutomodelApplicationModeExtraction/AutomodelApplicationModeExtractPositiveExamples.expected @@ -2,3 +2,4 @@ | Test.java:30:4:30:9 | target | path-injection\nrelated locations: $@, $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:28:3:32:3 | copy(...) | CallContext | Test.java:30:4:30:9 | target | MethodDoc | Test.java:30:4:30:9 | target | ClassDoc | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://copy:1:1:1:1 | copy | name | file://(Path,Path,CopyOption[]):1:1:1:1 | (Path,Path,CopyOption[]) | signature | file://Argument[1]:1:1:1:1 | Argument[1] | input | file://:1:1:1:1 | | output | file://false:1:1:1:1 | false | isVarargsArray | file://sinkModel:1:1:1:1 | sinkModel | extensibleType | | Test.java:37:4:37:11 | openPath | path-injection\nrelated locations: $@, $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:36:10:38:3 | newInputStream(...) | CallContext | Test.java:37:4:37:11 | openPath | MethodDoc | Test.java:37:4:37:11 | openPath | ClassDoc | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://newInputStream:1:1:1:1 | newInputStream | name | file://(Path,OpenOption[]):1:1:1:1 | (Path,OpenOption[]) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input | file://:1:1:1:1 | | output | file://false:1:1:1:1 | false | isVarargsArray | file://sinkModel:1:1:1:1 | sinkModel | extensibleType | | Test.java:63:3:63:20 | getInputStream(...) | remote\nrelated locations: $@, $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:63:3:63:20 | getInputStream(...) | CallContext | Test.java:63:3:63:20 | getInputStream(...) | MethodDoc | Test.java:63:3:63:20 | getInputStream(...) | ClassDoc | file://java.net:1:1:1:1 | java.net | package | file://URLConnection:1:1:1:1 | URLConnection | type | file://true:1:1:1:1 | true | subtypes | file://getInputStream:1:1:1:1 | getInputStream | name | file://():1:1:1:1 | () | signature | file://:1:1:1:1 | | input | file://ReturnValue:1:1:1:1 | ReturnValue | output | file://false:1:1:1:1 | false | isVarargsArray | file://sourceModel:1:1:1:1 | sourceModel | extensibleType | +| Test.java:87:28:87:28 | p | path-injection\nrelated locations: $@, $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:87:4:87:29 | createDirectories(...) | CallContext | Test.java:87:28:87:28 | p | MethodDoc | Test.java:87:28:87:28 | p | ClassDoc | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://createDirectories:1:1:1:1 | createDirectories | name | file://(Path,FileAttribute[]):1:1:1:1 | (Path,FileAttribute[]) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input | file://:1:1:1:1 | | output | file://false:1:1:1:1 | false | isVarargsArray | file://sinkModel:1:1:1:1 | sinkModel | extensibleType | diff --git a/java/ql/automodel/test/AutomodelApplicationModeExtraction/Test.java b/java/ql/automodel/test/AutomodelApplicationModeExtraction/Test.java index 3851a689969..e915d452ad8 100644 --- a/java/ql/automodel/test/AutomodelApplicationModeExtraction/Test.java +++ b/java/ql/automodel/test/AutomodelApplicationModeExtraction/Test.java @@ -52,7 +52,7 @@ class Test { public static void FilesWalkExample(Path p, FileVisitOption o) throws Exception { Files.walk( // the call is a source candidate - p, // negative example (modeled as a taint step) + p, // negative sink example (modeled as a taint step) o, // the implicit varargs array is a candidate o // not a candidate (only the first arg corresponding to a varargs array // is extracted) @@ -80,3 +80,11 @@ class TaskUtils { return ft; } } + +class MoreTests { + public static void FilesListExample(Path p) throws Exception { + Files.list( // the call is a source candidate + Files.createDirectories(p) // the call is a source candidate, but not a sink candidate (modeled as a taint step) + ); + } +} \ No newline at end of file diff --git a/java/ql/automodel/test/AutomodelFrameworkModeExtraction/AutomodelFrameworkModeExtractCandidates.expected b/java/ql/automodel/test/AutomodelFrameworkModeExtraction/AutomodelFrameworkModeExtractCandidates.expected index 17880ae9e4e..411f1c57b2c 100644 --- a/java/ql/automodel/test/AutomodelFrameworkModeExtraction/AutomodelFrameworkModeExtractCandidates.expected +++ b/java/ql/automodel/test/AutomodelFrameworkModeExtraction/AutomodelFrameworkModeExtractCandidates.expected @@ -9,6 +9,8 @@ | com/github/codeql/test/PublicClass.java:13:33:13:42 | arg | Related locations: $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | com/github/codeql/test/PublicClass.java:13:33:13:42 | arg | MethodDoc | com/github/codeql/test/PublicClass.java:13:33:13:42 | arg | ClassDoc | file://com.github.codeql.test:1:1:1:1 | com.github.codeql.test | package | file://PublicClass:1:1:1:1 | PublicClass | type | file://true:1:1:1:1 | true | subtypes | file://nonPublicStuff:1:1:1:1 | nonPublicStuff | name | file://(String):1:1:1:1 | (String) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input | file://:1:1:1:1 | | output | file://arg:1:1:1:1 | arg | parameterName | file://:1:1:1:1 | | alreadyAiModeled | file://sinkModel:1:1:1:1 | sinkModel | extensibleType | | com/github/codeql/test/PublicClass.java:22:10:22:20 | PublicClass | Related locations: $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | com/github/codeql/test/PublicClass.java:22:10:22:20 | PublicClass | MethodDoc | com/github/codeql/test/PublicClass.java:22:10:22:20 | PublicClass | ClassDoc | file://com.github.codeql.test:1:1:1:1 | com.github.codeql.test | package | file://PublicClass:1:1:1:1 | PublicClass | type | file://true:1:1:1:1 | true | subtypes | file://PublicClass:1:1:1:1 | PublicClass | name | file://(Object):1:1:1:1 | (Object) | signature | file://:1:1:1:1 | | input | file://ReturnValue:1:1:1:1 | ReturnValue | output | file://:1:1:1:1 | | parameterName | file://:1:1:1:1 | | alreadyAiModeled | file://sourceModel:1:1:1:1 | sourceModel | extensibleType | | com/github/codeql/test/PublicClass.java:22:22:22:33 | input | Related locations: $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | com/github/codeql/test/PublicClass.java:22:22:22:33 | input | MethodDoc | com/github/codeql/test/PublicClass.java:22:22:22:33 | input | ClassDoc | file://com.github.codeql.test:1:1:1:1 | com.github.codeql.test | package | file://PublicClass:1:1:1:1 | PublicClass | type | file://true:1:1:1:1 | true | subtypes | file://PublicClass:1:1:1:1 | PublicClass | name | file://(Object):1:1:1:1 | (Object) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input | file://:1:1:1:1 | | output | file://input:1:1:1:1 | input | parameterName | file://:1:1:1:1 | | alreadyAiModeled | file://sinkModel:1:1:1:1 | sinkModel | extensibleType | +| com/github/codeql/test/PublicClass.java:26:18:26:26 | isIgnored | Related locations: $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | com/github/codeql/test/PublicClass.java:26:18:26:26 | isIgnored | MethodDoc | com/github/codeql/test/PublicClass.java:26:18:26:26 | isIgnored | ClassDoc | file://com.github.codeql.test:1:1:1:1 | com.github.codeql.test | package | file://PublicClass:1:1:1:1 | PublicClass | type | file://true:1:1:1:1 | true | subtypes | file://isIgnored:1:1:1:1 | isIgnored | name | file://(Object):1:1:1:1 | (Object) | signature | file://:1:1:1:1 | | input | file://Parameter[this]:1:1:1:1 | Parameter[this] | output | file://this:1:1:1:1 | this | parameterName | file://:1:1:1:1 | | alreadyAiModeled | file://sourceModel:1:1:1:1 | sourceModel | extensibleType | +| com/github/codeql/test/PublicClass.java:26:28:26:39 | input | Related locations: $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | com/github/codeql/test/PublicClass.java:26:28:26:39 | input | MethodDoc | com/github/codeql/test/PublicClass.java:26:28:26:39 | input | ClassDoc | file://com.github.codeql.test:1:1:1:1 | com.github.codeql.test | package | file://PublicClass:1:1:1:1 | PublicClass | type | file://true:1:1:1:1 | true | subtypes | file://isIgnored:1:1:1:1 | isIgnored | name | file://(Object):1:1:1:1 | (Object) | signature | file://:1:1:1:1 | | input | file://Parameter[0]:1:1:1:1 | Parameter[0] | output | file://input:1:1:1:1 | input | parameterName | file://:1:1:1:1 | | alreadyAiModeled | file://sourceModel:1:1:1:1 | sourceModel | extensibleType | | com/github/codeql/test/PublicInterface.java:4:16:4:20 | stuff | Related locations: $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | com/github/codeql/test/PublicInterface.java:4:16:4:20 | stuff | MethodDoc | com/github/codeql/test/PublicInterface.java:4:16:4:20 | stuff | ClassDoc | file://com.github.codeql.test:1:1:1:1 | com.github.codeql.test | package | file://PublicInterface:1:1:1:1 | PublicInterface | type | file://true:1:1:1:1 | true | subtypes | file://stuff:1:1:1:1 | stuff | name | file://(String):1:1:1:1 | (String) | signature | file://:1:1:1:1 | | input | file://Parameter[this]:1:1:1:1 | Parameter[this] | output | file://this:1:1:1:1 | this | parameterName | file://:1:1:1:1 | | alreadyAiModeled | file://sourceModel:1:1:1:1 | sourceModel | extensibleType | | com/github/codeql/test/PublicInterface.java:4:16:4:20 | stuff | Related locations: $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | com/github/codeql/test/PublicInterface.java:4:16:4:20 | stuff | MethodDoc | com/github/codeql/test/PublicInterface.java:4:16:4:20 | stuff | ClassDoc | file://com.github.codeql.test:1:1:1:1 | com.github.codeql.test | package | file://PublicInterface:1:1:1:1 | PublicInterface | type | file://true:1:1:1:1 | true | subtypes | file://stuff:1:1:1:1 | stuff | name | file://(String):1:1:1:1 | (String) | signature | file://Argument[this]:1:1:1:1 | Argument[this] | input | file://:1:1:1:1 | | output | file://this:1:1:1:1 | this | parameterName | file://:1:1:1:1 | | alreadyAiModeled | file://sinkModel:1:1:1:1 | sinkModel | extensibleType | | com/github/codeql/test/PublicInterface.java:4:22:4:31 | arg | Related locations: $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | com/github/codeql/test/PublicInterface.java:4:22:4:31 | arg | MethodDoc | com/github/codeql/test/PublicInterface.java:4:22:4:31 | arg | ClassDoc | file://com.github.codeql.test:1:1:1:1 | com.github.codeql.test | package | file://PublicInterface:1:1:1:1 | PublicInterface | type | file://true:1:1:1:1 | true | subtypes | file://stuff:1:1:1:1 | stuff | name | file://(String):1:1:1:1 | (String) | signature | file://:1:1:1:1 | | input | file://Parameter[0]:1:1:1:1 | Parameter[0] | output | file://arg:1:1:1:1 | arg | parameterName | file://:1:1:1:1 | | alreadyAiModeled | file://sourceModel:1:1:1:1 | sourceModel | extensibleType | diff --git a/java/ql/automodel/test/AutomodelFrameworkModeExtraction/AutomodelFrameworkModeExtractNegativeExamples.expected b/java/ql/automodel/test/AutomodelFrameworkModeExtraction/AutomodelFrameworkModeExtractNegativeExamples.expected index 5e01f052425..e1bcaca7ddd 100644 --- a/java/ql/automodel/test/AutomodelFrameworkModeExtraction/AutomodelFrameworkModeExtractNegativeExamples.expected +++ b/java/ql/automodel/test/AutomodelFrameworkModeExtraction/AutomodelFrameworkModeExtractNegativeExamples.expected @@ -1,3 +1,6 @@ +| com/github/codeql/test/PublicClass.java:26:18:26:26 | isIgnored | unexploitable (is-style boolean method)\nrelated locations: $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@. | com/github/codeql/test/PublicClass.java:26:18:26:26 | isIgnored | MethodDoc | com/github/codeql/test/PublicClass.java:26:18:26:26 | isIgnored | ClassDoc | file://com.github.codeql.test:1:1:1:1 | com.github.codeql.test | package | file://PublicClass:1:1:1:1 | PublicClass | type | file://true:1:1:1:1 | true | subtypes | file://isIgnored:1:1:1:1 | isIgnored | name | file://(Object):1:1:1:1 | (Object) | signature | file://:1:1:1:1 | | input | file://ReturnValue:1:1:1:1 | ReturnValue | output | file://:1:1:1:1 | | parameterName | file://sourceModel:1:1:1:1 | sourceModel | extensibleType | +| com/github/codeql/test/PublicClass.java:26:18:26:26 | isIgnored | unexploitable (is-style boolean method)\nrelated locations: $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@. | com/github/codeql/test/PublicClass.java:26:18:26:26 | isIgnored | MethodDoc | com/github/codeql/test/PublicClass.java:26:18:26:26 | isIgnored | ClassDoc | file://com.github.codeql.test:1:1:1:1 | com.github.codeql.test | package | file://PublicClass:1:1:1:1 | PublicClass | type | file://true:1:1:1:1 | true | subtypes | file://isIgnored:1:1:1:1 | isIgnored | name | file://(Object):1:1:1:1 | (Object) | signature | file://Argument[this]:1:1:1:1 | Argument[this] | input | file://:1:1:1:1 | | output | file://this:1:1:1:1 | this | parameterName | file://sinkModel:1:1:1:1 | sinkModel | extensibleType | +| com/github/codeql/test/PublicClass.java:26:28:26:39 | input | unexploitable (is-style boolean method)\nrelated locations: $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@. | com/github/codeql/test/PublicClass.java:26:28:26:39 | input | MethodDoc | com/github/codeql/test/PublicClass.java:26:28:26:39 | input | ClassDoc | file://com.github.codeql.test:1:1:1:1 | com.github.codeql.test | package | file://PublicClass:1:1:1:1 | PublicClass | type | file://true:1:1:1:1 | true | subtypes | file://isIgnored:1:1:1:1 | isIgnored | name | file://(Object):1:1:1:1 | (Object) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input | file://:1:1:1:1 | | output | file://input:1:1:1:1 | input | parameterName | file://sinkModel:1:1:1:1 | sinkModel | extensibleType | | java/io/File.java:4:16:4:24 | compareTo | known non-sink\nrelated locations: $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@. | java/io/File.java:4:16:4:24 | compareTo | MethodDoc | java/io/File.java:4:16:4:24 | compareTo | ClassDoc | file://java.io:1:1:1:1 | java.io | package | file://File:1:1:1:1 | File | type | file://true:1:1:1:1 | true | subtypes | file://compareTo:1:1:1:1 | compareTo | name | file://(File):1:1:1:1 | (File) | signature | file://:1:1:1:1 | | input | file://Parameter[this]:1:1:1:1 | Parameter[this] | output | file://this:1:1:1:1 | this | parameterName | file://sourceModel:1:1:1:1 | sourceModel | extensibleType | | java/io/File.java:4:16:4:24 | compareTo | known non-sink\nrelated locations: $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@. | java/io/File.java:4:16:4:24 | compareTo | MethodDoc | java/io/File.java:4:16:4:24 | compareTo | ClassDoc | file://java.io:1:1:1:1 | java.io | package | file://File:1:1:1:1 | File | type | file://true:1:1:1:1 | true | subtypes | file://compareTo:1:1:1:1 | compareTo | name | file://(File):1:1:1:1 | (File) | signature | file://Argument[this]:1:1:1:1 | Argument[this] | input | file://:1:1:1:1 | | output | file://this:1:1:1:1 | this | parameterName | file://sinkModel:1:1:1:1 | sinkModel | extensibleType | | java/io/File.java:5:9:5:21 | pathname | known non-sink\nrelated locations: $@, $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@. | java/io/File.java:5:9:5:21 | pathname | MethodDoc | java/io/File.java:5:9:5:21 | pathname | ClassDoc | file://java.io:1:1:1:1 | java.io | package | file://File:1:1:1:1 | File | type | file://true:1:1:1:1 | true | subtypes | file://compareTo:1:1:1:1 | compareTo | name | file://(File):1:1:1:1 | (File) | signature | file://:1:1:1:1 | | input | file://Parameter[0]:1:1:1:1 | Parameter[0] | output | file://pathname:1:1:1:1 | pathname | parameterName | file://sourceModel:1:1:1:1 | sourceModel | extensibleType | diff --git a/java/ql/automodel/test/AutomodelFrameworkModeExtraction/com/github/codeql/test/PublicClass.java b/java/ql/automodel/test/AutomodelFrameworkModeExtraction/com/github/codeql/test/PublicClass.java index 33eb443368c..e0915ac628f 100644 --- a/java/ql/automodel/test/AutomodelFrameworkModeExtraction/com/github/codeql/test/PublicClass.java +++ b/java/ql/automodel/test/AutomodelFrameworkModeExtraction/com/github/codeql/test/PublicClass.java @@ -5,7 +5,7 @@ public class PublicClass { System.out.println(arg); } - public static void staticStuff(String arg) { // `arg` is a candidate, `this` is not a candidate (static method), `arg` is not a source candidate (static methods can not be overloaded) + public static void staticStuff(String arg) { // `arg` is a sink candidate, but not a source candidate (not overrideabe); `this` is not a candidate (static method) System.out.println(arg); } @@ -22,4 +22,8 @@ public class PublicClass { public PublicClass(Object input) { // the `this` qualifier is not a candidate } + + public Boolean isIgnored(Object input) { // `input` is a source candidate, but not a sink candidate (is-style method); `this` is not a candidate + return false; + } }