mirror of
https://github.com/github/codeql.git
synced 2026-04-30 11:15:13 +02:00
Java: drop automodel queries
This commit is contained in:
@@ -1,183 +0,0 @@
|
||||
private import java
|
||||
private import semmle.code.java.dataflow.ExternalFlow as ExternalFlow
|
||||
private import semmle.code.java.dataflow.TaintTracking
|
||||
private import semmle.code.java.security.RequestForgeryConfig
|
||||
private import semmle.code.java.security.CommandLineQuery
|
||||
private import semmle.code.java.security.SqlConcatenatedQuery
|
||||
private import semmle.code.java.security.SqlInjectionQuery
|
||||
private import semmle.code.java.security.UrlRedirectQuery
|
||||
private import semmle.code.java.security.TaintedPathQuery
|
||||
private import semmle.code.java.security.SqlInjectionQuery
|
||||
private import AutomodelJavaUtil
|
||||
|
||||
private newtype TSinkModel =
|
||||
MkSinkModel(
|
||||
string package, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string input, string kind, string provenance
|
||||
) {
|
||||
ExternalFlow::sinkModel(package, type, subtypes, name, signature, ext, input, kind, provenance,
|
||||
_)
|
||||
}
|
||||
|
||||
class SinkModel extends TSinkModel {
|
||||
string package;
|
||||
string type;
|
||||
boolean subtypes;
|
||||
string name;
|
||||
string signature;
|
||||
string ext;
|
||||
string input;
|
||||
string kind;
|
||||
string provenance;
|
||||
|
||||
SinkModel() {
|
||||
this = MkSinkModel(package, type, subtypes, name, signature, ext, input, kind, provenance)
|
||||
}
|
||||
|
||||
/** Gets the package for this sink model. */
|
||||
string getPackage() { result = package }
|
||||
|
||||
/** Gets the type for this sink model. */
|
||||
string getType() { result = type }
|
||||
|
||||
/** Gets whether this sink model considers subtypes. */
|
||||
boolean getSubtypes() { result = subtypes }
|
||||
|
||||
/** Gets the name for this sink model. */
|
||||
string getName() { result = name }
|
||||
|
||||
/** Gets the signature for this sink model. */
|
||||
string getSignature() { result = signature }
|
||||
|
||||
/** Gets the input for this sink model. */
|
||||
string getInput() { result = input }
|
||||
|
||||
/** Gets the extension for this sink model. */
|
||||
string getExt() { result = ext }
|
||||
|
||||
/** Gets the kind for this sink model. */
|
||||
string getKind() { result = kind }
|
||||
|
||||
/** Gets the provenance for this sink model. */
|
||||
string getProvenance() { result = provenance }
|
||||
|
||||
/** Gets the number of instances of this sink model. */
|
||||
int getInstanceCount() { result = count(PotentialSinkModelExpr p | p.getSinkModel() = this) }
|
||||
|
||||
/** Gets a string representation of this sink model. */
|
||||
string toString() {
|
||||
result =
|
||||
"SinkModel(" + package + ", " + type + ", " + subtypes + ", " + name + ", " + signature + ", "
|
||||
+ ext + ", " + input + ", " + kind + ", " + provenance + ")"
|
||||
}
|
||||
|
||||
/** Gets a string representation of this sink model as it would appear in a Models-as-Data file. */
|
||||
string getRepr() {
|
||||
result =
|
||||
"\"" + package + "\", \"" + type + "\", " + pyBool(subtypes) + ", \"" + name + "\", \"" +
|
||||
signature + "\", \"" + ext + "\", \"" + input + "\", \"" + kind + "\", \"" + provenance +
|
||||
"\""
|
||||
}
|
||||
}
|
||||
|
||||
/** An expression that may correspond to a sink model. */
|
||||
class PotentialSinkModelExpr extends Expr {
|
||||
/**
|
||||
* Holds if this expression has the given signature. The signature should contain enough
|
||||
* information to determine a corresponding sink model, if one exists.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate hasSignature(
|
||||
string package, string type, boolean subtypes, string name, string signature, string input
|
||||
) {
|
||||
exists(Call call, Callable callable, int argIdx |
|
||||
call.getCallee().getSourceDeclaration() = callable and
|
||||
(
|
||||
this = call.getArgument(argIdx)
|
||||
or
|
||||
this = call.getQualifier() and argIdx = -1
|
||||
) and
|
||||
(if argIdx = -1 then input = "Argument[this]" else input = "Argument[" + argIdx + "]") and
|
||||
package = callable.getDeclaringType().getPackage().getName() and
|
||||
type = callable.getDeclaringType().getErasure().(RefType).getNestedName() and
|
||||
subtypes = considerSubtypes(callable) and
|
||||
name = callable.getName() and
|
||||
signature = ExternalFlow::paramsString(callable)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a sink model that corresponds to this expression. */
|
||||
SinkModel getSinkModel() {
|
||||
this.hasSignature(result.getPackage(), result.getType(), result.getSubtypes(), result.getName(),
|
||||
result.getSignature(), result.getInput())
|
||||
}
|
||||
}
|
||||
|
||||
private string pyBool(boolean b) {
|
||||
b = true and result = "True"
|
||||
or
|
||||
b = false and result = "False"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a string representation of the existing sink model at the expression `e`, in the format in
|
||||
* which it would appear in a Models-as-Data file. Also restricts the provenance of the sink model
|
||||
* to be `ai-generated`.
|
||||
*/
|
||||
string getSinkModelRepr(PotentialSinkModelExpr e) {
|
||||
result = e.getSinkModel().getRepr() and
|
||||
e.getSinkModel().getProvenance() = "ai-generated"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the string representation of a sink model in a format suitable for appending to an alert
|
||||
* message.
|
||||
*/
|
||||
string getSinkModelQueryRepr(PotentialSinkModelExpr e) {
|
||||
result = "\nsinkModel: " + getSinkModelRepr(e)
|
||||
}
|
||||
|
||||
/**
|
||||
* A parameterised module that takes a dataflow config, and exposes a predicate for counting the
|
||||
* number of AI-generated sink models that appear in alerts for that query.
|
||||
*/
|
||||
private module SinkTallier<DataFlow::ConfigSig Config> {
|
||||
module ConfigFlow = TaintTracking::Global<Config>;
|
||||
|
||||
predicate getSinkModelCount(int c, SinkModel s) {
|
||||
s = any(ConfigFlow::PathNode sink).getNode().asExpr().(PotentialSinkModelExpr).getSinkModel() and
|
||||
c =
|
||||
strictcount(ConfigFlow::PathNode sink |
|
||||
ConfigFlow::flowPath(_, sink) and
|
||||
s = sink.getNode().asExpr().(PotentialSinkModelExpr).getSinkModel()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
predicate sinkModelTallyPerQuery(string queryName, int alertCount, SinkModel sinkModel) {
|
||||
queryName = "java/request-forgery" and
|
||||
SinkTallier<RequestForgeryConfig>::getSinkModelCount(alertCount, sinkModel)
|
||||
or
|
||||
queryName = "java/command-line-injection" and
|
||||
SinkTallier<InputToArgumentToExecFlowConfig>::getSinkModelCount(alertCount, sinkModel)
|
||||
or
|
||||
queryName = "java/concatenated-sql-query" and
|
||||
SinkTallier<UncontrolledStringBuilderSourceFlowConfig>::getSinkModelCount(alertCount, sinkModel)
|
||||
or
|
||||
queryName = "java/ssrf" and
|
||||
SinkTallier<RequestForgeryConfig>::getSinkModelCount(alertCount, sinkModel)
|
||||
or
|
||||
queryName = "java/path-injection" and
|
||||
SinkTallier<TaintedPathConfig>::getSinkModelCount(alertCount, sinkModel)
|
||||
or
|
||||
queryName = "java/unvalidated-url-redirection" and
|
||||
SinkTallier<UrlRedirectConfig>::getSinkModelCount(alertCount, sinkModel)
|
||||
or
|
||||
queryName = "java/sql-injection" and
|
||||
SinkTallier<QueryInjectionFlowConfig>::getSinkModelCount(alertCount, sinkModel)
|
||||
}
|
||||
|
||||
predicate sinkModelTally(int alertCount, SinkModel sinkModel) {
|
||||
sinkModelTallyPerQuery(_, _, sinkModel) and
|
||||
alertCount = sum(int c | sinkModelTallyPerQuery(_, c, sinkModel))
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
/**
|
||||
* @name Number of alerts per sink model
|
||||
* @description Counts the number of alerts using `ai-generated` sink models.
|
||||
* @kind table
|
||||
* @id java/ml/metrics-count-alerts-per-sink-model
|
||||
* @tags internal automodel metrics
|
||||
*/
|
||||
|
||||
private import java
|
||||
private import AutomodelAlertSinkUtil
|
||||
|
||||
from int alertCount, SinkModel s
|
||||
where sinkModelTally(alertCount, s) and s.getProvenance() = "ai-generated"
|
||||
select alertCount, s.getPackage() as package, s.getType() as type, s.getSubtypes() as subtypes,
|
||||
s.getName() as name, s.getSignature() as signature, s.getInput() as input, s.getExt() as ext,
|
||||
s.getKind() as kind, s.getProvenance() as provenance order by alertCount desc
|
||||
@@ -1,19 +0,0 @@
|
||||
/**
|
||||
* @name Number of alerts per sink model and query
|
||||
* @description Counts the number of alerts per query using `ai-generated` sink models.
|
||||
* @kind table
|
||||
* @id java/ml/metrics-count-alerts-per-sink-model-and-query
|
||||
* @tags internal automodel metrics
|
||||
*/
|
||||
|
||||
private import java
|
||||
private import AutomodelAlertSinkUtil
|
||||
|
||||
from string queryId, int alertCount, SinkModel s
|
||||
where
|
||||
sinkModelTallyPerQuery(queryId, alertCount, s) and
|
||||
s.getProvenance() = "ai-generated"
|
||||
select queryId, alertCount, s.getPackage() as package, s.getType() as type,
|
||||
s.getSubtypes() as subtypes, s.getName() as name, s.getSignature() as signature,
|
||||
s.getInput() as input, s.getExt() as ext, s.getKind() as kind, s.getProvenance() as provenance
|
||||
order by queryId, alertCount desc
|
||||
@@ -1,677 +0,0 @@
|
||||
/**
|
||||
* For internal use only.
|
||||
*/
|
||||
|
||||
private import java
|
||||
private import semmle.code.Location as Location
|
||||
private import semmle.code.java.dataflow.DataFlow
|
||||
private import semmle.code.java.dataflow.TaintTracking
|
||||
private import semmle.code.java.dataflow.ExternalFlow as ExternalFlow
|
||||
private import semmle.code.java.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
private import semmle.code.java.security.ExternalAPIs as ExternalAPIs
|
||||
private import semmle.code.java.Expr as Expr
|
||||
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
|
||||
import AutomodelSharedCharacteristics as SharedCharacteristics
|
||||
import AutomodelEndpointTypes as AutomodelEndpointTypes
|
||||
|
||||
newtype JavaRelatedLocationType =
|
||||
CallContext() or
|
||||
MethodDoc() or
|
||||
ClassDoc()
|
||||
|
||||
newtype TApplicationModeEndpoint =
|
||||
TExplicitArgument(Call call, DataFlow::Node arg) {
|
||||
AutomodelJavaUtil::isFromSource(call) and
|
||||
exists(Argument argExpr |
|
||||
arg.asExpr() = argExpr and call = argExpr.getCall() and not argExpr.isVararg()
|
||||
) and
|
||||
not AutomodelJavaUtil::isUnexploitableType(arg.getType())
|
||||
} or
|
||||
TInstanceArgument(Call call, DataFlow::Node arg) {
|
||||
AutomodelJavaUtil::isFromSource(call) and
|
||||
arg = DataFlow::getInstanceArgument(call) and
|
||||
not call instanceof ConstructorCall and
|
||||
not AutomodelJavaUtil::isUnexploitableType(arg.getType())
|
||||
} or
|
||||
TImplicitVarargsArray(Call call, DataFlow::ImplicitVarargsArray arg, int idx) {
|
||||
AutomodelJavaUtil::isFromSource(call) and
|
||||
call = arg.getCall() and
|
||||
idx = call.getCallee().getVaragsParameterIndex() and
|
||||
not AutomodelJavaUtil::isUnexploitableType(arg.getType())
|
||||
} or
|
||||
TMethodReturnValue(MethodCall call) {
|
||||
AutomodelJavaUtil::isFromSource(call) and
|
||||
not AutomodelJavaUtil::isUnexploitableType(call.getType())
|
||||
} or
|
||||
TOverriddenParameter(Parameter p, Method overriddenMethod) {
|
||||
AutomodelJavaUtil::isFromSource(p) and
|
||||
p.getCallable().(Method).overrides(overriddenMethod)
|
||||
}
|
||||
|
||||
/**
|
||||
* An endpoint is a node that is a candidate for modeling.
|
||||
*/
|
||||
abstract private class ApplicationModeEndpoint extends TApplicationModeEndpoint {
|
||||
/**
|
||||
* Gets the callable to be modeled that this endpoint represents.
|
||||
*/
|
||||
abstract Callable getCallable();
|
||||
|
||||
/**
|
||||
* Gets the input (if any) for this endpoint, eg.: `Argument[0]`.
|
||||
*
|
||||
* For endpoints that are source candidates, this will be `none()`.
|
||||
*/
|
||||
abstract string getMaDInput();
|
||||
|
||||
/**
|
||||
* Gets the output (if any) for this endpoint, eg.: `ReturnValue`.
|
||||
*
|
||||
* For endpoints that are sink candidates, this will be `none()`.
|
||||
*/
|
||||
abstract string getMaDOutput();
|
||||
|
||||
abstract Top asTop();
|
||||
|
||||
/**
|
||||
* Converts the endpoint to a node that can be used in a data flow graph.
|
||||
*/
|
||||
abstract DataFlow::Node asNode();
|
||||
|
||||
string getExtensibleType() {
|
||||
if not exists(this.getMaDInput()) and exists(this.getMaDOutput())
|
||||
then result = "sourceModel"
|
||||
else
|
||||
if exists(this.getMaDInput()) and not exists(this.getMaDOutput())
|
||||
then result = "sinkModel"
|
||||
else none() // if both exist, it would be a summaryModel (not yet supported)
|
||||
}
|
||||
|
||||
abstract string toString();
|
||||
}
|
||||
|
||||
class TCallArgument = TExplicitArgument or TInstanceArgument or TImplicitVarargsArray;
|
||||
|
||||
/**
|
||||
* An endpoint that represents an "argument" to a call in a broad sense, including
|
||||
* both explicit arguments and the instance argument.
|
||||
*/
|
||||
abstract class CallArgument extends ApplicationModeEndpoint, TCallArgument {
|
||||
Call call;
|
||||
DataFlow::Node arg;
|
||||
|
||||
override Callable getCallable() { result = call.getCallee().getSourceDeclaration() }
|
||||
|
||||
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 Top asTop() { result = arg.asExpr() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An endpoint that represents the instance argument to a call.
|
||||
*/
|
||||
class InstanceArgument extends CallArgument, TInstanceArgument {
|
||||
InstanceArgument() { this = TInstanceArgument(call, arg) }
|
||||
|
||||
override string getMaDInput() { result = "Argument[this]" }
|
||||
|
||||
override Top asTop() { if exists(arg.asExpr()) then result = arg.asExpr() else result = call }
|
||||
|
||||
override string toString() { result = arg.toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An endpoint that represents an implicit varargs array.
|
||||
* We choose to represent the varargs array as a single endpoint, rather than as multiple endpoints.
|
||||
*
|
||||
* This avoids the problem of having to deal with redundant endpoints downstream.
|
||||
*
|
||||
* 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 CallArgument, TImplicitVarargsArray {
|
||||
int idx;
|
||||
|
||||
ImplicitVarargsArray() { this = TImplicitVarargsArray(call, arg, idx) }
|
||||
|
||||
override string getMaDInput() { result = "Argument[" + idx + "]" }
|
||||
|
||||
override Top asTop() { result = call }
|
||||
}
|
||||
|
||||
/**
|
||||
* An endpoint that represents a method call. The `ReturnValue` of a method call
|
||||
* may be a source.
|
||||
*/
|
||||
class MethodReturnValue extends ApplicationModeEndpoint, TMethodReturnValue {
|
||||
MethodCall call;
|
||||
|
||||
MethodReturnValue() { this = TMethodReturnValue(call) }
|
||||
|
||||
override Callable getCallable() { result = call.getCallee().getSourceDeclaration() }
|
||||
|
||||
override string getMaDInput() { none() }
|
||||
|
||||
override string getMaDOutput() { result = "ReturnValue" }
|
||||
|
||||
override Top asTop() { result = call }
|
||||
|
||||
override DataFlow::Node asNode() { result.asExpr() = call }
|
||||
|
||||
override string toString() { result = call.toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An endpoint that represents a parameter of an overridden method that may be
|
||||
* a source.
|
||||
*/
|
||||
class OverriddenParameter extends ApplicationModeEndpoint, TOverriddenParameter {
|
||||
Parameter p;
|
||||
Method overriddenMethod;
|
||||
|
||||
OverriddenParameter() { this = TOverriddenParameter(p, overriddenMethod) }
|
||||
|
||||
override Callable getCallable() {
|
||||
// NB: we're returning the overridden callable here. This means that the
|
||||
// candidate model will be about the overridden method, not the overriding
|
||||
// method. This is a more general model, that also applies to other
|
||||
// subclasses of the overridden class.
|
||||
result = overriddenMethod.getSourceDeclaration()
|
||||
}
|
||||
|
||||
private int getArgIndex() { p.getCallable().getParameter(result) = p }
|
||||
|
||||
override string getMaDInput() { none() }
|
||||
|
||||
override string getMaDOutput() { result = "Parameter[" + this.getArgIndex() + "]" }
|
||||
|
||||
override Top asTop() { result = p }
|
||||
|
||||
override DataFlow::Node asNode() { result.(DataFlow::ParameterNode).asParameter() = p }
|
||||
|
||||
override string toString() { result = p.toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A candidates implementation.
|
||||
*
|
||||
* Some important notes:
|
||||
* - This mode is using arguments as endpoints.
|
||||
* - We use the `CallContext` (the surrounding call expression) as related location.
|
||||
*/
|
||||
module ApplicationCandidatesImpl implements SharedCharacteristics::CandidateSig {
|
||||
// for documentation of the implementations here, see the QLDoc in the CandidateSig signature module.
|
||||
class Endpoint = ApplicationModeEndpoint;
|
||||
|
||||
class EndpointType = AutomodelEndpointTypes::EndpointType;
|
||||
|
||||
class SinkType = AutomodelEndpointTypes::SinkType;
|
||||
|
||||
class SourceType = AutomodelEndpointTypes::SourceType;
|
||||
|
||||
class RelatedLocation = Location::Top;
|
||||
|
||||
class RelatedLocationType = JavaRelatedLocationType;
|
||||
|
||||
// Sanitizers are currently not modeled in MaD. TODO: check if this has large negative impact.
|
||||
predicate isSanitizer(Endpoint e, EndpointType t) {
|
||||
exists(t) and
|
||||
AutomodelJavaUtil::isUnexploitableType([
|
||||
// for most endpoints, we can get the type from the node
|
||||
e.asNode().getType(),
|
||||
// but not for calls to void methods, where we need to go via the AST
|
||||
e.asTop().(Expr).getType()
|
||||
])
|
||||
or
|
||||
t instanceof AutomodelEndpointTypes::PathInjectionSinkType and
|
||||
e.asNode() instanceof PathSanitizer::PathInjectionSanitizer
|
||||
}
|
||||
|
||||
RelatedLocation asLocation(Endpoint e) { result = e.asTop() }
|
||||
|
||||
predicate isKnownKind = AutomodelJavaUtil::isKnownKind/2;
|
||||
|
||||
predicate isSink(Endpoint e, string kind, string provenance) {
|
||||
exists(
|
||||
string package, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string input
|
||||
|
|
||||
sinkSpec(e, package, type, subtypes, name, signature, ext, input) and
|
||||
ExternalFlow::sinkModel(package, type, subtypes, name, [signature, ""], ext, input, kind,
|
||||
provenance, _)
|
||||
)
|
||||
or
|
||||
isCustomSink(e, kind) and provenance = "custom-sink"
|
||||
}
|
||||
|
||||
predicate isSource(Endpoint e, string kind, string provenance) {
|
||||
exists(
|
||||
string package, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string output
|
||||
|
|
||||
sourceSpec(e, package, type, subtypes, name, signature, ext, output) and
|
||||
ExternalFlow::sourceModel(package, type, subtypes, name, [signature, ""], ext, output, kind,
|
||||
provenance, _)
|
||||
)
|
||||
}
|
||||
|
||||
predicate isNeutral(Endpoint e) {
|
||||
exists(string package, string type, string name, string signature, string endpointType |
|
||||
sinkSpec(e, package, type, _, name, signature, _, _) and
|
||||
endpointType = "sink"
|
||||
or
|
||||
sourceSpec(e, package, type, _, name, signature, _, _) and
|
||||
endpointType = "source"
|
||||
|
|
||||
ExternalFlow::neutralModel(package, type, name, [signature, ""], endpointType, _)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the endpoint concerns a callable with the given package, type, name and signature.
|
||||
*
|
||||
* If `subtypes` is `false`, only the exact callable is considered. If `true`, the callable and
|
||||
* all its overrides are considered.
|
||||
*/
|
||||
additional predicate endpointCallable(
|
||||
Endpoint e, string package, string type, boolean subtypes, string name, string signature
|
||||
) {
|
||||
exists(Callable c |
|
||||
c = e.getCallable() and subtypes in [true, false]
|
||||
or
|
||||
e.getCallable().(Method).getSourceDeclaration().overrides+(c) and subtypes = true
|
||||
|
|
||||
c.hasQualifiedName(package, type, name) and
|
||||
signature = ExternalFlow::paramsString(c)
|
||||
)
|
||||
}
|
||||
|
||||
additional predicate sinkSpec(
|
||||
Endpoint e, string package, string type, boolean subtypes, string name, string signature,
|
||||
string ext, string input
|
||||
) {
|
||||
endpointCallable(e, package, type, subtypes, name, signature) and
|
||||
ext = "" and
|
||||
input = e.getMaDInput()
|
||||
}
|
||||
|
||||
additional predicate sourceSpec(
|
||||
Endpoint e, string package, string type, boolean subtypes, string name, string signature,
|
||||
string ext, string output
|
||||
) {
|
||||
endpointCallable(e, package, type, subtypes, name, signature) and
|
||||
ext = "" and
|
||||
output = e.getMaDOutput()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the related location for the given endpoint.
|
||||
*
|
||||
* The only related location we model is the the call expression surrounding to
|
||||
* which the endpoint is either argument or qualifier (known as the call context).
|
||||
*/
|
||||
RelatedLocation getRelatedLocation(Endpoint e, RelatedLocationType type) {
|
||||
type = CallContext() and
|
||||
result = e.(CallArgument).getCall()
|
||||
or
|
||||
type = MethodDoc() and
|
||||
result = e.getCallable().(Documentable).getJavadoc()
|
||||
or
|
||||
type = ClassDoc() and
|
||||
result = e.getCallable().getDeclaringType().(Documentable).getJavadoc()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains endpoints that are defined in QL code rather than as a MaD model. Ideally this predicate
|
||||
* should be empty.
|
||||
*/
|
||||
private predicate isCustomSink(Endpoint e, string kind) {
|
||||
e.asNode() instanceof QueryInjectionSink and kind = "sql"
|
||||
}
|
||||
|
||||
module CharacteristicsImpl =
|
||||
SharedCharacteristics::SharedCharacteristics<ApplicationCandidatesImpl>;
|
||||
|
||||
class EndpointCharacteristic = CharacteristicsImpl::EndpointCharacteristic;
|
||||
|
||||
class Endpoint = ApplicationCandidatesImpl::Endpoint;
|
||||
|
||||
/*
|
||||
* Predicates that are used to surface prompt examples and candidates for classification with an ML model.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A MetadataExtractor that extracts metadata for application mode.
|
||||
*/
|
||||
class ApplicationModeMetadataExtractor extends string {
|
||||
ApplicationModeMetadataExtractor() { this = "ApplicationModeMetadataExtractor" }
|
||||
|
||||
predicate hasMetadata(
|
||||
Endpoint e, string package, string type, string subtypes, string name, string signature,
|
||||
string input, string output, string isVarargsArray, string alreadyAiModeled,
|
||||
string extensibleType
|
||||
) {
|
||||
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).getNestedName() and
|
||||
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"
|
||||
) and
|
||||
extensibleType = e.getExtensibleType()
|
||||
) and
|
||||
(
|
||||
not CharacteristicsImpl::isModeled(e, _, extensibleType, _) and alreadyAiModeled = ""
|
||||
or
|
||||
CharacteristicsImpl::isModeled(e, _, extensibleType, alreadyAiModeled)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the given `endpoint` should be considered a candidate for the `extensibleType`.
|
||||
*
|
||||
* The other parameters record various other properties of interest.
|
||||
*/
|
||||
predicate isCandidate(
|
||||
Endpoint endpoint, string package, string type, string subtypes, string name, string signature,
|
||||
string input, string output, string isVarargs, string extensibleType, string alreadyAiModeled
|
||||
) {
|
||||
CharacteristicsImpl::isCandidate(endpoint, _) and
|
||||
not exists(CharacteristicsImpl::UninterestingToModelCharacteristic u |
|
||||
u.appliesToEndpoint(endpoint)
|
||||
) and
|
||||
any(ApplicationModeMetadataExtractor meta)
|
||||
.hasMetadata(endpoint, package, type, subtypes, name, signature, input, output, isVarargs,
|
||||
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
|
||||
AutomodelJavaUtil::includeAutomodelCandidate(package, type, name, signature)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the given `endpoint` is a negative example for the `extensibleType`
|
||||
* because of the `characteristic`.
|
||||
*
|
||||
* The other parameters record various other properties of interest.
|
||||
*/
|
||||
predicate isNegativeExample(
|
||||
Endpoint endpoint, EndpointCharacteristic characteristic, float confidence, string package,
|
||||
string type, string subtypes, string name, string signature, string input, string output,
|
||||
string isVarargsArray, string extensibleType
|
||||
) {
|
||||
characteristic.appliesToEndpoint(endpoint) and
|
||||
// the node is known not to be an endpoint of any appropriate type
|
||||
forall(AutomodelEndpointTypes::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
|
||||
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(CharacteristicsImpl::getAPotentialType(endpoint), true, confidence2)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the given `endpoint` is a positive example for the `endpointType`.
|
||||
*
|
||||
* The other parameters record various other properties of interest.
|
||||
*/
|
||||
predicate isPositiveExample(
|
||||
Endpoint endpoint, string endpointType, string package, string type, string subtypes, string name,
|
||||
string signature, string input, string output, string isVarargsArray, string extensibleType
|
||||
) {
|
||||
any(ApplicationModeMetadataExtractor meta)
|
||||
.hasMetadata(endpoint, package, type, subtypes, name, signature, input, output,
|
||||
isVarargsArray, _, extensibleType) and
|
||||
CharacteristicsImpl::isKnownAs(endpoint, endpointType, _) and
|
||||
exists(CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, CallContext()))
|
||||
}
|
||||
|
||||
/*
|
||||
* EndpointCharacteristic classes that are specific to Automodel for Java.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A negative characteristic that indicates that parameters of an is-style boolean method should not be considered sinks.
|
||||
*
|
||||
* 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
|
||||
* the dangerous/interesting thing, so we want the latter to be modeled as the sink.
|
||||
*
|
||||
* 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 {
|
||||
UnexploitableIsCharacteristic() { this = "argument of is-style boolean method" }
|
||||
|
||||
override predicate appliesToEndpoint(Endpoint e) {
|
||||
e.getCallable().getName().matches("is%") and
|
||||
e.getCallable().getReturnType() instanceof BooleanType and
|
||||
not ApplicationCandidatesImpl::isSink(e, _, _)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A negative characteristic that indicates that parameters of an existence-checking boolean method should not be
|
||||
* considered sinks.
|
||||
*
|
||||
* 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 = "argument of existence-checking boolean method" }
|
||||
|
||||
override predicate appliesToEndpoint(Endpoint e) {
|
||||
exists(Callable callable | callable = e.getCallable() |
|
||||
callable.getName().toLowerCase() = ["exists", "notexists"] and
|
||||
callable.getReturnType() instanceof BooleanType
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = "argument/result of exception-related method" }
|
||||
|
||||
override predicate appliesToEndpoint(Endpoint e) {
|
||||
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"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A negative characteristic that indicates that an endpoint is a MaD taint step. MaD modeled taint steps are global,
|
||||
* so they are not sinks for any query. Non-MaD taint steps might be specific to a particular query, so we don't
|
||||
* filter those out.
|
||||
*/
|
||||
private class IsMaDTaintStepCharacteristic extends CharacteristicsImpl::NotASinkCharacteristic {
|
||||
IsMaDTaintStepCharacteristic() { this = "taint step" }
|
||||
|
||||
override predicate appliesToEndpoint(Endpoint e) {
|
||||
FlowSummaryImpl::Private::Steps::summaryThroughStepValue(e.asNode(), _, _)
|
||||
or
|
||||
FlowSummaryImpl::Private::Steps::summaryThroughStepTaint(e.asNode(), _, _)
|
||||
or
|
||||
FlowSummaryImpl::Private::Steps::summaryGetterStep(e.asNode(), _, _, _)
|
||||
or
|
||||
FlowSummaryImpl::Private::Steps::summarySetterStep(e.asNode(), _, _, _)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to a method that's known locally will not be considered as a candidate to model.
|
||||
*
|
||||
* The reason is that we would expect data/taint flow into the method implementation to uncover
|
||||
* any sinks that are present there.
|
||||
*/
|
||||
private class LocalCall extends CharacteristicsImpl::UninterestingToModelCharacteristic {
|
||||
LocalCall() { this = "local call" }
|
||||
|
||||
override predicate appliesToEndpoint(Endpoint e) {
|
||||
e.(CallArgument).getCallable().fromSource()
|
||||
or
|
||||
e.(MethodReturnValue).getCallable().fromSource()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(e.getCallable())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A negative characteristic that filters out non-public methods. Non-public methods are not interesting to include in
|
||||
* the standard Java modeling, because they cannot be called from outside the package.
|
||||
*/
|
||||
private class NonPublicMethodCharacteristic extends CharacteristicsImpl::UninterestingToModelCharacteristic
|
||||
{
|
||||
NonPublicMethodCharacteristic() { this = "non-public method" }
|
||||
|
||||
override predicate appliesToEndpoint(Endpoint e) {
|
||||
exists(Callable c | c = e.getCallable() | not c.isPublic())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A negative characteristic that indicates that an endpoint is a non-sink argument to a method whose sinks have already
|
||||
* been modeled _manually_. This is restricted to manual sinks only, because only during the manual process do we have
|
||||
* the expectation that all sinks present in a method have been considered.
|
||||
*
|
||||
* WARNING: These endpoints should not be used as negative samples for training, because some sinks may have been missed
|
||||
* when the method was modeled. Specifically, as we start using ATM to merge in new declarations, we can be less sure
|
||||
* that a method with one argument modeled as a MaD sink has also had its remaining arguments manually reviewed. The
|
||||
* ML model might have predicted argument 0 of some method to be a sink but not argument 1, when in fact argument 1 is
|
||||
* also a sink.
|
||||
*/
|
||||
private class OtherArgumentToModeledMethodCharacteristic extends CharacteristicsImpl::LikelyNotASinkCharacteristic
|
||||
{
|
||||
OtherArgumentToModeledMethodCharacteristic() {
|
||||
this = "other argument to a method that has already been modeled manually"
|
||||
}
|
||||
|
||||
override predicate appliesToEndpoint(Endpoint e) {
|
||||
not ApplicationCandidatesImpl::isSink(e, _, _) and
|
||||
exists(CallArgument otherSink |
|
||||
ApplicationCandidatesImpl::isSink(otherSink, _, "manual") and
|
||||
e.(CallArgument).getCall() = otherSink.getCall() and
|
||||
e != otherSink
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the type of the given expression is annotated with `@FunctionalInterface`.
|
||||
*/
|
||||
predicate hasFunctionalInterfaceType(Expr e) {
|
||||
exists(RefType tp | tp = e.getType().getErasure() |
|
||||
tp.getAnAssociatedAnnotation().getType().hasQualifiedName("java.lang", "FunctionalInterface")
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A characteristic that marks functional expression as likely not sinks.
|
||||
*
|
||||
* These expressions may well _contain_ sinks, but rarely are sinks themselves.
|
||||
*/
|
||||
private class FunctionValueCharacteristic extends CharacteristicsImpl::LikelyNotASinkCharacteristic {
|
||||
FunctionValueCharacteristic() { this = "function value" }
|
||||
|
||||
override predicate appliesToEndpoint(Endpoint e) {
|
||||
exists(Expr expr | expr = e.asNode().asExpr() |
|
||||
expr instanceof FunctionalExpr or hasFunctionalInterfaceType(expr)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A negative characteristic that indicates that an endpoint is not a `to` node for any known taint step. Such a node
|
||||
* cannot be tainted, because taint can't flow into it.
|
||||
*
|
||||
* WARNING: These endpoints should not be used as negative samples for training, because they may include sinks for
|
||||
* which our taint tracking modeling is incomplete.
|
||||
*/
|
||||
private class CannotBeTaintedCharacteristic extends CharacteristicsImpl::LikelyNotASinkCharacteristic
|
||||
{
|
||||
CannotBeTaintedCharacteristic() { this = "cannot be tainted" }
|
||||
|
||||
override predicate appliesToEndpoint(Endpoint e) { not this.isKnownOutNodeForStep(e) }
|
||||
|
||||
/**
|
||||
* Holds if the node `n` is known as the predecessor in a modeled flow step.
|
||||
*/
|
||||
private predicate isKnownOutNodeForStep(Endpoint e) {
|
||||
e.asNode().asExpr() instanceof Call or // we just assume flow in that case
|
||||
TaintTracking::localTaintStep(_, e.asNode()) or
|
||||
FlowSummaryImpl::Private::Steps::summaryThroughStepValue(_, e.asNode(), _) or
|
||||
FlowSummaryImpl::Private::Steps::summaryThroughStepTaint(_, e.asNode(), _) or
|
||||
FlowSummaryImpl::Private::Steps::summaryGetterStep(_, _, e.asNode(), _) or
|
||||
FlowSummaryImpl::Private::Steps::summarySetterStep(_, _, e.asNode(), _)
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
/**
|
||||
* Surfaces the endpoints that are not already known to be sinks, and are therefore used as candidates for
|
||||
* classification with an ML model.
|
||||
*
|
||||
* Note: This query does not actually classify the endpoints using the model.
|
||||
*
|
||||
* @name Automodel candidates (application mode)
|
||||
* @description A query to extract automodel candidates in application mode.
|
||||
* @kind problem
|
||||
* @problem.severity recommendation
|
||||
* @id java/ml/extract-automodel-application-candidates
|
||||
* @tags internal extract automodel application-mode candidates
|
||||
*/
|
||||
|
||||
import java
|
||||
private import AutomodelApplicationModeCharacteristics
|
||||
private import AutomodelJavaUtil
|
||||
|
||||
/**
|
||||
* Gets a sample of endpoints (of at most `limit` samples) with the given method signature.
|
||||
*
|
||||
* The main purpose of this helper predicate is to avoid selecting too many candidates, as this may
|
||||
* cause the SARIF file to exceed the maximum size limit.
|
||||
*/
|
||||
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 alreadyAiModeled
|
||||
) {
|
||||
exists(int n, int num_endpoints, ApplicationModeMetadataExtractor meta |
|
||||
num_endpoints =
|
||||
count(Endpoint e |
|
||||
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
|
||||
meta.hasMetadata(e, package, type, subtypes, name, signature, input, output, isVarargs,
|
||||
alreadyAiModeled, extensibleType)
|
||||
|
|
||||
e
|
||||
order by
|
||||
loc.getFile().getAbsolutePath(), loc.getStartLine(), loc.getStartColumn(),
|
||||
loc.getEndLine(), loc.getEndColumn()
|
||||
) and
|
||||
// To avoid selecting samples that are too close together (as the ranking above goes by file
|
||||
// path first), we select `limit` evenly spaced samples from the ranked list of endpoints. By
|
||||
// default this would always include the first sample, so we add a random-chosen prime offset
|
||||
// to the first sample index, and reduce modulo the number of endpoints.
|
||||
// Finally, we add 1 to the result, as ranking results in a 1-indexed relation.
|
||||
n = 1 + (([0 .. limit - 1] * (num_endpoints / limit).floor() + 46337) % num_endpoints)
|
||||
)
|
||||
}
|
||||
|
||||
from
|
||||
Endpoint endpoint, DollarAtString package, DollarAtString type, DollarAtString subtypes,
|
||||
DollarAtString name, DollarAtString signature, DollarAtString input, DollarAtString output,
|
||||
DollarAtString isVarargsArray, DollarAtString alreadyAiModeled, DollarAtString extensibleType
|
||||
where
|
||||
isCandidate(endpoint, package, type, subtypes, name, signature, input, output, isVarargsArray,
|
||||
extensibleType, alreadyAiModeled) and
|
||||
endpoint =
|
||||
getSampleForSignature(9, package, type, subtypes, name, signature, input, output,
|
||||
isVarargsArray, extensibleType, alreadyAiModeled)
|
||||
select endpoint.asNode(),
|
||||
"Related locations: $@, $@, $@." + "\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@.", //
|
||||
CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, CallContext()), "CallContext", //
|
||||
CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, MethodDoc()), "MethodDoc", //
|
||||
CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, ClassDoc()), "ClassDoc", //
|
||||
package, "package", //
|
||||
type, "type", //
|
||||
subtypes, "subtypes", //
|
||||
name, "name", // method name
|
||||
signature, "signature", //
|
||||
input, "input", //
|
||||
output, "output", //
|
||||
isVarargsArray, "isVarargsArray", //
|
||||
alreadyAiModeled, "alreadyAiModeled", //
|
||||
extensibleType, "extensibleType"
|
||||
@@ -1,66 +0,0 @@
|
||||
/**
|
||||
* Surfaces endpoints that are non-sinks with high confidence, for use as negative examples in the prompt.
|
||||
*
|
||||
* @name Negative examples (application mode)
|
||||
* @kind problem
|
||||
* @problem.severity recommendation
|
||||
* @id java/ml/extract-automodel-application-negative-examples
|
||||
* @tags internal extract automodel application-mode negative examples
|
||||
*/
|
||||
|
||||
private import java
|
||||
private import AutomodelApplicationModeCharacteristics
|
||||
private import AutomodelEndpointTypes
|
||||
private import AutomodelJavaUtil
|
||||
|
||||
/**
|
||||
* Gets a sample of endpoints (of at most `limit` samples) for which the given characteristic applies.
|
||||
*
|
||||
* The main purpose of this helper predicate is to avoid selecting too many samples, as this may
|
||||
* cause the SARIF file to exceed the maximum size limit.
|
||||
*/
|
||||
bindingset[limit]
|
||||
Endpoint getSampleForCharacteristic(EndpointCharacteristic c, int limit) {
|
||||
exists(int n, int num_endpoints | num_endpoints = count(Endpoint e | c.appliesToEndpoint(e)) |
|
||||
result =
|
||||
rank[n](Endpoint e, Location loc |
|
||||
loc = e.asTop().getLocation() and c.appliesToEndpoint(e)
|
||||
|
|
||||
e
|
||||
order by
|
||||
loc.getFile().getAbsolutePath(), loc.getStartLine(), loc.getStartColumn(),
|
||||
loc.getEndLine(), loc.getEndColumn()
|
||||
) and
|
||||
// To avoid selecting samples that are too close together (as the ranking above goes by file
|
||||
// path first), we select `limit` evenly spaced samples from the ranked list of endpoints. By
|
||||
// default this would always include the first sample, so we add a random-chosen prime offset
|
||||
// to the first sample index, and reduce modulo the number of endpoints.
|
||||
// Finally, we add 1 to the result, as ranking results in a 1-indexed relation.
|
||||
n = 1 + (([0 .. limit - 1] * (num_endpoints / limit).floor() + 46337) % num_endpoints)
|
||||
)
|
||||
}
|
||||
|
||||
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
|
||||
isNegativeExample(endpoint, characteristic, confidence, package, type, subtypes, name, signature,
|
||||
input, output, isVarargsArray, extensibleType) and
|
||||
message = characteristic
|
||||
select endpoint.asNode(),
|
||||
message + "\nrelated locations: $@, $@, $@." + "\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@.", //
|
||||
CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, CallContext()), "CallContext", //
|
||||
CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, MethodDoc()), "MethodDoc", //
|
||||
CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, ClassDoc()), "ClassDoc", //
|
||||
package, "package", //
|
||||
type, "type", //
|
||||
subtypes, "subtypes", //
|
||||
name, "name", //
|
||||
signature, "signature", //
|
||||
input, "input", //
|
||||
output, "output", //
|
||||
isVarargsArray, "isVarargsArray", //
|
||||
extensibleType, "extensibleType"
|
||||
@@ -1,37 +0,0 @@
|
||||
/**
|
||||
* Surfaces endpoints that are sinks with high confidence, for use as positive examples in the prompt.
|
||||
*
|
||||
* @name Positive examples (application mode)
|
||||
* @kind problem
|
||||
* @problem.severity recommendation
|
||||
* @id java/ml/extract-automodel-application-positive-examples
|
||||
* @tags internal extract automodel application-mode positive examples
|
||||
*/
|
||||
|
||||
private import AutomodelApplicationModeCharacteristics
|
||||
private import AutomodelEndpointTypes
|
||||
private import AutomodelJavaUtil
|
||||
|
||||
from
|
||||
Endpoint endpoint, EndpointType endpointType, ApplicationModeMetadataExtractor meta,
|
||||
DollarAtString package, DollarAtString type, DollarAtString subtypes, DollarAtString name,
|
||||
DollarAtString signature, DollarAtString input, DollarAtString output,
|
||||
DollarAtString isVarargsArray, DollarAtString extensibleType
|
||||
where
|
||||
isPositiveExample(endpoint, endpointType, package, type, subtypes, name, signature, input, output,
|
||||
isVarargsArray, extensibleType)
|
||||
select endpoint.asNode(),
|
||||
endpointType + "\nrelated locations: $@, $@, $@." +
|
||||
"\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@.", //
|
||||
CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, CallContext()), "CallContext", //
|
||||
CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, MethodDoc()), "MethodDoc", //
|
||||
CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, ClassDoc()), "ClassDoc", //
|
||||
package, "package", //
|
||||
type, "type", //
|
||||
subtypes, "subtypes", //
|
||||
name, "name", //
|
||||
signature, "signature", //
|
||||
input, "input", //
|
||||
output, "output", //
|
||||
isVarargsArray, "isVarargsArray", //
|
||||
extensibleType, "extensibleType"
|
||||
@@ -1,5 +0,0 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/java-automodel-queries
|
||||
extensible: automodelCandidateFilter
|
||||
data: []
|
||||
@@ -1,19 +0,0 @@
|
||||
/**
|
||||
* @name Number of instances of each sink model
|
||||
* @description Counts the number of instances of `ai-generated` sink models.
|
||||
* @kind table
|
||||
* @id java/ml/metrics-count-instances-per-sink-model
|
||||
* @tags internal automodel metrics
|
||||
*/
|
||||
|
||||
private import java
|
||||
private import AutomodelAlertSinkUtil
|
||||
|
||||
from int instanceCount, SinkModel s
|
||||
where
|
||||
instanceCount = s.getInstanceCount() and
|
||||
instanceCount > 0 and
|
||||
s.getProvenance() = "ai-generated"
|
||||
select instanceCount, s.getPackage() as package, s.getType() as type, s.getSubtypes() as subtypes,
|
||||
s.getName() as name, s.getSignature() as signature, s.getInput() as input, s.getExt() as ext,
|
||||
s.getKind() as kind, s.getProvenance() as provenance order by instanceCount desc
|
||||
@@ -1,82 +0,0 @@
|
||||
/**
|
||||
* For internal use only.
|
||||
*
|
||||
* Defines the set of classes that endpoint scoring models can predict. Endpoint scoring models must
|
||||
* only predict classes defined within this file. This file is the source of truth for the integer
|
||||
* representation of each of these classes.
|
||||
*/
|
||||
|
||||
/** A class that can be predicted by a classifier. */
|
||||
abstract class EndpointType extends string {
|
||||
/**
|
||||
* Holds when the string matches the name of the sink / source type.
|
||||
*/
|
||||
bindingset[this]
|
||||
EndpointType() { any() }
|
||||
|
||||
/**
|
||||
* Gets the name of the sink/source kind for this endpoint type as used in models-as-data.
|
||||
*
|
||||
* See https://github.com/github/codeql/blob/44213f0144fdd54bb679ca48d68b28dcf820f7a8/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll#LL353C11-L357C31
|
||||
* for sink types, and https://github.com/github/codeql/blob/44213f0144fdd54bb679ca48d68b28dcf820f7a8/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll#L365
|
||||
* for source types.
|
||||
*/
|
||||
final string getKind() { result = this }
|
||||
}
|
||||
|
||||
/** A class for sink types that can be predicted by a classifier. */
|
||||
abstract class SinkType extends EndpointType {
|
||||
bindingset[this]
|
||||
SinkType() { any() }
|
||||
}
|
||||
|
||||
/** A sink relevant to the SQL injection query */
|
||||
class SqlInjectionSinkType extends SinkType {
|
||||
SqlInjectionSinkType() { this = "sql-injection" }
|
||||
}
|
||||
|
||||
/** A sink relevant to the tainted path injection query. */
|
||||
class PathInjectionSinkType extends SinkType {
|
||||
PathInjectionSinkType() { this = "path-injection" }
|
||||
}
|
||||
|
||||
/** A sink relevant to the SSRF query. */
|
||||
class RequestForgerySinkType extends SinkType {
|
||||
RequestForgerySinkType() { this = "request-forgery" }
|
||||
}
|
||||
|
||||
/** A sink relevant to the command injection query. */
|
||||
class CommandInjectionSinkType extends SinkType {
|
||||
CommandInjectionSinkType() { this = "command-injection" }
|
||||
}
|
||||
|
||||
/** A sink relevant to file storage. */
|
||||
class FileContentStoreSinkType extends SinkType {
|
||||
FileContentStoreSinkType() { this = "file-content-store" }
|
||||
}
|
||||
|
||||
/** A sink relevant to HTML injection. */
|
||||
class HtmlInjectionSinkType extends SinkType {
|
||||
HtmlInjectionSinkType() { this = "html-injection" }
|
||||
}
|
||||
|
||||
/** A sink relevant to LDAP injection. */
|
||||
class LdapInjectionSinkType extends SinkType {
|
||||
LdapInjectionSinkType() { this = "ldap-injection" }
|
||||
}
|
||||
|
||||
/** A sink relevant to URL redirection. */
|
||||
class UrlRedirectionSinkType extends SinkType {
|
||||
UrlRedirectionSinkType() { this = "url-redirection" }
|
||||
}
|
||||
|
||||
/** A class for source types that can be predicted by a classifier. */
|
||||
abstract class SourceType extends EndpointType {
|
||||
bindingset[this]
|
||||
SourceType() { any() }
|
||||
}
|
||||
|
||||
/** A source of remote data. */
|
||||
class RemoteSourceType extends SourceType {
|
||||
RemoteSourceType() { this = "remote" }
|
||||
}
|
||||
@@ -1,507 +0,0 @@
|
||||
/**
|
||||
* For internal use only.
|
||||
*/
|
||||
|
||||
private import java
|
||||
private import semmle.code.Location as Location
|
||||
private import semmle.code.java.dataflow.DataFlow
|
||||
private import semmle.code.java.dataflow.TaintTracking
|
||||
private import semmle.code.java.dataflow.ExternalFlow as ExternalFlow
|
||||
private import semmle.code.java.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
private import semmle.code.java.security.ExternalAPIs as ExternalAPIs
|
||||
private import semmle.code.java.Expr as Expr
|
||||
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
|
||||
import AutomodelSharedCharacteristics as SharedCharacteristics
|
||||
import AutomodelEndpointTypes as AutomodelEndpointTypes
|
||||
|
||||
newtype JavaRelatedLocationType =
|
||||
MethodDoc() or
|
||||
ClassDoc()
|
||||
|
||||
newtype TFrameworkModeEndpoint =
|
||||
TExplicitParameter(Parameter p) {
|
||||
AutomodelJavaUtil::isFromSource(p) and
|
||||
not AutomodelJavaUtil::isUnexploitableType(p.getType())
|
||||
} or
|
||||
TQualifier(Callable c) { AutomodelJavaUtil::isFromSource(c) and not c instanceof Constructor } or
|
||||
TReturnValue(Callable c) {
|
||||
AutomodelJavaUtil::isFromSource(c) and
|
||||
c instanceof Constructor
|
||||
or
|
||||
AutomodelJavaUtil::isFromSource(c) and
|
||||
c instanceof Method and
|
||||
not AutomodelJavaUtil::isUnexploitableType(c.getReturnType())
|
||||
} or
|
||||
TOverridableParameter(Method m, Parameter p) {
|
||||
AutomodelJavaUtil::isFromSource(p) and
|
||||
not AutomodelJavaUtil::isUnexploitableType(p.getType()) and
|
||||
p.getCallable() = m and
|
||||
m instanceof ModelExclusions::ModelApi and
|
||||
AutomodelJavaUtil::isOverridable(m)
|
||||
} or
|
||||
TOverridableQualifier(Method m) {
|
||||
AutomodelJavaUtil::isFromSource(m) and
|
||||
m instanceof ModelExclusions::ModelApi and
|
||||
AutomodelJavaUtil::isOverridable(m)
|
||||
}
|
||||
|
||||
/**
|
||||
* A framework mode endpoint.
|
||||
*/
|
||||
abstract class FrameworkModeEndpoint extends TFrameworkModeEndpoint {
|
||||
/**
|
||||
* Gets the input (if any) for this endpoint, eg.: `Argument[0]`.
|
||||
*
|
||||
* For endpoints that are source candidates, this will be `none()`.
|
||||
*/
|
||||
abstract string getMaDInput();
|
||||
|
||||
/**
|
||||
* Gets the output (if any) for this endpoint, eg.: `ReturnValue`.
|
||||
*
|
||||
* For endpoints that are sink candidates, this will be `none()`.
|
||||
*/
|
||||
abstract string getMaDOutput();
|
||||
|
||||
/**
|
||||
* Returns the name of the parameter of the endpoint.
|
||||
*/
|
||||
abstract string getParamName();
|
||||
|
||||
/**
|
||||
* Returns the callable that contains the endpoint.
|
||||
*/
|
||||
abstract Callable getCallable();
|
||||
|
||||
abstract Top asTop();
|
||||
|
||||
abstract string getExtensibleType();
|
||||
|
||||
string toString() { result = this.asTop().toString() }
|
||||
|
||||
Location getLocation() { result = this.asTop().getLocation() }
|
||||
}
|
||||
|
||||
class ExplicitParameterEndpoint extends FrameworkModeEndpoint, TExplicitParameter {
|
||||
Parameter param;
|
||||
|
||||
ExplicitParameterEndpoint() { this = TExplicitParameter(param) and param.fromSource() }
|
||||
|
||||
override string getMaDInput() { result = "Argument[" + param.getPosition() + "]" }
|
||||
|
||||
override string getMaDOutput() { none() }
|
||||
|
||||
override string getParamName() { result = param.getName() }
|
||||
|
||||
override Callable getCallable() { result = param.getCallable() }
|
||||
|
||||
override Top asTop() { result = param }
|
||||
|
||||
override string getExtensibleType() { result = "sinkModel" }
|
||||
}
|
||||
|
||||
class QualifierEndpoint extends FrameworkModeEndpoint, TQualifier {
|
||||
Callable callable;
|
||||
|
||||
QualifierEndpoint() {
|
||||
this = TQualifier(callable) and not callable.isStatic() and callable.fromSource()
|
||||
}
|
||||
|
||||
override string getMaDInput() { result = "Argument[this]" }
|
||||
|
||||
override string getMaDOutput() { none() }
|
||||
|
||||
override string getParamName() { result = "this" }
|
||||
|
||||
override Callable getCallable() { result = callable }
|
||||
|
||||
override Top asTop() { result = callable }
|
||||
|
||||
override string getExtensibleType() { result = "sinkModel" }
|
||||
}
|
||||
|
||||
class ReturnValue extends FrameworkModeEndpoint, TReturnValue {
|
||||
Callable callable;
|
||||
|
||||
ReturnValue() { this = TReturnValue(callable) and callable.fromSource() }
|
||||
|
||||
override string getMaDInput() { none() }
|
||||
|
||||
override string getMaDOutput() { result = "ReturnValue" }
|
||||
|
||||
override string getParamName() { none() }
|
||||
|
||||
override Callable getCallable() { result = callable }
|
||||
|
||||
override Top asTop() { result = callable }
|
||||
|
||||
override string getExtensibleType() { result = "sourceModel" }
|
||||
}
|
||||
|
||||
class OverridableParameter extends FrameworkModeEndpoint, TOverridableParameter {
|
||||
Method method;
|
||||
Parameter param;
|
||||
|
||||
OverridableParameter() { this = TOverridableParameter(method, param) }
|
||||
|
||||
override string getMaDInput() { none() }
|
||||
|
||||
override string getMaDOutput() { result = "Parameter[" + param.getPosition() + "]" }
|
||||
|
||||
override string getParamName() { result = param.getName() }
|
||||
|
||||
override Callable getCallable() { result = method }
|
||||
|
||||
override Top asTop() { result = param }
|
||||
|
||||
override string getExtensibleType() { result = "sourceModel" }
|
||||
}
|
||||
|
||||
class OverridableQualifier extends FrameworkModeEndpoint, TOverridableQualifier {
|
||||
Method m;
|
||||
|
||||
OverridableQualifier() { this = TOverridableQualifier(m) }
|
||||
|
||||
override string getMaDInput() { none() }
|
||||
|
||||
override string getMaDOutput() { result = "Parameter[this]" }
|
||||
|
||||
override string getParamName() { result = "this" }
|
||||
|
||||
override Callable getCallable() { result = m }
|
||||
|
||||
override Top asTop() { result = m }
|
||||
|
||||
override string getExtensibleType() { result = "sourceModel" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A candidates implementation for framework mode.
|
||||
*
|
||||
* Some important notes:
|
||||
* - This mode is using parameters as endpoints.
|
||||
* - Sink- and neutral-information is being used from MaD models.
|
||||
* - When available, we use method- and class-java-docs as related locations.
|
||||
*/
|
||||
module FrameworkCandidatesImpl implements SharedCharacteristics::CandidateSig {
|
||||
// for documentation of the implementations here, see the QLDoc in the CandidateSig signature module.
|
||||
class Endpoint = FrameworkModeEndpoint;
|
||||
|
||||
class EndpointType = AutomodelEndpointTypes::EndpointType;
|
||||
|
||||
class SinkType = AutomodelEndpointTypes::SinkType;
|
||||
|
||||
class SourceType = AutomodelEndpointTypes::SourceType;
|
||||
|
||||
class RelatedLocation = Location::Top;
|
||||
|
||||
class RelatedLocationType = JavaRelatedLocationType;
|
||||
|
||||
// Sanitizers are currently not modeled in MaD. TODO: check if this has large negative impact.
|
||||
predicate isSanitizer(Endpoint e, EndpointType t) { none() }
|
||||
|
||||
RelatedLocation asLocation(Endpoint e) { result = e.asTop() }
|
||||
|
||||
predicate isKnownKind = AutomodelJavaUtil::isKnownKind/2;
|
||||
|
||||
predicate isSink(Endpoint e, string kind, string provenance) {
|
||||
exists(
|
||||
string package, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string input
|
||||
|
|
||||
sinkSpec(e, package, type, subtypes, name, signature, ext, input) and
|
||||
ExternalFlow::sinkModel(package, type, subtypes, name, [signature, ""], ext, input, kind,
|
||||
provenance, _)
|
||||
)
|
||||
}
|
||||
|
||||
predicate isSource(Endpoint e, string kind, string provenance) {
|
||||
exists(
|
||||
string package, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string output
|
||||
|
|
||||
sourceSpec(e, package, type, subtypes, name, signature, ext, output) and
|
||||
ExternalFlow::sourceModel(package, type, subtypes, name, [signature, ""], ext, output, kind,
|
||||
provenance, _)
|
||||
)
|
||||
}
|
||||
|
||||
predicate isNeutral(Endpoint e) {
|
||||
exists(string package, string type, string name, string signature, string endpointType |
|
||||
sinkSpec(e, package, type, _, name, signature, _, _) and
|
||||
endpointType = "sink"
|
||||
or
|
||||
sourceSpec(e, package, type, _, name, signature, _, _) and
|
||||
endpointType = "source"
|
||||
|
|
||||
ExternalFlow::neutralModel(package, type, name, [signature, ""], endpointType, _)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the endpoint concerns a callable with the given package, type, name and signature.
|
||||
*
|
||||
* If `subtypes` is `false`, only the exact callable is considered. If `true`, the callable and
|
||||
* all its overrides are considered.
|
||||
*/
|
||||
additional predicate endpointCallable(
|
||||
Endpoint e, string package, string type, boolean subtypes, string name, string signature
|
||||
) {
|
||||
exists(Callable c |
|
||||
c = e.getCallable() and subtypes in [true, false]
|
||||
or
|
||||
e.getCallable().(Method).getSourceDeclaration().overrides+(c) and subtypes = true
|
||||
|
|
||||
c.hasQualifiedName(package, type, name) and
|
||||
signature = ExternalFlow::paramsString(c)
|
||||
)
|
||||
}
|
||||
|
||||
additional predicate sinkSpec(
|
||||
Endpoint e, string package, string type, boolean subtypes, string name, string signature,
|
||||
string ext, string input
|
||||
) {
|
||||
endpointCallable(e, package, type, subtypes, name, signature) and
|
||||
ext = "" and
|
||||
input = e.getMaDInput()
|
||||
}
|
||||
|
||||
additional predicate sourceSpec(
|
||||
Endpoint e, string package, string type, boolean subtypes, string name, string signature,
|
||||
string ext, string output
|
||||
) {
|
||||
endpointCallable(e, package, type, subtypes, name, signature) and
|
||||
ext = "" and
|
||||
output = e.getMaDOutput()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the related location for the given endpoint.
|
||||
*
|
||||
* Related locations can be JavaDoc comments of the class or the method.
|
||||
*/
|
||||
RelatedLocation getRelatedLocation(Endpoint e, RelatedLocationType type) {
|
||||
type = MethodDoc() and
|
||||
result = e.getCallable().(Documentable).getJavadoc()
|
||||
or
|
||||
type = ClassDoc() and
|
||||
result = e.getCallable().getDeclaringType().(Documentable).getJavadoc()
|
||||
}
|
||||
}
|
||||
|
||||
module CharacteristicsImpl = SharedCharacteristics::SharedCharacteristics<FrameworkCandidatesImpl>;
|
||||
|
||||
class EndpointCharacteristic = CharacteristicsImpl::EndpointCharacteristic;
|
||||
|
||||
class Endpoint = FrameworkCandidatesImpl::Endpoint;
|
||||
|
||||
/*
|
||||
* Predicates that are used to surface prompt examples and candidates for classification with an ML model.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A MetadataExtractor that extracts metadata for framework mode.
|
||||
*/
|
||||
class FrameworkModeMetadataExtractor extends string {
|
||||
FrameworkModeMetadataExtractor() { this = "FrameworkModeMetadataExtractor" }
|
||||
|
||||
predicate hasMetadata(
|
||||
Endpoint e, string package, string type, string subtypes, string name, string signature,
|
||||
string input, string output, string parameterName, string alreadyAiModeled,
|
||||
string extensibleType
|
||||
) {
|
||||
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).getNestedName() 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)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the given `endpoint` should be considered a candidate for the `extensibleType`.
|
||||
*
|
||||
* The other parameters record various other properties of interest.
|
||||
*/
|
||||
predicate isCandidate(
|
||||
Endpoint endpoint, string package, string type, string subtypes, string name, string signature,
|
||||
string input, string output, string parameterName, string extensibleType, string alreadyAiModeled
|
||||
) {
|
||||
CharacteristicsImpl::isCandidate(endpoint, _) and
|
||||
not exists(CharacteristicsImpl::UninterestingToModelCharacteristic u |
|
||||
u.appliesToEndpoint(endpoint)
|
||||
) and
|
||||
any(FrameworkModeMetadataExtractor 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
|
||||
AutomodelJavaUtil::includeAutomodelCandidate(package, type, name, signature)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the given `endpoint` is a negative example for the `extensibleType`
|
||||
* because of the `characteristic`.
|
||||
*
|
||||
* The other parameters record various other properties of interest.
|
||||
*/
|
||||
predicate isNegativeExample(
|
||||
Endpoint endpoint, EndpointCharacteristic characteristic, float confidence, string package,
|
||||
string type, string subtypes, string name, string signature, string input, string output,
|
||||
string parameterName, string extensibleType
|
||||
) {
|
||||
characteristic.appliesToEndpoint(endpoint) and
|
||||
// the node is known not to be an endpoint of any appropriate type
|
||||
forall(AutomodelEndpointTypes::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
|
||||
any(FrameworkModeMetadataExtractor 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(CharacteristicsImpl::getAPotentialType(endpoint), true, confidence2)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the given `endpoint` is a positive example for the `endpointType`.
|
||||
*
|
||||
* The other parameters record various other properties of interest.
|
||||
*/
|
||||
predicate isPositiveExample(
|
||||
Endpoint endpoint, string endpointType, string package, string type, string subtypes, string name,
|
||||
string signature, string input, string output, string parameterName, string extensibleType
|
||||
) {
|
||||
any(FrameworkModeMetadataExtractor meta)
|
||||
.hasMetadata(endpoint, package, type, subtypes, name, signature, input, output, parameterName,
|
||||
_, extensibleType) and
|
||||
CharacteristicsImpl::isKnownAs(endpoint, endpointType, _)
|
||||
}
|
||||
|
||||
/*
|
||||
* EndpointCharacteristic classes that are specific to Automodel for Java.
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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
|
||||
* the dangerous/interesting thing, so we want the latter to be modeled as the sink.
|
||||
*
|
||||
* TODO: this might filter too much, it's possible that methods with more than one parameter contain interesting sinks
|
||||
*/
|
||||
private class UnexploitableIsCharacteristic extends CharacteristicsImpl::NeitherSourceNorSinkCharacteristic
|
||||
{
|
||||
UnexploitableIsCharacteristic() { this = "argument of is-style boolean method" }
|
||||
|
||||
override predicate appliesToEndpoint(Endpoint e) {
|
||||
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 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 UnexploitableExistsCharacteristic extends CharacteristicsImpl::NeitherSourceNorSinkCharacteristic
|
||||
{
|
||||
UnexploitableExistsCharacteristic() { this = "argument of 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 = "argument/result of exception-related method" }
|
||||
|
||||
override predicate appliesToEndpoint(Endpoint e) {
|
||||
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"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A characteristic that limits candidates to parameters of methods that are recognized as `ModelApi`, iow., APIs that
|
||||
* are considered worth modeling.
|
||||
*/
|
||||
private class NotAModelApi extends CharacteristicsImpl::UninterestingToModelCharacteristic {
|
||||
NotAModelApi() { this = "not a model API" }
|
||||
|
||||
override predicate appliesToEndpoint(Endpoint e) {
|
||||
not e.getCallable() instanceof ModelExclusions::ModelApi
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/**
|
||||
* Surfaces the endpoints that are not already known to be sinks, and are therefore used as candidates for
|
||||
* classification with an ML model.
|
||||
*
|
||||
* Note: This query does not actually classify the endpoints using the model.
|
||||
*
|
||||
* @name Automodel candidates (framework mode)
|
||||
* @description A query to extract automodel candidates in framework mode.
|
||||
* @kind problem
|
||||
* @problem.severity recommendation
|
||||
* @id java/ml/extract-automodel-framework-candidates
|
||||
* @tags internal extract automodel framework-mode candidates
|
||||
*/
|
||||
|
||||
private import AutomodelFrameworkModeCharacteristics
|
||||
private import AutomodelJavaUtil
|
||||
|
||||
from
|
||||
Endpoint endpoint, DollarAtString package, DollarAtString type, DollarAtString subtypes,
|
||||
DollarAtString name, DollarAtString signature, DollarAtString input, DollarAtString output,
|
||||
DollarAtString parameterName, DollarAtString alreadyAiModeled, DollarAtString extensibleType
|
||||
where
|
||||
isCandidate(endpoint, package, type, subtypes, name, signature, input, output, parameterName,
|
||||
extensibleType, alreadyAiModeled)
|
||||
select endpoint,
|
||||
"Related locations: $@, $@." + "\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@.", //
|
||||
CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, MethodDoc()), "MethodDoc", //
|
||||
CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, ClassDoc()), "ClassDoc", //
|
||||
package, "package", //
|
||||
type, "type", //
|
||||
subtypes, "subtypes", //
|
||||
name, "name", //
|
||||
signature, "signature", //
|
||||
input, "input", //
|
||||
output, "output", //
|
||||
parameterName, "parameterName", //
|
||||
alreadyAiModeled, "alreadyAiModeled", //
|
||||
extensibleType, "extensibleType"
|
||||
@@ -1,36 +0,0 @@
|
||||
/**
|
||||
* Surfaces endpoints that are non-sinks with high confidence, for use as negative examples in the prompt.
|
||||
*
|
||||
* @name Negative examples (framework mode)
|
||||
* @kind problem
|
||||
* @problem.severity recommendation
|
||||
* @id java/ml/extract-automodel-framework-negative-examples
|
||||
* @tags internal extract automodel framework-mode negative examples
|
||||
*/
|
||||
|
||||
private import AutomodelFrameworkModeCharacteristics
|
||||
private import AutomodelEndpointTypes
|
||||
private import AutomodelJavaUtil
|
||||
|
||||
from
|
||||
Endpoint endpoint, EndpointCharacteristic characteristic, float confidence,
|
||||
DollarAtString package, DollarAtString type, DollarAtString subtypes, DollarAtString name,
|
||||
DollarAtString signature, DollarAtString input, DollarAtString output,
|
||||
DollarAtString parameterName, DollarAtString extensibleType
|
||||
where
|
||||
isNegativeExample(endpoint, characteristic, confidence, package, type, subtypes, name, signature,
|
||||
input, output, parameterName, extensibleType)
|
||||
select endpoint,
|
||||
characteristic + "\nrelated locations: $@, $@." +
|
||||
"\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@.", //
|
||||
CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, MethodDoc()), "MethodDoc", //
|
||||
CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, ClassDoc()), "ClassDoc", //
|
||||
package, "package", //
|
||||
type, "type", //
|
||||
subtypes, "subtypes", //
|
||||
name, "name", //
|
||||
signature, "signature", //
|
||||
input, "input", //
|
||||
output, "output", //
|
||||
parameterName, "parameterName", //
|
||||
extensibleType, "extensibleType"
|
||||
@@ -1,34 +0,0 @@
|
||||
/**
|
||||
* Surfaces endpoints that are sinks with high confidence, for use as positive examples in the prompt.
|
||||
*
|
||||
* @name Positive examples (framework mode)
|
||||
* @kind problem
|
||||
* @problem.severity recommendation
|
||||
* @id java/ml/extract-automodel-framework-positive-examples
|
||||
* @tags internal extract automodel framework-mode positive examples
|
||||
*/
|
||||
|
||||
private import AutomodelFrameworkModeCharacteristics
|
||||
private import AutomodelEndpointTypes
|
||||
private import AutomodelJavaUtil
|
||||
|
||||
from
|
||||
Endpoint endpoint, EndpointType endpointType, DollarAtString package, DollarAtString type,
|
||||
DollarAtString subtypes, DollarAtString name, DollarAtString signature, DollarAtString input,
|
||||
DollarAtString output, DollarAtString parameterName, DollarAtString extensibleType
|
||||
where
|
||||
isPositiveExample(endpoint, endpointType, package, type, subtypes, name, signature, input, output,
|
||||
parameterName, extensibleType)
|
||||
select endpoint,
|
||||
endpointType + "\nrelated locations: $@, $@." + "\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@.", //
|
||||
CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, MethodDoc()), "MethodDoc", //
|
||||
CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, ClassDoc()), "ClassDoc", //
|
||||
package, "package", //
|
||||
type, "type", //
|
||||
subtypes, "subtypes", //
|
||||
name, "name", //
|
||||
signature, "signature", //
|
||||
input, "input", //
|
||||
output, "output", //
|
||||
parameterName, "parameterName", //
|
||||
extensibleType, "extensibleType"
|
||||
@@ -1,111 +0,0 @@
|
||||
private import java
|
||||
private import AutomodelEndpointTypes as AutomodelEndpointTypes
|
||||
|
||||
/**
|
||||
* A helper class to represent a string value that can be returned by a query using $@ notation.
|
||||
*
|
||||
* It extends `string`, but adds a mock `hasLocationInfo` method that returns the string itself as the file name.
|
||||
*
|
||||
* Use this, when you want to return a string value from a query using $@ notation - the string value
|
||||
* will be included in the sarif file.
|
||||
*
|
||||
*
|
||||
* Background information on `hasLocationInfo`:
|
||||
* https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/#providing-location-information
|
||||
*/
|
||||
class DollarAtString extends string {
|
||||
bindingset[this]
|
||||
DollarAtString() { any() }
|
||||
|
||||
bindingset[this]
|
||||
predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
|
||||
path = this and sl = 1 and sc = 1 and el = 1 and ec = 1
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds for all combinations of MaD kinds (`kind`) and their human readable
|
||||
* descriptions.
|
||||
*/
|
||||
predicate isKnownKind(string kind, AutomodelEndpointTypes::EndpointType type) {
|
||||
kind = type.getKind()
|
||||
}
|
||||
|
||||
/**
|
||||
* By convention, the subtypes property of the MaD declaration should only be
|
||||
* true when there _can_ exist any subtypes with a different implementation.
|
||||
*
|
||||
* It would technically be ok to always use the value 'true', but this would
|
||||
* break convention.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
boolean considerSubtypes(Callable callable) {
|
||||
if
|
||||
callable.isStatic() or
|
||||
callable.getDeclaringType().isStatic() or
|
||||
callable.isFinal() or
|
||||
callable.getDeclaringType().isFinal()
|
||||
then result = false
|
||||
else result = true
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the given package, type, name and signature is a candidate for automodeling.
|
||||
*
|
||||
* This predicate is extensible, so that different endpoints can be selected at runtime.
|
||||
*/
|
||||
extensible predicate automodelCandidateFilter(
|
||||
string package, string type, string name, string signature
|
||||
);
|
||||
|
||||
/**
|
||||
* Holds if the given package, type, name and signature is a candidate for automodeling.
|
||||
*
|
||||
* This relies on an extensible predicate, and if that is not supplied then
|
||||
* all endpoints are considered candidates.
|
||||
*/
|
||||
bindingset[package, type, name, signature]
|
||||
predicate includeAutomodelCandidate(string package, string type, string name, string signature) {
|
||||
not automodelCandidateFilter(_, _, _, _) or
|
||||
automodelCandidateFilter(package, type, name, signature)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the given program element corresponds to a piece of source code,
|
||||
* that is, it is not compiler-generated.
|
||||
*
|
||||
* Note: This is a stricter check than `Element::fromSource`, which simply
|
||||
* checks whether the element is in a source file as opposed to a JAR file.
|
||||
* There can be compiler-generated elements in source files (especially for
|
||||
* Kotlin), which we also want to exclude.
|
||||
*/
|
||||
predicate isFromSource(Element e) {
|
||||
// from a source file (not a JAR)
|
||||
e.fromSource() and
|
||||
// not explicitly marked as compiler-generated
|
||||
not e.isCompilerGenerated() and
|
||||
// does not have a dummy location
|
||||
not e.hasLocationInfo(_, 0, 0, 0, 0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint cannot flow through the given type (because it is a numeric
|
||||
* type or some other type with a fixed set of values).
|
||||
*/
|
||||
predicate isUnexploitableType(Type tp) {
|
||||
tp instanceof PrimitiveType or
|
||||
tp instanceof BoxedType or
|
||||
tp instanceof NumberType or
|
||||
tp instanceof VoidType
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the given method can be overridden, that is, it is not final,
|
||||
* static, or private.
|
||||
*/
|
||||
predicate isOverridable(Method m) {
|
||||
not m.getDeclaringType().isFinal() and
|
||||
not m.isFinal() and
|
||||
not m.isStatic() and
|
||||
not m.isPrivate()
|
||||
}
|
||||
@@ -1,412 +0,0 @@
|
||||
float maximalConfidence() { result = 1.0 }
|
||||
|
||||
float highConfidence() { result = 0.9 }
|
||||
|
||||
float mediumConfidence() { result = 0.6 }
|
||||
|
||||
/**
|
||||
* A specification of how to instantiate the shared characteristics for a given candidate class.
|
||||
*
|
||||
* The `CandidateSig` implementation specifies a type to use for Endpoints (eg., `ParameterNode`), as well as a type
|
||||
* to label endpoint classes (the `EndpointType`). One of the endpoint classes needs to be a 'negative' class, meaning
|
||||
* "not any of the other known endpoint types".
|
||||
*/
|
||||
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 {
|
||||
/**
|
||||
* Gets the kind of this endpoint, either "sourceModel" or "sinkModel".
|
||||
*/
|
||||
string getExtensibleType();
|
||||
|
||||
/**
|
||||
* Gets a string representation of this endpoint.
|
||||
*/
|
||||
string toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* A related location for an endpoint. This will typically be bound to the supertype of all AST nodes (eg., `Top`).
|
||||
*/
|
||||
class RelatedLocation;
|
||||
|
||||
/**
|
||||
* A label for a related location.
|
||||
*
|
||||
* Eg., method-doc, class-doc, etc.
|
||||
*/
|
||||
class RelatedLocationType;
|
||||
|
||||
/**
|
||||
* An endpoint type considered by this specification.
|
||||
*/
|
||||
class EndpointType extends string;
|
||||
|
||||
/**
|
||||
* A sink endpoint type considered by this specification.
|
||||
*/
|
||||
class SinkType extends EndpointType;
|
||||
|
||||
/**
|
||||
* A source endpoint type considered by this specification.
|
||||
*/
|
||||
class SourceType extends EndpointType;
|
||||
|
||||
/**
|
||||
* Gets the endpoint as a location.
|
||||
*
|
||||
* This is a utility function to convert an endpoint to its corresponding location.
|
||||
*/
|
||||
RelatedLocation asLocation(Endpoint e);
|
||||
|
||||
/**
|
||||
* Defines what MaD kinds are known, and what endpoint type they correspond to.
|
||||
*/
|
||||
predicate isKnownKind(string kind, EndpointType type);
|
||||
|
||||
/**
|
||||
* Holds if `e` is a flow sanitizer, and has type `t`.
|
||||
*/
|
||||
predicate isSanitizer(Endpoint e, EndpointType t);
|
||||
|
||||
/**
|
||||
* Holds if `e` is a sink with the label `kind`, and provenance `provenance`.
|
||||
*/
|
||||
predicate isSink(Endpoint e, string kind, string provenance);
|
||||
|
||||
/**
|
||||
* Holds if `e` is a source with the label `kind`, and provenance `provenance`.
|
||||
*/
|
||||
predicate isSource(Endpoint e, string kind, string provenance);
|
||||
|
||||
/**
|
||||
* Holds if `e` is not a source or sink of any kind.
|
||||
*/
|
||||
predicate isNeutral(Endpoint e);
|
||||
|
||||
/**
|
||||
* Gets a related location.
|
||||
*
|
||||
* A related location is a source code location that may hold extra information about an endpoint that can be useful
|
||||
* to the machine learning model.
|
||||
*
|
||||
* For example, a related location for a method call may be the documentation comment of a method.
|
||||
*/
|
||||
RelatedLocation getRelatedLocation(Endpoint e, RelatedLocationType name);
|
||||
}
|
||||
|
||||
/**
|
||||
* A set of shared characteristics for a given candidate class.
|
||||
*
|
||||
* This module is language-agnostic, although the `CandidateSig` module will be language-specific.
|
||||
*
|
||||
* The language specific implementation can also further extend the behavior of this module by adding additional
|
||||
* implementations of endpoint characteristics exported by this module.
|
||||
*/
|
||||
module SharedCharacteristics<CandidateSig Candidate> {
|
||||
predicate isSink = Candidate::isSink/3;
|
||||
|
||||
predicate isNeutral = Candidate::isNeutral/1;
|
||||
|
||||
predicate isModeled(Candidate::Endpoint e, string kind, string extensibleKind, string provenance) {
|
||||
Candidate::isSink(e, kind, provenance) and extensibleKind = "sinkModel"
|
||||
or
|
||||
Candidate::isSource(e, kind, provenance) and extensibleKind = "sourceModel"
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `endpoint` is modeled as `endpointType`.
|
||||
*/
|
||||
predicate isKnownAs(
|
||||
Candidate::Endpoint endpoint, Candidate::EndpointType endpointType,
|
||||
EndpointCharacteristic characteristic
|
||||
) {
|
||||
// If the list of characteristics includes positive indicators with maximal confidence for this class, then it's a
|
||||
// known sink for the class.
|
||||
characteristic.appliesToEndpoint(endpoint) and
|
||||
characteristic.hasImplications(endpointType, true, maximalConfidence())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a potential type of this endpoint to make sure that sources are
|
||||
* associated with source types and sinks with sink types.
|
||||
*/
|
||||
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))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the related location of `e` with name `name`, if it exists.
|
||||
* Otherwise, gets the candidate itself.
|
||||
*/
|
||||
Candidate::RelatedLocation getRelatedLocationOrCandidate(
|
||||
Candidate::Endpoint e, Candidate::RelatedLocationType type
|
||||
) {
|
||||
if exists(Candidate::getRelatedLocation(e, type))
|
||||
then result = Candidate::getRelatedLocation(e, type)
|
||||
else result = Candidate::asLocation(e)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a characteristics that disbar `endpoint` from being a candidate for `endpointType`
|
||||
* with at least medium confidence.
|
||||
*/
|
||||
EndpointCharacteristic getAnExcludingCharacteristic(
|
||||
Candidate::Endpoint endpoint, Candidate::EndpointType endpointType
|
||||
) {
|
||||
result.appliesToEndpoint(endpoint) and
|
||||
exists(float confidence |
|
||||
confidence >= mediumConfidence() and
|
||||
result.hasImplications(endpointType, false, confidence)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A set of characteristics that a particular endpoint might have. This set of characteristics is used to make decisions
|
||||
* about whether to include the endpoint in the training set and with what kind, as well as whether to score the
|
||||
* endpoint at inference time.
|
||||
*/
|
||||
abstract class EndpointCharacteristic extends string {
|
||||
/**
|
||||
* Holds for the string that is the name of the characteristic. This should describe some property of an endpoint
|
||||
* that is meaningful for determining whether it's a sink, and if so, of which sink type.
|
||||
*/
|
||||
bindingset[this]
|
||||
EndpointCharacteristic() { any() }
|
||||
|
||||
/**
|
||||
* Holds for endpoints that have this characteristic.
|
||||
*/
|
||||
abstract predicate appliesToEndpoint(Candidate::Endpoint n);
|
||||
|
||||
/**
|
||||
* This predicate describes what the characteristic tells us about an endpoint.
|
||||
*
|
||||
* Params:
|
||||
* endpointType: The sink/source type.
|
||||
* isPositiveIndicator: If true, this characteristic indicates that this endpoint _is_ a member of the class; if
|
||||
* false, it indicates that it _isn't_ a member of the class.
|
||||
* confidence: A float in [0, 1], which tells us how strong an indicator this characteristic is for the endpoint
|
||||
* belonging / not belonging to the given class. A confidence near zero means this characteristic is a very weak
|
||||
* indicator of whether or not the endpoint belongs to the class. A confidence of 1 means that all endpoints with
|
||||
* this characteristic definitively do/don't belong to the class.
|
||||
*/
|
||||
abstract predicate hasImplications(
|
||||
Candidate::EndpointType endpointType, boolean isPositiveIndicator, float confidence
|
||||
);
|
||||
|
||||
/** Indicators with confidence at or above this threshold are considered to be high-confidence indicators. */
|
||||
final float getHighConfidenceThreshold() { result = 0.8 }
|
||||
}
|
||||
|
||||
/**
|
||||
* A high-confidence characteristic that indicates that an endpoint is a sink of a specified type. These endpoints can
|
||||
* be used as positive samples for training or for a few-shot prompt.
|
||||
*/
|
||||
abstract class SinkCharacteristic extends EndpointCharacteristic {
|
||||
bindingset[this]
|
||||
SinkCharacteristic() { any() }
|
||||
|
||||
abstract Candidate::EndpointType getSinkType();
|
||||
|
||||
final override predicate hasImplications(
|
||||
Candidate::EndpointType endpointType, boolean isPositiveIndicator, float confidence
|
||||
) {
|
||||
endpointType = this.getSinkType() and
|
||||
isPositiveIndicator = true and
|
||||
confidence = maximalConfidence()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A high-confidence characteristic that indicates that an endpoint is a source of a specified type. These endpoints can
|
||||
* be used as positive samples for training or for a few-shot prompt.
|
||||
*/
|
||||
abstract class SourceCharacteristic extends EndpointCharacteristic {
|
||||
bindingset[this]
|
||||
SourceCharacteristic() { any() }
|
||||
|
||||
abstract Candidate::EndpointType getSourceType();
|
||||
|
||||
final override predicate hasImplications(
|
||||
Candidate::EndpointType endpointType, boolean isPositiveIndicator, float confidence
|
||||
) {
|
||||
endpointType = this.getSourceType() and
|
||||
isPositiveIndicator = true and
|
||||
confidence = maximalConfidence()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A high-confidence characteristic that indicates that an endpoint is not a sink of any type. These endpoints can be
|
||||
* used as negative samples for training or for a few-shot prompt.
|
||||
*/
|
||||
abstract class NotASinkCharacteristic extends EndpointCharacteristic {
|
||||
bindingset[this]
|
||||
NotASinkCharacteristic() { any() }
|
||||
|
||||
override predicate hasImplications(
|
||||
Candidate::EndpointType endpointType, boolean isPositiveIndicator, float confidence
|
||||
) {
|
||||
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
|
||||
* not, however, be used as negative samples for training or for a few-shot prompt, because they may include a small
|
||||
* number of sinks.
|
||||
*/
|
||||
abstract class LikelyNotASinkCharacteristic extends EndpointCharacteristic {
|
||||
bindingset[this]
|
||||
LikelyNotASinkCharacteristic() { any() }
|
||||
|
||||
override predicate hasImplications(
|
||||
Candidate::EndpointType endpointType, boolean isPositiveIndicator, float confidence
|
||||
) {
|
||||
endpointType instanceof Candidate::SinkType and
|
||||
isPositiveIndicator = false and
|
||||
confidence = mediumConfidence()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A characteristic that indicates not necessarily that an endpoint is not a sink, but rather that it is not a sink
|
||||
* that's interesting to model in the standard Java libraries. These filters should be removed when extracting sink
|
||||
* candidates within a user's codebase for customized modeling.
|
||||
*
|
||||
* These endpoints should not be used as negative samples for training or for a few-shot prompt, because they are not
|
||||
* necessarily non-sinks.
|
||||
*/
|
||||
abstract class UninterestingToModelCharacteristic extends EndpointCharacteristic {
|
||||
bindingset[this]
|
||||
UninterestingToModelCharacteristic() { any() }
|
||||
|
||||
override predicate hasImplications(
|
||||
Candidate::EndpointType endpointType, boolean isPositiveIndicator, float confidence
|
||||
) {
|
||||
endpointType instanceof Candidate::SinkType and
|
||||
isPositiveIndicator = false and
|
||||
confidence = mediumConfidence()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains default implementations that are derived solely from the `CandidateSig` implementation.
|
||||
*/
|
||||
private module DefaultCharacteristicImplementations {
|
||||
/**
|
||||
* Endpoints identified as sinks by the `CandidateSig` implementation are sinks with maximal confidence.
|
||||
*/
|
||||
private class KnownSinkCharacteristic extends SinkCharacteristic {
|
||||
string madKind;
|
||||
Candidate::EndpointType endpointType;
|
||||
string provenance;
|
||||
|
||||
KnownSinkCharacteristic() {
|
||||
Candidate::isKnownKind(madKind, endpointType) and
|
||||
// bind "this" to a unique string differing from that of the SinkType classes
|
||||
this = madKind + "_" + provenance + "_characteristic" and
|
||||
Candidate::isSink(_, madKind, provenance)
|
||||
}
|
||||
|
||||
override predicate appliesToEndpoint(Candidate::Endpoint e) {
|
||||
Candidate::isSink(e, madKind, provenance)
|
||||
}
|
||||
|
||||
override Candidate::EndpointType getSinkType() { result = endpointType }
|
||||
}
|
||||
|
||||
private class KnownSourceCharacteristic extends SourceCharacteristic {
|
||||
string madKind;
|
||||
Candidate::EndpointType endpointType;
|
||||
string provenance;
|
||||
|
||||
KnownSourceCharacteristic() {
|
||||
Candidate::isKnownKind(madKind, endpointType) and
|
||||
// bind "this" to a unique string differing from that of the SinkType classes
|
||||
this = madKind + "_" + provenance + "_characteristic" and
|
||||
Candidate::isSource(_, madKind, provenance)
|
||||
}
|
||||
|
||||
override predicate appliesToEndpoint(Candidate::Endpoint e) {
|
||||
Candidate::isSource(e, madKind, provenance)
|
||||
}
|
||||
|
||||
override Candidate::EndpointType getSourceType() { result = endpointType }
|
||||
}
|
||||
|
||||
/**
|
||||
* A negative characteristic that indicates that an endpoint was manually modeled as a neutral model.
|
||||
*/
|
||||
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 a sanitizer, and thus not a source.
|
||||
*/
|
||||
private class IsSanitizerCharacteristic extends NotASourceCharacteristic {
|
||||
IsSanitizerCharacteristic() { this = "known sanitizer" }
|
||||
|
||||
override predicate appliesToEndpoint(Candidate::Endpoint e) { Candidate::isSanitizer(e, _) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
/**
|
||||
* This file contains query predicates for use when gathering metrics at scale using Multi Repo
|
||||
* Variant Analysis.
|
||||
*/
|
||||
|
||||
private import java
|
||||
private import AutomodelAlertSinkUtil
|
||||
|
||||
/**
|
||||
* Holds if `alertCount` is the number of alerts for the query with ID `queryId` for which the
|
||||
* sinks correspond to the given `ai-generated` sink model.
|
||||
*/
|
||||
query predicate sinkModelCountPerQuery(
|
||||
string queryId, int alertCount, string package, string type, boolean subtypes, string name,
|
||||
string signature, string input, string ext, string kind, string provenance
|
||||
) {
|
||||
exists(SinkModel s |
|
||||
sinkModelTallyPerQuery(queryId, alertCount, s) and
|
||||
s.getProvenance() = "ai-generated" and
|
||||
s.getPackage() = package and
|
||||
s.getType() = type and
|
||||
s.getSubtypes() = subtypes and
|
||||
s.getName() = name and
|
||||
s.getSignature() = signature and
|
||||
s.getInput() = input and
|
||||
s.getExt() = ext and
|
||||
s.getKind() = kind and
|
||||
s.getProvenance() = provenance
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instanceCount` is the number of instances corresponding to the given `ai-generated`
|
||||
* sink model (as identified by the `package`, `name`, `input`, etc.).
|
||||
*/
|
||||
query predicate instanceCount(
|
||||
int instanceCount, string package, string type, boolean subtypes, string name, string signature,
|
||||
string input, string ext, string kind, string provenance
|
||||
) {
|
||||
exists(SinkModel s |
|
||||
instanceCount = s.getInstanceCount() and
|
||||
instanceCount > 0 and
|
||||
s.getProvenance() = "ai-generated" and
|
||||
s.getPackage() = package and
|
||||
s.getType() = type and
|
||||
s.getSubtypes() = subtypes and
|
||||
s.getName() = name and
|
||||
s.getSignature() = signature and
|
||||
s.getInput() = input and
|
||||
s.getExt() = ext and
|
||||
s.getKind() = kind and
|
||||
s.getProvenance() = provenance
|
||||
)
|
||||
}
|
||||
|
||||
// MRVA requires a select clause, so we repurpose it to tell us which query predicates had results.
|
||||
from string hadResults
|
||||
where
|
||||
sinkModelCountPerQuery(_, _, _, _, _, _, _, _, _, _, _) and hadResults = "sinkModelCountPerQuery"
|
||||
or
|
||||
instanceCount(_, _, _, _, _, _, _, _, _, _) and hadResults = "instanceCount"
|
||||
select hadResults
|
||||
@@ -1,2 +0,0 @@
|
||||
---
|
||||
lastReleaseVersion: 1.0.11
|
||||
@@ -1,10 +0,0 @@
|
||||
name: codeql/java-automodel-queries
|
||||
version: 1.0.12-dev
|
||||
groups:
|
||||
- java
|
||||
- automodel
|
||||
dependencies:
|
||||
codeql/java-all: ${workspace}
|
||||
dataExtensions:
|
||||
- AutomodelCandidateFilter.yml
|
||||
warnOnImplicitThis: true
|
||||
Reference in New Issue
Block a user