Merge pull request #15326 from github/max-schaefer/automodel-negative-sink-models

Automodel: Apply negative characteristics only to endpoints of the right kind.
This commit is contained in:
Max Schaefer
2024-01-17 15:54:28 +00:00
committed by GitHub
17 changed files with 380 additions and 296 deletions

View File

@@ -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.

View File

@@ -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: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@.", //

View File

@@ -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: $@, $@, $@, $@, $@, $@, $@, $@, $@.", //

View File

@@ -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(),

View File

@@ -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" }

View File

@@ -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
}
}

View File

@@ -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: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@.", //

View File

@@ -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,

View File

@@ -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: $@, $@, $@, $@, $@, $@, $@, $@, $@.", //

View File

@@ -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<CandidateSig Candidate> {
}
/**
* 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<CandidateSig Candidate> {
) {
// 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<CandidateSig Candidate> {
}
/**
* 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<CandidateSig Candidate> {
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<CandidateSig Candidate> {
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<CandidateSig Candidate> {
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<CandidateSig Candidate> {
/**
* 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, _) }

View File

@@ -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);
}

View File

@@ -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 |

View File

@@ -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 |

View File

@@ -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)
);
}
}

View File

@@ -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 |

View File

@@ -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 |

View File

@@ -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;
}
}