mirror of
https://github.com/github/codeql.git
synced 2026-05-02 04:05:14 +02:00
Merge branch 'main' into merge-package-type-columns
This commit is contained in:
@@ -2,8 +2,8 @@
|
||||
|
||||
This directory contains the source code of the JavaScript extractor. The extractor depends on various libraries that are not currently bundled with the source code, so at present it cannot be built in isolation.
|
||||
|
||||
The extractor consists of a parser for the latest version of ECMAScript, including a few proposed and historic extensions (see `src/com/semmle/jcorn`), classes for representing JavaScript and TypeScript ASTs (`src/com/semmle/js/ast` and `src/com/semmle/ts/ast`), and various other bits of functionality. Historically, the main entry point of the JavaScript extractor has been `com.semmle.js.extractor.Main`. However, this class is slowly being phased out in favour of `com.semmle.js.extractor.AutoBuild`, which is the entry point used by LGTM.
|
||||
The extractor consists of a parser for the latest version of ECMAScript, including a few proposed and historic extensions (see `src/com/semmle/jcorn`), classes for representing JavaScript and TypeScript ASTs (`src/com/semmle/js/ast` and `src/com/semmle/ts/ast`), and various other bits of functionality. Historically, the main entry point of the JavaScript extractor has been `com.semmle.js.extractor.Main`. However, this class is slowly being phased out in favour of `com.semmle.js.extractor.AutoBuild`, which is the entry point used by CodeQL.
|
||||
|
||||
## License
|
||||
|
||||
Like the LGTM queries, the JavaScript extractor is licensed under [Apache License 2.0](LICENSE) by [GitHub](https://github.com). Some code is derived from other projects, whose licenses are noted in other `LICENSE-*.md` files in this folder.
|
||||
Like the CodeQL queries, the JavaScript extractor is licensed under [Apache License 2.0](LICENSE) by [GitHub](https://github.com). Some code is derived from other projects, whose licenses are noted in other `LICENSE-*.md` files in this folder.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/**
|
||||
* For internal use only.
|
||||
*
|
||||
* Configures boosting for adaptive threat modeling (ATM).
|
||||
@@ -6,7 +6,8 @@
|
||||
|
||||
private import javascript as JS
|
||||
import EndpointTypes
|
||||
import EndpointCharacteristics
|
||||
import EndpointCharacteristics as EndpointCharacteristics
|
||||
import AdaptiveThreatModeling::ATM::ResultsInfo as AtmResultsInfo
|
||||
|
||||
/**
|
||||
* EXPERIMENTAL. This API may change in the future.
|
||||
@@ -29,10 +30,23 @@ import EndpointCharacteristics
|
||||
* `isAdditionalFlowStep` with a more generalised definition of additional edges. See
|
||||
* `NosqlInjectionATM.qll` for an example of doing this.
|
||||
*/
|
||||
abstract class AtmConfig extends string {
|
||||
abstract class AtmConfig extends JS::TaintTracking::Configuration {
|
||||
bindingset[this]
|
||||
AtmConfig() { any() }
|
||||
|
||||
/**
|
||||
* Holds if `source` is a relevant taint source. When sources are not boosted, `isSource` is equivalent to
|
||||
* `isKnownSource` (i.e there are no "effective" sources to be classified by an ML model).
|
||||
*/
|
||||
override predicate isSource(JS::DataFlow::Node source) { this.isKnownSource(source) }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a known taint sink or an "effective" sink (a candidate to be classified by an ML model).
|
||||
*/
|
||||
override predicate isSink(JS::DataFlow::Node sink) {
|
||||
this.isKnownSink(sink) or this.isEffectiveSink(sink)
|
||||
}
|
||||
|
||||
/**
|
||||
* EXPERIMENTAL. This API may change in the future.
|
||||
*
|
||||
@@ -48,10 +62,10 @@ abstract class AtmConfig extends string {
|
||||
final predicate isKnownSink(JS::DataFlow::Node sink) {
|
||||
// If the list of characteristics includes positive indicators with maximal confidence for this class, then it's a
|
||||
// known sink for the class.
|
||||
exists(EndpointCharacteristic characteristic |
|
||||
characteristic.getEndpoints(sink) and
|
||||
exists(EndpointCharacteristics::EndpointCharacteristic characteristic |
|
||||
characteristic.appliesToEndpoint(sink) and
|
||||
characteristic
|
||||
.getImplications(this.getASinkEndpointType(), true, characteristic.maximalConfidence())
|
||||
.hasImplications(this.getASinkEndpointType(), true, characteristic.maximalConfidence())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -69,7 +83,38 @@ abstract class AtmConfig extends string {
|
||||
* Holds if the candidate sink `candidateSink` predicted by the machine learning model should be
|
||||
* an effective sink, i.e. one considered as a possible sink of flow in the boosted query.
|
||||
*/
|
||||
predicate isEffectiveSink(JS::DataFlow::Node candidateSink) { none() }
|
||||
predicate isEffectiveSink(JS::DataFlow::Node candidateSink) {
|
||||
not exists(this.getAReasonSinkExcluded(candidateSink))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of characteristics that cause `candidateSink` to be excluded as an effective sink.
|
||||
*/
|
||||
final EndpointCharacteristics::EndpointCharacteristic getAReasonSinkExcluded(
|
||||
JS::DataFlow::Node candidateSink
|
||||
) {
|
||||
// An endpoint is an effective sink (sink candidate) if none of its characteristics give much indication whether or
|
||||
// not it is a sink. Historically, we used endpoint filters, and scored endpoints that are filtered out neither by
|
||||
// a standard endpoint filter nor by an endpoint filter specific to this sink type. To replicate this behavior, we
|
||||
// have given the endpoint filter characteristics medium confidence, and we exclude endpoints that have a
|
||||
// medium-confidence characteristic that indicates that they are not sinks, either in general or for this sink type.
|
||||
exists(EndpointCharacteristics::EndpointCharacteristic filter, float confidence |
|
||||
filter.appliesToEndpoint(candidateSink) and
|
||||
confidence >= filter.mediumConfidence() and
|
||||
// TODO: Experiment with excluding all endpoints that have a medium- or high-confidence characteristic that
|
||||
// implies they're not sinks, rather than using only medium-confidence characteristics, by deleting the following
|
||||
// line.
|
||||
confidence < filter.highConfidence() and
|
||||
(
|
||||
// Exclude endpoints that have a characteristic that implies they're not sinks for _any_ sink type.
|
||||
filter.hasImplications(any(NegativeType negative), true, confidence)
|
||||
or
|
||||
// Exclude endpoints that have a characteristic that implies they're not sinks for _this particular_ sink type.
|
||||
filter.hasImplications(this.getASinkEndpointType(), false, confidence)
|
||||
) and
|
||||
result = filter
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* EXPERIMENTAL. This API may change in the future.
|
||||
@@ -85,7 +130,7 @@ abstract class AtmConfig extends string {
|
||||
* Get an endpoint type for the sinks of this query. A query may have multiple applicable
|
||||
* endpoint types for its sinks.
|
||||
*/
|
||||
EndpointType getASinkEndpointType() { none() }
|
||||
abstract EndpointType getASinkEndpointType();
|
||||
|
||||
/**
|
||||
* EXPERIMENTAL. This API may change in the future.
|
||||
@@ -96,6 +141,19 @@ abstract class AtmConfig extends string {
|
||||
* A cut-off value of 1 produces all alerts including those that are likely false-positives.
|
||||
*/
|
||||
float getScoreCutoff() { result = 0.0 }
|
||||
|
||||
/**
|
||||
* Holds if there's an ATM alert (a flow path from `source` to `sink` with ML-determined likelihood `score`) according
|
||||
* to this ML-boosted configuration, whereas the unboosted base query does not contain this source and sink
|
||||
* combination.
|
||||
*/
|
||||
predicate hasBoostedFlowPath(
|
||||
JS::DataFlow::PathNode source, JS::DataFlow::PathNode sink, float score
|
||||
) {
|
||||
this.hasFlowPath(source, sink) and
|
||||
not AtmResultsInfo::isFlowLikelyInBaseQuery(source.getNode(), sink.getNode()) and
|
||||
score = AtmResultsInfo::getScoreForFlow(source.getNode(), sink.getNode())
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for AtmConfig */
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/**
|
||||
* For internal use only.
|
||||
*
|
||||
* Provides information about the results of boosted queries for use in adaptive threat modeling (ATM).
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/**
|
||||
* For internal use only.
|
||||
*
|
||||
* Provides shared scoring functionality for use in adaptive threat modeling (ATM).
|
||||
|
||||
@@ -1,225 +0,0 @@
|
||||
/*
|
||||
* For internal use only.
|
||||
*
|
||||
* Provides predicates that expose the knowledge of models
|
||||
* in the core CodeQL JavaScript libraries.
|
||||
*/
|
||||
|
||||
private import javascript
|
||||
private import semmle.javascript.security.dataflow.XxeCustomizations
|
||||
private import semmle.javascript.security.dataflow.RemotePropertyInjectionCustomizations
|
||||
private import semmle.javascript.security.dataflow.TypeConfusionThroughParameterTamperingCustomizations
|
||||
private import semmle.javascript.security.dataflow.ZipSlipCustomizations
|
||||
private import semmle.javascript.security.dataflow.TaintedPathCustomizations
|
||||
private import semmle.javascript.security.dataflow.CleartextLoggingCustomizations
|
||||
private import semmle.javascript.security.dataflow.XpathInjectionCustomizations
|
||||
private import semmle.javascript.security.dataflow.Xss::Shared as Xss
|
||||
private import semmle.javascript.security.dataflow.StackTraceExposureCustomizations
|
||||
private import semmle.javascript.security.dataflow.ClientSideUrlRedirectCustomizations
|
||||
private import semmle.javascript.security.dataflow.CodeInjectionCustomizations
|
||||
private import semmle.javascript.security.dataflow.RequestForgeryCustomizations
|
||||
private import semmle.javascript.security.dataflow.CorsMisconfigurationForCredentialsCustomizations
|
||||
private import semmle.javascript.security.dataflow.ShellCommandInjectionFromEnvironmentCustomizations
|
||||
private import semmle.javascript.security.dataflow.DifferentKindsComparisonBypassCustomizations
|
||||
private import semmle.javascript.security.dataflow.CommandInjectionCustomizations
|
||||
private import semmle.javascript.security.dataflow.PrototypePollutionCustomizations
|
||||
private import semmle.javascript.security.dataflow.UnvalidatedDynamicMethodCallCustomizations
|
||||
private import semmle.javascript.security.dataflow.TaintedFormatStringCustomizations
|
||||
private import semmle.javascript.security.dataflow.NosqlInjectionCustomizations
|
||||
private import semmle.javascript.security.dataflow.PostMessageStarCustomizations
|
||||
private import semmle.javascript.security.dataflow.RegExpInjectionCustomizations
|
||||
private import semmle.javascript.security.dataflow.SqlInjectionCustomizations
|
||||
private import semmle.javascript.security.dataflow.InsecureRandomnessCustomizations
|
||||
private import semmle.javascript.security.dataflow.XmlBombCustomizations
|
||||
private import semmle.javascript.security.dataflow.InsufficientPasswordHashCustomizations
|
||||
private import semmle.javascript.security.dataflow.HardcodedCredentialsCustomizations
|
||||
private import semmle.javascript.security.dataflow.FileAccessToHttpCustomizations
|
||||
private import semmle.javascript.security.dataflow.UnsafeDynamicMethodAccessCustomizations
|
||||
private import semmle.javascript.security.dataflow.UnsafeDeserializationCustomizations
|
||||
private import semmle.javascript.security.dataflow.HardcodedDataInterpretedAsCodeCustomizations
|
||||
private import semmle.javascript.security.dataflow.ServerSideUrlRedirectCustomizations
|
||||
private import semmle.javascript.security.dataflow.IndirectCommandInjectionCustomizations
|
||||
private import semmle.javascript.security.dataflow.ConditionalBypassCustomizations
|
||||
private import semmle.javascript.security.dataflow.HttpToFileAccessCustomizations
|
||||
private import semmle.javascript.security.dataflow.BrokenCryptoAlgorithmCustomizations
|
||||
private import semmle.javascript.security.dataflow.LoopBoundInjectionCustomizations
|
||||
private import semmle.javascript.security.dataflow.CleartextStorageCustomizations
|
||||
import FilteringReasons
|
||||
|
||||
/**
|
||||
* Holds if the node `n` is a known sink in a modeled library, or a sibling-argument of such a sink.
|
||||
*/
|
||||
predicate isArgumentToKnownLibrarySinkFunction(DataFlow::Node n) {
|
||||
exists(DataFlow::InvokeNode invk, DataFlow::Node known |
|
||||
invk.getAnArgument() = n and invk.getAnArgument() = known and isKnownLibrarySink(known)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the node `n` is a known sink for the external API security query.
|
||||
*
|
||||
* This corresponds to known sinks from security queries whose sources include remote flow and
|
||||
* DOM-based sources.
|
||||
*/
|
||||
predicate isKnownExternalApiQuerySink(DataFlow::Node n) {
|
||||
n instanceof Xxe::Sink or
|
||||
n instanceof TaintedPath::Sink or
|
||||
n instanceof XpathInjection::Sink or
|
||||
n instanceof Xss::Sink or
|
||||
n instanceof ClientSideUrlRedirect::Sink or
|
||||
n instanceof CodeInjection::Sink or
|
||||
n instanceof RequestForgery::Sink or
|
||||
n instanceof CorsMisconfigurationForCredentials::Sink or
|
||||
n instanceof CommandInjection::Sink or
|
||||
n instanceof PrototypePollution::Sink or
|
||||
n instanceof UnvalidatedDynamicMethodCall::Sink or
|
||||
n instanceof TaintedFormatString::Sink or
|
||||
n instanceof NosqlInjection::Sink or
|
||||
n instanceof PostMessageStar::Sink or
|
||||
n instanceof RegExpInjection::Sink or
|
||||
n instanceof SqlInjection::Sink or
|
||||
n instanceof XmlBomb::Sink or
|
||||
n instanceof ZipSlip::Sink or
|
||||
n instanceof UnsafeDeserialization::Sink or
|
||||
n instanceof ServerSideUrlRedirect::Sink or
|
||||
n instanceof CleartextStorage::Sink or
|
||||
n instanceof HttpToFileAccess::Sink
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for isKnownExternalApiQuerySink */
|
||||
deprecated predicate isKnownExternalAPIQuerySink = isKnownExternalApiQuerySink/1;
|
||||
|
||||
/**
|
||||
* Holds if the node `n` is a known sink in a modeled library.
|
||||
*/
|
||||
predicate isKnownLibrarySink(DataFlow::Node n) {
|
||||
isKnownExternalApiQuerySink(n) or
|
||||
n instanceof CleartextLogging::Sink or
|
||||
n instanceof StackTraceExposure::Sink or
|
||||
n instanceof ShellCommandInjectionFromEnvironment::Sink or
|
||||
n instanceof InsecureRandomness::Sink or
|
||||
n instanceof FileAccessToHttp::Sink or
|
||||
n instanceof IndirectCommandInjection::Sink
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the node `n` is known as the predecessor in a modeled flow step.
|
||||
*/
|
||||
predicate isKnownStepSrc(DataFlow::Node n) {
|
||||
TaintTracking::sharedTaintStep(n, _) or
|
||||
DataFlow::SharedFlowStep::step(n, _) or
|
||||
DataFlow::SharedFlowStep::step(n, _, _, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n` is an argument to a function of a builtin object.
|
||||
*/
|
||||
private predicate isArgumentToBuiltinFunction(DataFlow::Node n, FilteringReason reason) {
|
||||
exists(DataFlow::SourceNode builtin, DataFlow::SourceNode receiver, DataFlow::InvokeNode invk |
|
||||
(
|
||||
builtin instanceof DataFlow::ArrayCreationNode and
|
||||
reason instanceof ArgumentToArrayReason
|
||||
or
|
||||
builtin =
|
||||
DataFlow::globalVarRef([
|
||||
"Map", "Set", "WeakMap", "WeakSet", "Number", "Object", "String", "Array", "Error",
|
||||
"Math", "Boolean"
|
||||
]) and
|
||||
reason instanceof ArgumentToBuiltinGlobalVarRefReason
|
||||
)
|
||||
|
|
||||
receiver = [builtin.getAnInvocation(), builtin] and
|
||||
invk = [receiver, receiver.getAPropertyRead()].getAnInvocation() and
|
||||
invk.getAnArgument() = n
|
||||
)
|
||||
or
|
||||
exists(Expr primitive, MethodCallExpr c |
|
||||
primitive instanceof ConstantString or
|
||||
primitive instanceof NumberLiteral or
|
||||
primitive instanceof BooleanLiteral
|
||||
|
|
||||
c.calls(primitive, _) and
|
||||
c.getAnArgument() = n.asExpr() and
|
||||
reason instanceof ConstantReceiverReason
|
||||
)
|
||||
or
|
||||
exists(DataFlow::CallNode call |
|
||||
call.getAnArgument() = n and
|
||||
call.getCalleeName() =
|
||||
[
|
||||
"indexOf", "hasOwnProperty", "substring", "isDecimal", "decode", "encode", "keys", "shift",
|
||||
"values", "forEach", "toString", "slice", "splice", "push", "isArray", "sort"
|
||||
] and
|
||||
reason instanceof BuiltinCallNameReason
|
||||
)
|
||||
}
|
||||
|
||||
predicate isOtherModeledArgument(DataFlow::Node n, FilteringReason reason) {
|
||||
isArgumentToBuiltinFunction(n, reason)
|
||||
or
|
||||
any(LodashUnderscore::Member m).getACall().getAnArgument() = n and
|
||||
reason instanceof LodashUnderscoreArgumentReason
|
||||
or
|
||||
any(JQuery::MethodCall m).getAnArgument() = n and
|
||||
reason instanceof JQueryArgumentReason
|
||||
or
|
||||
exists(ClientRequest r |
|
||||
r.getAnArgument() = n or n = r.getUrl() or n = r.getHost() or n = r.getADataNode()
|
||||
) and
|
||||
reason instanceof ClientRequestReason
|
||||
or
|
||||
exists(PromiseDefinition p |
|
||||
n = [p.getResolveParameter(), p.getRejectParameter()].getACall().getAnArgument()
|
||||
) and
|
||||
reason instanceof PromiseDefinitionReason
|
||||
or
|
||||
n instanceof CryptographicKey and reason instanceof CryptographicKeyReason
|
||||
or
|
||||
any(CryptographicOperation op).getInput() = n and
|
||||
reason instanceof CryptographicOperationFlowReason
|
||||
or
|
||||
exists(DataFlow::CallNode call | n = call.getAnArgument() |
|
||||
call.getCalleeName() = getAStandardLoggerMethodName() and
|
||||
reason instanceof LoggerMethodReason
|
||||
or
|
||||
call.getCalleeName() = ["setTimeout", "clearTimeout"] and
|
||||
reason instanceof TimeoutReason
|
||||
or
|
||||
call.getReceiver() = DataFlow::globalVarRef(["localStorage", "sessionStorage"]) and
|
||||
reason instanceof ReceiverStorageReason
|
||||
or
|
||||
call instanceof StringOps::StartsWith and reason instanceof StringStartsWithReason
|
||||
or
|
||||
call instanceof StringOps::EndsWith and reason instanceof StringEndsWithReason
|
||||
or
|
||||
call instanceof StringOps::RegExpTest and reason instanceof StringRegExpTestReason
|
||||
or
|
||||
call instanceof EventRegistration and reason instanceof EventRegistrationReason
|
||||
or
|
||||
call instanceof EventDispatch and reason instanceof EventDispatchReason
|
||||
or
|
||||
call = any(MembershipCandidate c).getTest() and
|
||||
reason instanceof MembershipCandidateTestReason
|
||||
or
|
||||
call instanceof FileSystemAccess and reason instanceof FileSystemAccessReason
|
||||
or
|
||||
// TODO database accesses are less well defined than database query sinks, so this may cover unmodeled sinks on existing database models
|
||||
[
|
||||
call, call.getAMethodCall()
|
||||
/* command pattern where the query is built, and then exec'ed later */ ] instanceof
|
||||
DatabaseAccess and
|
||||
reason instanceof DatabaseAccessReason
|
||||
or
|
||||
call = DOM::domValueRef() and reason instanceof DomReason
|
||||
or
|
||||
call.getCalleeName() = "next" and
|
||||
exists(DataFlow::FunctionNode f | call = f.getLastParameter().getACall()) and
|
||||
reason instanceof NextFunctionCallReason
|
||||
or
|
||||
call = DataFlow::globalVarRef("dojo").getAPropertyRead("require").getACall() and
|
||||
reason instanceof DojoRequireReason
|
||||
)
|
||||
or
|
||||
(exists(Base64::Decode d | n = d.getInput()) or exists(Base64::Encode d | n = d.getInput())) and
|
||||
reason instanceof Base64ManipulationReason
|
||||
}
|
||||
@@ -7,6 +7,46 @@ private import semmle.javascript.security.dataflow.SqlInjectionCustomizations
|
||||
private import semmle.javascript.security.dataflow.DomBasedXssCustomizations
|
||||
private import semmle.javascript.security.dataflow.NosqlInjectionCustomizations
|
||||
private import semmle.javascript.security.dataflow.TaintedPathCustomizations
|
||||
private import semmle.javascript.heuristics.SyntacticHeuristics as SyntacticHeuristics
|
||||
private import semmle.javascript.filters.ClassifyFiles as ClassifyFiles
|
||||
private import semmle.javascript.security.dataflow.XxeCustomizations
|
||||
private import semmle.javascript.security.dataflow.RemotePropertyInjectionCustomizations
|
||||
private import semmle.javascript.security.dataflow.TypeConfusionThroughParameterTamperingCustomizations
|
||||
private import semmle.javascript.security.dataflow.ZipSlipCustomizations
|
||||
private import semmle.javascript.security.dataflow.TaintedPathCustomizations
|
||||
private import semmle.javascript.security.dataflow.CleartextLoggingCustomizations
|
||||
private import semmle.javascript.security.dataflow.XpathInjectionCustomizations
|
||||
private import semmle.javascript.security.dataflow.Xss::Shared as Xss
|
||||
private import semmle.javascript.security.dataflow.StackTraceExposureCustomizations
|
||||
private import semmle.javascript.security.dataflow.ClientSideUrlRedirectCustomizations
|
||||
private import semmle.javascript.security.dataflow.CodeInjectionCustomizations
|
||||
private import semmle.javascript.security.dataflow.RequestForgeryCustomizations
|
||||
private import semmle.javascript.security.dataflow.CorsMisconfigurationForCredentialsCustomizations
|
||||
private import semmle.javascript.security.dataflow.ShellCommandInjectionFromEnvironmentCustomizations
|
||||
private import semmle.javascript.security.dataflow.DifferentKindsComparisonBypassCustomizations
|
||||
private import semmle.javascript.security.dataflow.CommandInjectionCustomizations
|
||||
private import semmle.javascript.security.dataflow.PrototypePollutionCustomizations
|
||||
private import semmle.javascript.security.dataflow.UnvalidatedDynamicMethodCallCustomizations
|
||||
private import semmle.javascript.security.dataflow.TaintedFormatStringCustomizations
|
||||
private import semmle.javascript.security.dataflow.NosqlInjectionCustomizations
|
||||
private import semmle.javascript.security.dataflow.PostMessageStarCustomizations
|
||||
private import semmle.javascript.security.dataflow.RegExpInjectionCustomizations
|
||||
private import semmle.javascript.security.dataflow.SqlInjectionCustomizations
|
||||
private import semmle.javascript.security.dataflow.InsecureRandomnessCustomizations
|
||||
private import semmle.javascript.security.dataflow.XmlBombCustomizations
|
||||
private import semmle.javascript.security.dataflow.InsufficientPasswordHashCustomizations
|
||||
private import semmle.javascript.security.dataflow.HardcodedCredentialsCustomizations
|
||||
private import semmle.javascript.security.dataflow.FileAccessToHttpCustomizations
|
||||
private import semmle.javascript.security.dataflow.UnsafeDynamicMethodAccessCustomizations
|
||||
private import semmle.javascript.security.dataflow.UnsafeDeserializationCustomizations
|
||||
private import semmle.javascript.security.dataflow.HardcodedDataInterpretedAsCodeCustomizations
|
||||
private import semmle.javascript.security.dataflow.ServerSideUrlRedirectCustomizations
|
||||
private import semmle.javascript.security.dataflow.IndirectCommandInjectionCustomizations
|
||||
private import semmle.javascript.security.dataflow.ConditionalBypassCustomizations
|
||||
private import semmle.javascript.security.dataflow.HttpToFileAccessCustomizations
|
||||
private import semmle.javascript.security.dataflow.BrokenCryptoAlgorithmCustomizations
|
||||
private import semmle.javascript.security.dataflow.LoopBoundInjectionCustomizations
|
||||
private import semmle.javascript.security.dataflow.CleartextStorageCustomizations
|
||||
|
||||
/**
|
||||
* A set of characteristics that a particular endpoint might have. This set of characteristics is used to make decisions
|
||||
@@ -25,7 +65,7 @@ abstract class EndpointCharacteristic extends string {
|
||||
* Holds for endpoints that have this characteristic. This predicate contains the logic that applies characteristics
|
||||
* to the appropriate set of dataflow nodes.
|
||||
*/
|
||||
abstract predicate getEndpoints(DataFlow::Node n);
|
||||
abstract predicate appliesToEndpoint(DataFlow::Node n);
|
||||
|
||||
/**
|
||||
* This predicate describes what the characteristic tells us about an endpoint.
|
||||
@@ -41,7 +81,7 @@ abstract class EndpointCharacteristic extends string {
|
||||
* 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 getImplications(
|
||||
abstract predicate hasImplications(
|
||||
EndpointType endpointClass, boolean isPositiveIndicator, float confidence
|
||||
);
|
||||
|
||||
@@ -57,6 +97,110 @@ abstract class EndpointCharacteristic extends string {
|
||||
final float mediumConfidence() { result = 0.6 }
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper predicates.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Holds if the node `n` is a known sink for the external API security query.
|
||||
*
|
||||
* This corresponds to known sinks from security queries whose sources include remote flow and
|
||||
* DOM-based sources.
|
||||
*/
|
||||
private predicate isKnownExternalApiQuerySink(DataFlow::Node n) {
|
||||
n instanceof Xxe::Sink or
|
||||
n instanceof TaintedPath::Sink or
|
||||
n instanceof XpathInjection::Sink or
|
||||
n instanceof Xss::Sink or
|
||||
n instanceof ClientSideUrlRedirect::Sink or
|
||||
n instanceof CodeInjection::Sink or
|
||||
n instanceof RequestForgery::Sink or
|
||||
n instanceof CorsMisconfigurationForCredentials::Sink or
|
||||
n instanceof CommandInjection::Sink or
|
||||
n instanceof PrototypePollution::Sink or
|
||||
n instanceof UnvalidatedDynamicMethodCall::Sink or
|
||||
n instanceof TaintedFormatString::Sink or
|
||||
n instanceof NosqlInjection::Sink or
|
||||
n instanceof PostMessageStar::Sink or
|
||||
n instanceof RegExpInjection::Sink or
|
||||
n instanceof SqlInjection::Sink or
|
||||
n instanceof XmlBomb::Sink or
|
||||
n instanceof ZipSlip::Sink or
|
||||
n instanceof UnsafeDeserialization::Sink or
|
||||
n instanceof ServerSideUrlRedirect::Sink or
|
||||
n instanceof CleartextStorage::Sink or
|
||||
n instanceof HttpToFileAccess::Sink
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the node `n` is a known sink in a modeled library.
|
||||
*/
|
||||
private predicate isKnownLibrarySink(DataFlow::Node n) {
|
||||
isKnownExternalApiQuerySink(n) or
|
||||
n instanceof CleartextLogging::Sink or
|
||||
n instanceof StackTraceExposure::Sink or
|
||||
n instanceof ShellCommandInjectionFromEnvironment::Sink or
|
||||
n instanceof InsecureRandomness::Sink or
|
||||
n instanceof FileAccessToHttp::Sink or
|
||||
n instanceof IndirectCommandInjection::Sink
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the node `n` is known as the predecessor in a modeled flow step.
|
||||
*/
|
||||
private predicate isKnownStepSrc(DataFlow::Node n) {
|
||||
TaintTracking::sharedTaintStep(n, _) or
|
||||
DataFlow::SharedFlowStep::step(n, _) or
|
||||
DataFlow::SharedFlowStep::step(n, _, _, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the data flow node is a (possibly indirect) argument of a likely external library call.
|
||||
*
|
||||
* This includes direct arguments of likely external library calls as well as nested object
|
||||
* literals within those calls.
|
||||
*/
|
||||
private predicate flowsToArgumentOfLikelyExternalLibraryCall(DataFlow::Node n) {
|
||||
n = getACallWithoutCallee().getAnArgument()
|
||||
or
|
||||
exists(DataFlow::SourceNode src | flowsToArgumentOfLikelyExternalLibraryCall(src) |
|
||||
n = src.getAPropertyWrite().getRhs()
|
||||
)
|
||||
or
|
||||
exists(DataFlow::ArrayCreationNode arr | flowsToArgumentOfLikelyExternalLibraryCall(arr) |
|
||||
n = arr.getAnElement()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get calls for which we do not have the callee (i.e. the definition of the called function). This
|
||||
* acts as a heuristic for identifying calls to external library functions.
|
||||
*/
|
||||
private DataFlow::CallNode getACallWithoutCallee() {
|
||||
forall(Function callee | callee = result.getACallee() | callee.getTopLevel().isExterns()) and
|
||||
not exists(DataFlow::ParameterNode param, DataFlow::FunctionNode callback |
|
||||
param.flowsTo(result.getCalleeNode()) and
|
||||
callback = getACallback(param, DataFlow::TypeBackTracker::end())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a node that flows to callback-parameter `p`.
|
||||
*/
|
||||
private DataFlow::SourceNode getACallback(DataFlow::ParameterNode p, DataFlow::TypeBackTracker t) {
|
||||
t.start() and
|
||||
result = p and
|
||||
any(DataFlow::FunctionNode f).getLastParameter() = p and
|
||||
exists(p.getACall())
|
||||
or
|
||||
exists(DataFlow::TypeBackTracker t2 | result = getACallback(p, t2).backtrack(t2, t))
|
||||
}
|
||||
|
||||
/**
|
||||
* Get calls which are likely to be to external non-built-in libraries.
|
||||
*/
|
||||
DataFlow::CallNode getALikelyExternalLibraryCall() { result = getACallWithoutCallee() }
|
||||
|
||||
/*
|
||||
* Characteristics that are indicative of a sink.
|
||||
* NOTE: Initially each sink type has only one characteristic, which is that it's a sink of this type in the standard
|
||||
@@ -69,9 +213,9 @@ abstract class EndpointCharacteristic extends string {
|
||||
private class DomBasedXssSinkCharacteristic extends EndpointCharacteristic {
|
||||
DomBasedXssSinkCharacteristic() { this = "DomBasedXssSink" }
|
||||
|
||||
override predicate getEndpoints(DataFlow::Node n) { n instanceof DomBasedXss::Sink }
|
||||
override predicate appliesToEndpoint(DataFlow::Node n) { n instanceof DomBasedXss::Sink }
|
||||
|
||||
override predicate getImplications(
|
||||
override predicate hasImplications(
|
||||
EndpointType endpointClass, boolean isPositiveIndicator, float confidence
|
||||
) {
|
||||
endpointClass instanceof XssSinkType and
|
||||
@@ -87,9 +231,9 @@ private class DomBasedXssSinkCharacteristic extends EndpointCharacteristic {
|
||||
private class TaintedPathSinkCharacteristic extends EndpointCharacteristic {
|
||||
TaintedPathSinkCharacteristic() { this = "TaintedPathSink" }
|
||||
|
||||
override predicate getEndpoints(DataFlow::Node n) { n instanceof TaintedPath::Sink }
|
||||
override predicate appliesToEndpoint(DataFlow::Node n) { n instanceof TaintedPath::Sink }
|
||||
|
||||
override predicate getImplications(
|
||||
override predicate hasImplications(
|
||||
EndpointType endpointClass, boolean isPositiveIndicator, float confidence
|
||||
) {
|
||||
endpointClass instanceof TaintedPathSinkType and
|
||||
@@ -105,9 +249,9 @@ private class TaintedPathSinkCharacteristic extends EndpointCharacteristic {
|
||||
private class SqlInjectionSinkCharacteristic extends EndpointCharacteristic {
|
||||
SqlInjectionSinkCharacteristic() { this = "SqlInjectionSink" }
|
||||
|
||||
override predicate getEndpoints(DataFlow::Node n) { n instanceof SqlInjection::Sink }
|
||||
override predicate appliesToEndpoint(DataFlow::Node n) { n instanceof SqlInjection::Sink }
|
||||
|
||||
override predicate getImplications(
|
||||
override predicate hasImplications(
|
||||
EndpointType endpointClass, boolean isPositiveIndicator, float confidence
|
||||
) {
|
||||
endpointClass instanceof SqlInjectionSinkType and
|
||||
@@ -123,9 +267,9 @@ private class SqlInjectionSinkCharacteristic extends EndpointCharacteristic {
|
||||
private class NosqlInjectionSinkCharacteristic extends EndpointCharacteristic {
|
||||
NosqlInjectionSinkCharacteristic() { this = "NosqlInjectionSink" }
|
||||
|
||||
override predicate getEndpoints(DataFlow::Node n) { n instanceof NosqlInjection::Sink }
|
||||
override predicate appliesToEndpoint(DataFlow::Node n) { n instanceof NosqlInjection::Sink }
|
||||
|
||||
override predicate getImplications(
|
||||
override predicate hasImplications(
|
||||
EndpointType endpointClass, boolean isPositiveIndicator, float confidence
|
||||
) {
|
||||
endpointClass instanceof NosqlInjectionSinkType and
|
||||
@@ -135,14 +279,23 @@ private class NosqlInjectionSinkCharacteristic extends EndpointCharacteristic {
|
||||
}
|
||||
|
||||
/*
|
||||
* Characteristics that are indicative of not being a sink of any type.
|
||||
* Characteristics that are indicative of not being a sink of any type, and have historically been used to select
|
||||
* negative samples for training.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A characteristic that is an indicator of not being a sink of any type, because it's a modeled argument.
|
||||
*/
|
||||
abstract class OtherModeledArgumentCharacteristic extends EndpointCharacteristic {
|
||||
bindingset[this]
|
||||
OtherModeledArgumentCharacteristic() { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A characteristic that is an indicator of not being a sink of any type, because it's an argument to a function of a
|
||||
* builtin object.
|
||||
*/
|
||||
abstract private class ArgumentToBuiltinFunctionCharacteristic extends EndpointCharacteristic {
|
||||
abstract private class ArgumentToBuiltinFunctionCharacteristic extends OtherModeledArgumentCharacteristic {
|
||||
bindingset[this]
|
||||
ArgumentToBuiltinFunctionCharacteristic() { any() }
|
||||
}
|
||||
@@ -154,7 +307,7 @@ abstract private class NotASinkCharacteristic extends EndpointCharacteristic {
|
||||
bindingset[this]
|
||||
NotASinkCharacteristic() { any() }
|
||||
|
||||
override predicate getImplications(
|
||||
override predicate hasImplications(
|
||||
EndpointType endpointClass, boolean isPositiveIndicator, float confidence
|
||||
) {
|
||||
endpointClass instanceof NegativeType and
|
||||
@@ -173,7 +326,7 @@ abstract class LikelyNotASinkCharacteristic extends EndpointCharacteristic {
|
||||
bindingset[this]
|
||||
LikelyNotASinkCharacteristic() { any() }
|
||||
|
||||
override predicate getImplications(
|
||||
override predicate hasImplications(
|
||||
EndpointType endpointClass, boolean isPositiveIndicator, float confidence
|
||||
) {
|
||||
endpointClass instanceof NegativeType and
|
||||
@@ -182,152 +335,169 @@ abstract class LikelyNotASinkCharacteristic extends EndpointCharacteristic {
|
||||
}
|
||||
}
|
||||
|
||||
private class LodashUnderscore extends NotASinkCharacteristic {
|
||||
LodashUnderscore() { this = "LodashUnderscoreArgument" }
|
||||
private class LodashUnderscoreCharacteristic extends NotASinkCharacteristic,
|
||||
OtherModeledArgumentCharacteristic {
|
||||
LodashUnderscoreCharacteristic() { this = "LodashUnderscoreArgument" }
|
||||
|
||||
override predicate getEndpoints(DataFlow::Node n) {
|
||||
override predicate appliesToEndpoint(DataFlow::Node n) {
|
||||
any(LodashUnderscore::Member m).getACall().getAnArgument() = n
|
||||
}
|
||||
}
|
||||
|
||||
private class JQueryArgumentCharacteristic extends NotASinkCharacteristic {
|
||||
private class JQueryArgumentCharacteristic extends NotASinkCharacteristic,
|
||||
OtherModeledArgumentCharacteristic {
|
||||
JQueryArgumentCharacteristic() { this = "JQueryArgument" }
|
||||
|
||||
override predicate getEndpoints(DataFlow::Node n) {
|
||||
override predicate appliesToEndpoint(DataFlow::Node n) {
|
||||
any(JQuery::MethodCall m).getAnArgument() = n
|
||||
}
|
||||
}
|
||||
|
||||
private class ClientRequestCharacteristic extends NotASinkCharacteristic {
|
||||
private class ClientRequestCharacteristic extends NotASinkCharacteristic,
|
||||
OtherModeledArgumentCharacteristic {
|
||||
ClientRequestCharacteristic() { this = "ClientRequest" }
|
||||
|
||||
override predicate getEndpoints(DataFlow::Node n) {
|
||||
override predicate appliesToEndpoint(DataFlow::Node n) {
|
||||
exists(ClientRequest r |
|
||||
r.getAnArgument() = n or n = r.getUrl() or n = r.getHost() or n = r.getADataNode()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class PromiseDefinitionCharacteristic extends NotASinkCharacteristic {
|
||||
private class PromiseDefinitionCharacteristic extends NotASinkCharacteristic,
|
||||
OtherModeledArgumentCharacteristic {
|
||||
PromiseDefinitionCharacteristic() { this = "PromiseDefinition" }
|
||||
|
||||
override predicate getEndpoints(DataFlow::Node n) {
|
||||
override predicate appliesToEndpoint(DataFlow::Node n) {
|
||||
exists(PromiseDefinition p |
|
||||
n = [p.getResolveParameter(), p.getRejectParameter()].getACall().getAnArgument()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class CryptographicKeyCharacteristic extends NotASinkCharacteristic {
|
||||
private class CryptographicKeyCharacteristic extends NotASinkCharacteristic,
|
||||
OtherModeledArgumentCharacteristic {
|
||||
CryptographicKeyCharacteristic() { this = "CryptographicKey" }
|
||||
|
||||
override predicate getEndpoints(DataFlow::Node n) { n instanceof CryptographicKey }
|
||||
override predicate appliesToEndpoint(DataFlow::Node n) { n instanceof CryptographicKey }
|
||||
}
|
||||
|
||||
private class CryptographicOperationFlowCharacteristic extends NotASinkCharacteristic {
|
||||
private class CryptographicOperationFlowCharacteristic extends NotASinkCharacteristic,
|
||||
OtherModeledArgumentCharacteristic {
|
||||
CryptographicOperationFlowCharacteristic() { this = "CryptographicOperationFlow" }
|
||||
|
||||
override predicate getEndpoints(DataFlow::Node n) {
|
||||
override predicate appliesToEndpoint(DataFlow::Node n) {
|
||||
any(CryptographicOperation op).getInput() = n
|
||||
}
|
||||
}
|
||||
|
||||
private class LoggerMethodCharacteristic extends NotASinkCharacteristic {
|
||||
private class LoggerMethodCharacteristic extends NotASinkCharacteristic,
|
||||
OtherModeledArgumentCharacteristic {
|
||||
LoggerMethodCharacteristic() { this = "LoggerMethod" }
|
||||
|
||||
override predicate getEndpoints(DataFlow::Node n) {
|
||||
override predicate appliesToEndpoint(DataFlow::Node n) {
|
||||
exists(DataFlow::CallNode call | n = call.getAnArgument() |
|
||||
call.getCalleeName() = getAStandardLoggerMethodName()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class TimeoutCharacteristic extends NotASinkCharacteristic {
|
||||
private class TimeoutCharacteristic extends NotASinkCharacteristic,
|
||||
OtherModeledArgumentCharacteristic {
|
||||
TimeoutCharacteristic() { this = "Timeout" }
|
||||
|
||||
override predicate getEndpoints(DataFlow::Node n) {
|
||||
override predicate appliesToEndpoint(DataFlow::Node n) {
|
||||
exists(DataFlow::CallNode call | n = call.getAnArgument() |
|
||||
call.getCalleeName() = ["setTimeout", "clearTimeout"]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class ReceiverStorageCharacteristic extends NotASinkCharacteristic {
|
||||
private class ReceiverStorageCharacteristic extends NotASinkCharacteristic,
|
||||
OtherModeledArgumentCharacteristic {
|
||||
ReceiverStorageCharacteristic() { this = "ReceiverStorage" }
|
||||
|
||||
override predicate getEndpoints(DataFlow::Node n) {
|
||||
override predicate appliesToEndpoint(DataFlow::Node n) {
|
||||
exists(DataFlow::CallNode call | n = call.getAnArgument() |
|
||||
call.getReceiver() = DataFlow::globalVarRef(["localStorage", "sessionStorage"])
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class StringStartsWithCharacteristic extends NotASinkCharacteristic {
|
||||
private class StringStartsWithCharacteristic extends NotASinkCharacteristic,
|
||||
OtherModeledArgumentCharacteristic {
|
||||
StringStartsWithCharacteristic() { this = "StringStartsWith" }
|
||||
|
||||
override predicate getEndpoints(DataFlow::Node n) {
|
||||
override predicate appliesToEndpoint(DataFlow::Node n) {
|
||||
exists(DataFlow::CallNode call | n = call.getAnArgument() |
|
||||
call instanceof StringOps::StartsWith
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class StringEndsWithCharacteristic extends NotASinkCharacteristic {
|
||||
private class StringEndsWithCharacteristic extends NotASinkCharacteristic,
|
||||
OtherModeledArgumentCharacteristic {
|
||||
StringEndsWithCharacteristic() { this = "StringEndsWith" }
|
||||
|
||||
override predicate getEndpoints(DataFlow::Node n) {
|
||||
override predicate appliesToEndpoint(DataFlow::Node n) {
|
||||
exists(DataFlow::CallNode call | n = call.getAnArgument() | call instanceof StringOps::EndsWith)
|
||||
}
|
||||
}
|
||||
|
||||
private class StringRegExpTestCharacteristic extends NotASinkCharacteristic {
|
||||
private class StringRegExpTestCharacteristic extends NotASinkCharacteristic,
|
||||
OtherModeledArgumentCharacteristic {
|
||||
StringRegExpTestCharacteristic() { this = "StringRegExpTest" }
|
||||
|
||||
override predicate getEndpoints(DataFlow::Node n) {
|
||||
override predicate appliesToEndpoint(DataFlow::Node n) {
|
||||
exists(DataFlow::CallNode call | n = call.getAnArgument() |
|
||||
call instanceof StringOps::RegExpTest
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class EventRegistrationCharacteristic extends NotASinkCharacteristic {
|
||||
private class EventRegistrationCharacteristic extends NotASinkCharacteristic,
|
||||
OtherModeledArgumentCharacteristic {
|
||||
EventRegistrationCharacteristic() { this = "EventRegistration" }
|
||||
|
||||
override predicate getEndpoints(DataFlow::Node n) {
|
||||
override predicate appliesToEndpoint(DataFlow::Node n) {
|
||||
exists(DataFlow::CallNode call | n = call.getAnArgument() | call instanceof EventRegistration)
|
||||
}
|
||||
}
|
||||
|
||||
private class EventDispatchCharacteristic extends NotASinkCharacteristic {
|
||||
private class EventDispatchCharacteristic extends NotASinkCharacteristic,
|
||||
OtherModeledArgumentCharacteristic {
|
||||
EventDispatchCharacteristic() { this = "EventDispatch" }
|
||||
|
||||
override predicate getEndpoints(DataFlow::Node n) {
|
||||
override predicate appliesToEndpoint(DataFlow::Node n) {
|
||||
exists(DataFlow::CallNode call | n = call.getAnArgument() | call instanceof EventDispatch)
|
||||
}
|
||||
}
|
||||
|
||||
private class MembershipCandidateTestCharacteristic extends NotASinkCharacteristic {
|
||||
private class MembershipCandidateTestCharacteristic extends NotASinkCharacteristic,
|
||||
OtherModeledArgumentCharacteristic {
|
||||
MembershipCandidateTestCharacteristic() { this = "MembershipCandidateTest" }
|
||||
|
||||
override predicate getEndpoints(DataFlow::Node n) {
|
||||
override predicate appliesToEndpoint(DataFlow::Node n) {
|
||||
exists(DataFlow::CallNode call | n = call.getAnArgument() |
|
||||
call = any(MembershipCandidate c).getTest()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class FileSystemAccessCharacteristic extends NotASinkCharacteristic {
|
||||
private class FileSystemAccessCharacteristic extends NotASinkCharacteristic,
|
||||
OtherModeledArgumentCharacteristic {
|
||||
FileSystemAccessCharacteristic() { this = "FileSystemAccess" }
|
||||
|
||||
override predicate getEndpoints(DataFlow::Node n) {
|
||||
override predicate appliesToEndpoint(DataFlow::Node n) {
|
||||
exists(DataFlow::CallNode call | n = call.getAnArgument() | call instanceof FileSystemAccess)
|
||||
}
|
||||
}
|
||||
|
||||
private class DatabaseAccessCharacteristic extends NotASinkCharacteristic {
|
||||
private class DatabaseAccessCharacteristic extends NotASinkCharacteristic,
|
||||
OtherModeledArgumentCharacteristic {
|
||||
DatabaseAccessCharacteristic() { this = "DatabaseAccess" }
|
||||
|
||||
override predicate getEndpoints(DataFlow::Node n) {
|
||||
override predicate appliesToEndpoint(DataFlow::Node n) {
|
||||
// TODO database accesses are less well defined than database query sinks, so this may cover unmodeled sinks on
|
||||
// existing database models
|
||||
exists(DataFlow::CallNode call | n = call.getAnArgument() |
|
||||
@@ -339,18 +509,19 @@ private class DatabaseAccessCharacteristic extends NotASinkCharacteristic {
|
||||
}
|
||||
}
|
||||
|
||||
private class DomCharacteristic extends NotASinkCharacteristic {
|
||||
private class DomCharacteristic extends NotASinkCharacteristic, OtherModeledArgumentCharacteristic {
|
||||
DomCharacteristic() { this = "DOM" }
|
||||
|
||||
override predicate getEndpoints(DataFlow::Node n) {
|
||||
override predicate appliesToEndpoint(DataFlow::Node n) {
|
||||
exists(DataFlow::CallNode call | n = call.getAnArgument() | call = DOM::domValueRef())
|
||||
}
|
||||
}
|
||||
|
||||
private class NextFunctionCallCharacteristic extends NotASinkCharacteristic {
|
||||
private class NextFunctionCallCharacteristic extends NotASinkCharacteristic,
|
||||
OtherModeledArgumentCharacteristic {
|
||||
NextFunctionCallCharacteristic() { this = "NextFunctionCall" }
|
||||
|
||||
override predicate getEndpoints(DataFlow::Node n) {
|
||||
override predicate appliesToEndpoint(DataFlow::Node n) {
|
||||
exists(DataFlow::CallNode call | n = call.getAnArgument() |
|
||||
call.getCalleeName() = "next" and
|
||||
exists(DataFlow::FunctionNode f | call = f.getLastParameter().getACall())
|
||||
@@ -358,20 +529,22 @@ private class NextFunctionCallCharacteristic extends NotASinkCharacteristic {
|
||||
}
|
||||
}
|
||||
|
||||
private class DojoRequireCharacteristic extends NotASinkCharacteristic {
|
||||
private class DojoRequireCharacteristic extends NotASinkCharacteristic,
|
||||
OtherModeledArgumentCharacteristic {
|
||||
DojoRequireCharacteristic() { this = "DojoRequire" }
|
||||
|
||||
override predicate getEndpoints(DataFlow::Node n) {
|
||||
override predicate appliesToEndpoint(DataFlow::Node n) {
|
||||
exists(DataFlow::CallNode call | n = call.getAnArgument() |
|
||||
call = DataFlow::globalVarRef("dojo").getAPropertyRead("require").getACall()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class Base64ManipulationCharacteristic extends NotASinkCharacteristic {
|
||||
private class Base64ManipulationCharacteristic extends NotASinkCharacteristic,
|
||||
OtherModeledArgumentCharacteristic {
|
||||
Base64ManipulationCharacteristic() { this = "Base64Manipulation" }
|
||||
|
||||
override predicate getEndpoints(DataFlow::Node n) {
|
||||
override predicate appliesToEndpoint(DataFlow::Node n) {
|
||||
exists(Base64::Decode d | n = d.getInput()) or
|
||||
exists(Base64::Encode d | n = d.getInput())
|
||||
}
|
||||
@@ -381,7 +554,7 @@ private class ArgumentToArrayCharacteristic extends ArgumentToBuiltinFunctionCha
|
||||
LikelyNotASinkCharacteristic {
|
||||
ArgumentToArrayCharacteristic() { this = "ArgumentToArray" }
|
||||
|
||||
override predicate getEndpoints(DataFlow::Node n) {
|
||||
override predicate appliesToEndpoint(DataFlow::Node n) {
|
||||
exists(DataFlow::SourceNode builtin, DataFlow::SourceNode receiver, DataFlow::InvokeNode invk |
|
||||
builtin instanceof DataFlow::ArrayCreationNode
|
||||
|
|
||||
@@ -396,7 +569,7 @@ private class ArgumentToBuiltinGlobalVarRefCharacteristic extends ArgumentToBuil
|
||||
LikelyNotASinkCharacteristic {
|
||||
ArgumentToBuiltinGlobalVarRefCharacteristic() { this = "ArgumentToBuiltinGlobalVarRef" }
|
||||
|
||||
override predicate getEndpoints(DataFlow::Node n) {
|
||||
override predicate appliesToEndpoint(DataFlow::Node n) {
|
||||
exists(DataFlow::SourceNode builtin, DataFlow::SourceNode receiver, DataFlow::InvokeNode invk |
|
||||
builtin =
|
||||
DataFlow::globalVarRef([
|
||||
@@ -415,7 +588,7 @@ private class ConstantReceiverCharacteristic extends ArgumentToBuiltinFunctionCh
|
||||
NotASinkCharacteristic {
|
||||
ConstantReceiverCharacteristic() { this = "ConstantReceiver" }
|
||||
|
||||
override predicate getEndpoints(DataFlow::Node n) {
|
||||
override predicate appliesToEndpoint(DataFlow::Node n) {
|
||||
exists(Expr primitive, MethodCallExpr c |
|
||||
primitive instanceof ConstantString or
|
||||
primitive instanceof NumberLiteral or
|
||||
@@ -431,7 +604,7 @@ private class BuiltinCallNameCharacteristic extends ArgumentToBuiltinFunctionCha
|
||||
NotASinkCharacteristic {
|
||||
BuiltinCallNameCharacteristic() { this = "BuiltinCallName" }
|
||||
|
||||
override predicate getEndpoints(DataFlow::Node n) {
|
||||
override predicate appliesToEndpoint(DataFlow::Node n) {
|
||||
exists(DataFlow::CallNode call |
|
||||
call.getAnArgument() = n and
|
||||
call.getCalleeName() =
|
||||
@@ -442,3 +615,430 @@ private class BuiltinCallNameCharacteristic extends ArgumentToBuiltinFunctionCha
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Characteristics that have historically acted as endpoint filters to exclude endpoints from scoring at inference time.
|
||||
*/
|
||||
|
||||
/** A characteristic that has historically acted as an endpoint filter for inference-time scoring. */
|
||||
abstract class EndpointFilterCharacteristic extends EndpointCharacteristic {
|
||||
bindingset[this]
|
||||
EndpointFilterCharacteristic() { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An EndpointFilterCharacteristic that indicates that an endpoint is unlikely to be a sink of any type.
|
||||
*/
|
||||
abstract private class StandardEndpointFilterCharacteristic extends EndpointFilterCharacteristic {
|
||||
bindingset[this]
|
||||
StandardEndpointFilterCharacteristic() { any() }
|
||||
|
||||
override predicate hasImplications(
|
||||
EndpointType endpointClass, boolean isPositiveIndicator, float confidence
|
||||
) {
|
||||
endpointClass instanceof NegativeType and
|
||||
isPositiveIndicator = true and
|
||||
confidence = mediumConfidence()
|
||||
}
|
||||
}
|
||||
|
||||
class IsArgumentToModeledFunctionCharacteristic extends StandardEndpointFilterCharacteristic {
|
||||
IsArgumentToModeledFunctionCharacteristic() { this = "argument to modeled function" }
|
||||
|
||||
override predicate appliesToEndpoint(DataFlow::Node n) {
|
||||
exists(DataFlow::InvokeNode invk, DataFlow::Node known |
|
||||
invk.getAnArgument() = n and
|
||||
invk.getAnArgument() = known and
|
||||
(
|
||||
isKnownLibrarySink(known)
|
||||
or
|
||||
isKnownStepSrc(known)
|
||||
or
|
||||
exists(OtherModeledArgumentCharacteristic characteristic |
|
||||
characteristic.appliesToEndpoint(known)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class IsArgumentToSinklessLibraryCharacteristic extends StandardEndpointFilterCharacteristic {
|
||||
IsArgumentToSinklessLibraryCharacteristic() { this = "argument to sinkless library" }
|
||||
|
||||
override predicate appliesToEndpoint(DataFlow::Node n) {
|
||||
exists(DataFlow::InvokeNode invk, DataFlow::SourceNode commonSafeLibrary, string libraryName |
|
||||
libraryName = ["slugify", "striptags", "marked"]
|
||||
|
|
||||
commonSafeLibrary = DataFlow::moduleImport(libraryName) and
|
||||
invk = [commonSafeLibrary, commonSafeLibrary.getAPropertyRead()].getAnInvocation() and
|
||||
n = invk.getAnArgument()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class IsSanitizerCharacteristic extends StandardEndpointFilterCharacteristic {
|
||||
IsSanitizerCharacteristic() { this = "sanitizer" }
|
||||
|
||||
override predicate appliesToEndpoint(DataFlow::Node n) {
|
||||
exists(DataFlow::CallNode call | n = call.getAnArgument() |
|
||||
call.getCalleeName().regexpMatch("(?i).*(escape|valid(ate)?|sanitize|purify).*")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class IsPredicateCharacteristic extends StandardEndpointFilterCharacteristic {
|
||||
IsPredicateCharacteristic() { this = "predicate" }
|
||||
|
||||
override predicate appliesToEndpoint(DataFlow::Node n) {
|
||||
exists(DataFlow::CallNode call | n = call.getAnArgument() |
|
||||
call.getCalleeName().regexpMatch("(equals|(|is|has|can)(_|[A-Z])).*")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class IsHashCharacteristic extends StandardEndpointFilterCharacteristic {
|
||||
IsHashCharacteristic() { this = "hash" }
|
||||
|
||||
override predicate appliesToEndpoint(DataFlow::Node n) {
|
||||
exists(DataFlow::CallNode call | n = call.getAnArgument() |
|
||||
call.getCalleeName().regexpMatch("(?i)^(sha\\d*|md5|hash)$")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class IsNumericCharacteristic extends StandardEndpointFilterCharacteristic {
|
||||
IsNumericCharacteristic() { this = "numeric" }
|
||||
|
||||
override predicate appliesToEndpoint(DataFlow::Node n) {
|
||||
SyntacticHeuristics::isReadFrom(n, ".*index.*")
|
||||
}
|
||||
}
|
||||
|
||||
private class InIrrelevantFileCharacteristic extends StandardEndpointFilterCharacteristic {
|
||||
private string category;
|
||||
|
||||
InIrrelevantFileCharacteristic() {
|
||||
this = "in " + category + " file" and category = ["externs", "generated", "library", "test"]
|
||||
}
|
||||
|
||||
override predicate appliesToEndpoint(DataFlow::Node n) {
|
||||
// Ignore candidate sinks within externs, generated, library, and test code
|
||||
ClassifyFiles::classify(n.getFile(), category)
|
||||
}
|
||||
}
|
||||
|
||||
/** An EndpointFilterCharacteristic that indicates that an endpoint is unlikely to be a NoSQL injection sink. */
|
||||
abstract private class NosqlInjectionSinkEndpointFilterCharacteristic extends EndpointFilterCharacteristic {
|
||||
bindingset[this]
|
||||
NosqlInjectionSinkEndpointFilterCharacteristic() { any() }
|
||||
|
||||
override predicate hasImplications(
|
||||
EndpointType endpointClass, boolean isPositiveIndicator, float confidence
|
||||
) {
|
||||
endpointClass instanceof NosqlInjectionSinkType and
|
||||
isPositiveIndicator = false and
|
||||
confidence = mediumConfidence()
|
||||
}
|
||||
}
|
||||
|
||||
private class DatabaseAccessCallHeuristicCharacteristic extends NosqlInjectionSinkEndpointFilterCharacteristic {
|
||||
DatabaseAccessCallHeuristicCharacteristic() { this = "matches database access call heuristic" }
|
||||
|
||||
override predicate appliesToEndpoint(DataFlow::Node n) {
|
||||
exists(DataFlow::MethodCallNode call | n = call.getAnArgument() |
|
||||
// additional databases accesses that aren't modeled yet
|
||||
call.getMethodName() = ["create", "createCollection", "createIndexes"]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class ModeledSinkCharacteristic extends NosqlInjectionSinkEndpointFilterCharacteristic {
|
||||
ModeledSinkCharacteristic() { this = "modeled sink" }
|
||||
|
||||
/**
|
||||
* Holds if the node `n` is a known sink in a modeled library, or a sibling-argument of such a sink.
|
||||
*/
|
||||
predicate isArgumentToKnownLibrarySinkFunction(DataFlow::Node n) {
|
||||
exists(DataFlow::InvokeNode invk, DataFlow::Node known |
|
||||
invk.getAnArgument() = n and invk.getAnArgument() = known and isKnownLibrarySink(known)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate appliesToEndpoint(DataFlow::Node n) {
|
||||
exists(DataFlow::CallNode call | n = call.getAnArgument() |
|
||||
// Remove modeled sinks
|
||||
isArgumentToKnownLibrarySinkFunction(n)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class PredecessorInModeledFlowStepCharacteristic extends NosqlInjectionSinkEndpointFilterCharacteristic {
|
||||
PredecessorInModeledFlowStepCharacteristic() { this = "predecessor in a modeled flow step" }
|
||||
|
||||
override predicate appliesToEndpoint(DataFlow::Node n) {
|
||||
exists(DataFlow::CallNode call | n = call.getAnArgument() |
|
||||
// Remove common kinds of unlikely sinks
|
||||
isKnownStepSrc(n)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class ModeledDatabaseAccessCharacteristic extends NosqlInjectionSinkEndpointFilterCharacteristic {
|
||||
ModeledDatabaseAccessCharacteristic() { this = "modeled database access" }
|
||||
|
||||
override predicate appliesToEndpoint(DataFlow::Node n) {
|
||||
exists(DataFlow::CallNode call | n = call.getAnArgument() |
|
||||
// Remove modeled database calls. Arguments to modeled calls are very likely to be modeled
|
||||
// as sinks if they are true positives. Therefore arguments that are not modeled as sinks
|
||||
// are unlikely to be true positives.
|
||||
call instanceof DatabaseAccess
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class ReceiverIsHttpRequestExpressionCharacteristic extends NosqlInjectionSinkEndpointFilterCharacteristic {
|
||||
ReceiverIsHttpRequestExpressionCharacteristic() { this = "receiver is a HTTP request expression" }
|
||||
|
||||
override predicate appliesToEndpoint(DataFlow::Node n) {
|
||||
exists(DataFlow::CallNode call | n = call.getAnArgument() |
|
||||
// Remove calls to APIs that aren't relevant to NoSQL injection
|
||||
call.getReceiver() instanceof Http::RequestNode
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class ReceiverIsHttpResponseExpressionCharacteristic extends NosqlInjectionSinkEndpointFilterCharacteristic {
|
||||
ReceiverIsHttpResponseExpressionCharacteristic() {
|
||||
this = "receiver is a HTTP response expression"
|
||||
}
|
||||
|
||||
override predicate appliesToEndpoint(DataFlow::Node n) {
|
||||
exists(DataFlow::CallNode call | n = call.getAnArgument() |
|
||||
// Remove calls to APIs that aren't relevant to NoSQL injection
|
||||
call.getReceiver() instanceof Http::ResponseNode
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class NotDirectArgumentToLikelyExternalLibraryCallOrHeuristicSinkNosqlCharacteristic extends NosqlInjectionSinkEndpointFilterCharacteristic {
|
||||
NotDirectArgumentToLikelyExternalLibraryCallOrHeuristicSinkNosqlCharacteristic() {
|
||||
this = "not a direct argument to a likely external library call or a heuristic sink (nosql)"
|
||||
}
|
||||
|
||||
override predicate appliesToEndpoint(DataFlow::Node n) {
|
||||
// Require NoSQL injection sink candidates to be (a) direct arguments to external library calls
|
||||
// or (b) heuristic sinks for NoSQL injection.
|
||||
//
|
||||
// ## Direct arguments to external library calls
|
||||
//
|
||||
// The `flowsToArgumentOfLikelyExternalLibraryCall` endpoint filter
|
||||
// allows sink candidates which are within object literals or array literals, for example
|
||||
// `req.sendFile(_, { path: ENDPOINT })`.
|
||||
//
|
||||
// However, the NoSQL injection query deals differently with these types of sinks compared to
|
||||
// other security queries. Other security queries such as SQL injection tend to treat
|
||||
// `ENDPOINT` as the ground truth sink, but the NoSQL injection query instead treats
|
||||
// `{ path: ENDPOINT }` as the ground truth sink and defines an additional flow step to ensure
|
||||
// data flows from `ENDPOINT` to the ground truth sink `{ path: ENDPOINT }`.
|
||||
//
|
||||
// Therefore for the NoSQL injection boosted query, we must ignore sink candidates within object
|
||||
// literals or array literals, to avoid having multiple alerts for the same security
|
||||
// vulnerability (one FP where the sink is `ENDPOINT` and one TP where the sink is
|
||||
// `{ path: ENDPOINT }`). We accomplish this by directly testing that the sink candidate is an
|
||||
// argument of a likely external library call.
|
||||
//
|
||||
// ## Heuristic sinks
|
||||
//
|
||||
// We also allow heuristic sinks in addition to direct arguments to external library calls.
|
||||
// These are copied from the `HeuristicNosqlInjectionSink` class defined within
|
||||
// `codeql/javascript/ql/src/semmle/javascript/heuristics/AdditionalSinks.qll`.
|
||||
// We can't reuse the class because importing that file would cause us to treat these
|
||||
// heuristic sinks as known sinks.
|
||||
not n = getALikelyExternalLibraryCall().getAnArgument() and
|
||||
not (
|
||||
SyntacticHeuristics::isAssignedToOrConcatenatedWith(n, "(?i)(nosql|query)") or
|
||||
SyntacticHeuristics::isArgTo(n, "(?i)(query)")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** An EndpointFilterCharacteristic that indicates that an endpoint is unlikely to be a SQL injection sink. */
|
||||
abstract private class SqlInjectionSinkEndpointFilterCharacteristic extends EndpointFilterCharacteristic {
|
||||
bindingset[this]
|
||||
SqlInjectionSinkEndpointFilterCharacteristic() { any() }
|
||||
|
||||
override predicate hasImplications(
|
||||
EndpointType endpointClass, boolean isPositiveIndicator, float confidence
|
||||
) {
|
||||
endpointClass instanceof SqlInjectionSinkType and
|
||||
isPositiveIndicator = false and
|
||||
confidence = mediumConfidence()
|
||||
}
|
||||
}
|
||||
|
||||
private class PreparedSqlStatementCharacteristic extends SqlInjectionSinkEndpointFilterCharacteristic {
|
||||
PreparedSqlStatementCharacteristic() { this = "prepared SQL statement" }
|
||||
|
||||
override predicate appliesToEndpoint(DataFlow::Node n) {
|
||||
exists(DataFlow::CallNode call | n = call.getAnArgument() |
|
||||
// prepared statements for SQL
|
||||
any(DataFlow::CallNode cn | cn.getCalleeName() = "prepare")
|
||||
.getAMethodCall("run")
|
||||
.getAnArgument() = n
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class ArrayCreationCharacteristic extends SqlInjectionSinkEndpointFilterCharacteristic {
|
||||
ArrayCreationCharacteristic() { this = "array creation" }
|
||||
|
||||
override predicate appliesToEndpoint(DataFlow::Node n) {
|
||||
exists(DataFlow::CallNode call | n = call.getAnArgument() |
|
||||
n instanceof DataFlow::ArrayCreationNode
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class HtmlOrRenderingCharacteristic extends SqlInjectionSinkEndpointFilterCharacteristic {
|
||||
HtmlOrRenderingCharacteristic() { this = "HTML / rendering" }
|
||||
|
||||
override predicate appliesToEndpoint(DataFlow::Node n) {
|
||||
exists(DataFlow::CallNode call | n = call.getAnArgument() |
|
||||
// UI is unrelated to SQL
|
||||
call.getCalleeName().regexpMatch("(?i).*(render|html).*")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class NotAnArgumentToLikelyExternalLibraryCallOrHeuristicSinkCharacteristic extends SqlInjectionSinkEndpointFilterCharacteristic {
|
||||
NotAnArgumentToLikelyExternalLibraryCallOrHeuristicSinkCharacteristic() {
|
||||
this = "not an argument to a likely external library call or a heuristic sink"
|
||||
}
|
||||
|
||||
override predicate appliesToEndpoint(DataFlow::Node n) {
|
||||
// Require SQL injection sink candidates to be (a) arguments to external library calls
|
||||
// (possibly indirectly), or (b) heuristic sinks.
|
||||
//
|
||||
// Heuristic sinks are copied from the `HeuristicSqlInjectionSink` class defined within
|
||||
// `codeql/javascript/ql/src/semmle/javascript/heuristics/AdditionalSinks.qll`.
|
||||
// We can't reuse the class because importing that file would cause us to treat these
|
||||
// heuristic sinks as known sinks.
|
||||
not flowsToArgumentOfLikelyExternalLibraryCall(n) and
|
||||
not (
|
||||
SyntacticHeuristics::isAssignedToOrConcatenatedWith(n, "(?i)(sql|query)") or
|
||||
SyntacticHeuristics::isArgTo(n, "(?i)(query)") or
|
||||
SyntacticHeuristics::isConcatenatedWithString(n,
|
||||
"(?s).*(ALTER|COUNT|CREATE|DATABASE|DELETE|DISTINCT|DROP|FROM|GROUP|INSERT|INTO|LIMIT|ORDER|SELECT|TABLE|UPDATE|WHERE).*")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** An EndpointFilterCharacteristic that indicates that an endpoint is unlikely to be a tainted path injection sink. */
|
||||
abstract private class TaintedPathSinkEndpointFilterCharacteristic extends EndpointFilterCharacteristic {
|
||||
bindingset[this]
|
||||
TaintedPathSinkEndpointFilterCharacteristic() { any() }
|
||||
|
||||
override predicate hasImplications(
|
||||
EndpointType endpointClass, boolean isPositiveIndicator, float confidence
|
||||
) {
|
||||
endpointClass instanceof TaintedPathSinkType and
|
||||
isPositiveIndicator = false and
|
||||
confidence = mediumConfidence()
|
||||
}
|
||||
}
|
||||
|
||||
private class NotDirectArgumentToLikelyExternalLibraryCallOrHeuristicSinkTaintedPathCharacteristic extends TaintedPathSinkEndpointFilterCharacteristic {
|
||||
NotDirectArgumentToLikelyExternalLibraryCallOrHeuristicSinkTaintedPathCharacteristic() {
|
||||
this =
|
||||
"not a direct argument to a likely external library call or a heuristic sink (tainted path)"
|
||||
}
|
||||
|
||||
override predicate appliesToEndpoint(DataFlow::Node n) {
|
||||
// Require path injection sink candidates to be (a) arguments to external library calls
|
||||
// (possibly indirectly), or (b) heuristic sinks.
|
||||
//
|
||||
// Heuristic sinks are mostly copied from the `HeuristicTaintedPathSink` class defined within
|
||||
// `codeql/javascript/ql/src/semmle/javascript/heuristics/AdditionalSinks.qll`.
|
||||
// We can't reuse the class because importing that file would cause us to treat these
|
||||
// heuristic sinks as known sinks.
|
||||
not flowsToArgumentOfLikelyExternalLibraryCall(n) and
|
||||
not (
|
||||
SyntacticHeuristics::isAssignedToOrConcatenatedWith(n, "(?i)(file|folder|dir|absolute)")
|
||||
or
|
||||
SyntacticHeuristics::isArgTo(n, "(?i)(get|read)file")
|
||||
or
|
||||
exists(string pathPattern |
|
||||
// paths with at least two parts, and either a trailing or leading slash
|
||||
pathPattern = "(?i)([a-z0-9_.-]+/){2,}" or
|
||||
pathPattern = "(?i)(/[a-z0-9_.-]+){2,}"
|
||||
|
|
||||
SyntacticHeuristics::isConcatenatedWithString(n, pathPattern)
|
||||
)
|
||||
or
|
||||
SyntacticHeuristics::isConcatenatedWithStrings(".*/", n, "/.*")
|
||||
or
|
||||
// In addition to the names from `HeuristicTaintedPathSink` in the
|
||||
// `isAssignedToOrConcatenatedWith` predicate call above, we also allow the noisier "path"
|
||||
// name.
|
||||
SyntacticHeuristics::isAssignedToOrConcatenatedWith(n, "(?i)path")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** An EndpointFilterCharacteristic that indicates that an endpoint is unlikely to be an XSS sink. */
|
||||
abstract private class XssSinkEndpointFilterCharacteristic extends EndpointFilterCharacteristic {
|
||||
bindingset[this]
|
||||
XssSinkEndpointFilterCharacteristic() { any() }
|
||||
|
||||
override predicate hasImplications(
|
||||
EndpointType endpointClass, boolean isPositiveIndicator, float confidence
|
||||
) {
|
||||
endpointClass instanceof XssSinkType and
|
||||
isPositiveIndicator = false and
|
||||
confidence = mediumConfidence()
|
||||
}
|
||||
}
|
||||
|
||||
private class SetStateCallsInReactApplicationsCharacteristic extends XssSinkEndpointFilterCharacteristic {
|
||||
SetStateCallsInReactApplicationsCharacteristic() {
|
||||
this = "setState calls ought to be safe in react applications"
|
||||
}
|
||||
|
||||
override predicate appliesToEndpoint(DataFlow::Node n) {
|
||||
exists(DataFlow::CallNode call | n = call.getAnArgument() | call.getCalleeName() = "setState")
|
||||
}
|
||||
}
|
||||
|
||||
private class NotDirectArgumentToLikelyExternalLibraryCallOrHeuristicSinkXssCharacteristic extends XssSinkEndpointFilterCharacteristic {
|
||||
NotDirectArgumentToLikelyExternalLibraryCallOrHeuristicSinkXssCharacteristic() {
|
||||
this = "not a direct argument to a likely external library call or a heuristic sink (xss)"
|
||||
}
|
||||
|
||||
override predicate appliesToEndpoint(DataFlow::Node n) {
|
||||
// Require XSS sink candidates to be (a) arguments to external library calls (possibly
|
||||
// indirectly), or (b) heuristic sinks.
|
||||
//
|
||||
// Heuristic sinks are copied from the `HeuristicDomBasedXssSink` class defined within
|
||||
// `codeql/javascript/ql/src/semmle/javascript/heuristics/AdditionalSinks.qll`.
|
||||
// We can't reuse the class because importing that file would cause us to treat these
|
||||
// heuristic sinks as known sinks.
|
||||
not flowsToArgumentOfLikelyExternalLibraryCall(n) and
|
||||
not (
|
||||
SyntacticHeuristics::isAssignedToOrConcatenatedWith(n, "(?i)(html|innerhtml)")
|
||||
or
|
||||
SyntacticHeuristics::isArgTo(n, "(?i)(html|render)")
|
||||
or
|
||||
n instanceof StringOps::HtmlConcatenationLeaf
|
||||
or
|
||||
SyntacticHeuristics::isConcatenatedWithStrings("(?is).*<[a-z ]+.*", n, "(?s).*>.*")
|
||||
or
|
||||
// In addition to the heuristic sinks from `HeuristicDomBasedXssSink`, explicitly allow
|
||||
// property writes like `elem.innerHTML = <TAINT>` that may not be picked up as HTML
|
||||
// concatenation leaves.
|
||||
exists(DataFlow::PropWrite pw |
|
||||
pw.getPropertyName().regexpMatch("(?i).*html*") and
|
||||
pw.getRhs() = n
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/**
|
||||
* For internal use only.
|
||||
*
|
||||
* Extracts data about the database for use in adaptive threat modeling (ATM).
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/**
|
||||
* For internal use only.
|
||||
*
|
||||
* Provides an implementation of scoring alerts for use in adaptive threat modeling (ATM).
|
||||
|
||||
@@ -1,220 +0,0 @@
|
||||
/**
|
||||
* For internal use only.
|
||||
*
|
||||
* Defines a set of reasons why a particular endpoint was filtered out. This set of reasons
|
||||
* contains both reasons why an endpoint could be `NotASink` and reasons why an endpoint could be
|
||||
* `LikelyNotASink`. The `NotASinkReason`s defined here are exhaustive, but the
|
||||
* `LikelyNotASinkReason`s are not exhaustive.
|
||||
*/
|
||||
newtype TFilteringReason =
|
||||
TIsArgumentToBuiltinFunctionReason() or
|
||||
TLodashUnderscoreArgumentReason() or
|
||||
TClientRequestReason() or
|
||||
TPromiseDefinitionReason() or
|
||||
TCryptographicKeyReason() or
|
||||
TCryptographicOperationFlowReason() or
|
||||
TLoggerMethodReason() or
|
||||
TTimeoutReason() or
|
||||
TReceiverStorageReason() or
|
||||
TStringStartsWithReason() or
|
||||
TStringEndsWithReason() or
|
||||
TStringRegExpTestReason() or
|
||||
TEventRegistrationReason() or
|
||||
TEventDispatchReason() or
|
||||
TMembershipCandidateTestReason() or
|
||||
TFileSystemAccessReason() or
|
||||
TDatabaseAccessReason() or
|
||||
TDomReason() or
|
||||
TNextFunctionCallReason() or
|
||||
TArgumentToArrayReason() or
|
||||
TArgumentToBuiltinGlobalVarRefReason() or
|
||||
TConstantReceiverReason() or
|
||||
TBuiltinCallNameReason() or
|
||||
TBase64ManipulationReason() or
|
||||
TJQueryArgumentReason() or
|
||||
TDojoRequireReason()
|
||||
|
||||
/** A reason why a particular endpoint was filtered out by the endpoint filters. */
|
||||
abstract class FilteringReason extends TFilteringReason {
|
||||
abstract string getDescription();
|
||||
|
||||
abstract int getEncoding();
|
||||
|
||||
string toString() { result = getDescription() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A reason why a particular endpoint might be considered to be `NotASink`.
|
||||
*
|
||||
* An endpoint is `NotASink` if it has at least one `NotASinkReason`, it does not have any
|
||||
* `LikelyNotASinkReason`s, and it is not a known sink.
|
||||
*/
|
||||
abstract class NotASinkReason extends FilteringReason { }
|
||||
|
||||
/**
|
||||
* A reason why a particular endpoint might be considered to be `LikelyNotASink`.
|
||||
*
|
||||
* An endpoint is `LikelyNotASink` if it has at least one `LikelyNotASinkReason` and it is not a
|
||||
* known sink.
|
||||
*/
|
||||
abstract class LikelyNotASinkReason extends FilteringReason { }
|
||||
|
||||
class IsArgumentToBuiltinFunctionReason extends NotASinkReason, TIsArgumentToBuiltinFunctionReason {
|
||||
override string getDescription() { result = "IsArgumentToBuiltinFunction" }
|
||||
|
||||
override int getEncoding() { result = 5 }
|
||||
}
|
||||
|
||||
class LodashUnderscoreArgumentReason extends NotASinkReason, TLodashUnderscoreArgumentReason {
|
||||
override string getDescription() { result = "LodashUnderscoreArgument" }
|
||||
|
||||
override int getEncoding() { result = 6 }
|
||||
}
|
||||
|
||||
class ClientRequestReason extends NotASinkReason, TClientRequestReason {
|
||||
override string getDescription() { result = "ClientRequest" }
|
||||
|
||||
override int getEncoding() { result = 7 }
|
||||
}
|
||||
|
||||
class PromiseDefinitionReason extends NotASinkReason, TPromiseDefinitionReason {
|
||||
override string getDescription() { result = "PromiseDefinition" }
|
||||
|
||||
override int getEncoding() { result = 8 }
|
||||
}
|
||||
|
||||
class CryptographicKeyReason extends NotASinkReason, TCryptographicKeyReason {
|
||||
override string getDescription() { result = "CryptographicKey" }
|
||||
|
||||
override int getEncoding() { result = 9 }
|
||||
}
|
||||
|
||||
class CryptographicOperationFlowReason extends NotASinkReason, TCryptographicOperationFlowReason {
|
||||
override string getDescription() { result = "CryptographicOperationFlow" }
|
||||
|
||||
override int getEncoding() { result = 10 }
|
||||
}
|
||||
|
||||
class LoggerMethodReason extends NotASinkReason, TLoggerMethodReason {
|
||||
override string getDescription() { result = "LoggerMethod" }
|
||||
|
||||
override int getEncoding() { result = 11 }
|
||||
}
|
||||
|
||||
class TimeoutReason extends NotASinkReason, TTimeoutReason {
|
||||
override string getDescription() { result = "Timeout" }
|
||||
|
||||
override int getEncoding() { result = 12 }
|
||||
}
|
||||
|
||||
class ReceiverStorageReason extends NotASinkReason, TReceiverStorageReason {
|
||||
override string getDescription() { result = "ReceiverStorage" }
|
||||
|
||||
override int getEncoding() { result = 13 }
|
||||
}
|
||||
|
||||
class StringStartsWithReason extends NotASinkReason, TStringStartsWithReason {
|
||||
override string getDescription() { result = "StringStartsWith" }
|
||||
|
||||
override int getEncoding() { result = 14 }
|
||||
}
|
||||
|
||||
class StringEndsWithReason extends NotASinkReason, TStringEndsWithReason {
|
||||
override string getDescription() { result = "StringEndsWith" }
|
||||
|
||||
override int getEncoding() { result = 15 }
|
||||
}
|
||||
|
||||
class StringRegExpTestReason extends NotASinkReason, TStringRegExpTestReason {
|
||||
override string getDescription() { result = "StringRegExpTest" }
|
||||
|
||||
override int getEncoding() { result = 16 }
|
||||
}
|
||||
|
||||
class EventRegistrationReason extends NotASinkReason, TEventRegistrationReason {
|
||||
override string getDescription() { result = "EventRegistration" }
|
||||
|
||||
override int getEncoding() { result = 17 }
|
||||
}
|
||||
|
||||
class EventDispatchReason extends NotASinkReason, TEventDispatchReason {
|
||||
override string getDescription() { result = "EventDispatch" }
|
||||
|
||||
override int getEncoding() { result = 18 }
|
||||
}
|
||||
|
||||
class MembershipCandidateTestReason extends NotASinkReason, TMembershipCandidateTestReason {
|
||||
override string getDescription() { result = "MembershipCandidateTest" }
|
||||
|
||||
override int getEncoding() { result = 19 }
|
||||
}
|
||||
|
||||
class FileSystemAccessReason extends NotASinkReason, TFileSystemAccessReason {
|
||||
override string getDescription() { result = "FileSystemAccess" }
|
||||
|
||||
override int getEncoding() { result = 20 }
|
||||
}
|
||||
|
||||
class DatabaseAccessReason extends NotASinkReason, TDatabaseAccessReason {
|
||||
override string getDescription() { result = "DatabaseAccess" }
|
||||
|
||||
override int getEncoding() { result = 21 }
|
||||
}
|
||||
|
||||
class DomReason extends NotASinkReason, TDomReason {
|
||||
override string getDescription() { result = "DOM" }
|
||||
|
||||
override int getEncoding() { result = 22 }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for DomReason */
|
||||
deprecated class DOMReason = DomReason;
|
||||
|
||||
class NextFunctionCallReason extends NotASinkReason, TNextFunctionCallReason {
|
||||
override string getDescription() { result = "NextFunctionCall" }
|
||||
|
||||
override int getEncoding() { result = 23 }
|
||||
}
|
||||
|
||||
class ArgumentToArrayReason extends LikelyNotASinkReason, TArgumentToArrayReason {
|
||||
override string getDescription() { result = "ArgumentToArray" }
|
||||
|
||||
override int getEncoding() { result = 24 }
|
||||
}
|
||||
|
||||
class ArgumentToBuiltinGlobalVarRefReason extends LikelyNotASinkReason,
|
||||
TArgumentToBuiltinGlobalVarRefReason {
|
||||
override string getDescription() { result = "ArgumentToBuiltinGlobalVarRef" }
|
||||
|
||||
override int getEncoding() { result = 25 }
|
||||
}
|
||||
|
||||
class ConstantReceiverReason extends NotASinkReason, TConstantReceiverReason {
|
||||
override string getDescription() { result = "ConstantReceiver" }
|
||||
|
||||
override int getEncoding() { result = 26 }
|
||||
}
|
||||
|
||||
class BuiltinCallNameReason extends NotASinkReason, TBuiltinCallNameReason {
|
||||
override string getDescription() { result = "BuiltinCallName" }
|
||||
|
||||
override int getEncoding() { result = 27 }
|
||||
}
|
||||
|
||||
class Base64ManipulationReason extends NotASinkReason, TBase64ManipulationReason {
|
||||
override string getDescription() { result = "Base64Manipulation" }
|
||||
|
||||
override int getEncoding() { result = 28 }
|
||||
}
|
||||
|
||||
class JQueryArgumentReason extends NotASinkReason, TJQueryArgumentReason {
|
||||
override string getDescription() { result = "JQueryArgument" }
|
||||
|
||||
override int getEncoding() { result = 29 }
|
||||
}
|
||||
|
||||
class DojoRequireReason extends NotASinkReason, TDojoRequireReason {
|
||||
override string getDescription() { result = "DojoRequire" }
|
||||
|
||||
override int getEncoding() { result = 30 }
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/**
|
||||
* FunctionBodyFeatures.qll
|
||||
*
|
||||
* Contains logic relating to the `enclosingFunctionBody` and `enclosingFunctionName` features.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/**
|
||||
* For internal use only.
|
||||
*
|
||||
* A taint-tracking configuration for reasoning about NoSQL injection vulnerabilities.
|
||||
* Defines shared code used by the NoSQL injection boosted query.
|
||||
*/
|
||||
|
||||
@@ -8,145 +9,21 @@ import javascript
|
||||
private import semmle.javascript.heuristics.SyntacticHeuristics
|
||||
private import semmle.javascript.security.dataflow.NosqlInjectionCustomizations
|
||||
import AdaptiveThreatModeling
|
||||
private import CoreKnowledge as CoreKnowledge
|
||||
private import StandardEndpointFilters as StandardEndpointFilters
|
||||
|
||||
module SinkEndpointFilter {
|
||||
/**
|
||||
* Provides a set of reasons why a given data flow node should be excluded as a sink candidate.
|
||||
*
|
||||
* If this predicate has no results for a sink candidate `n`, then we should treat `n` as an
|
||||
* effective sink.
|
||||
*/
|
||||
string getAReasonSinkExcluded(DataFlow::Node sinkCandidate) {
|
||||
result = StandardEndpointFilters::getAReasonSinkExcluded(sinkCandidate)
|
||||
or
|
||||
exists(DataFlow::CallNode call | sinkCandidate = call.getAnArgument() |
|
||||
// additional databases accesses that aren't modeled yet
|
||||
call.(DataFlow::MethodCallNode).getMethodName() =
|
||||
["create", "createCollection", "createIndexes"] and
|
||||
result = "matches database access call heuristic"
|
||||
or
|
||||
// Remove modeled sinks
|
||||
CoreKnowledge::isArgumentToKnownLibrarySinkFunction(sinkCandidate) and
|
||||
result = "modeled sink"
|
||||
or
|
||||
// Remove common kinds of unlikely sinks
|
||||
CoreKnowledge::isKnownStepSrc(sinkCandidate) and
|
||||
result = "predecessor in a modeled flow step"
|
||||
or
|
||||
// Remove modeled database calls. Arguments to modeled calls are very likely to be modeled
|
||||
// as sinks if they are true positives. Therefore arguments that are not modeled as sinks
|
||||
// are unlikely to be true positives.
|
||||
call instanceof DatabaseAccess and
|
||||
result = "modeled database access"
|
||||
or
|
||||
// Remove calls to APIs that aren't relevant to NoSQL injection
|
||||
call.getReceiver() instanceof Http::RequestNode and
|
||||
result = "receiver is a HTTP request expression"
|
||||
or
|
||||
call.getReceiver() instanceof Http::ResponseNode and
|
||||
result = "receiver is a HTTP response expression"
|
||||
)
|
||||
or
|
||||
// Require NoSQL injection sink candidates to be (a) direct arguments to external library calls
|
||||
// or (b) heuristic sinks for NoSQL injection.
|
||||
//
|
||||
// ## Direct arguments to external library calls
|
||||
//
|
||||
// The `StandardEndpointFilters::flowsToArgumentOfLikelyExternalLibraryCall` endpoint filter
|
||||
// allows sink candidates which are within object literals or array literals, for example
|
||||
// `req.sendFile(_, { path: ENDPOINT })`.
|
||||
//
|
||||
// However, the NoSQL injection query deals differently with these types of sinks compared to
|
||||
// other security queries. Other security queries such as SQL injection tend to treat
|
||||
// `ENDPOINT` as the ground truth sink, but the NoSQL injection query instead treats
|
||||
// `{ path: ENDPOINT }` as the ground truth sink and defines an additional flow step to ensure
|
||||
// data flows from `ENDPOINT` to the ground truth sink `{ path: ENDPOINT }`.
|
||||
//
|
||||
// Therefore for the NoSQL injection boosted query, we must ignore sink candidates within object
|
||||
// literals or array literals, to avoid having multiple alerts for the same security
|
||||
// vulnerability (one FP where the sink is `ENDPOINT` and one TP where the sink is
|
||||
// `{ path: ENDPOINT }`). We accomplish this by directly testing that the sink candidate is an
|
||||
// argument of a likely external library call.
|
||||
//
|
||||
// ## Heuristic sinks
|
||||
//
|
||||
// We also allow heuristic sinks in addition to direct arguments to external library calls.
|
||||
// These are copied from the `HeuristicNosqlInjectionSink` class defined within
|
||||
// `codeql/javascript/ql/src/semmle/javascript/heuristics/AdditionalSinks.qll`.
|
||||
// We can't reuse the class because importing that file would cause us to treat these
|
||||
// heuristic sinks as known sinks.
|
||||
not sinkCandidate = StandardEndpointFilters::getALikelyExternalLibraryCall().getAnArgument() and
|
||||
not (
|
||||
isAssignedToOrConcatenatedWith(sinkCandidate, "(?i)(nosql|query)") or
|
||||
isArgTo(sinkCandidate, "(?i)(query)")
|
||||
) and
|
||||
result = "not a direct argument to a likely external library call or a heuristic sink"
|
||||
}
|
||||
}
|
||||
|
||||
class NosqlInjectionAtmConfig extends AtmConfig {
|
||||
NosqlInjectionAtmConfig() { this = "NosqlInjectionATMConfig" }
|
||||
NosqlInjectionAtmConfig() { this = "NosqlInjectionAtmConfig" }
|
||||
|
||||
override predicate isKnownSource(DataFlow::Node source) {
|
||||
source instanceof NosqlInjection::Source or TaintedObject::isSource(source, _)
|
||||
}
|
||||
|
||||
override predicate isEffectiveSink(DataFlow::Node sinkCandidate) {
|
||||
not exists(SinkEndpointFilter::getAReasonSinkExcluded(sinkCandidate))
|
||||
}
|
||||
|
||||
override EndpointType getASinkEndpointType() { result instanceof NosqlInjectionSinkType }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for NosqlInjectionAtmConfig */
|
||||
deprecated class NosqlInjectionATMConfig = NosqlInjectionAtmConfig;
|
||||
|
||||
/** Holds if src -> trg is an additional flow step in the non-boosted NoSql injection security query. */
|
||||
predicate isBaseAdditionalFlowStep(
|
||||
DataFlow::Node src, DataFlow::Node trg, DataFlow::FlowLabel inlbl, DataFlow::FlowLabel outlbl
|
||||
) {
|
||||
TaintedObject::step(src, trg, inlbl, outlbl)
|
||||
or
|
||||
// additional flow step to track taint through NoSQL query objects
|
||||
inlbl = TaintedObject::label() and
|
||||
outlbl = TaintedObject::label() and
|
||||
exists(NoSql::Query query, DataFlow::SourceNode queryObj |
|
||||
queryObj.flowsTo(query) and
|
||||
queryObj.flowsTo(trg) and
|
||||
src = queryObj.getAPropertyWrite().getRhs()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a value that is (transitively) written to `query`, where `query` is a NoSQL sink.
|
||||
*
|
||||
* This predicate allows us to propagate data flow through property writes and array constructors
|
||||
* within a query object, enabling the security query to pick up NoSQL injection vulnerabilities
|
||||
* involving more complex queries.
|
||||
*/
|
||||
DataFlow::Node getASubexpressionWithinQuery(DataFlow::Node query) {
|
||||
any(NosqlInjectionAtmConfig cfg).isEffectiveSink(query) and
|
||||
exists(DataFlow::SourceNode receiver |
|
||||
receiver = [getASubexpressionWithinQuery(query), query].getALocalSource()
|
||||
|
|
||||
result =
|
||||
[receiver.getAPropertyWrite().getRhs(), receiver.(DataFlow::ArrayCreationNode).getAnElement()]
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for reasoning about NoSQL injection vulnerabilities.
|
||||
*
|
||||
* This is largely a copy of the taint tracking configuration for the standard NoSQL injection
|
||||
* query, except additional ATM sinks have been added and the additional flow step has been
|
||||
* generalised to cover the sinks predicted by ATM.
|
||||
*/
|
||||
class Configuration extends TaintTracking::Configuration {
|
||||
Configuration() { this = "NosqlInjectionATM" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof NosqlInjection::Source }
|
||||
/*
|
||||
* This is largely a copy of the taint tracking configuration for the standard NoSQL injection
|
||||
* query, except additional ATM sinks have been added and the additional flow step has been
|
||||
* generalised to cover the sinks predicted by ATM.
|
||||
*/
|
||||
|
||||
override predicate isSource(DataFlow::Node source, DataFlow::FlowLabel label) {
|
||||
TaintedObject::isSource(source, label)
|
||||
@@ -156,7 +33,7 @@ class Configuration extends TaintTracking::Configuration {
|
||||
sink.(NosqlInjection::Sink).getAFlowLabel() = label
|
||||
or
|
||||
// Allow effective sinks to have any taint label
|
||||
any(NosqlInjectionAtmConfig cfg).isEffectiveSink(sink)
|
||||
isEffectiveSink(sink)
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) {
|
||||
@@ -175,7 +52,43 @@ class Configuration extends TaintTracking::Configuration {
|
||||
isBaseAdditionalFlowStep(src, trg, inlbl, outlbl)
|
||||
or
|
||||
// relaxed version of previous step to track taint through unmodeled NoSQL query objects
|
||||
any(NosqlInjectionAtmConfig cfg).isEffectiveSink(trg) and
|
||||
isEffectiveSink(trg) and
|
||||
src = getASubexpressionWithinQuery(trg)
|
||||
}
|
||||
|
||||
/** Holds if src -> trg is an additional flow step in the non-boosted NoSql injection security query. */
|
||||
private predicate isBaseAdditionalFlowStep(
|
||||
DataFlow::Node src, DataFlow::Node trg, DataFlow::FlowLabel inlbl, DataFlow::FlowLabel outlbl
|
||||
) {
|
||||
TaintedObject::step(src, trg, inlbl, outlbl)
|
||||
or
|
||||
// additional flow step to track taint through NoSQL query objects
|
||||
inlbl = TaintedObject::label() and
|
||||
outlbl = TaintedObject::label() and
|
||||
exists(NoSql::Query query, DataFlow::SourceNode queryObj |
|
||||
queryObj.flowsTo(query) and
|
||||
queryObj.flowsTo(trg) and
|
||||
src = queryObj.getAPropertyWrite().getRhs()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a value that is (transitively) written to `query`, where `query` is a NoSQL sink.
|
||||
*
|
||||
* This predicate allows us to propagate data flow through property writes and array constructors
|
||||
* within a query object, enabling the security query to pick up NoSQL injection vulnerabilities
|
||||
* involving more complex queries.
|
||||
*/
|
||||
private DataFlow::Node getASubexpressionWithinQuery(DataFlow::Node query) {
|
||||
isEffectiveSink(query) and
|
||||
exists(DataFlow::SourceNode receiver |
|
||||
receiver = [getASubexpressionWithinQuery(query), query].getALocalSource()
|
||||
|
|
||||
result =
|
||||
[
|
||||
receiver.getAPropertyWrite().getRhs(),
|
||||
receiver.(DataFlow::ArrayCreationNode).getAnElement()
|
||||
]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,94 +1,25 @@
|
||||
/**
|
||||
* For internal use only.
|
||||
*
|
||||
* A taint-tracking configuration for reasoning about SQL injection vulnerabilities.
|
||||
* Defines shared code used by the SQL injection boosted query.
|
||||
*/
|
||||
|
||||
import semmle.javascript.heuristics.SyntacticHeuristics
|
||||
import semmle.javascript.security.dataflow.SqlInjectionCustomizations
|
||||
import AdaptiveThreatModeling
|
||||
import CoreKnowledge as CoreKnowledge
|
||||
import StandardEndpointFilters as StandardEndpointFilters
|
||||
|
||||
/**
|
||||
* This module provides logic to filter candidate sinks to those which are likely SQL injection
|
||||
* sinks.
|
||||
*/
|
||||
module SinkEndpointFilter {
|
||||
private import javascript
|
||||
private import SQL
|
||||
|
||||
/**
|
||||
* Provides a set of reasons why a given data flow node should be excluded as a sink candidate.
|
||||
*
|
||||
* If this predicate has no results for a sink candidate `n`, then we should treat `n` as an
|
||||
* effective sink.
|
||||
*/
|
||||
string getAReasonSinkExcluded(DataFlow::Node sinkCandidate) {
|
||||
result = StandardEndpointFilters::getAReasonSinkExcluded(sinkCandidate)
|
||||
or
|
||||
exists(DataFlow::CallNode call | sinkCandidate = call.getAnArgument() |
|
||||
// prepared statements for SQL
|
||||
any(DataFlow::CallNode cn | cn.getCalleeName() = "prepare")
|
||||
.getAMethodCall("run")
|
||||
.getAnArgument() = sinkCandidate and
|
||||
result = "prepared SQL statement"
|
||||
or
|
||||
sinkCandidate instanceof DataFlow::ArrayCreationNode and
|
||||
result = "array creation"
|
||||
or
|
||||
// UI is unrelated to SQL
|
||||
call.getCalleeName().regexpMatch("(?i).*(render|html).*") and
|
||||
result = "HTML / rendering"
|
||||
)
|
||||
or
|
||||
// Require SQL injection sink candidates to be (a) arguments to external library calls
|
||||
// (possibly indirectly), or (b) heuristic sinks.
|
||||
//
|
||||
// Heuristic sinks are copied from the `HeuristicSqlInjectionSink` class defined within
|
||||
// `codeql/javascript/ql/src/semmle/javascript/heuristics/AdditionalSinks.qll`.
|
||||
// We can't reuse the class because importing that file would cause us to treat these
|
||||
// heuristic sinks as known sinks.
|
||||
not StandardEndpointFilters::flowsToArgumentOfLikelyExternalLibraryCall(sinkCandidate) and
|
||||
not (
|
||||
isAssignedToOrConcatenatedWith(sinkCandidate, "(?i)(sql|query)") or
|
||||
isArgTo(sinkCandidate, "(?i)(query)") or
|
||||
isConcatenatedWithString(sinkCandidate,
|
||||
"(?s).*(ALTER|COUNT|CREATE|DATABASE|DELETE|DISTINCT|DROP|FROM|GROUP|INSERT|INTO|LIMIT|ORDER|SELECT|TABLE|UPDATE|WHERE).*")
|
||||
) and
|
||||
result = "not an argument to a likely external library call or a heuristic sink"
|
||||
}
|
||||
}
|
||||
|
||||
class SqlInjectionAtmConfig extends AtmConfig {
|
||||
SqlInjectionAtmConfig() { this = "SqlInjectionATMConfig" }
|
||||
SqlInjectionAtmConfig() { this = "SqlInjectionAtmConfig" }
|
||||
|
||||
override predicate isKnownSource(DataFlow::Node source) { source instanceof SqlInjection::Source }
|
||||
|
||||
override predicate isEffectiveSink(DataFlow::Node sinkCandidate) {
|
||||
not exists(SinkEndpointFilter::getAReasonSinkExcluded(sinkCandidate))
|
||||
}
|
||||
|
||||
override EndpointType getASinkEndpointType() { result instanceof SqlInjectionSinkType }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for SqlInjectionAtmConfig */
|
||||
deprecated class SqlInjectionATMConfig = SqlInjectionAtmConfig;
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for reasoning about SQL injection vulnerabilities.
|
||||
*
|
||||
* This is largely a copy of the taint tracking configuration for the standard SQL injection
|
||||
* query, except additional sinks have been added using the sink endpoint filter.
|
||||
*/
|
||||
class Configuration extends TaintTracking::Configuration {
|
||||
Configuration() { this = "SqlInjectionATM" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof SqlInjection::Source }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
sink instanceof SqlInjection::Sink or any(SqlInjectionAtmConfig cfg).isEffectiveSink(sink)
|
||||
}
|
||||
/*
|
||||
* This is largely a copy of the taint tracking configuration for the standard SQL injection
|
||||
* query, except additional sinks have been added using the sink endpoint filter.
|
||||
*/
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) {
|
||||
super.isSanitizer(node) or
|
||||
|
||||
@@ -1,134 +0,0 @@
|
||||
/**
|
||||
* For internal use only.
|
||||
*
|
||||
* Provides classes and predicates that are useful for endpoint filters.
|
||||
*
|
||||
* The standard use of this library is to make use of `isPotentialEffectiveSink/1`
|
||||
*/
|
||||
|
||||
private import javascript
|
||||
private import semmle.javascript.filters.ClassifyFiles as ClassifyFiles
|
||||
private import semmle.javascript.heuristics.SyntacticHeuristics
|
||||
private import CoreKnowledge as CoreKnowledge
|
||||
|
||||
/** Provides a set of reasons why a given data flow node should be excluded as a sink candidate. */
|
||||
string getAReasonSinkExcluded(DataFlow::Node n) {
|
||||
isArgumentToModeledFunction(n) and result = "argument to modeled function"
|
||||
or
|
||||
isArgumentToSinklessLibrary(n) and result = "argument to sinkless library"
|
||||
or
|
||||
isSanitizer(n) and result = "sanitizer"
|
||||
or
|
||||
isPredicate(n) and result = "predicate"
|
||||
or
|
||||
isHash(n) and result = "hash"
|
||||
or
|
||||
isNumeric(n) and result = "numeric"
|
||||
or
|
||||
// Ignore candidate sinks within externs, generated, library, and test code
|
||||
exists(string category | category = ["externs", "generated", "library", "test"] |
|
||||
ClassifyFiles::classify(n.getFile(), category) and
|
||||
result = "in " + category + " file"
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the node `n` is an argument to a function that has a manual model.
|
||||
*/
|
||||
predicate isArgumentToModeledFunction(DataFlow::Node n) {
|
||||
exists(DataFlow::InvokeNode invk, DataFlow::Node known |
|
||||
invk.getAnArgument() = n and invk.getAnArgument() = known and isSomeModeledArgument(known)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the node `n` is an argument that has a manual model.
|
||||
*/
|
||||
predicate isSomeModeledArgument(DataFlow::Node n) {
|
||||
CoreKnowledge::isKnownLibrarySink(n) or
|
||||
CoreKnowledge::isKnownStepSrc(n) or
|
||||
CoreKnowledge::isOtherModeledArgument(n, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n` appears to be a numeric value.
|
||||
*/
|
||||
predicate isNumeric(DataFlow::Node n) { isReadFrom(n, ".*index.*") }
|
||||
|
||||
/**
|
||||
* Holds if `n` is an argument to a library without sinks.
|
||||
*/
|
||||
predicate isArgumentToSinklessLibrary(DataFlow::Node n) {
|
||||
exists(DataFlow::InvokeNode invk, DataFlow::SourceNode commonSafeLibrary, string libraryName |
|
||||
libraryName = ["slugify", "striptags", "marked"]
|
||||
|
|
||||
commonSafeLibrary = DataFlow::moduleImport(libraryName) and
|
||||
invk = [commonSafeLibrary, commonSafeLibrary.getAPropertyRead()].getAnInvocation() and
|
||||
n = invk.getAnArgument()
|
||||
)
|
||||
}
|
||||
|
||||
predicate isSanitizer(DataFlow::Node n) {
|
||||
exists(DataFlow::CallNode call | n = call.getAnArgument() |
|
||||
call.getCalleeName().regexpMatch("(?i).*(escape|valid(ate)?|sanitize|purify).*")
|
||||
)
|
||||
}
|
||||
|
||||
predicate isPredicate(DataFlow::Node n) {
|
||||
exists(DataFlow::CallNode call | n = call.getAnArgument() |
|
||||
call.getCalleeName().regexpMatch("(equals|(|is|has|can)(_|[A-Z])).*")
|
||||
)
|
||||
}
|
||||
|
||||
predicate isHash(DataFlow::Node n) {
|
||||
exists(DataFlow::CallNode call | n = call.getAnArgument() |
|
||||
call.getCalleeName().regexpMatch("(?i)^(sha\\d*|md5|hash)$")
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the data flow node is a (possibly indirect) argument of a likely external library call.
|
||||
*
|
||||
* This includes direct arguments of likely external library calls as well as nested object
|
||||
* literals within those calls.
|
||||
*/
|
||||
predicate flowsToArgumentOfLikelyExternalLibraryCall(DataFlow::Node n) {
|
||||
n = getACallWithoutCallee().getAnArgument()
|
||||
or
|
||||
exists(DataFlow::SourceNode src | flowsToArgumentOfLikelyExternalLibraryCall(src) |
|
||||
n = src.getAPropertyWrite().getRhs()
|
||||
)
|
||||
or
|
||||
exists(DataFlow::ArrayCreationNode arr | flowsToArgumentOfLikelyExternalLibraryCall(arr) |
|
||||
n = arr.getAnElement()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get calls which are likely to be to external non-built-in libraries.
|
||||
*/
|
||||
DataFlow::CallNode getALikelyExternalLibraryCall() { result = getACallWithoutCallee() }
|
||||
|
||||
/**
|
||||
* Gets a node that flows to callback-parameter `p`.
|
||||
*/
|
||||
private DataFlow::SourceNode getACallback(DataFlow::ParameterNode p, DataFlow::TypeBackTracker t) {
|
||||
t.start() and
|
||||
result = p and
|
||||
any(DataFlow::FunctionNode f).getLastParameter() = p and
|
||||
exists(p.getACall())
|
||||
or
|
||||
exists(DataFlow::TypeBackTracker t2 | result = getACallback(p, t2).backtrack(t2, t))
|
||||
}
|
||||
|
||||
/**
|
||||
* Get calls for which we do not have the callee (i.e. the definition of the called function). This
|
||||
* acts as a heuristic for identifying calls to external library functions.
|
||||
*/
|
||||
private DataFlow::CallNode getACallWithoutCallee() {
|
||||
forall(Function callee | callee = result.getACallee() | callee.getTopLevel().isExterns()) and
|
||||
not exists(DataFlow::ParameterNode param, DataFlow::FunctionNode callback |
|
||||
param.flowsTo(result.getCalleeNode()) and
|
||||
callback = getACallback(param, DataFlow::TypeBackTracker::end())
|
||||
)
|
||||
}
|
||||
@@ -1,95 +1,31 @@
|
||||
/**
|
||||
* For internal use only.
|
||||
*
|
||||
* A taint-tracking configuration for reasoning about path injection vulnerabilities.
|
||||
* Defines shared code used by the path injection boosted query.
|
||||
*/
|
||||
|
||||
import semmle.javascript.heuristics.SyntacticHeuristics
|
||||
import semmle.javascript.security.dataflow.TaintedPathCustomizations
|
||||
import AdaptiveThreatModeling
|
||||
import CoreKnowledge as CoreKnowledge
|
||||
import StandardEndpointFilters as StandardEndpointFilters
|
||||
|
||||
/**
|
||||
* This module provides logic to filter candidate sinks to those which are likely path injection
|
||||
* sinks.
|
||||
*/
|
||||
module SinkEndpointFilter {
|
||||
private import javascript
|
||||
private import TaintedPath
|
||||
|
||||
/**
|
||||
* Provides a set of reasons why a given data flow node should be excluded as a sink candidate.
|
||||
*
|
||||
* If this predicate has no results for a sink candidate `n`, then we should treat `n` as an
|
||||
* effective sink.
|
||||
*/
|
||||
string getAReasonSinkExcluded(DataFlow::Node sinkCandidate) {
|
||||
result = StandardEndpointFilters::getAReasonSinkExcluded(sinkCandidate)
|
||||
or
|
||||
// Require path injection sink candidates to be (a) arguments to external library calls
|
||||
// (possibly indirectly), or (b) heuristic sinks.
|
||||
//
|
||||
// Heuristic sinks are mostly copied from the `HeuristicTaintedPathSink` class defined within
|
||||
// `codeql/javascript/ql/src/semmle/javascript/heuristics/AdditionalSinks.qll`.
|
||||
// We can't reuse the class because importing that file would cause us to treat these
|
||||
// heuristic sinks as known sinks.
|
||||
not StandardEndpointFilters::flowsToArgumentOfLikelyExternalLibraryCall(sinkCandidate) and
|
||||
not (
|
||||
isAssignedToOrConcatenatedWith(sinkCandidate, "(?i)(file|folder|dir|absolute)")
|
||||
or
|
||||
isArgTo(sinkCandidate, "(?i)(get|read)file")
|
||||
or
|
||||
exists(string pathPattern |
|
||||
// paths with at least two parts, and either a trailing or leading slash
|
||||
pathPattern = "(?i)([a-z0-9_.-]+/){2,}" or
|
||||
pathPattern = "(?i)(/[a-z0-9_.-]+){2,}"
|
||||
|
|
||||
isConcatenatedWithString(sinkCandidate, pathPattern)
|
||||
)
|
||||
or
|
||||
isConcatenatedWithStrings(".*/", sinkCandidate, "/.*")
|
||||
or
|
||||
// In addition to the names from `HeuristicTaintedPathSink` in the
|
||||
// `isAssignedToOrConcatenatedWith` predicate call above, we also allow the noisier "path"
|
||||
// name.
|
||||
isAssignedToOrConcatenatedWith(sinkCandidate, "(?i)path")
|
||||
) and
|
||||
result = "not a direct argument to a likely external library call or a heuristic sink"
|
||||
}
|
||||
}
|
||||
|
||||
class TaintedPathAtmConfig extends AtmConfig {
|
||||
TaintedPathAtmConfig() { this = "TaintedPathATMConfig" }
|
||||
TaintedPathAtmConfig() { this = "TaintedPathAtmConfig" }
|
||||
|
||||
override predicate isKnownSource(DataFlow::Node source) { source instanceof TaintedPath::Source }
|
||||
|
||||
override predicate isEffectiveSink(DataFlow::Node sinkCandidate) {
|
||||
not exists(SinkEndpointFilter::getAReasonSinkExcluded(sinkCandidate))
|
||||
}
|
||||
|
||||
override EndpointType getASinkEndpointType() { result instanceof TaintedPathSinkType }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for TaintedPathAtmConfig */
|
||||
deprecated class TaintedPathATMConfig = TaintedPathAtmConfig;
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for reasoning about path injection vulnerabilities.
|
||||
*
|
||||
* This is largely a copy of the taint tracking configuration for the standard path injection
|
||||
* query, except additional ATM sinks have been added to the `isSink` predicate.
|
||||
*/
|
||||
class Configuration extends TaintTracking::Configuration {
|
||||
Configuration() { this = "TaintedPathATM" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof TaintedPath::Source }
|
||||
/*
|
||||
* This is largely a copy of the taint tracking configuration for the standard path injection
|
||||
* query, except additional ATM sinks have been added to the `isSink` predicate.
|
||||
*/
|
||||
|
||||
override predicate isSink(DataFlow::Node sink, DataFlow::FlowLabel label) {
|
||||
label = sink.(TaintedPath::Sink).getAFlowLabel()
|
||||
or
|
||||
// Allow effective sinks to have any taint label
|
||||
any(TaintedPathAtmConfig cfg).isEffectiveSink(sink)
|
||||
isEffectiveSink(sink)
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof TaintedPath::Sanitizer }
|
||||
@@ -115,7 +51,7 @@ class Configuration extends TaintTracking::Configuration {
|
||||
* of barrier guards, we port the barrier guards for the boosted query from the standard library to
|
||||
* sanitizer guards here.
|
||||
*/
|
||||
class BarrierGuardNodeAsSanitizerGuardNode extends TaintTracking::LabeledSanitizerGuardNode {
|
||||
private class BarrierGuardNodeAsSanitizerGuardNode extends TaintTracking::LabeledSanitizerGuardNode {
|
||||
BarrierGuardNodeAsSanitizerGuardNode() { this instanceof TaintedPath::BarrierGuardNode }
|
||||
|
||||
override predicate sanitizes(boolean outcome, Expr e) {
|
||||
|
||||
@@ -1,95 +1,25 @@
|
||||
/**
|
||||
* For internal use only.
|
||||
*
|
||||
* A taint-tracking configuration for reasoning about XSS vulnerabilities.
|
||||
* Defines shared code used by the XSS boosted query.
|
||||
*/
|
||||
|
||||
private import semmle.javascript.heuristics.SyntacticHeuristics
|
||||
private import semmle.javascript.security.dataflow.DomBasedXssCustomizations
|
||||
import AdaptiveThreatModeling
|
||||
import CoreKnowledge as CoreKnowledge
|
||||
import StandardEndpointFilters as StandardEndpointFilters
|
||||
|
||||
/**
|
||||
* This module provides logic to filter candidate sinks to those which are likely XSS sinks.
|
||||
*/
|
||||
module SinkEndpointFilter {
|
||||
private import javascript
|
||||
private import DomBasedXss
|
||||
|
||||
/**
|
||||
* Provides a set of reasons why a given data flow node should be excluded as a sink candidate.
|
||||
*
|
||||
* If this predicate has no results for a sink candidate `n`, then we should treat `n` as an
|
||||
* effective sink.
|
||||
*/
|
||||
string getAReasonSinkExcluded(DataFlow::Node sinkCandidate) {
|
||||
result = StandardEndpointFilters::getAReasonSinkExcluded(sinkCandidate)
|
||||
or
|
||||
exists(DataFlow::CallNode call | sinkCandidate = call.getAnArgument() |
|
||||
call.getCalleeName() = "setState"
|
||||
) and
|
||||
result = "setState calls ought to be safe in react applications"
|
||||
or
|
||||
// Require XSS sink candidates to be (a) arguments to external library calls (possibly
|
||||
// indirectly), or (b) heuristic sinks.
|
||||
//
|
||||
// Heuristic sinks are copied from the `HeuristicDomBasedXssSink` class defined within
|
||||
// `codeql/javascript/ql/src/semmle/javascript/heuristics/AdditionalSinks.qll`.
|
||||
// We can't reuse the class because importing that file would cause us to treat these
|
||||
// heuristic sinks as known sinks.
|
||||
not StandardEndpointFilters::flowsToArgumentOfLikelyExternalLibraryCall(sinkCandidate) and
|
||||
not (
|
||||
isAssignedToOrConcatenatedWith(sinkCandidate, "(?i)(html|innerhtml)")
|
||||
or
|
||||
isArgTo(sinkCandidate, "(?i)(html|render)")
|
||||
or
|
||||
sinkCandidate instanceof StringOps::HtmlConcatenationLeaf
|
||||
or
|
||||
isConcatenatedWithStrings("(?is).*<[a-z ]+.*", sinkCandidate, "(?s).*>.*")
|
||||
or
|
||||
// In addition to the heuristic sinks from `HeuristicDomBasedXssSink`, explicitly allow
|
||||
// property writes like `elem.innerHTML = <TAINT>` that may not be picked up as HTML
|
||||
// concatenation leaves.
|
||||
exists(DataFlow::PropWrite pw |
|
||||
pw.getPropertyName().regexpMatch("(?i).*html*") and
|
||||
pw.getRhs() = sinkCandidate
|
||||
)
|
||||
) and
|
||||
result = "not a direct argument to a likely external library call or a heuristic sink"
|
||||
}
|
||||
}
|
||||
|
||||
class DomBasedXssAtmConfig extends AtmConfig {
|
||||
DomBasedXssAtmConfig() { this = "DomBasedXssATMConfig" }
|
||||
DomBasedXssAtmConfig() { this = "DomBasedXssAtmConfig" }
|
||||
|
||||
override predicate isKnownSource(DataFlow::Node source) { source instanceof DomBasedXss::Source }
|
||||
|
||||
override predicate isEffectiveSink(DataFlow::Node sinkCandidate) {
|
||||
not exists(SinkEndpointFilter::getAReasonSinkExcluded(sinkCandidate))
|
||||
}
|
||||
|
||||
override EndpointType getASinkEndpointType() { result instanceof XssSinkType }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for DomBasedXssAtmConfig */
|
||||
deprecated class DomBasedXssATMConfig = DomBasedXssAtmConfig;
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for reasoning about XSS vulnerabilities.
|
||||
*
|
||||
* This is largely a copy of the taint tracking configuration for the standard XSSThroughDom query,
|
||||
* except additional ATM sinks have been added to the `isSink` predicate.
|
||||
*/
|
||||
class Configuration extends TaintTracking::Configuration {
|
||||
Configuration() { this = "DomBasedXssATMConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof DomBasedXss::Source }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
sink instanceof DomBasedXss::Sink or
|
||||
any(DomBasedXssAtmConfig cfg).isEffectiveSink(sink)
|
||||
}
|
||||
/*
|
||||
* This is largely a copy of the taint tracking configuration for the standard XSSThroughDom query,
|
||||
* except additional ATM sinks have been added to the `isSink` predicate.
|
||||
*/
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) {
|
||||
super.isSanitizer(node) or
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
name: codeql/javascript-experimental-atm-lib
|
||||
version: 0.4.2
|
||||
description: CodeQL libraries for the experimental ML-powered queries
|
||||
version: 0.4.3
|
||||
extractor: javascript
|
||||
library: true
|
||||
groups:
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
name: codeql/javascript-experimental-atm-model
|
||||
description: Machine learning model supporting the experimental ML-powered queries
|
||||
version: 0.3.1
|
||||
groups:
|
||||
- javascript
|
||||
|
||||
@@ -12,19 +12,23 @@
|
||||
import javascript
|
||||
import experimental.adaptivethreatmodeling.ATMConfig
|
||||
import extraction.ExtractEndpointDataTraining
|
||||
private import experimental.adaptivethreatmodeling.NosqlInjectionATM as NosqlInjectionAtm
|
||||
private import experimental.adaptivethreatmodeling.SqlInjectionATM as SqlInjectionAtm
|
||||
private import experimental.adaptivethreatmodeling.TaintedPathATM as TaintedPathAtm
|
||||
private import experimental.adaptivethreatmodeling.XssATM as XssAtm
|
||||
|
||||
string getAReasonSinkExcluded(DataFlow::Node sinkCandidate, Query query) {
|
||||
query instanceof NosqlInjectionQuery and
|
||||
result = NosqlInjectionAtm::SinkEndpointFilter::getAReasonSinkExcluded(sinkCandidate)
|
||||
result = any(NosqlInjectionAtm::NosqlInjectionAtmConfig cfg).getAReasonSinkExcluded(sinkCandidate)
|
||||
or
|
||||
query instanceof SqlInjectionQuery and
|
||||
result = SqlInjectionAtm::SinkEndpointFilter::getAReasonSinkExcluded(sinkCandidate)
|
||||
result = any(SqlInjectionAtm::SqlInjectionAtmConfig cfg).getAReasonSinkExcluded(sinkCandidate)
|
||||
or
|
||||
query instanceof TaintedPathQuery and
|
||||
result = TaintedPathAtm::SinkEndpointFilter::getAReasonSinkExcluded(sinkCandidate)
|
||||
result = any(TaintedPathAtm::TaintedPathAtmConfig cfg).getAReasonSinkExcluded(sinkCandidate)
|
||||
or
|
||||
query instanceof XssQuery and
|
||||
result = XssAtm::SinkEndpointFilter::getAReasonSinkExcluded(sinkCandidate)
|
||||
result = any(XssAtm::DomBasedXssAtmConfig cfg).getAReasonSinkExcluded(sinkCandidate)
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/**
|
||||
* For internal use only.
|
||||
*
|
||||
*
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/**
|
||||
* For internal use only.
|
||||
*
|
||||
* Defines files that should be excluded from the evaluation of ML models.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/**
|
||||
* For internal use only.
|
||||
*
|
||||
* Extracts training data we can use to train ML models for ML-powered queries.
|
||||
@@ -10,10 +10,10 @@ import experimental.adaptivethreatmodeling.EndpointFeatures as EndpointFeatures
|
||||
import NoFeaturizationRestrictionsConfig
|
||||
private import Exclusions as Exclusions
|
||||
import Queries
|
||||
import experimental.adaptivethreatmodeling.NosqlInjectionATM as NosqlInjectionAtm
|
||||
import experimental.adaptivethreatmodeling.SqlInjectionATM as SqlInjectionAtm
|
||||
import experimental.adaptivethreatmodeling.TaintedPathATM as TaintedPathAtm
|
||||
import experimental.adaptivethreatmodeling.XssATM as XssAtm
|
||||
private import experimental.adaptivethreatmodeling.NosqlInjectionATM as NosqlInjectionAtm
|
||||
private import experimental.adaptivethreatmodeling.SqlInjectionATM as SqlInjectionAtm
|
||||
private import experimental.adaptivethreatmodeling.TaintedPathATM as TaintedPathAtm
|
||||
private import experimental.adaptivethreatmodeling.XssATM as XssAtm
|
||||
|
||||
/**
|
||||
* Gets the set of featureName-featureValue pairs for each endpoint in the training set.
|
||||
@@ -53,7 +53,7 @@ predicate tokenFeatures(DataFlow::Node endpoint, string featureName, string feat
|
||||
query predicate trainingEndpoints(
|
||||
DataFlow::Node endpoint, EndpointType endpointClass, EndpointCharacteristic characteristic
|
||||
) {
|
||||
characteristic.getEndpoints(endpoint) and
|
||||
characteristic.appliesToEndpoint(endpoint) and
|
||||
// Only consider the source code for the project being analyzed.
|
||||
exists(endpoint.getFile().getRelativePath()) and
|
||||
// Only select endpoints that can be part of a tainted flow: Constant expressions always evaluate to a constant
|
||||
@@ -69,16 +69,20 @@ query predicate trainingEndpoints(
|
||||
not (
|
||||
endpointClass instanceof NegativeType and
|
||||
exists(EndpointCharacteristic c |
|
||||
c.getEndpoints(endpoint) and
|
||||
c.appliesToEndpoint(endpoint) and
|
||||
c instanceof LikelyNotASinkCharacteristic
|
||||
)
|
||||
) and
|
||||
// Don't surface endpoint filters as characteristics, because they were previously not surfaced.
|
||||
// TODO: Experiment with surfacing these to the modeling code by removing the following line (and then make
|
||||
// EndpointFilterCharacteristic private).
|
||||
not characteristic instanceof EndpointFilterCharacteristic and
|
||||
(
|
||||
// If the list of characteristics includes positive indicators with high confidence for this class, select this as a
|
||||
// training sample belonging to the class.
|
||||
exists(EndpointCharacteristic characteristic2, float confidence |
|
||||
characteristic2.getEndpoints(endpoint) and
|
||||
characteristic2.getImplications(endpointClass, true, confidence) and
|
||||
characteristic2.appliesToEndpoint(endpoint) and
|
||||
characteristic2.hasImplications(endpointClass, true, confidence) and
|
||||
confidence >= characteristic2.getHighConfidenceThreshold()
|
||||
) and
|
||||
(
|
||||
@@ -89,8 +93,8 @@ query predicate trainingEndpoints(
|
||||
not endpointClass instanceof NegativeType
|
||||
or
|
||||
not exists(EndpointCharacteristic characteristic3, float confidence3, EndpointType posClass |
|
||||
characteristic3.getEndpoints(endpoint) and
|
||||
characteristic3.getImplications(posClass, true, confidence3) and
|
||||
characteristic3.appliesToEndpoint(endpoint) and
|
||||
characteristic3.hasImplications(posClass, true, confidence3) and
|
||||
confidence3 >= characteristic3.getHighConfidenceThreshold() and
|
||||
not posClass instanceof NegativeType
|
||||
)
|
||||
@@ -102,8 +106,8 @@ query predicate trainingEndpoints(
|
||||
endpointClass instanceof NegativeType and
|
||||
forall(EndpointType otherClass | not otherClass instanceof NegativeType |
|
||||
exists(EndpointCharacteristic characteristic2, float confidence |
|
||||
characteristic2.getEndpoints(endpoint) and
|
||||
characteristic2.getImplications(otherClass, false, confidence) and
|
||||
characteristic2.appliesToEndpoint(endpoint) and
|
||||
characteristic2.hasImplications(otherClass, false, confidence) and
|
||||
confidence >= characteristic2.getHighConfidenceThreshold()
|
||||
)
|
||||
)
|
||||
@@ -176,18 +180,22 @@ query predicate reformattedTrainingEndpoints(
|
||||
// The reason, or reasons, why the endpoint was labeled NotASink for this query, only for negative examples.
|
||||
key = "notASinkReason" and
|
||||
exists(EndpointCharacteristic characteristic, EndpointType endpointClass |
|
||||
characteristic.getEndpoints(endpoint) and
|
||||
characteristic.getImplications(endpointClass, true, _) and
|
||||
characteristic.appliesToEndpoint(endpoint) and
|
||||
characteristic.hasImplications(endpointClass, true, _) and
|
||||
endpointClass instanceof NegativeType and
|
||||
value = characteristic
|
||||
) and
|
||||
// Don't include a notASinkReason for endpoints that are also known sinks.
|
||||
not exists(EndpointCharacteristic characteristic3, float confidence3, EndpointType posClass |
|
||||
characteristic3.getEndpoints(endpoint) and
|
||||
characteristic3.getImplications(posClass, true, confidence3) and
|
||||
characteristic3.appliesToEndpoint(endpoint) and
|
||||
characteristic3.hasImplications(posClass, true, confidence3) and
|
||||
confidence3 >= characteristic3.getHighConfidenceThreshold() and
|
||||
not posClass instanceof NegativeType
|
||||
) and
|
||||
// Don't surface endpoint filters as notASinkReasons, because they were previously not surfaced.
|
||||
// TODO: Experiment with surfacing these to the modeling code by removing the following line (and then make
|
||||
// EndpointFilterCharacteristic private).
|
||||
not value instanceof EndpointFilterCharacteristic and
|
||||
valueType = "string"
|
||||
)
|
||||
)
|
||||
@@ -198,13 +206,14 @@ query predicate reformattedTrainingEndpoints(
|
||||
* TODO: Delete this once we are no longer surfacing `hasFlowFromSource`.
|
||||
*/
|
||||
DataFlow::Configuration getDataFlowCfg(Query query) {
|
||||
query instanceof NosqlInjectionQuery and result instanceof NosqlInjectionAtm::Configuration
|
||||
query instanceof NosqlInjectionQuery and
|
||||
result instanceof NosqlInjectionAtm::NosqlInjectionAtmConfig
|
||||
or
|
||||
query instanceof SqlInjectionQuery and result instanceof SqlInjectionAtm::Configuration
|
||||
query instanceof SqlInjectionQuery and result instanceof SqlInjectionAtm::SqlInjectionAtmConfig
|
||||
or
|
||||
query instanceof TaintedPathQuery and result instanceof TaintedPathAtm::Configuration
|
||||
query instanceof TaintedPathQuery and result instanceof TaintedPathAtm::TaintedPathAtmConfig
|
||||
or
|
||||
query instanceof XssQuery and result instanceof XssAtm::Configuration
|
||||
query instanceof XssQuery and result instanceof XssAtm::DomBasedXssAtmConfig
|
||||
}
|
||||
|
||||
// TODO: Delete this once we are no longer surfacing `hasFlowFromSource`.
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
name: codeql/javascript-experimental-atm-model-building
|
||||
description: CodeQL libraries for building machine learning models for the experimental ML-powered queries
|
||||
extractor: javascript
|
||||
library: false
|
||||
groups:
|
||||
|
||||
@@ -17,11 +17,8 @@ import ATM::ResultsInfo
|
||||
import DataFlow::PathGraph
|
||||
import experimental.adaptivethreatmodeling.NosqlInjectionATM
|
||||
|
||||
from DataFlow::Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, float score
|
||||
where
|
||||
cfg.hasFlowPath(source, sink) and
|
||||
not isFlowLikelyInBaseQuery(source.getNode(), sink.getNode()) and
|
||||
score = getScoreForFlow(source.getNode(), sink.getNode())
|
||||
from AtmConfig cfg, DataFlow::PathNode source, DataFlow::PathNode sink, float score
|
||||
where cfg.hasBoostedFlowPath(source, sink, score)
|
||||
select sink.getNode(), source, sink,
|
||||
"(Experimental) This may be a database query that depends on $@. Identified using machine learning.",
|
||||
source.getNode(), "a user-provided value", score
|
||||
|
||||
@@ -17,11 +17,8 @@ import experimental.adaptivethreatmodeling.SqlInjectionATM
|
||||
import ATM::ResultsInfo
|
||||
import DataFlow::PathGraph
|
||||
|
||||
from DataFlow::Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, float score
|
||||
where
|
||||
cfg.hasFlowPath(source, sink) and
|
||||
not isFlowLikelyInBaseQuery(source.getNode(), sink.getNode()) and
|
||||
score = getScoreForFlow(source.getNode(), sink.getNode())
|
||||
from AtmConfig cfg, DataFlow::PathNode source, DataFlow::PathNode sink, float score
|
||||
where cfg.hasBoostedFlowPath(source, sink, score)
|
||||
select sink.getNode(), source, sink,
|
||||
"(Experimental) This may be a database query that depends on $@. Identified using machine learning.",
|
||||
source.getNode(), "a user-provided value", score
|
||||
|
||||
@@ -21,11 +21,8 @@ import ATM::ResultsInfo
|
||||
import DataFlow::PathGraph
|
||||
import experimental.adaptivethreatmodeling.TaintedPathATM
|
||||
|
||||
from DataFlow::Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, float score
|
||||
where
|
||||
cfg.hasFlowPath(source, sink) and
|
||||
not isFlowLikelyInBaseQuery(source.getNode(), sink.getNode()) and
|
||||
score = getScoreForFlow(source.getNode(), sink.getNode())
|
||||
from AtmConfig cfg, DataFlow::PathNode source, DataFlow::PathNode sink, float score
|
||||
where cfg.hasBoostedFlowPath(source, sink, score)
|
||||
select sink.getNode(), source, sink,
|
||||
"(Experimental) This may be a path that depends on $@. Identified using machine learning.",
|
||||
source.getNode(), "a user-provided value", score
|
||||
|
||||
@@ -18,11 +18,8 @@ import ATM::ResultsInfo
|
||||
import DataFlow::PathGraph
|
||||
import experimental.adaptivethreatmodeling.XssATM
|
||||
|
||||
from DataFlow::Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, float score
|
||||
where
|
||||
cfg.hasFlowPath(source, sink) and
|
||||
not isFlowLikelyInBaseQuery(source.getNode(), sink.getNode()) and
|
||||
score = getScoreForFlow(source.getNode(), sink.getNode())
|
||||
from AtmConfig cfg, DataFlow::PathNode source, DataFlow::PathNode sink, float score
|
||||
where cfg.hasBoostedFlowPath(source, sink, score)
|
||||
select sink.getNode(), source, sink,
|
||||
"(Experimental) This may be a cross-site scripting vulnerability due to $@. Identified using machine learning.",
|
||||
source.getNode(), "a user-provided value", score
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
name: codeql/javascript-experimental-atm-queries
|
||||
description: Experimental ML-powered queries for JavaScript
|
||||
language: javascript
|
||||
version: 0.4.2
|
||||
version: 0.4.3
|
||||
suites: codeql-suites
|
||||
defaultSuiteFile: codeql-suites/javascript-atm-code-scanning.qls
|
||||
groups:
|
||||
|
||||
@@ -12,16 +12,17 @@ import experimental.adaptivethreatmodeling.SqlInjectionATM as SqlInjectionAtm
|
||||
import experimental.adaptivethreatmodeling.TaintedPathATM as TaintedPathAtm
|
||||
import experimental.adaptivethreatmodeling.XssATM as XssAtm
|
||||
import experimental.adaptivethreatmodeling.EndpointFeatures as EndpointFeatures
|
||||
import experimental.adaptivethreatmodeling.StandardEndpointFilters as StandardEndpointFilters
|
||||
import extraction.NoFeaturizationRestrictionsConfig
|
||||
private import experimental.adaptivethreatmodeling.EndpointCharacteristics as EndpointCharacteristics
|
||||
|
||||
query predicate tokenFeatures(DataFlow::Node endpoint, string featureName, string featureValue) {
|
||||
(
|
||||
not exists(NosqlInjectionAtm::SinkEndpointFilter::getAReasonSinkExcluded(endpoint)) or
|
||||
not exists(SqlInjectionAtm::SinkEndpointFilter::getAReasonSinkExcluded(endpoint)) or
|
||||
not exists(TaintedPathAtm::SinkEndpointFilter::getAReasonSinkExcluded(endpoint)) or
|
||||
not exists(XssAtm::SinkEndpointFilter::getAReasonSinkExcluded(endpoint)) or
|
||||
StandardEndpointFilters::isArgumentToModeledFunction(endpoint)
|
||||
not exists(any(NosqlInjectionAtm::NosqlInjectionAtmConfig cfg).getAReasonSinkExcluded(endpoint)) or
|
||||
not exists(any(SqlInjectionAtm::SqlInjectionAtmConfig cfg).getAReasonSinkExcluded(endpoint)) or
|
||||
not exists(any(TaintedPathAtm::TaintedPathAtmConfig cfg).getAReasonSinkExcluded(endpoint)) or
|
||||
not exists(any(XssAtm::DomBasedXssAtmConfig cfg).getAReasonSinkExcluded(endpoint)) or
|
||||
any(EndpointCharacteristics::IsArgumentToModeledFunctionCharacteristic characteristic)
|
||||
.appliesToEndpoint(endpoint)
|
||||
) and
|
||||
EndpointFeatures::tokenFeatures(endpoint, featureName, featureValue)
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,16 +1,16 @@
|
||||
nosqlFilteredTruePositives
|
||||
| autogenerated/NosqlAndSqlInjection/untyped/mongoose.js:111:14:111:18 | query | not a direct argument to a likely external library call or a heuristic sink |
|
||||
| autogenerated/NosqlAndSqlInjection/untyped/mongoose.js:111:14:111:18 | query | not a direct argument to a likely external library call or a heuristic sink (nosql) |
|
||||
sqlFilteredTruePositives
|
||||
| autogenerated/NosqlAndSqlInjection/untyped/tst2.js:7:13:7:45 | select ... e id = | not an argument to a likely external library call or a heuristic sink |
|
||||
| autogenerated/NosqlAndSqlInjection/untyped/tst2.js:7:48:7:60 | req.params.id | not an argument to a likely external library call or a heuristic sink |
|
||||
taintedPathFilteredTruePositives
|
||||
| autogenerated/TaintedPath/TaintedPath.js:66:26:66:31 | "SAFE" | not a direct argument to a likely external library call or a heuristic sink |
|
||||
| autogenerated/TaintedPath/TaintedPath.js:71:26:71:45 | Cookie.get("unsafe") | not a direct argument to a likely external library call or a heuristic sink |
|
||||
| autogenerated/TaintedPath/TaintedPath.js:66:26:66:31 | "SAFE" | not a direct argument to a likely external library call or a heuristic sink (tainted path) |
|
||||
| autogenerated/TaintedPath/TaintedPath.js:71:26:71:45 | Cookie.get("unsafe") | not a direct argument to a likely external library call or a heuristic sink (tainted path) |
|
||||
xssFilteredTruePositives
|
||||
| autogenerated/Xss/DomBasedXss/d3.js:12:20:12:29 | getTaint() | not a direct argument to a likely external library call or a heuristic sink |
|
||||
| autogenerated/Xss/DomBasedXss/d3.js:14:20:14:29 | getTaint() | not a direct argument to a likely external library call or a heuristic sink |
|
||||
| autogenerated/Xss/DomBasedXss/express.js:7:15:7:33 | req.param("wobble") | not a direct argument to a likely external library call or a heuristic sink |
|
||||
| autogenerated/Xss/DomBasedXss/jwt-server.js:11:19:11:29 | decoded.foo | not a direct argument to a likely external library call or a heuristic sink |
|
||||
| autogenerated/Xss/DomBasedXss/tst.js:316:35:316:42 | location | not a direct argument to a likely external library call or a heuristic sink |
|
||||
| autogenerated/Xss/DomBasedXss/typeahead.js:10:16:10:18 | loc | not a direct argument to a likely external library call or a heuristic sink |
|
||||
| autogenerated/Xss/DomBasedXss/typeahead.js:25:18:25:20 | val | not a direct argument to a likely external library call or a heuristic sink |
|
||||
| autogenerated/Xss/DomBasedXss/d3.js:12:20:12:29 | getTaint() | not a direct argument to a likely external library call or a heuristic sink (xss) |
|
||||
| autogenerated/Xss/DomBasedXss/d3.js:14:20:14:29 | getTaint() | not a direct argument to a likely external library call or a heuristic sink (xss) |
|
||||
| autogenerated/Xss/DomBasedXss/express.js:7:15:7:33 | req.param("wobble") | not a direct argument to a likely external library call or a heuristic sink (xss) |
|
||||
| autogenerated/Xss/DomBasedXss/jwt-server.js:11:19:11:29 | decoded.foo | not a direct argument to a likely external library call or a heuristic sink (xss) |
|
||||
| autogenerated/Xss/DomBasedXss/tst.js:316:35:316:42 | location | not a direct argument to a likely external library call or a heuristic sink (xss) |
|
||||
| autogenerated/Xss/DomBasedXss/typeahead.js:10:16:10:18 | loc | not a direct argument to a likely external library call or a heuristic sink (xss) |
|
||||
| autogenerated/Xss/DomBasedXss/typeahead.js:25:18:25:20 | val | not a direct argument to a likely external library call or a heuristic sink (xss) |
|
||||
|
||||
@@ -16,7 +16,6 @@ import semmle.javascript.security.dataflow.NosqlInjectionCustomizations
|
||||
import semmle.javascript.security.dataflow.SqlInjectionCustomizations
|
||||
import semmle.javascript.security.dataflow.TaintedPathCustomizations
|
||||
import semmle.javascript.security.dataflow.DomBasedXssCustomizations
|
||||
import experimental.adaptivethreatmodeling.StandardEndpointFilters as StandardEndpointFilters
|
||||
import experimental.adaptivethreatmodeling.NosqlInjectionATM as NosqlInjectionAtm
|
||||
import experimental.adaptivethreatmodeling.SqlInjectionATM as SqlInjectionAtm
|
||||
import experimental.adaptivethreatmodeling.TaintedPathATM as TaintedPathAtm
|
||||
@@ -24,24 +23,24 @@ import experimental.adaptivethreatmodeling.XssATM as XssAtm
|
||||
|
||||
query predicate nosqlFilteredTruePositives(DataFlow::Node endpoint, string reason) {
|
||||
endpoint instanceof NosqlInjection::Sink and
|
||||
reason = NosqlInjectionAtm::SinkEndpointFilter::getAReasonSinkExcluded(endpoint) and
|
||||
reason = any(NosqlInjectionAtm::NosqlInjectionAtmConfig cfg).getAReasonSinkExcluded(endpoint) and
|
||||
not reason = ["argument to modeled function", "modeled sink", "modeled database access"]
|
||||
}
|
||||
|
||||
query predicate sqlFilteredTruePositives(DataFlow::Node endpoint, string reason) {
|
||||
endpoint instanceof SqlInjection::Sink and
|
||||
reason = SqlInjectionAtm::SinkEndpointFilter::getAReasonSinkExcluded(endpoint) and
|
||||
reason = any(SqlInjectionAtm::SqlInjectionAtmConfig cfg).getAReasonSinkExcluded(endpoint) and
|
||||
reason != "argument to modeled function"
|
||||
}
|
||||
|
||||
query predicate taintedPathFilteredTruePositives(DataFlow::Node endpoint, string reason) {
|
||||
endpoint instanceof TaintedPath::Sink and
|
||||
reason = TaintedPathAtm::SinkEndpointFilter::getAReasonSinkExcluded(endpoint) and
|
||||
reason = any(TaintedPathAtm::TaintedPathAtmConfig cfg).getAReasonSinkExcluded(endpoint) and
|
||||
reason != "argument to modeled function"
|
||||
}
|
||||
|
||||
query predicate xssFilteredTruePositives(DataFlow::Node endpoint, string reason) {
|
||||
endpoint instanceof DomBasedXss::Sink and
|
||||
reason = XssAtm::SinkEndpointFilter::getAReasonSinkExcluded(endpoint) and
|
||||
reason = any(XssAtm::DomBasedXssAtmConfig cfg).getAReasonSinkExcluded(endpoint) and
|
||||
reason != "argument to modeled function"
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import experimental.adaptivethreatmodeling.StandardEndpointFilters
|
||||
import experimental.adaptivethreatmodeling.EndpointCharacteristics as EndpointCharacteristics
|
||||
|
||||
select getALikelyExternalLibraryCall()
|
||||
select EndpointCharacteristics::getALikelyExternalLibraryCall()
|
||||
|
||||
@@ -2,5 +2,5 @@ import javascript
|
||||
import experimental.adaptivethreatmodeling.NosqlInjectionATM as NosqlInjectionAtm
|
||||
|
||||
query predicate effectiveSinks(DataFlow::Node node) {
|
||||
not exists(NosqlInjectionAtm::SinkEndpointFilter::getAReasonSinkExcluded(node))
|
||||
not exists(any(NosqlInjectionAtm::NosqlInjectionAtmConfig cfg).getAReasonSinkExcluded(node))
|
||||
}
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
## 0.3.4
|
||||
|
||||
### Major Analysis Improvements
|
||||
|
||||
* Added support for TypeScript 4.9.
|
||||
|
||||
## 0.3.3
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: majorAnalysis
|
||||
---
|
||||
* Added support for TypeScript 4.9.
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Deleted the deprecated `Instance` class from the `Vue` module.
|
||||
* Deleted the deprecated `VHtmlSourceWrite` class from `DomBasedXssQuery.qll`.
|
||||
* Deleted all the deprecated `[QueryName].qll` files from the `javascript/ql/lib/semmle/javascript/security/dataflow` folder, use the corresponding `[QueryName]Query.qll` files instead.
|
||||
5
javascript/ql/lib/change-notes/released/0.3.4.md
Normal file
5
javascript/ql/lib/change-notes/released/0.3.4.md
Normal file
@@ -0,0 +1,5 @@
|
||||
## 0.3.4
|
||||
|
||||
### Major Analysis Improvements
|
||||
|
||||
* Added support for TypeScript 4.9.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.3.3
|
||||
lastReleaseVersion: 0.3.4
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/javascript-all
|
||||
version: 0.3.4-dev
|
||||
version: 0.3.5-dev
|
||||
groups: javascript
|
||||
dbscheme: semmlecode.javascript.dbscheme
|
||||
extractor: javascript
|
||||
|
||||
@@ -9,10 +9,28 @@ module Hapi {
|
||||
/**
|
||||
* An expression that creates a new Hapi server.
|
||||
*/
|
||||
class ServerDefinition extends Http::Servers::StandardServerDefinition, DataFlow::NewNode {
|
||||
class ServerDefinition extends Http::Servers::StandardServerDefinition, DataFlow::Node {
|
||||
ServerDefinition() {
|
||||
// `server = new Hapi.Server()`
|
||||
this = DataFlow::moduleMember("hapi", "Server").getAnInstantiation()
|
||||
or
|
||||
// `server = Glue.compose(manifest, composeOptions)`
|
||||
this = DataFlow::moduleMember("@hapi/glue", "compose").getAnInvocation()
|
||||
or
|
||||
// `register (server, options)`
|
||||
// `module.exports.plugin = {register, pkg};`
|
||||
this =
|
||||
any(Module m)
|
||||
.getAnExportedValue("plugin")
|
||||
.getALocalSource()
|
||||
.getAPropertySource("register")
|
||||
.getAFunctionValue()
|
||||
.getParameter(0)
|
||||
or
|
||||
// `const after = function (server) {...};`
|
||||
// `server.dependency('name', after);`
|
||||
this =
|
||||
any(ServerDefinition s).ref().getAMethodCall("dependency").getABoundCallbackParameter(1, 0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,7 +141,7 @@ module Hapi {
|
||||
kind = "parameter" and
|
||||
exists(DataFlow::PropRead query |
|
||||
// `request.query.name`
|
||||
query.accesses(request, "query") and
|
||||
query.accesses(request, ["query", "params"]) and
|
||||
this.(DataFlow::PropRead).accesses(query, _)
|
||||
)
|
||||
or
|
||||
@@ -131,7 +149,7 @@ module Hapi {
|
||||
// `request.url.path`
|
||||
kind = "url" and
|
||||
url.accesses(request, "url") and
|
||||
this.(DataFlow::PropRead).accesses(url, "path")
|
||||
this.(DataFlow::PropRead).accesses(url, ["path", "origin"])
|
||||
)
|
||||
or
|
||||
exists(DataFlow::PropRead state |
|
||||
@@ -209,6 +227,17 @@ module Hapi {
|
||||
// server.ext('/', fun)
|
||||
this.getMethodName() = "ext" and
|
||||
handler = this.getArgument(1)
|
||||
or
|
||||
// server.route([{ handler(request){}])
|
||||
this.getMethodName() = "route" and
|
||||
handler =
|
||||
this.getArgument(0)
|
||||
.getALocalSource()
|
||||
.(DataFlow::ArrayCreationNode)
|
||||
.getAnElement()
|
||||
.getALocalSource()
|
||||
.getAPropertySource("handler")
|
||||
.getAFunctionValue()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -240,7 +269,7 @@ module Hapi {
|
||||
RouteHandlerCandidate() {
|
||||
exists(string request, string responseToolkit |
|
||||
(request = "request" or request = "req") and
|
||||
responseToolkit = "h" and
|
||||
responseToolkit = ["h", "hapi"] and
|
||||
// heuristic: parameter names match the Hapi documentation
|
||||
astNode.getNumParameter() = 2 and
|
||||
astNode.getParameter(0).getName() = request and
|
||||
|
||||
@@ -249,7 +249,7 @@ private module Redis {
|
||||
"set", "publish", "append", "bitfield", "decrby", "getset", "hincrby", "hincrbyfloat",
|
||||
"hset", "hsetnx", "incrby", "incrbyfloat", "linsert", "lpush", "lpushx", "lset", "ltrim",
|
||||
"rename", "renamenx", "rpushx", "setbit", "setex", "smove", "zincrby", "zinterstore",
|
||||
"hdel", "lpush", "pfadd", "rpush", "sadd", "sdiffstore", "srem"
|
||||
"hdel", "pfadd", "rpush", "sadd", "sdiffstore", "srem"
|
||||
] and
|
||||
argIndex = 0
|
||||
or
|
||||
|
||||
@@ -115,11 +115,6 @@ module Vue {
|
||||
kind = DataFlow::MemberKind::setter() and result = "set"
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED. This class has been renamed to `Vue::Component`.
|
||||
*/
|
||||
deprecated class Instance = Component;
|
||||
|
||||
/**
|
||||
* A Vue component, such as a `new Vue({ ... })` call or a `.vue` file.
|
||||
*
|
||||
@@ -383,23 +378,6 @@ module Vue {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED. Use `Vue::Component` instead.
|
||||
*
|
||||
* A Vue component from `new Vue({...})`.
|
||||
*/
|
||||
deprecated class VueInstance extends Component {
|
||||
VueInstance() {
|
||||
// restrict charpred to match original behavior
|
||||
this = MkComponentInstantiation(vueLibrary().getAnInstantiation())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED. Use `Vue::ComponentExtension` or `Vue::Component` instead.
|
||||
*/
|
||||
deprecated class ExtendedVue = ComponentExtension;
|
||||
|
||||
/**
|
||||
* A component created via an explicit call to `Vue.extend({...})` or `CustomComponent.extend({...})`.
|
||||
*/
|
||||
@@ -429,19 +407,6 @@ module Vue {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED. Use `Vue::Component` instead.
|
||||
*
|
||||
* An instance of an extended Vue, for example `instance` of `var Ext = Vue.extend({...}); var instance = new Ext({...})`.
|
||||
*/
|
||||
deprecated class ExtendedInstance extends Component {
|
||||
ExtendedInstance() {
|
||||
// restrict charpred to match original behavior
|
||||
this =
|
||||
MkComponentInstantiation(vueLibrary().getMember("extend").getReturn().getAnInstantiation())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A Vue component from `Vue.component("my-component", { ... })`.
|
||||
*/
|
||||
@@ -568,9 +533,6 @@ module Vue {
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED. Do not use. */
|
||||
deprecated class InstanceHeapStep = PropStep;
|
||||
|
||||
/**
|
||||
* A Vue `v-html` attribute.
|
||||
*/
|
||||
@@ -609,11 +571,6 @@ module Vue {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED. Do not use.
|
||||
*/
|
||||
deprecated class VHtmlSourceWrite = VHtmlAttributeStep;
|
||||
|
||||
/*
|
||||
* Provides classes for working with Vue templates.
|
||||
*/
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
/** DEPRECATED. Import `BrokenCryptoAlgorithmQuery` instead. */
|
||||
|
||||
import javascript
|
||||
private import BrokenCryptoAlgorithmQuery as BrokenCryptoAlgorithmQuery // ignore-query-import
|
||||
|
||||
/** DEPRECATED. Import `BrokenCryptoAlgorithmQuery` instead. */
|
||||
deprecated module BrokenCryptoAlgorithm = BrokenCryptoAlgorithmQuery;
|
||||
@@ -1,7 +0,0 @@
|
||||
/** DEPRECATED. Import `BuildArtifactLeakQuery` instead. */
|
||||
|
||||
import javascript
|
||||
private import BuildArtifactLeakQuery as BuildArtifactLeakQuery // ignore-query-import
|
||||
|
||||
/** DEPRECATED. Import `BuildArtifactLeakQuery` instead. */
|
||||
deprecated module BuildArtifactLeak = BuildArtifactLeakQuery;
|
||||
@@ -1,7 +0,0 @@
|
||||
/** DEPRECATED. Import `CleartextLoggingQuery` instead. */
|
||||
|
||||
import javascript
|
||||
private import CleartextLoggingQuery as CleartextLoggingQuery // ignore-query-import
|
||||
|
||||
/** DEPRECATED. Import `CleartextLoggingQuery` instead. */
|
||||
deprecated module CleartextLogging = CleartextLoggingQuery;
|
||||
@@ -1,7 +0,0 @@
|
||||
/** DEPRECATED. Import `CleartextStorageQuery` instead. */
|
||||
|
||||
import javascript
|
||||
private import CleartextStorageQuery as CleartextStorageQuery // ignore-query-import
|
||||
|
||||
/** DEPRECATED. Import `CleartextStorageQuery` instead. */
|
||||
deprecated module CleartextStorage = CleartextStorageQuery;
|
||||
@@ -1,8 +0,0 @@
|
||||
/** DEPRECATED. Import `ClientSideUrlRedirectQuery` instead. */
|
||||
|
||||
import javascript
|
||||
import UrlConcatenation
|
||||
private import ClientSideUrlRedirectQuery as ClientSideUrlRedirectQuery // ignore-query-import
|
||||
|
||||
/** DEPRECATED. Import `ClientSideUrlRedirectQuery` instead. */
|
||||
deprecated module ClientSideUrlRedirect = ClientSideUrlRedirectQuery;
|
||||
@@ -1,7 +0,0 @@
|
||||
/** DEPRECATED. Import `CodeInjectionQuery` instead. */
|
||||
|
||||
import javascript
|
||||
private import CodeInjectionQuery as CodeInjectionQuery // ignore-query-import
|
||||
|
||||
/** DEPRECATED. Import `CodeInjectionQuery` instead. */
|
||||
deprecated module CodeInjection = CodeInjectionQuery;
|
||||
@@ -1,7 +0,0 @@
|
||||
/** DEPRECATED. Import `CommandInjectionQuery` instead. */
|
||||
|
||||
import javascript
|
||||
private import CommandInjectionQuery as CommandInjectionQuery // ignore-query-import
|
||||
|
||||
/** DEPRECATED. Import `CommandInjectionQuery` instead. */
|
||||
deprecated module CommandInjection = CommandInjectionQuery;
|
||||
@@ -1,7 +0,0 @@
|
||||
/** DEPRECATED. Import `ConditionalBypassQuery` instead. */
|
||||
|
||||
import javascript
|
||||
private import ConditionalBypassQuery as ConditionalBypassQuery // ignore-query-import
|
||||
|
||||
/** DEPRECATED. Import `ConditionalBypassQuery` instead. */
|
||||
deprecated module ConditionalBypass = ConditionalBypassQuery;
|
||||
@@ -1,7 +0,0 @@
|
||||
/** DEPRECATED. Import `CorsMisconfigurationForCredentialsQuery` instead. */
|
||||
|
||||
import javascript
|
||||
private import CorsMisconfigurationForCredentialsQuery as CorsMisconfigurationForCredentialsQuery // ignore-query-import
|
||||
|
||||
/** DEPRECATED. Import `CorsMisconfigurationForCredentialsQuery` instead. */
|
||||
deprecated module CorsMisconfigurationForCredentials = CorsMisconfigurationForCredentialsQuery;
|
||||
@@ -1,8 +0,0 @@
|
||||
/** DEPRECATED. Import `DeepObjectResourceExhaustionQuery` instead. */
|
||||
|
||||
import javascript
|
||||
import semmle.javascript.security.TaintedObject
|
||||
private import DeepObjectResourceExhaustionQuery as DeepObjectResourceExhaustionQuery // ignore-query-import
|
||||
|
||||
/** DEPRECATED. Import `DeepObjectResourceExhaustionQuery` instead. */
|
||||
deprecated module DeepObjectResourceExhaustion = DeepObjectResourceExhaustionQuery;
|
||||
@@ -1,7 +0,0 @@
|
||||
/** DEPRECATED. Import `DifferentKindsComparisonBypassQuery` instead. */
|
||||
|
||||
import javascript
|
||||
private import DifferentKindsComparisonBypassQuery as DifferentKindsComparisonBypassQuery // ignore-query-import
|
||||
|
||||
/** DEPRECATED. Import `DifferentKindsComparisonBypassQuery` instead. */
|
||||
deprecated module DifferentKindsComparisonBypass = DifferentKindsComparisonBypassQuery;
|
||||
@@ -1,7 +0,0 @@
|
||||
/** DEPRECATED. Import `DomBasedXssQuery` instead. */
|
||||
|
||||
import javascript
|
||||
private import DomBasedXssQuery as DomBasedXssQuery // ignore-query-import
|
||||
|
||||
/** DEPRECATED. Import `DomBasedXssQuery` instead. */
|
||||
deprecated module DomBasedXss = DomBasedXssQuery;
|
||||
@@ -8,11 +8,6 @@ private import semmle.javascript.security.TaintedUrlSuffix
|
||||
import DomBasedXssCustomizations::DomBasedXss
|
||||
private import Xss::Shared as Shared
|
||||
|
||||
/**
|
||||
* DEPRECATED. Use `Vue::VHtmlSourceWrite` instead.
|
||||
*/
|
||||
deprecated class VHtmlSourceWrite = Vue::VHtmlSourceWrite;
|
||||
|
||||
/** DEPRECATED. Use `Configuration`. */
|
||||
deprecated class HtmlInjectionConfiguration = Configuration;
|
||||
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
/** DEPRECATED. Import `ExceptionXssQuery` instead. */
|
||||
|
||||
import javascript
|
||||
private import ExceptionXssQuery as ExceptionXssQuery // ignore-query-import
|
||||
|
||||
/** DEPRECATED. Import `ExceptionXssQuery` instead. */
|
||||
deprecated module ExceptionXss = ExceptionXssQuery;
|
||||
@@ -1,7 +0,0 @@
|
||||
/** DEPRECATED. Import `FileAccessToHttpQuery` instead. */
|
||||
|
||||
import javascript
|
||||
private import FileAccessToHttpQuery as FileAccessToHttpQuery // ignore-query-import
|
||||
|
||||
/** DEPRECATED. Import `FileAccessToHttpQuery` instead. */
|
||||
deprecated module FileAccessToHttp = FileAccessToHttpQuery;
|
||||
@@ -1,7 +0,0 @@
|
||||
/** DEPRECATED. Import `HardcodedCredentialsQuery` instead. */
|
||||
|
||||
import javascript
|
||||
private import HardcodedCredentialsQuery as HardcodedCredentialsQuery // ignore-query-import
|
||||
|
||||
/** DEPRECATED. Import `HardcodedCredentialsQuery` instead. */
|
||||
deprecated module HardcodedCredentials = HardcodedCredentialsQuery;
|
||||
@@ -1,7 +0,0 @@
|
||||
/** DEPRECATED. Import `HardcodedDataInterpretedAsCodeQuery` instead. */
|
||||
|
||||
import javascript
|
||||
private import HardcodedDataInterpretedAsCodeQuery as HardcodedDataInterpretedAsCodeQuery // ignore-query-import
|
||||
|
||||
/** DEPRECATED. Import `HardcodedDataInterpretedAsCodeQuery` instead. */
|
||||
deprecated module HardcodedDataInterpretedAsCode = HardcodedDataInterpretedAsCodeQuery;
|
||||
@@ -1,7 +0,0 @@
|
||||
/** DEPRECATED. Import `HostHeaderPoisoningInEmailGenerationQuery` instead. */
|
||||
|
||||
import javascript
|
||||
private import HostHeaderPoisoningInEmailGenerationQuery as HostHeaderPoisoningInEmailGenerationQuery // ignore-query-import
|
||||
|
||||
/** DEPRECATED. Import `HostHeaderPoisoningInEmailGenerationQuery` instead. */
|
||||
deprecated module HostHeaderPoisoningInEmailGeneration = HostHeaderPoisoningInEmailGenerationQuery;
|
||||
@@ -1,7 +0,0 @@
|
||||
/** DEPRECATED. Import `HttpToFileAccessQuery` instead. */
|
||||
|
||||
import javascript
|
||||
private import HttpToFileAccessQuery as HttpToFileAccessQuery // ignore-query-import
|
||||
|
||||
/** DEPRECATED. Import `HttpToFileAccessQuery` instead. */
|
||||
deprecated module HttpToFileAccess = HttpToFileAccessQuery;
|
||||
@@ -1,7 +0,0 @@
|
||||
/** DEPRECATED. Import `ImproperCodeSanitizationQuery` instead. */
|
||||
|
||||
import javascript
|
||||
private import ImproperCodeSanitizationQuery as ImproperCodeSanitizationQuery // ignore-query-import
|
||||
|
||||
/** DEPRECATED. Import `ImproperCodeSanitizationQuery` instead. */
|
||||
deprecated module ImproperCodeSanitization = ImproperCodeSanitizationQuery;
|
||||
@@ -1,7 +0,0 @@
|
||||
/** DEPRECATED. Import `IncompleteHtmlAttributeSanitizationQuery` instead. */
|
||||
|
||||
import javascript
|
||||
private import IncompleteHtmlAttributeSanitizationQuery as IncompleteHtmlAttributeSanitizationQuery // ignore-query-import
|
||||
|
||||
/** DEPRECATED. Import `IncompleteHtmlAttributeSanitizationQuery` instead. */
|
||||
deprecated module IncompleteHtmlAttributeSanitization = IncompleteHtmlAttributeSanitizationQuery;
|
||||
@@ -1,7 +0,0 @@
|
||||
/** DEPRECATED. Import `IndirectCommandInjectionQuery` instead. */
|
||||
|
||||
import javascript
|
||||
private import IndirectCommandInjectionQuery as IndirectCommandInjectionQuery // ignore-query-import
|
||||
|
||||
/** DEPRECATED. Import `IndirectCommandInjectionQuery` instead. */
|
||||
deprecated module IndirectCommandInjection = IndirectCommandInjectionQuery;
|
||||
@@ -1,7 +0,0 @@
|
||||
/** DEPRECATED. Import `InsecureDownloadQuery` instead. */
|
||||
|
||||
import javascript
|
||||
private import InsecureDownloadQuery as InsecureDownloadQuery // ignore-query-import
|
||||
|
||||
/** DEPRECATED. Import `InsecureDownloadQuery` instead. */
|
||||
deprecated module InsecureDownload = InsecureDownloadQuery;
|
||||
@@ -1,7 +0,0 @@
|
||||
/** DEPRECATED. Import `InsecureRandomnessQuery` instead. */
|
||||
|
||||
import javascript
|
||||
private import InsecureRandomnessQuery as InsecureRandomnessQuery // ignore-query-import
|
||||
|
||||
/** DEPRECATED. Import `InsecureRandomnessQuery` instead. */
|
||||
deprecated module InsecureRandomness = InsecureRandomnessQuery;
|
||||
@@ -1,7 +0,0 @@
|
||||
/** DEPRECATED. Import `InsufficientPasswordHashQuery` instead. */
|
||||
|
||||
import javascript
|
||||
private import InsufficientPasswordHashQuery as InsufficientPasswordHashQuery // ignore-query-import
|
||||
|
||||
/** DEPRECATED. Import `InsufficientPasswordHashQuery` instead. */
|
||||
deprecated module InsufficientPasswordHash = InsufficientPasswordHashQuery;
|
||||
@@ -1,7 +0,0 @@
|
||||
/** DEPRECATED. Import `LogInjectionQuery` instead. */
|
||||
|
||||
import javascript
|
||||
private import LogInjectionQuery as LogInjectionQuery // ignore-query-import
|
||||
|
||||
/** DEPRECATED. Import `LogInjectionQuery` instead. */
|
||||
deprecated module LogInjection = LogInjectionQuery;
|
||||
@@ -1,8 +0,0 @@
|
||||
/** DEPRECATED. Import `LoopBoundInjectionQuery` instead. */
|
||||
|
||||
import javascript
|
||||
import semmle.javascript.security.TaintedObject
|
||||
private import LoopBoundInjectionQuery as LoopBoundInjectionQuery // ignore-query-import
|
||||
|
||||
/** DEPRECATED. Import `LoopBoundInjectionQuery` instead. */
|
||||
deprecated module LoopBoundInjection = LoopBoundInjectionQuery;
|
||||
@@ -122,10 +122,10 @@ module LoopBoundInjection {
|
||||
"flattenDeep", "flattenDepth", "initial", "intersection", "intersectionBy",
|
||||
"intersectionWith", "join", "remove", "reverse", "slice", "sortedUniq", "sortedUniqBy",
|
||||
"tail", "union", "unionBy", "unionWith", "uniqBy", "unzip", "unzipWith", "without", "zip",
|
||||
"zipObject", "zipObjectDeep", "zipWith", "countBy", "each", "forEach", "eachRight",
|
||||
"forEachRight", "filter", "find", "findLast", "flatMap", "flatMapDeep", "flatMapDepth",
|
||||
"forEach", "forEachRight", "groupBy", "invokeMap", "keyBy", "map", "orderBy", "partition",
|
||||
"reduce", "reduceRight", "reject", "sortBy"
|
||||
"zipObject", "zipObjectDeep", "zipWith", "countBy", "each", "eachRight", "forEachRight",
|
||||
"filter", "find", "findLast", "flatMap", "flatMapDeep", "flatMapDepth", "forEach",
|
||||
"groupBy", "invokeMap", "keyBy", "map", "orderBy", "partition", "reduce", "reduceRight",
|
||||
"reject", "sortBy"
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
/** DEPRECATED. Import `NosqlInjectionQuery` instead. */
|
||||
|
||||
import javascript
|
||||
import semmle.javascript.security.TaintedObject
|
||||
private import NosqlInjectionQuery as NosqlInjectionQuery // ignore-query-import
|
||||
|
||||
/** DEPRECATED. Import `NosqlInjectionQuery` instead. */
|
||||
deprecated module NosqlInjection = NosqlInjectionQuery;
|
||||
@@ -1,7 +0,0 @@
|
||||
/** DEPRECATED. Import `PostMessageStarQuery` instead. */
|
||||
|
||||
import javascript
|
||||
private import PostMessageStarQuery as PostMessageStarQuery // ignore-query-import
|
||||
|
||||
/** DEPRECATED. Import `PostMessageStarQuery` instead. */
|
||||
deprecated module PostMessageStar = PostMessageStarQuery;
|
||||
@@ -1,6 +0,0 @@
|
||||
/** DEPRECATED. Import `PrototypePollutingAssignmentQuery` instead. */
|
||||
|
||||
private import PrototypePollutingAssignmentQuery as PrototypePollutingAssignmentQuery // ignore-query-import
|
||||
|
||||
/** DEPRECATED. Import `PrototypePollutingAssignmentQuery` instead. */
|
||||
deprecated module PrototypePollutingAssignment = PrototypePollutingAssignmentQuery;
|
||||
@@ -1,10 +0,0 @@
|
||||
/** DEPRECATED. Import `PrototypePollutionQuery` instead. */
|
||||
|
||||
import javascript
|
||||
import semmle.javascript.security.TaintedObject
|
||||
import semmle.javascript.dependencies.Dependencies
|
||||
import semmle.javascript.dependencies.SemVer
|
||||
private import PrototypePollutionQuery as PrototypePollutionQuery // ignore-query-import
|
||||
|
||||
/** DEPRECATED. Import `PrototypePollutionQuery` instead. */
|
||||
deprecated module PrototypePollution = PrototypePollutionQuery;
|
||||
@@ -1,7 +0,0 @@
|
||||
/** DEPRECATED. Import `ReflectedXssQuery` instead. */
|
||||
|
||||
import javascript
|
||||
private import ReflectedXssQuery as ReflectedXssQuery // ignore-query-import
|
||||
|
||||
/** DEPRECATED. Import `ReflectedXssQuery` instead. */
|
||||
deprecated module ReflectedXss = ReflectedXssQuery;
|
||||
@@ -1,7 +0,0 @@
|
||||
/** DEPRECATED. Import `RegExpInjectionQuery` instead. */
|
||||
|
||||
import javascript
|
||||
private import RegExpInjectionQuery as RegExpInjectionQuery // ignore-query-import
|
||||
|
||||
/** DEPRECATED. Import `RegExpInjectionQuery` instead. */
|
||||
deprecated module RegExpInjection = RegExpInjectionQuery;
|
||||
@@ -1,7 +0,0 @@
|
||||
/** DEPRECATED. Import `RemotePropertyInjectionQuery` instead. */
|
||||
|
||||
import javascript
|
||||
private import RemotePropertyInjectionQuery as RemotePropertyInjectionQuery // ignore-query-import
|
||||
|
||||
/** DEPRECATED. Import `RemotePropertyInjectionQuery` instead. */
|
||||
deprecated module RemotePropertyInjection = RemotePropertyInjectionQuery;
|
||||
@@ -1,8 +0,0 @@
|
||||
/** DEPRECATED. Import `RequestForgeryQuery` instead. */
|
||||
|
||||
import javascript
|
||||
import UrlConcatenation
|
||||
private import RequestForgeryQuery as RequestForgeryQuery // ignore-query-import
|
||||
|
||||
/** DEPRECATED. Import `RequestForgeryQuery` instead. */
|
||||
deprecated module RequestForgery = RequestForgeryQuery;
|
||||
@@ -12,7 +12,7 @@ import SecondOrderCommandInjectionCustomizations::SecondOrderCommandInjection
|
||||
private import semmle.javascript.security.TaintedObject
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for reasoning about command-injection vulnerabilities.
|
||||
* A taint-tracking configuration for reasoning about second order command-injection vulnerabilities.
|
||||
*/
|
||||
class Configuration extends TaintTracking::Configuration {
|
||||
Configuration() { this = "SecondOrderCommandInjection" }
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
/** DEPRECATED. Import `ServerSideUrlRedirectQuery` instead. */
|
||||
|
||||
import javascript
|
||||
import RemoteFlowSources
|
||||
import UrlConcatenation
|
||||
private import ServerSideUrlRedirectQuery as ServerSideUrlRedirectQuery // ignore-query-import
|
||||
|
||||
/** DEPRECATED. Import `ServerSideUrlRedirectQuery` instead. */
|
||||
deprecated module ServerSideUrlRedirect = ServerSideUrlRedirectQuery;
|
||||
@@ -1,7 +0,0 @@
|
||||
/** DEPRECATED. Import `ShellCommandInjectionFromEnvironmentQuery` instead. */
|
||||
|
||||
import javascript
|
||||
private import ShellCommandInjectionFromEnvironmentQuery as ShellCommandInjectionFromEnvironmentQuery // ignore-query-import
|
||||
|
||||
/** DEPRECATED. Import `ShellCommandInjectionFromEnvironmentQuery` instead. */
|
||||
deprecated module ShellCommandInjectionFromEnvironment = ShellCommandInjectionFromEnvironmentQuery;
|
||||
@@ -1,7 +0,0 @@
|
||||
/** DEPRECATED. Import `SqlInjectionQuery` instead. */
|
||||
|
||||
import javascript
|
||||
private import SqlInjectionQuery as SqlInjectionQuery // ignore-query-import
|
||||
|
||||
/** DEPRECATED. Import `SqlInjectionQuery` instead. */
|
||||
deprecated module SqlInjection = SqlInjectionQuery;
|
||||
@@ -1,7 +0,0 @@
|
||||
/** DEPRECATED. Import `StackTraceExposureQuery` instead. */
|
||||
|
||||
import javascript
|
||||
private import StackTraceExposureQuery as StackTraceExposureQuery // ignore-query-import
|
||||
|
||||
/** DEPRECATED. Import `StackTraceExposureQuery` instead. */
|
||||
deprecated module StackTraceExposure = StackTraceExposureQuery;
|
||||
@@ -1,7 +0,0 @@
|
||||
/** DEPRECATED. Import `StoredXssQuery` instead. */
|
||||
|
||||
import javascript
|
||||
private import StoredXssQuery as StoredXssQuery // ignore-query-import
|
||||
|
||||
/** DEPRECATED. Import `StoredXssQuery` instead. */
|
||||
deprecated module StoredXss = StoredXssQuery;
|
||||
@@ -1,8 +0,0 @@
|
||||
/** DEPRECATED. Import `TaintedFormatStringQuery` instead. */
|
||||
|
||||
import javascript
|
||||
import semmle.javascript.security.dataflow.DOM
|
||||
private import TaintedFormatStringQuery as TaintedFormatStringQuery // ignore-query-import
|
||||
|
||||
/** DEPRECATED. Import `TaintedFormatStringQuery` instead. */
|
||||
deprecated module TaintedFormatString = TaintedFormatStringQuery;
|
||||
@@ -1,7 +0,0 @@
|
||||
/** DEPRECATED. Import `TaintedPathQuery` instead. */
|
||||
|
||||
import javascript
|
||||
private import TaintedPathQuery as TaintedPathQuery // ignore-query-import
|
||||
|
||||
/** DEPRECATED. Import `TaintedPathQuery` instead. */
|
||||
deprecated module TaintedPath = TaintedPathQuery;
|
||||
@@ -1,7 +0,0 @@
|
||||
/** DEPRECATED. Import `TemplateObjectInjectionQuery` instead. */
|
||||
|
||||
import javascript
|
||||
private import TemplateObjectInjectionQuery as TemplateObjectInjectionQuery // ignore-query-import
|
||||
|
||||
/** DEPRECATED. Import `TemplateObjectInjectionQuery` instead. */
|
||||
deprecated module TemplateObjectInjection = TemplateObjectInjectionQuery;
|
||||
@@ -1,8 +0,0 @@
|
||||
/** DEPRECATED. Import `TypeConfusionThroughParameterTamperingQuery` instead. */
|
||||
|
||||
import javascript
|
||||
private import TypeConfusionThroughParameterTamperingQuery as TypeConfusionThroughParameterTamperingQuery // ignore-query-import
|
||||
|
||||
/** DEPRECATED. Import `TypeConfusionThroughParameterTamperingQuery` instead. */
|
||||
deprecated module TypeConfusionThroughParameterTampering =
|
||||
TypeConfusionThroughParameterTamperingQuery;
|
||||
@@ -1,7 +0,0 @@
|
||||
/** DEPRECATED. Import `UnsafeDeserializationQuery` instead. */
|
||||
|
||||
import javascript
|
||||
private import UnsafeDeserializationQuery as UnsafeDeserializationQuery // ignore-query-import
|
||||
|
||||
/** DEPRECATED. Import `UnsafeDeserializationQuery` instead. */
|
||||
deprecated module UnsafeDeserialization = UnsafeDeserializationQuery;
|
||||
@@ -1,8 +0,0 @@
|
||||
/** DEPRECATED. Import `UnsafeDynamicMethodAccessQuery` instead. */
|
||||
|
||||
import javascript
|
||||
import PropertyInjectionShared
|
||||
private import UnsafeDynamicMethodAccessQuery as UnsafeDynamicMethodAccessQuery // ignore-query-import
|
||||
|
||||
/** DEPRECATED. Import `UnsafeDynamicMethodAccessQuery` instead. */
|
||||
deprecated module UnsafeDynamicMethodAccess = UnsafeDynamicMethodAccessQuery;
|
||||
@@ -1,7 +0,0 @@
|
||||
/** DEPRECATED. Import `UnsafeHtmlConstructionQuery` instead. */
|
||||
|
||||
import javascript
|
||||
private import UnsafeHtmlConstructionQuery as UnsafeHtmlConstructionQuery // ignore-query-import
|
||||
|
||||
/** DEPRECATED. Import `UnsafeHtmlConstructionQuery` instead. */
|
||||
deprecated module UnsafeHtmlConstruction = UnsafeHtmlConstructionQuery;
|
||||
@@ -1,8 +0,0 @@
|
||||
/** DEPRECATED. Import `UnsafeJQueryPluginQuery` instead. */
|
||||
|
||||
import javascript
|
||||
import semmle.javascript.security.dataflow.Xss
|
||||
private import UnsafeJQueryPluginQuery as UnsafeJQueryPluginQuery // ignore-query-import
|
||||
|
||||
/** DEPRECATED. Import `UnsafeJQueryPluginQuery` instead. */
|
||||
deprecated module UnsafeJQueryPlugin = UnsafeJQueryPluginQuery;
|
||||
@@ -1,7 +0,0 @@
|
||||
/** DEPRECATED. Import `UnsafeShellCommandConstructionQuery` instead. */
|
||||
|
||||
import javascript
|
||||
private import UnsafeShellCommandConstructionQuery as UnsafeShellCommandConstructionQuery // ignore-query-import
|
||||
|
||||
/** DEPRECATED. Import `UnsafeShellCommandConstructionQuery` instead. */
|
||||
deprecated module UnsafeShellCommandConstruction = UnsafeShellCommandConstructionQuery;
|
||||
@@ -156,14 +156,9 @@ module UnsafeShellCommandConstruction {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a node that ends up in an array that is ultimately executed as a shell script by `sys`.
|
||||
* Holds if the arguments array given to `sys` is joined as a string because `shell` is set to true.
|
||||
*/
|
||||
private DataFlow::SourceNode endsInShellExecutedArray(
|
||||
DataFlow::TypeBackTracker t, SystemCommandExecution sys
|
||||
) {
|
||||
t.start() and
|
||||
result = sys.getArgumentList().getALocalSource() and
|
||||
// the array gets joined to a string when `shell` is set to true.
|
||||
predicate executesArrayAsShell(SystemCommandExecution sys) {
|
||||
sys.getOptionsArg()
|
||||
.getALocalSource()
|
||||
.getAPropertyWrite("shell")
|
||||
@@ -171,6 +166,17 @@ module UnsafeShellCommandConstruction {
|
||||
.asExpr()
|
||||
.(BooleanLiteral)
|
||||
.getValue() = "true"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a node that ends up in an array that is ultimately executed as a shell script by `sys`.
|
||||
*/
|
||||
private DataFlow::SourceNode endsInShellExecutedArray(
|
||||
DataFlow::TypeBackTracker t, SystemCommandExecution sys
|
||||
) {
|
||||
t.start() and
|
||||
result = sys.getArgumentList().getALocalSource() and
|
||||
executesArrayAsShell(sys)
|
||||
or
|
||||
exists(DataFlow::TypeBackTracker t2 |
|
||||
result = endsInShellExecutedArray(t2, sys).backtrack(t2, t)
|
||||
@@ -193,6 +199,10 @@ module UnsafeShellCommandConstruction {
|
||||
or
|
||||
this = arr.getAMethodCall(["push", "unshift"]).getAnArgument()
|
||||
)
|
||||
or
|
||||
this = sys.getArgumentList() and
|
||||
not this instanceof DataFlow::ArrayCreationNode and
|
||||
executesArrayAsShell(sys)
|
||||
}
|
||||
|
||||
override string getSinkType() { result = "shell argument" }
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
/** DEPRECATED. Import `UnvalidatedDynamicMethodCallQuery` instead. */
|
||||
|
||||
import javascript
|
||||
import semmle.javascript.frameworks.Express
|
||||
import PropertyInjectionShared
|
||||
private import UnvalidatedDynamicMethodCallQuery as UnvalidatedDynamicMethodCallQuery // ignore-query-import
|
||||
|
||||
/** DEPRECATED. Import `UnvalidatedDynamicMethodCallQuery` instead. */
|
||||
deprecated module UnvalidatedDynamicMethodCall = UnvalidatedDynamicMethodCallQuery;
|
||||
@@ -1,7 +0,0 @@
|
||||
/** DEPRECATED. Import `XmlBombQuery` instead. */
|
||||
|
||||
import javascript
|
||||
private import XmlBombQuery as XmlBombQuery // ignore-query-import
|
||||
|
||||
/** DEPRECATED. Import `XmlBombQuery` instead. */
|
||||
deprecated module XmlBomb = XmlBombQuery;
|
||||
@@ -1,8 +0,0 @@
|
||||
/** DEPRECATED. Import `XpathInjectionQuery` instead. */
|
||||
|
||||
import javascript
|
||||
import semmle.javascript.security.dataflow.DOM
|
||||
private import XpathInjectionQuery as XpathInjectionQuery // ignore-query-import
|
||||
|
||||
/** DEPRECATED. Import `XpathInjectionQuery` instead. */
|
||||
deprecated module XpathInjection = XpathInjectionQuery;
|
||||
@@ -1,7 +0,0 @@
|
||||
/** DEPRECATED. Import `XssThroughDomQuery` instead. */
|
||||
|
||||
import javascript
|
||||
private import XssThroughDomQuery as XssThroughDomQuery // ignore-query-import
|
||||
|
||||
/** DEPRECATED. Import `XssThroughDomQuery` instead. */
|
||||
deprecated module XssThroughDom = XssThroughDomQuery;
|
||||
@@ -1,7 +0,0 @@
|
||||
/** DEPRECATED. Import `XxeQuery` instead. */
|
||||
|
||||
import javascript
|
||||
private import XxeQuery as XxeQuery // ignore-query-import
|
||||
|
||||
/** DEPRECATED. Import `XxeQuery` instead. */
|
||||
deprecated module Xxe = XxeQuery;
|
||||
@@ -1,7 +0,0 @@
|
||||
/** DEPRECATED. Import `ZipSlipQuery` instead. */
|
||||
|
||||
import javascript
|
||||
private import ZipSlipQuery as ZipSlipQuery // ignore-query-import
|
||||
|
||||
/** DEPRECATED. Import `ZipSlipQuery` instead. */
|
||||
deprecated module ZipSlip = ZipSlipQuery;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user