Merge pull request #18467 from github/js/shared-dataflow-branch

JS: Migrate to shared data flow library (targeting main!) 🚀
This commit is contained in:
Asger F
2025-01-16 11:28:57 +01:00
committed by GitHub
531 changed files with 28648 additions and 35414 deletions

View File

@@ -15,13 +15,13 @@
import javascript
import semmle.javascript.security.regexp.PolynomialReDoSQuery
import DataFlow::PathGraph
import PolynomialReDoSFlow::PathGraph
from
Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, Sink sinkNode,
PolynomialReDoSFlow::PathNode source, PolynomialReDoSFlow::PathNode sink, Sink sinkNode,
PolynomialBackTrackingTerm regexp
where
cfg.hasFlowPath(source, sink) and
PolynomialReDoSFlow::flowPath(source, sink) and
sinkNode = sink.getNode() and
regexp = sinkNode.getRegExp() and
not (

View File

@@ -11,10 +11,12 @@
import javascript
import semmle.javascript.security.dataflow.ExternalAPIUsedWithUntrustedDataQuery
import DataFlow::PathGraph
import ExternalAPIUsedWithUntrustedDataFlow::PathGraph
from Configuration config, DataFlow::PathNode source, DataFlow::PathNode sink
where config.hasFlowPath(source, sink)
from
ExternalAPIUsedWithUntrustedDataFlow::PathNode source,
ExternalAPIUsedWithUntrustedDataFlow::PathNode sink
where ExternalAPIUsedWithUntrustedDataFlow::flowPath(source, sink)
select sink, source, sink,
"Call to " + sink.getNode().(Sink).getApiName() + " with untrusted data from $@.", source,
source.toString()

View File

@@ -17,9 +17,9 @@
import javascript
import semmle.javascript.security.dataflow.TaintedPathQuery
import DataFlow::PathGraph
import DataFlow::DeduplicatePathGraph<TaintedPathFlow::PathNode, TaintedPathFlow::PathGraph>
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
from PathNode source, PathNode sink
where TaintedPathFlow::flowPath(source.getAnOriginalPathNode(), sink.getAnOriginalPathNode())
select sink.getNode(), source, sink, "This path depends on a $@.", source.getNode(),
"user-provided value"

View File

@@ -14,10 +14,10 @@
import javascript
import semmle.javascript.security.dataflow.ZipSlipQuery
import DataFlow::PathGraph
import DataFlow::DeduplicatePathGraph<ZipSlipFlow::PathNode, ZipSlipFlow::PathGraph>
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
from PathNode source, PathNode sink
where ZipSlipFlow::flowPath(source.getAnOriginalPathNode(), sink.getAnOriginalPathNode())
select source.getNode(), source, sink,
"Unsanitized archive entry, which may contain '..', is used in a $@.", sink.getNode(),
"file system operation"

View File

@@ -12,10 +12,11 @@
*/
import javascript
import DataFlow::PathGraph
import semmle.javascript.security.dataflow.TemplateObjectInjectionQuery
import DataFlow::DeduplicatePathGraph<TemplateObjectInjectionFlow::PathNode, TemplateObjectInjectionFlow::PathGraph>
from DataFlow::Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
from PathNode source, PathNode sink
where
TemplateObjectInjectionFlow::flowPath(source.getAnOriginalPathNode(), sink.getAnOriginalPathNode())
select sink.getNode(), source, sink, "Template object depends on a $@.", source.getNode(),
"user-provided value"

View File

@@ -15,16 +15,16 @@
import javascript
import semmle.javascript.security.dataflow.CommandInjectionQuery
import DataFlow::PathGraph
import CommandInjectionFlow::PathGraph
from
Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, DataFlow::Node highlight,
Source sourceNode
CommandInjectionFlow::PathNode source, CommandInjectionFlow::PathNode sink,
DataFlow::Node highlight, Source sourceNode
where
cfg.hasFlowPath(source, sink) and
CommandInjectionFlow::flowPath(source, sink) and
(
if cfg.isSinkWithHighlight(sink.getNode(), _)
then cfg.isSinkWithHighlight(sink.getNode(), highlight)
if isSinkWithHighlight(sink.getNode(), _)
then isSinkWithHighlight(sink.getNode(), highlight)
else highlight = sink.getNode()
) and
sourceNode = source.getNode()

View File

@@ -15,14 +15,16 @@
*/
import javascript
import DataFlow::PathGraph
import semmle.javascript.security.dataflow.IndirectCommandInjectionQuery
import IndirectCommandInjectionFlow::PathGraph
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, DataFlow::Node highlight
from
IndirectCommandInjectionFlow::PathNode source, IndirectCommandInjectionFlow::PathNode sink,
DataFlow::Node highlight
where
cfg.hasFlowPath(source, sink) and
if cfg.isSinkWithHighlight(sink.getNode(), _)
then cfg.isSinkWithHighlight(sink.getNode(), highlight)
IndirectCommandInjectionFlow::flowPath(source, sink) and
if IndirectCommandInjectionConfig::isSinkWithHighlight(sink.getNode(), _)
then IndirectCommandInjectionConfig::isSinkWithHighlight(sink.getNode(), highlight)
else highlight = sink.getNode()
select highlight, source, sink, "This command depends on an unsanitized $@.", source.getNode(),
source.getNode().(Source).describe()

View File

@@ -14,11 +14,14 @@
*/
import javascript
import DataFlow::PathGraph
import semmle.javascript.security.dataflow.SecondOrderCommandInjectionQuery
import DataFlow::DeduplicatePathGraph<SecondOrderCommandInjectionFlow::PathNode, SecondOrderCommandInjectionFlow::PathGraph>
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, Sink sinkNode
where cfg.hasFlowPath(source, sink) and sinkNode = sink.getNode()
from PathNode source, PathNode sink, Sink sinkNode
where
SecondOrderCommandInjectionFlow::flowPath(source.getAnOriginalPathNode(),
sink.getAnOriginalPathNode()) and
sinkNode = sink.getNode()
select sink.getNode(), source, sink,
"Command line argument that depends on $@ can execute an arbitrary command if " +
sinkNode.getVulnerableArgumentExample() + " is used with " + sinkNode.getCommand() + ".",

View File

@@ -14,17 +14,18 @@
*/
import javascript
import DataFlow::PathGraph
import semmle.javascript.security.dataflow.ShellCommandInjectionFromEnvironmentQuery
import ShellCommandInjectionFromEnvironmentFlow::PathGraph
from
Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, DataFlow::Node highlight,
ShellCommandInjectionFromEnvironmentFlow::PathNode source,
ShellCommandInjectionFromEnvironmentFlow::PathNode sink, DataFlow::Node highlight,
Source sourceNode
where
sourceNode = source.getNode() and
cfg.hasFlowPath(source, sink) and
if cfg.isSinkWithHighlight(sink.getNode(), _)
then cfg.isSinkWithHighlight(sink.getNode(), highlight)
ShellCommandInjectionFromEnvironmentFlow::flowPath(source, sink) and
if ShellCommandInjectionFromEnvironmentConfig::isSinkWithHighlight(sink.getNode(), _)
then ShellCommandInjectionFromEnvironmentConfig::isSinkWithHighlight(sink.getNode(), highlight)
else highlight = sink.getNode()
select highlight, source, sink, "This shell command depends on an uncontrolled $@.", sourceNode,
sourceNode.getSourceType()

View File

@@ -15,10 +15,12 @@
import javascript
import semmle.javascript.security.dataflow.UnsafeShellCommandConstructionQuery
import DataFlow::PathGraph
import UnsafeShellCommandConstructionFlow::PathGraph
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, Sink sinkNode
where cfg.hasFlowPath(source, sink) and sinkNode = sink.getNode()
from
UnsafeShellCommandConstructionFlow::PathNode source,
UnsafeShellCommandConstructionFlow::PathNode sink, Sink sinkNode
where UnsafeShellCommandConstructionFlow::flowPath(source, sink) and sinkNode = sink.getNode()
select sinkNode.getAlertLocation(), source, sink,
"This " + sinkNode.getSinkType() + " which depends on $@ is later used in a $@.",
source.getNode(), "library input", sinkNode.getCommandExecution(), "shell command"

View File

@@ -14,10 +14,10 @@
import javascript
import semmle.javascript.security.dataflow.ExceptionXssQuery
import DataFlow::PathGraph
import DataFlow::DeduplicatePathGraph<ExceptionXssFlow::PathNode, ExceptionXssFlow::PathGraph>
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
from PathNode source, PathNode sink
where ExceptionXssFlow::flowPath(source.getAnOriginalPathNode(), sink.getAnOriginalPathNode())
select sink.getNode(), source, sink,
"$@ is reinterpreted as HTML without escaping meta-characters.", source.getNode(),
source.getNode().(Source).getDescription()

View File

@@ -14,9 +14,9 @@
import javascript
import semmle.javascript.security.dataflow.ReflectedXssQuery
import DataFlow::PathGraph
import ReflectedXssFlow::PathGraph
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
from ReflectedXssFlow::PathNode source, ReflectedXssFlow::PathNode sink
where ReflectedXssFlow::flowPath(source, sink)
select sink.getNode(), source, sink, "Cross-site scripting vulnerability due to a $@.",
source.getNode(), "user-provided value"

View File

@@ -14,9 +14,9 @@
import javascript
import semmle.javascript.security.dataflow.StoredXssQuery
import DataFlow::PathGraph
import StoredXssFlow::PathGraph
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
from StoredXssFlow::PathNode source, StoredXssFlow::PathNode sink
where StoredXssFlow::flowPath(source, sink)
select sink.getNode(), source, sink, "Stored cross-site scripting vulnerability due to $@.",
source.getNode(), "stored value"

View File

@@ -13,11 +13,13 @@
*/
import javascript
import DataFlow::PathGraph
import semmle.javascript.security.dataflow.UnsafeHtmlConstructionQuery
import DataFlow::DeduplicatePathGraph<UnsafeHtmlConstructionFlow::PathNode, UnsafeHtmlConstructionFlow::PathGraph>
from DataFlow::Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, Sink sinkNode
where cfg.hasFlowPath(source, sink) and sink.getNode() = sinkNode
from PathNode source, PathNode sink, Sink sinkNode
where
UnsafeHtmlConstructionFlow::flowPath(source.getAnOriginalPathNode(), sink.getAnOriginalPathNode()) and
sink.getNode() = sinkNode
select sinkNode, source, sink,
"This " + sinkNode.describe() + " which depends on $@ might later allow $@.", source.getNode(),
"library input", sinkNode.getSink(), sinkNode.getVulnerabilityKind().toLowerCase()

View File

@@ -14,13 +14,13 @@
import javascript
import semmle.javascript.security.dataflow.UnsafeJQueryPluginQuery
import DataFlow::PathGraph
import UnsafeJQueryPluginFlow::PathGraph
from
Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink,
UnsafeJQueryPluginFlow::PathNode source, UnsafeJQueryPluginFlow::PathNode sink,
JQuery::JQueryPluginMethod plugin
where
cfg.hasFlowPath(source, sink) and
UnsafeJQueryPluginFlow::flowPath(source, sink) and
source.getNode().(Source).getPlugin() = plugin
select sink.getNode(), source, sink, "Potential XSS vulnerability in the $@.", plugin,
"'$.fn." + plugin.getPluginName() + "' plugin"

View File

@@ -14,10 +14,10 @@
import javascript
import semmle.javascript.security.dataflow.DomBasedXssQuery
import DataFlow::PathGraph
import DataFlow::DeduplicatePathGraph<DomBasedXssFlow::PathNode, DomBasedXssFlow::PathGraph>
from DataFlow::Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
from PathNode source, PathNode sink
where DomBasedXssFlow::flowPath(source.getAnOriginalPathNode(), sink.getAnOriginalPathNode())
select sink.getNode(), source, sink,
sink.getNode().(Sink).getVulnerabilityKind() + " vulnerability due to $@.", source.getNode(),
"user-provided value"

View File

@@ -14,9 +14,11 @@
import javascript
import semmle.javascript.security.dataflow.XssThroughDomQuery
import DataFlow::PathGraph
import XssThroughDomFlow::PathGraph
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
from XssThroughDomFlow::PathNode source, XssThroughDomFlow::PathNode sink
where
XssThroughDomFlow::flowPath(source, sink) and
not isIgnoredSourceSinkPair(source.getNode(), sink.getNode())
select sink.getNode(), source, sink,
"$@ is reinterpreted as HTML without escaping meta-characters.", source.getNode(), "DOM text"

View File

@@ -14,17 +14,23 @@
*/
import javascript
import semmle.javascript.security.dataflow.SqlInjectionQuery as SqlInjection
import semmle.javascript.security.dataflow.NosqlInjectionQuery as NosqlInjection
import DataFlow::PathGraph
import semmle.javascript.security.dataflow.SqlInjectionQuery as Sql
import semmle.javascript.security.dataflow.NosqlInjectionQuery as Nosql
from DataFlow::Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, string type
module Merged =
DataFlow::MergePathGraph<Sql::SqlInjectionFlow::PathNode, Nosql::NosqlInjectionFlow::PathNode,
Sql::SqlInjectionFlow::PathGraph, Nosql::NosqlInjectionFlow::PathGraph>;
import DataFlow::DeduplicatePathGraph<Merged::PathNode, Merged::PathGraph>
from PathNode source, PathNode sink, string type
where
(
cfg instanceof SqlInjection::Configuration and type = "string"
or
cfg instanceof NosqlInjection::Configuration and type = "object"
) and
cfg.hasFlowPath(source, sink)
Sql::SqlInjectionFlow::flowPath(source.getAnOriginalPathNode().asPathNode1(),
sink.getAnOriginalPathNode().asPathNode1()) and
type = "string"
or
Nosql::NosqlInjectionFlow::flowPath(source.getAnOriginalPathNode().asPathNode2(),
sink.getAnOriginalPathNode().asPathNode2()) and
type = "object"
select sink.getNode(), source, sink, "This query " + type + " depends on a $@.", source.getNode(),
"user-provided value"

View File

@@ -16,9 +16,9 @@
import javascript
import semmle.javascript.security.dataflow.CodeInjectionQuery
import DataFlow::PathGraph
import CodeInjectionFlow::PathGraph
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
from CodeInjectionFlow::PathNode source, CodeInjectionFlow::PathNode sink
where CodeInjectionFlow::flowPath(source, sink)
select sink.getNode(), source, sink, sink.getNode().(Sink).getMessagePrefix() + " depends on a $@.",
source.getNode(), "user-provided value"

View File

@@ -14,9 +14,9 @@
import javascript
import semmle.javascript.security.dataflow.ImproperCodeSanitizationQuery
import DataFlow::PathGraph
private import semmle.javascript.heuristics.HeuristicSinks
private import semmle.javascript.security.dataflow.CodeInjectionCustomizations
import ImproperCodeSanitizationFlow::PathGraph
/**
* Gets a type-tracked instance of `RemoteFlowSource` using type-tracker `t`.
@@ -60,9 +60,9 @@ private DataFlow::Node endsInCodeInjectionSink() {
result = endsInCodeInjectionSink(DataFlow::TypeBackTracker::end())
}
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
from ImproperCodeSanitizationFlow::PathNode source, ImproperCodeSanitizationFlow::PathNode sink
where
cfg.hasFlowPath(source, sink) and
ImproperCodeSanitizationFlow::flowPath(source, sink) and
// Basic detection of duplicate results with `js/code-injection`.
not (
sink.getNode().(StringOps::ConcatenationLeaf).getRoot() = endsInCodeInjectionSink() and

View File

@@ -14,11 +14,13 @@
*/
import javascript
import DataFlow::PathGraph
import semmle.javascript.security.dataflow.UnsafeCodeConstruction::UnsafeCodeConstruction
import UnsafeCodeConstructionFlow::PathGraph
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, Sink sinkNode
where cfg.hasFlowPath(source, sink) and sinkNode = sink.getNode()
from
UnsafeCodeConstructionFlow::PathNode source, UnsafeCodeConstructionFlow::PathNode sink,
Sink sinkNode
where UnsafeCodeConstructionFlow::flowPath(source, sink) and sinkNode = sink.getNode()
select sink.getNode(), source, sink,
"This " + sinkNode.getSinkType() + " which depends on $@ is later $@.", source.getNode(),
"library input", sinkNode.getCodeSink(), "interpreted as code"

View File

@@ -12,10 +12,10 @@
import javascript
import semmle.javascript.security.dataflow.UnsafeDynamicMethodAccessQuery
import DataFlow::PathGraph
import UnsafeDynamicMethodAccessFlow::PathGraph
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
from UnsafeDynamicMethodAccessFlow::PathNode source, UnsafeDynamicMethodAccessFlow::PathNode sink
where UnsafeDynamicMethodAccessFlow::flowPath(source, sink)
select sink, source, sink,
"This method is invoked using a $@, which may allow remote code execution.", source.getNode(),
"user-controlled value"

View File

@@ -15,9 +15,9 @@
*/
import javascript
import DataFlow::PathGraph
import semmle.javascript.security.dataflow.IncompleteHtmlAttributeSanitizationQuery
import semmle.javascript.security.IncompleteBlacklistSanitizer
import DataFlow::DeduplicatePathGraph<IncompleteHtmlAttributeSanitizationFlow::PathNode, IncompleteHtmlAttributeSanitizationFlow::PathGraph>
/**
* Gets a pretty string of the dangerous characters for `sink`.
@@ -31,8 +31,10 @@ string prettyPrintDangerousCharaters(Sink sink) {
).regexpReplaceAll(",(?=[^,]+$)", " or")
}
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
from PathNode source, PathNode sink
where
IncompleteHtmlAttributeSanitizationFlow::flowPath(source.getAnOriginalPathNode(),
sink.getAnOriginalPathNode())
select sink.getNode(), source, sink,
// this message is slightly sub-optimal as we do not have an easy way
// to get the flow labels that reach the sink, so the message includes

View File

@@ -12,10 +12,10 @@
*/
import javascript
import DataFlow::PathGraph
import semmle.javascript.security.dataflow.LogInjectionQuery
import LogInjectionFlow::PathGraph
from LogInjectionConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink
where config.hasFlowPath(source, sink)
from LogInjectionFlow::PathNode source, LogInjectionFlow::PathNode sink
where LogInjectionFlow::flowPath(source, sink)
select sink.getNode(), source, sink, "Log entry depends on a $@.", source.getNode(),
"user-provided value"

View File

@@ -12,9 +12,9 @@
import javascript
import semmle.javascript.security.dataflow.TaintedFormatStringQuery
import DataFlow::PathGraph
import TaintedFormatStringFlow::PathGraph
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
from TaintedFormatStringFlow::PathNode source, TaintedFormatStringFlow::PathNode sink
where TaintedFormatStringFlow::flowPath(source, sink)
select sink.getNode(), source, sink, "Format string depends on a $@.", source.getNode(),
"user-provided value"

View File

@@ -12,9 +12,9 @@
import javascript
import semmle.javascript.security.dataflow.FileAccessToHttpQuery
import DataFlow::PathGraph
import FileAccessToHttpFlow::PathGraph
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
from FileAccessToHttpFlow::PathNode source, FileAccessToHttpFlow::PathNode sink
where FileAccessToHttpFlow::flowPath(source, sink)
select sink.getNode(), source, sink, "Outbound network request depends on $@.", source.getNode(),
"file data"

View File

@@ -15,9 +15,9 @@
import javascript
import semmle.javascript.security.dataflow.PostMessageStarQuery
import DataFlow::PathGraph
import PostMessageStarFlow::PathGraph
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
from PostMessageStarFlow::PathNode source, PostMessageStarFlow::PathNode sink
where PostMessageStarFlow::flowPath(source, sink)
select sink.getNode(), source, sink, "$@ is sent to another window without origin restriction.",
source.getNode(), "Sensitive data"

View File

@@ -15,9 +15,9 @@
import javascript
import semmle.javascript.security.dataflow.StackTraceExposureQuery
import DataFlow::PathGraph
import StackTraceExposureFlow::PathGraph
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
from StackTraceExposureFlow::PathNode source, StackTraceExposureFlow::PathNode sink
where StackTraceExposureFlow::flowPath(source, sink)
select sink.getNode(), source, sink, "This information exposed to the user depends on $@.",
source.getNode(), "stack trace information"

View File

@@ -15,10 +15,10 @@
import javascript
import semmle.javascript.security.dataflow.BuildArtifactLeakQuery
import DataFlow::PathGraph
import BuildArtifactLeakFlow::PathGraph
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
from BuildArtifactLeakFlow::PathNode source, BuildArtifactLeakFlow::PathNode sink
where BuildArtifactLeakFlow::flowPath(source, sink)
select sink.getNode(), source, sink, "This creates a build artifact that depends on $@.",
source.getNode(),
"sensitive data returned by" + source.getNode().(CleartextLogging::Source).describe()

View File

@@ -15,7 +15,7 @@
import javascript
import semmle.javascript.security.dataflow.CleartextLoggingQuery
import DataFlow::PathGraph
import CleartextLoggingFlow::PathGraph
/**
* Holds if `tl` is used in a browser environment.
@@ -33,9 +33,9 @@ predicate inBrowserEnvironment(TopLevel tl) {
)
}
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
from CleartextLoggingFlow::PathNode source, CleartextLoggingFlow::PathNode sink
where
cfg.hasFlowPath(source, sink) and
CleartextLoggingFlow::flowPath(source, sink) and
// ignore logging to the browser console (even though it is not a good practice)
not inBrowserEnvironment(sink.getNode().asExpr().getTopLevel())
select sink.getNode(), source, sink, "This logs sensitive data returned by $@ as clear text.",

View File

@@ -15,9 +15,9 @@
import javascript
import semmle.javascript.security.dataflow.CleartextStorageQuery
import DataFlow::PathGraph
import ClearTextStorageFlow::PathGraph
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
from ClearTextStorageFlow::PathNode source, ClearTextStorageFlow::PathNode sink
where ClearTextStorageFlow::flowPath(source, sink)
select sink.getNode(), source, sink, "This stores sensitive data returned by $@ as clear text.",
source.getNode(), source.getNode().(Source).describe()

View File

@@ -14,13 +14,13 @@
import javascript
import semmle.javascript.security.dataflow.BrokenCryptoAlgorithmQuery
import semmle.javascript.security.SensitiveActions
import DataFlow::PathGraph
import BrokenCryptoAlgorithmFlow::PathGraph
from
Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, Source sourceNode,
Sink sinkNode
BrokenCryptoAlgorithmFlow::PathNode source, BrokenCryptoAlgorithmFlow::PathNode sink,
Source sourceNode, Sink sinkNode
where
cfg.hasFlowPath(source, sink) and
BrokenCryptoAlgorithmFlow::flowPath(source, sink) and
sourceNode = source.getNode() and
sinkNode = sink.getNode() and
not sourceNode instanceof CleartextPasswordExpr // flagged by js/insufficient-password-hash

View File

@@ -14,10 +14,10 @@
import javascript
import semmle.javascript.security.dataflow.InsecureRandomnessQuery
import DataFlow::PathGraph
import InsecureRandomnessFlow::PathGraph
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
from InsecureRandomnessFlow::PathNode source, InsecureRandomnessFlow::PathNode sink
where InsecureRandomnessFlow::flowPath(source, sink)
select sink.getNode(), source, sink,
"This uses a cryptographically insecure random number generated at $@ in a security context.",
source.getNode(), source.getNode().toString()

View File

@@ -14,10 +14,10 @@
import javascript
import semmle.javascript.security.dataflow.CorsMisconfigurationForCredentialsQuery
import DataFlow::PathGraph
import CorsMisconfigurationFlow::PathGraph
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
from CorsMisconfigurationFlow::PathNode source, CorsMisconfigurationFlow::PathNode sink
where CorsMisconfigurationFlow::flowPath(source, sink)
select sink.getNode(), source, sink, "$@ leak vulnerability due to a $@.",
sink.getNode().(Sink).getCredentialsHeader(), "Credential", source.getNode(),
"misconfigured CORS header value"

View File

@@ -13,10 +13,10 @@
*/
import javascript
import DataFlow::PathGraph
import semmle.javascript.security.dataflow.InsecureTemporaryFileQuery
import InsecureTemporaryFileFlow::PathGraph
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
from InsecureTemporaryFileFlow::PathNode source, InsecureTemporaryFileFlow::PathNode sink
where InsecureTemporaryFileFlow::flowPath(source, sink)
select sink.getNode(), source, sink, "Insecure creation of file in $@.", source.getNode(),
"the os temp dir"

View File

@@ -11,14 +11,13 @@
*/
import javascript
import DataFlow::PathGraph
import semmle.javascript.security.dataflow.DeepObjectResourceExhaustionQuery
import DataFlow::DeduplicatePathGraph<DeepObjectResourceExhaustionFlow::PathNode, DeepObjectResourceExhaustionFlow::PathGraph>
from
Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, DataFlow::Node link,
string reason
from PathNode source, PathNode sink, DataFlow::Node link, string reason
where
cfg.hasFlowPath(source, sink) and
DeepObjectResourceExhaustionFlow::flowPath(source.getAnOriginalPathNode(),
sink.getAnOriginalPathNode()) and
sink.getNode().(Sink).hasReason(link, reason)
select sink, source, sink, "Denial of service caused by processing $@ with $@.", source.getNode(),
"user input", link, reason

View File

@@ -14,9 +14,9 @@
import javascript
import semmle.javascript.security.dataflow.RemotePropertyInjectionQuery
import DataFlow::PathGraph
import RemotePropertyInjectionFlow::PathGraph
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
from RemotePropertyInjectionFlow::PathNode source, RemotePropertyInjectionFlow::PathNode sink
where RemotePropertyInjectionFlow::flowPath(source, sink)
select sink.getNode(), source, sink, sink.getNode().(Sink).getMessage() + " depends on a $@.",
source.getNode(), "user-provided value"

View File

@@ -13,9 +13,9 @@
import javascript
import semmle.javascript.security.dataflow.UnsafeDeserializationQuery
import DataFlow::PathGraph
import UnsafeDeserializationFlow::PathGraph
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
from UnsafeDeserializationFlow::PathNode source, UnsafeDeserializationFlow::PathNode sink
where UnsafeDeserializationFlow::flowPath(source, sink)
select sink.getNode(), source, sink, "Unsafe deserialization depends on a $@.", source.getNode(),
"user-provided value"

View File

@@ -14,10 +14,12 @@
import javascript
import semmle.javascript.security.dataflow.HardcodedDataInterpretedAsCodeQuery
import DataFlow::PathGraph
import DataFlow::DeduplicatePathGraph<HardcodedDataInterpretedAsCodeFlow::PathNode, HardcodedDataInterpretedAsCodeFlow::PathGraph>
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
from PathNode source, PathNode sink
where
HardcodedDataInterpretedAsCodeFlow::flowPath(source.getAnOriginalPathNode(),
sink.getAnOriginalPathNode())
select sink.getNode(), source, sink,
"$@ is interpreted as " + sink.getNode().(Sink).getKind() + ".", source.getNode(),
"Hard-coded data"

View File

@@ -15,9 +15,10 @@
import javascript
import semmle.javascript.security.dataflow.ClientSideUrlRedirectQuery
import DataFlow::PathGraph
import DataFlow::DeduplicatePathGraph<ClientSideUrlRedirectFlow::PathNode, ClientSideUrlRedirectFlow::PathGraph>
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
from PathNode source, PathNode sink
where
ClientSideUrlRedirectFlow::flowPath(source.getAnOriginalPathNode(), sink.getAnOriginalPathNode())
select sink.getNode(), source, sink, "Untrusted URL redirection depends on a $@.", source.getNode(),
"user-provided value"

View File

@@ -13,9 +13,9 @@
import javascript
import semmle.javascript.security.dataflow.ServerSideUrlRedirectQuery
import DataFlow::PathGraph
import ServerSideUrlRedirectFlow::PathGraph
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
from ServerSideUrlRedirectFlow::PathNode source, ServerSideUrlRedirectFlow::PathNode sink
where ServerSideUrlRedirectFlow::flowPath(source, sink)
select sink.getNode(), source, sink, "Untrusted URL redirection depends on a $@.", source.getNode(),
"user-provided value"

View File

@@ -14,10 +14,10 @@
import javascript
import semmle.javascript.security.dataflow.XxeQuery
import DataFlow::PathGraph
import XxeFlow::PathGraph
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
from XxeFlow::PathNode source, XxeFlow::PathNode sink
where XxeFlow::flowPath(source, sink)
select sink.getNode(), source, sink,
"XML parsing depends on a $@ without guarding against external entity expansion.",
source.getNode(), "user-provided value"

View File

@@ -13,9 +13,9 @@
import javascript
import semmle.javascript.security.dataflow.HostHeaderPoisoningInEmailGenerationQuery
import DataFlow::PathGraph
import HostHeaderPoisoningFlow::PathGraph
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
from HostHeaderPoisoningFlow::PathNode source, HostHeaderPoisoningFlow::PathNode sink
where HostHeaderPoisoningFlow::flowPath(source, sink)
select sink.getNode(), source, sink, "Links in this email can be hijacked by poisoning the $@.",
source.getNode(), "HTTP host header"

View File

@@ -13,9 +13,9 @@
import javascript
import semmle.javascript.security.dataflow.XpathInjectionQuery
import DataFlow::PathGraph
import XpathInjectionFlow::PathGraph
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
from XpathInjectionFlow::PathNode source, XpathInjectionFlow::PathNode sink
where XpathInjectionFlow::flowPath(source, sink)
select sink.getNode(), source, sink, "XPath expression depends on a $@.", source.getNode(),
"user-provided value"

View File

@@ -15,9 +15,9 @@
import javascript
import semmle.javascript.security.dataflow.RegExpInjectionQuery
import DataFlow::PathGraph
import RegExpInjectionFlow::PathGraph
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
from RegExpInjectionFlow::PathNode source, RegExpInjectionFlow::PathNode sink
where RegExpInjectionFlow::flowPath(source, sink)
select sink.getNode(), source, sink, "This regular expression is constructed from a $@.",
source.getNode(), source.getNode().(Source).describe()

View File

@@ -13,10 +13,12 @@
import javascript
import semmle.javascript.security.dataflow.UnvalidatedDynamicMethodCallQuery
import DataFlow::PathGraph
import DataFlow::DeduplicatePathGraph<UnvalidatedDynamicMethodCallFlow::PathNode, UnvalidatedDynamicMethodCallFlow::PathGraph>
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
from PathNode source, PathNode sink
where
UnvalidatedDynamicMethodCallFlow::flowPath(source.getAnOriginalPathNode(),
sink.getAnOriginalPathNode())
select sink.getNode(), source, sink,
"Invocation of method with $@ name may dispatch to unexpected target and cause an exception.",
source.getNode(), "user-controlled"

View File

@@ -13,10 +13,10 @@
*/
import javascript
import DataFlow::PathGraph
import semmle.javascript.security.dataflow.ResourceExhaustionQuery
import ResourceExhaustionFlow::PathGraph
from Configuration dataflow, DataFlow::PathNode source, DataFlow::PathNode sink
where dataflow.hasFlowPath(source, sink)
from ResourceExhaustionFlow::PathNode source, ResourceExhaustionFlow::PathNode sink
where ResourceExhaustionFlow::flowPath(source, sink)
select sink, source, sink, sink.getNode().(Sink).getProblemDescription() + " from a $@.", source,
"user-provided value"

View File

@@ -14,10 +14,10 @@
import javascript
import semmle.javascript.security.dataflow.XmlBombQuery
import DataFlow::PathGraph
import XmlBombFlow::PathGraph
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
from XmlBombFlow::PathNode source, XmlBombFlow::PathNode sink
where XmlBombFlow::flowPath(source, sink)
select sink.getNode(), source, sink,
"XML parsing depends on a $@ without guarding against uncontrolled entity expansion.",
source.getNode(), "user-provided value"

View File

@@ -15,14 +15,14 @@
import javascript
import semmle.javascript.security.dataflow.HardcodedCredentialsQuery
import DataFlow::PathGraph
import HardcodedCredentials::PathGraph
bindingset[s]
predicate looksLikeATemplate(string s) { s.regexpMatch(".*((\\{\\{.*\\}\\})|(<.*>)|(\\(.*\\))).*") }
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, string value
from HardcodedCredentials::PathNode source, HardcodedCredentials::PathNode sink, string value
where
cfg.hasFlowPath(source, sink) and
HardcodedCredentials::flowPath(source, sink) and
// use source value in message if it's available
if source.getNode().asExpr() instanceof ConstantString
then

View File

@@ -13,11 +13,13 @@
import javascript
import semmle.javascript.security.dataflow.ConditionalBypassQuery
import DataFlow::PathGraph
import ConditionalBypassFlow::PathGraph
from DataFlow::PathNode source, DataFlow::PathNode sink, SensitiveAction action
from
ConditionalBypassFlow::PathNode source, ConditionalBypassFlow::PathNode sink,
SensitiveAction action
where
isTaintedGuardForSensitiveAction(sink, source, action) and
not isEarlyAbortGuard(sink, action)
isTaintedGuardNodeForSensitiveAction(sink, source, action) and
not isEarlyAbortGuardNode(sink, action)
select sink.getNode(), source, sink, "This condition guards a sensitive $@, but a $@ controls it.",
action, "action", source.getNode(), "user-provided value"

View File

@@ -13,9 +13,9 @@
import javascript
import semmle.javascript.security.dataflow.InsecureDownloadQuery
import DataFlow::PathGraph
import DataFlow::DeduplicatePathGraph<InsecureDownloadFlow::PathNode, InsecureDownloadFlow::PathGraph>
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
from PathNode source, PathNode sink
where InsecureDownloadFlow::flowPath(source.getAnOriginalPathNode(), sink.getAnOriginalPathNode())
select sink.getNode(), source, sink, "$@ of sensitive file from $@.",
sink.getNode().(Sink).getDownloadCall(), "Download", source.getNode(), "HTTP source"

View File

@@ -14,10 +14,10 @@
import javascript
import semmle.javascript.security.dataflow.LoopBoundInjectionQuery
import DataFlow::PathGraph
import LoopBoundInjectionFlow::PathGraph
from Configuration dataflow, DataFlow::PathNode source, DataFlow::PathNode sink
where dataflow.hasFlowPath(source, sink)
from LoopBoundInjectionFlow::PathNode source, LoopBoundInjectionFlow::PathNode sink
where LoopBoundInjectionFlow::flowPath(source, sink)
select sink, source, sink,
"Iteration over a user-controlled object with a potentially unbounded .length property from a $@.",
source, "user-provided value"

View File

@@ -12,10 +12,10 @@
import javascript
import semmle.javascript.security.dataflow.TypeConfusionThroughParameterTamperingQuery
import DataFlow::PathGraph
import TypeConfusionFlow::PathGraph
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
from TypeConfusionFlow::PathNode source, TypeConfusionFlow::PathNode sink
where TypeConfusionFlow::flowPath(source, sink)
select sink.getNode(), source, sink,
"Potential type confusion as $@ may be either an array or a string.", source.getNode(),
"this HTTP request parameter"

View File

@@ -13,9 +13,9 @@
import javascript
import semmle.javascript.security.dataflow.HttpToFileAccessQuery
import DataFlow::PathGraph
import HttpToFileAccessFlow::PathGraph
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
from HttpToFileAccessFlow::PathNode source, HttpToFileAccessFlow::PathNode sink
where HttpToFileAccessFlow::flowPath(source, sink)
select sink.getNode(), source, sink, "Write to file system depends on $@.", source.getNode(),
"Untrusted data"

View File

@@ -19,10 +19,13 @@
import javascript
import semmle.javascript.security.dataflow.PrototypePollutingAssignmentQuery
import DataFlow::PathGraph
import PrototypePollutingAssignmentFlow::PathGraph
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
from
PrototypePollutingAssignmentFlow::PathNode source, PrototypePollutingAssignmentFlow::PathNode sink
where
PrototypePollutingAssignmentFlow::flowPath(source, sink) and
not isIgnoredLibraryFlow(source.getNode(), sink.getNode())
select sink, source, sink,
"This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@.",
source.getNode(), source.getNode().(Source).describe()

View File

@@ -17,11 +17,10 @@
*/
import javascript
import DataFlow
import PathGraph
import semmle.javascript.DynamicPropertyAccess
private import semmle.javascript.dataflow.InferredTypes
// WIN: gained TP in Lucifier/r.js:2757, though not sure why it wasn't flagged to start with.
/**
* A call of form `x.split(".")` where `x` is a parameter.
*
@@ -30,14 +29,14 @@ private import semmle.javascript.dataflow.InferredTypes
class SplitCall extends StringSplitCall {
SplitCall() {
this.getSeparator() = "." and
this.getBaseString().getALocalSource() instanceof ParameterNode
this.getBaseString().getALocalSource() instanceof DataFlow::ParameterNode
}
}
/**
* Holds if `pred -> succ` should preserve polluted property names.
*/
predicate copyArrayStep(SourceNode pred, SourceNode succ) {
predicate copyArrayStep(DataFlow::SourceNode pred, DataFlow::SourceNode succ) {
// x -> [...x]
exists(SpreadElement spread |
pred.flowsTo(spread.getOperand().flow()) and
@@ -45,7 +44,7 @@ predicate copyArrayStep(SourceNode pred, SourceNode succ) {
)
or
// `x -> y` in `y.push( x[i] )`
exists(MethodCallNode push |
exists(DataFlow::MethodCallNode push |
push = succ.getAMethodCall("push") and
(
getAnEnumeratedArrayElement(pred).flowsTo(push.getAnArgument())
@@ -55,7 +54,7 @@ predicate copyArrayStep(SourceNode pred, SourceNode succ) {
)
or
// x -> x.concat(...)
exists(MethodCallNode concat_ |
exists(DataFlow::MethodCallNode concat_ |
concat_.getMethodName() = "concat" and
(pred = concat_.getReceiver() or pred = concat_.getAnArgument()) and
succ = concat_
@@ -66,21 +65,21 @@ predicate copyArrayStep(SourceNode pred, SourceNode succ) {
* Holds if `node` may refer to a `SplitCall` or a copy thereof, possibly
* returned through a function call.
*/
predicate isSplitArray(SourceNode node) {
predicate isSplitArray(DataFlow::SourceNode node) {
node instanceof SplitCall
or
exists(SourceNode pred | isSplitArray(pred) |
exists(DataFlow::SourceNode pred | isSplitArray(pred) |
copyArrayStep(pred, node)
or
pred.flowsToExpr(node.(CallNode).getACallee().getAReturnedExpr())
pred.flowsToExpr(node.(DataFlow::CallNode).getACallee().getAReturnedExpr())
)
}
/**
* A property name originating from a `x.split(".")` call.
*/
class SplitPropName extends SourceNode {
SourceNode array;
class SplitPropName extends DataFlow::SourceNode {
DataFlow::SourceNode array;
SplitPropName() {
isSplitArray(array) and
@@ -90,7 +89,7 @@ class SplitPropName extends SourceNode {
/**
* Gets the array from which this property name was obtained (the result from `split`).
*/
SourceNode getArray() { result = array }
DataFlow::SourceNode getArray() { result = array }
/** Gets an element accessed on the same underlying array. */
SplitPropName getAnAlias() { result.getArray() = this.getArray() }
@@ -117,18 +116,18 @@ predicate isPollutedPropNameSource(DataFlow::Node node) {
* Holds if `node` may flow from a source of polluted propery names, possibly
* into function calls (but not returns).
*/
predicate isPollutedPropName(Node node) {
predicate isPollutedPropName(DataFlow::Node node) {
isPollutedPropNameSource(node)
or
exists(Node pred | isPollutedPropName(pred) |
exists(DataFlow::Node pred | isPollutedPropName(pred) |
node = pred.getASuccessor()
or
argumentPassingStep(_, pred, _, node)
DataFlow::argumentPassingStep(_, pred, _, node)
or
// Handle one level of callbacks
exists(FunctionNode function, ParameterNode callback, int i |
exists(DataFlow::FunctionNode function, DataFlow::ParameterNode callback, int i |
pred = callback.getAnInvocation().getArgument(i) and
argumentPassingStep(_, function, _, callback) and
DataFlow::argumentPassingStep(_, function, _, callback) and
node = function.getParameter(i)
)
)
@@ -138,8 +137,8 @@ predicate isPollutedPropName(Node node) {
* Holds if `node` may refer to `Object.prototype` obtained through dynamic property
* read of a property obtained through property enumeration.
*/
predicate isPotentiallyObjectPrototype(SourceNode node) {
exists(Node base, Node key |
predicate isPotentiallyObjectPrototype(DataFlow::SourceNode node) {
exists(DataFlow::Node base, DataFlow::Node key |
dynamicPropReadStep(base, key, node) and
isPollutedPropName(key) and
// Ignore cases where the properties of `base` are enumerated, to avoid FPs
@@ -149,8 +148,8 @@ predicate isPotentiallyObjectPrototype(SourceNode node) {
not arePropertiesEnumerated(base.getALocalSource())
)
or
exists(Node use | isPotentiallyObjectPrototype(use.getALocalSource()) |
argumentPassingStep(_, use, _, node)
exists(DataFlow::Node use | isPotentiallyObjectPrototype(use.getALocalSource()) |
DataFlow::argumentPassingStep(_, use, _, node)
)
}
@@ -193,14 +192,6 @@ string unsafePropName() {
result = "constructor"
}
/**
* A flow label representing an unsafe property name, or an object obtained
* by using such a property in a dynamic read.
*/
class UnsafePropLabel extends FlowLabel {
UnsafePropLabel() { this = unsafePropName() }
}
/**
* Tracks data from property enumerations to dynamic property writes.
*
@@ -233,11 +224,13 @@ class UnsafePropLabel extends FlowLabel {
* for coinciding paths afterwards. This means this configuration can't be used as
* a standalone configuration like in most path queries.
*/
class PropNameTracking extends DataFlow::Configuration {
PropNameTracking() { this = "PropNameTracking" }
module PropNameTrackingConfig implements DataFlow::StateConfigSig {
class FlowState extends string {
FlowState() { this = unsafePropName() }
}
override predicate isSource(DataFlow::Node node, FlowLabel label) {
label instanceof UnsafePropLabel and
predicate isSource(DataFlow::Node node, FlowState state) {
exists(state) and
(
isPollutedPropNameSource(node)
or
@@ -245,8 +238,8 @@ class PropNameTracking extends DataFlow::Configuration {
)
}
override predicate isSink(DataFlow::Node node, FlowLabel label) {
label instanceof UnsafePropLabel and
predicate isSink(DataFlow::Node node, FlowState state) {
exists(state) and
(
dynamicPropWrite(node, _, _) or
dynamicPropWrite(_, node, _) or
@@ -254,51 +247,67 @@ class PropNameTracking extends DataFlow::Configuration {
)
}
override predicate isAdditionalFlowStep(
DataFlow::Node pred, DataFlow::Node succ, FlowLabel predlbl, FlowLabel succlbl
predicate isBarrier(DataFlow::Node node, FlowState state) {
node = DataFlow::MakeStateBarrierGuard<FlowState, BarrierGuard>::getABarrierNode(state)
}
predicate isAdditionalFlowStep(
DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2
) {
predlbl instanceof UnsafePropLabel and
succlbl = predlbl and
exists(state1) and
state2 = state1 and
(
// Step through `p -> x[p]`
exists(PropRead read |
pred = read.getPropertyNameExpr().flow() and
exists(DataFlow::PropRead read |
node1 = read.getPropertyNameExpr().flow() and
not read.(DynamicPropRead).hasDominatingAssignment() and
succ = read
node2 = read
)
or
// Step through `x -> x[p]`
exists(DynamicPropRead read |
not read.hasDominatingAssignment() and
pred = read.getBase() and
succ = read
node1 = read.getBase() and
node2 = read
)
)
}
override predicate isBarrier(DataFlow::Node node) {
super.isBarrier(node)
or
node instanceof DataFlow::VarAccessBarrier
predicate isBarrier(DataFlow::Node node) {
node instanceof DataFlow::VarAccessBarrier or
node = DataFlow::MakeBarrierGuard<BarrierGuard>::getABarrierNode()
}
override predicate isBarrierGuard(DataFlow::BarrierGuardNode node) {
node instanceof DenyListEqualityGuard or
node instanceof AllowListEqualityGuard or
node instanceof HasOwnPropertyGuard or
node instanceof InExprGuard or
node instanceof InstanceOfGuard or
node instanceof TypeofGuard or
node instanceof DenyListInclusionGuard or
node instanceof AllowListInclusionGuard or
node instanceof IsPlainObjectGuard
int accessPathLimit() {
// Speed up the query. For the pattern we're looking for the value rarely
// flows through any contents, apart from a capture content.
result = 1
}
}
class FlowState = PropNameTrackingConfig::FlowState;
module PropNameTracking = DataFlow::GlobalWithState<PropNameTrackingConfig>;
/**
* A barrier guard for prototype pollution.
*/
abstract class BarrierGuard extends DataFlow::Node {
/**
* Holds if this node acts as a barrier for data flow, blocking further flow from `e` if `this` evaluates to `outcome`.
*/
predicate blocksExpr(boolean outcome, Expr e) { none() }
/**
* Holds if this node acts as a barrier for `state`, blocking further flow from `e` if `this` evaluates to `outcome`.
*/
predicate blocksExpr(boolean outcome, Expr e, FlowState state) { none() }
}
/**
* A sanitizer guard of form `x === "__proto__"` or `x === "constructor"`.
*/
class DenyListEqualityGuard extends DataFlow::LabeledBarrierGuardNode, ValueNode {
class DenyListEqualityGuard extends BarrierGuard, DataFlow::ValueNode {
override EqualityTest astNode;
string propName;
@@ -307,17 +316,17 @@ class DenyListEqualityGuard extends DataFlow::LabeledBarrierGuardNode, ValueNode
propName = unsafePropName()
}
override predicate blocks(boolean outcome, Expr e, FlowLabel label) {
override predicate blocksExpr(boolean outcome, Expr e, FlowState state) {
e = astNode.getAnOperand() and
outcome = astNode.getPolarity().booleanNot() and
label = propName
state = propName
}
}
/**
* An equality test with something other than `__proto__` or `constructor`.
*/
class AllowListEqualityGuard extends DataFlow::LabeledBarrierGuardNode, ValueNode {
class AllowListEqualityGuard extends BarrierGuard, DataFlow::ValueNode {
override EqualityTest astNode;
AllowListEqualityGuard() {
@@ -325,10 +334,9 @@ class AllowListEqualityGuard extends DataFlow::LabeledBarrierGuardNode, ValueNod
astNode.getAnOperand() instanceof Literal
}
override predicate blocks(boolean outcome, Expr e, FlowLabel label) {
override predicate blocksExpr(boolean outcome, Expr e) {
e = astNode.getAnOperand() and
outcome = astNode.getPolarity() and
label instanceof UnsafePropLabel
outcome = astNode.getPolarity()
}
}
@@ -339,7 +347,7 @@ class AllowListEqualityGuard extends DataFlow::LabeledBarrierGuardNode, ValueNod
* but the destination object generally doesn't. It is therefore only a sanitizer when
* used on the destination object.
*/
class HasOwnPropertyGuard extends DataFlow::BarrierGuardNode instanceof HasOwnPropertyCall {
class HasOwnPropertyGuard extends BarrierGuard instanceof HasOwnPropertyCall {
HasOwnPropertyGuard() {
// Try to avoid `src.hasOwnProperty` by requiring that the receiver
// does not locally have its properties enumerated. Typically there is no
@@ -347,7 +355,7 @@ class HasOwnPropertyGuard extends DataFlow::BarrierGuardNode instanceof HasOwnPr
not arePropertiesEnumerated(super.getObject().getALocalSource())
}
override predicate blocks(boolean outcome, Expr e) {
override predicate blocksExpr(boolean outcome, Expr e) {
e = super.getProperty().asExpr() and outcome = true
}
}
@@ -358,7 +366,7 @@ class HasOwnPropertyGuard extends DataFlow::BarrierGuardNode instanceof HasOwnPr
* Since `"__proto__" in obj` and `"constructor" in obj` is true for most objects,
* this is seen as a sanitizer for `key` in the false outcome.
*/
class InExprGuard extends DataFlow::BarrierGuardNode, DataFlow::ValueNode {
class InExprGuard extends BarrierGuard, DataFlow::ValueNode {
override InExpr astNode;
InExprGuard() {
@@ -366,7 +374,7 @@ class InExprGuard extends DataFlow::BarrierGuardNode, DataFlow::ValueNode {
not arePropertiesEnumerated(astNode.getRightOperand().flow().getALocalSource())
}
override predicate blocks(boolean outcome, Expr e) {
override predicate blocksExpr(boolean outcome, Expr e) {
e = astNode.getLeftOperand() and outcome = false
}
}
@@ -374,41 +382,41 @@ class InExprGuard extends DataFlow::BarrierGuardNode, DataFlow::ValueNode {
/**
* A sanitizer guard for `instanceof` expressions.
*
* `Object.prototype instanceof X` is never true, so this blocks the `__proto__` label.
* `Object.prototype instanceof X` is never true, so this blocks the `__proto__` state.
*
* It is still possible to get to `Function.prototype` through `constructor.constructor.prototype`
* so we do not block the `constructor` label.
* so we do not block the `constructor` state.
*/
class InstanceOfGuard extends DataFlow::LabeledBarrierGuardNode, DataFlow::ValueNode {
class InstanceOfGuard extends BarrierGuard, DataFlow::ValueNode {
override InstanceOfExpr astNode;
override predicate blocks(boolean outcome, Expr e, DataFlow::FlowLabel label) {
e = astNode.getLeftOperand() and outcome = true and label = "__proto__"
override predicate blocksExpr(boolean outcome, Expr e, FlowState state) {
e = astNode.getLeftOperand() and outcome = true and state = "__proto__"
}
}
/**
* Sanitizer guard of form `typeof x === "object"` or `typeof x === "function"`.
*
* The former blocks the `constructor` label as that payload must pass through a function,
* and the latter blocks the `__proto__` label as that only passes through objects.
* The former blocks the `constructor` state as that payload must pass through a function,
* and the latter blocks the `__proto__` state as that only passes through objects.
*/
class TypeofGuard extends DataFlow::LabeledBarrierGuardNode, DataFlow::ValueNode {
class TypeofGuard extends BarrierGuard, DataFlow::ValueNode {
override EqualityTest astNode;
Expr operand;
TypeofTag tag;
TypeofGuard() { TaintTracking::isTypeofGuard(astNode, operand, tag) }
override predicate blocks(boolean outcome, Expr e, DataFlow::FlowLabel label) {
override predicate blocksExpr(boolean outcome, Expr e, FlowState state) {
e = operand and
outcome = astNode.getPolarity() and
(
tag = "object" and
label = "constructor"
state = "constructor"
or
tag = "function" and
label = "__proto__"
state = "__proto__"
)
or
e = operand and
@@ -417,10 +425,10 @@ class TypeofGuard extends DataFlow::LabeledBarrierGuardNode, DataFlow::ValueNode
// If something is not an object, sanitize object, as both must end
// in non-function prototype object.
tag = "object" and
label instanceof UnsafePropLabel
exists(state)
or
tag = "function" and
label = "constructor"
state = "constructor"
)
}
}
@@ -428,27 +436,27 @@ class TypeofGuard extends DataFlow::LabeledBarrierGuardNode, DataFlow::ValueNode
/**
* A check of form `["__proto__"].includes(x)` or similar.
*/
class DenyListInclusionGuard extends DataFlow::LabeledBarrierGuardNode, InclusionTest {
UnsafePropLabel label;
class DenyListInclusionGuard extends BarrierGuard, InclusionTest {
string blockedProp;
DenyListInclusionGuard() {
exists(DataFlow::ArrayCreationNode array |
array.getAnElement().getStringValue() = label and
array.getAnElement().getStringValue() = blockedProp and
array.flowsTo(this.getContainerNode())
)
}
override predicate blocks(boolean outcome, Expr e, DataFlow::FlowLabel lbl) {
override predicate blocksExpr(boolean outcome, Expr e, FlowState state) {
outcome = this.getPolarity().booleanNot() and
e = this.getContainedNode().asExpr() and
label = lbl
blockedProp = state
}
}
/**
* A check of form `xs.includes(x)` or similar, which sanitizes `x` in the true case.
*/
class AllowListInclusionGuard extends DataFlow::LabeledBarrierGuardNode {
class AllowListInclusionGuard extends BarrierGuard {
AllowListInclusionGuard() {
this instanceof TaintTracking::PositiveIndexOfSanitizer
or
@@ -456,9 +464,8 @@ class AllowListInclusionGuard extends DataFlow::LabeledBarrierGuardNode {
not this = any(MembershipCandidate::ObjectPropertyNameMembershipCandidate c).getTest() // handled with more precision in `HasOwnPropertyGuard`
}
override predicate blocks(boolean outcome, Expr e, DataFlow::FlowLabel lbl) {
this.(TaintTracking::AdditionalSanitizerGuardNode).sanitizes(outcome, e) and
lbl instanceof UnsafePropLabel
override predicate blocksExpr(boolean outcome, Expr e) {
this.(TaintTracking::AdditionalBarrierGuard).blocksExpr(outcome, e)
}
}
@@ -467,17 +474,17 @@ class AllowListInclusionGuard extends DataFlow::LabeledBarrierGuardNode {
* payload in the true case, since it rejects objects with a non-standard `constructor`
* property.
*/
class IsPlainObjectGuard extends DataFlow::LabeledBarrierGuardNode, DataFlow::CallNode {
class IsPlainObjectGuard extends BarrierGuard, DataFlow::CallNode {
IsPlainObjectGuard() {
exists(string name | name = "is-plain-object" or name = "is-extendable" |
this = moduleImport(name).getACall()
this = DataFlow::moduleImport(name).getACall()
)
}
override predicate blocks(boolean outcome, Expr e, DataFlow::FlowLabel lbl) {
override predicate blocksExpr(boolean outcome, Expr e, FlowState state) {
e = this.getArgument(0).asExpr() and
outcome = true and
lbl = "constructor"
state = "constructor"
}
}
@@ -507,26 +514,26 @@ string deriveExprName(DataFlow::Node node) {
* In most cases this will result in an alert, the exception being the case where
* `base` does not have a prototype at all.
*/
predicate isPrototypePollutingAssignment(Node base, Node prop, Node rhs, Node propNameSource) {
predicate isPrototypePollutingAssignment(
DataFlow::Node base, DataFlow::Node prop, DataFlow::Node rhs, DataFlow::Node propNameSource
) {
dynamicPropWrite(base, prop, rhs) and
isPollutedPropNameSource(propNameSource) and
exists(PropNameTracking cfg |
cfg.hasFlow(propNameSource, base) and
if propNameSource instanceof EnumeratedPropName
then
cfg.hasFlow(propNameSource, prop) and
cfg.hasFlow([propNameSource, AccessPath::getAnAliasedSourceNode(propNameSource)]
.(EnumeratedPropName)
.getASourceProp(), rhs)
else (
cfg.hasFlow(propNameSource.(SplitPropName).getAnAlias(), prop) and
rhs.getALocalSource() instanceof ParameterNode
)
PropNameTracking::flow(propNameSource, base) and
if propNameSource instanceof EnumeratedPropName
then
PropNameTracking::flow(propNameSource, prop) and
PropNameTracking::flow([propNameSource, AccessPath::getAnAliasedSourceNode(propNameSource)]
.(EnumeratedPropName)
.getASourceProp(), rhs)
else (
PropNameTracking::flow(propNameSource.(SplitPropName).getAnAlias(), prop) and
rhs.getALocalSource() instanceof DataFlow::ParameterNode
)
}
/** Gets a data flow node leading to the base of a prototype-polluting assignment. */
private DataFlow::SourceNode getANodeLeadingToBase(DataFlow::TypeBackTracker t, Node base) {
private DataFlow::SourceNode getANodeLeadingToBase(DataFlow::TypeBackTracker t, DataFlow::Node base) {
t.start() and
isPrototypePollutingAssignment(base, _, _, _) and
result = base.getALocalSource()
@@ -542,7 +549,9 @@ private DataFlow::SourceNode getANodeLeadingToBase(DataFlow::TypeBackTracker t,
* This dynamic read is where the reference to a built-in prototype object is obtained,
* and we need this to ensure that this object actually has a prototype.
*/
private DataFlow::SourceNode getANodeLeadingToBaseBase(DataFlow::TypeBackTracker t, Node base) {
private DataFlow::SourceNode getANodeLeadingToBaseBase(
DataFlow::TypeBackTracker t, DataFlow::Node base
) {
exists(DynamicPropRead read |
read = getANodeLeadingToBase(t, base) and
result = read.getBase().getALocalSource()
@@ -553,29 +562,31 @@ private DataFlow::SourceNode getANodeLeadingToBaseBase(DataFlow::TypeBackTracker
)
}
DataFlow::SourceNode getANodeLeadingToBaseBase(Node base) {
DataFlow::SourceNode getANodeLeadingToBaseBase(DataFlow::Node base) {
result = getANodeLeadingToBaseBase(DataFlow::TypeBackTracker::end(), base)
}
/** A call to `Object.create(null)`. */
class ObjectCreateNullCall extends CallNode {
class ObjectCreateNullCall extends DataFlow::CallNode {
ObjectCreateNullCall() {
this = globalVarRef("Object").getAMemberCall("create") and
this = DataFlow::globalVarRef("Object").getAMemberCall("create") and
this.getArgument(0).asExpr() instanceof NullLiteral
}
}
import DataFlow::DeduplicatePathGraph<PropNameTracking::PathNode, PropNameTracking::PathGraph>
from
PropNameTracking cfg, DataFlow::PathNode source, DataFlow::PathNode sink, Node propNameSource,
Node base, string msg, Node col1, Node col2
PathNode source, PathNode sink, DataFlow::Node propNameSource, DataFlow::Node base, string msg,
DataFlow::Node col1, DataFlow::Node col2
where
isPollutedPropName(propNameSource) and
cfg.hasFlowPath(source, sink) and
PropNameTracking::flowPath(source.getAnOriginalPathNode(), sink.getAnOriginalPathNode()) and
isPrototypePollutingAssignment(base, _, _, propNameSource) and
sink.getNode() = base and
source.getNode() = propNameSource and
(
getANodeLeadingToBaseBase(base) instanceof ObjectLiteralNode
getANodeLeadingToBaseBase(base) instanceof DataFlow::ObjectLiteralNode
or
not getANodeLeadingToBaseBase(base) instanceof ObjectCreateNullCall
) and

View File

@@ -19,13 +19,11 @@
import javascript
import semmle.javascript.security.dataflow.PrototypePollutionQuery
import DataFlow::PathGraph
import DataFlow::DeduplicatePathGraph<PrototypePollutionFlow::PathNode, PrototypePollutionFlow::PathGraph>
from
Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, string moduleName,
Locatable dependencyLoc
from PathNode source, PathNode sink, string moduleName, Locatable dependencyLoc
where
cfg.hasFlowPath(source, sink) and
PrototypePollutionFlow::flowPath(source.getAnOriginalPathNode(), sink.getAnOriginalPathNode()) and
sink.getNode().(Sink).dependencyInfo(moduleName, dependencyLoc)
select sink.getNode(), source, sink,
"Prototype pollution caused by merging a $@ using a vulnerable version of $@.", source,

View File

@@ -12,9 +12,9 @@
import javascript
import semmle.javascript.security.dataflow.InsufficientPasswordHashQuery
import DataFlow::PathGraph
import InsufficientPasswordHashFlow::PathGraph
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
from InsufficientPasswordHashFlow::PathNode source, InsufficientPasswordHashFlow::PathNode sink
where InsufficientPasswordHashFlow::flowPath(source, sink)
select sink.getNode(), source, sink, "Password from $@ is hashed insecurely.", source.getNode(),
source.getNode().(Source).describe()

View File

@@ -13,11 +13,13 @@
import javascript
import semmle.javascript.security.dataflow.ClientSideRequestForgeryQuery
import DataFlow::PathGraph
import ClientSideRequestForgeryFlow::PathGraph
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, DataFlow::Node request
from
ClientSideRequestForgeryFlow::PathNode source, ClientSideRequestForgeryFlow::PathNode sink,
DataFlow::Node request
where
cfg.hasFlowPath(source, sink) and
ClientSideRequestForgeryFlow::flowPath(source, sink) and
request = sink.getNode().(Sink).getARequest()
select request, source, sink, "The $@ of this request depends on a $@.", sink.getNode(),
sink.getNode().(Sink).getKind(), source, "user-provided value"

View File

@@ -12,11 +12,11 @@
import javascript
import semmle.javascript.security.dataflow.RequestForgeryQuery
import DataFlow::PathGraph
import RequestForgeryFlow::PathGraph
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, DataFlow::Node request
from RequestForgeryFlow::PathNode source, RequestForgeryFlow::PathNode sink, DataFlow::Node request
where
cfg.hasFlowPath(source, sink) and
RequestForgeryFlow::flowPath(source, sink) and
request = sink.getNode().(Sink).getARequest()
select request, source, sink, "The $@ of this request depends on a $@.", sink.getNode(),
sink.getNode().(Sink).getKind(), source, "user-provided value"

View File

@@ -1,5 +1,5 @@
/**
* @name Code injection
* @name Code injection from dynamically imported code
* @description Interpreting unsanitized user input as code allows a malicious user arbitrary
* code execution.
* @kind path-problem
@@ -15,13 +15,11 @@
*/
import javascript
import DataFlow
import DataFlow::PathGraph
abstract class Sanitizer extends DataFlow::Node { }
abstract class Barrier extends DataFlow::Node { }
/** A non-first leaf in a string-concatenation. Seen as a sanitizer for dynamic import code injection. */
class NonFirstStringConcatLeaf extends Sanitizer {
class NonFirstStringConcatLeaf extends Barrier {
NonFirstStringConcatLeaf() {
exists(StringOps::ConcatenationRoot root |
this = root.getALeaf() and
@@ -51,39 +49,51 @@ class WorkerThreads extends DataFlow::Node {
}
}
class UrlConstructorLabel extends FlowLabel {
UrlConstructorLabel() { this = "UrlConstructorLabel" }
}
newtype TFlowState =
TTaint() or
TUrlConstructor()
/**
* A taint-tracking configuration for reasoning about code injection vulnerabilities.
*/
class Configuration extends TaintTracking::Configuration {
Configuration() { this = "CodeInjection" }
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
override predicate isSink(DataFlow::Node sink) { sink instanceof DynamicImport }
override predicate isSink(DataFlow::Node sink, FlowLabel label) {
sink instanceof WorkerThreads and label instanceof UrlConstructorLabel
module CodeInjectionConfig implements DataFlow::StateConfigSig {
class FlowState extends TFlowState {
string toString() {
this = TTaint() and result = "taint"
or
this = TUrlConstructor() and result = "url-constructor"
}
}
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
predicate isSource(DataFlow::Node source, FlowState state) {
source instanceof ActiveThreatModelSource and state = TTaint()
}
override predicate isAdditionalFlowStep(
DataFlow::Node pred, DataFlow::Node succ, FlowLabel predlbl, FlowLabel succlbl
predicate isSink(DataFlow::Node sink) { sink instanceof DynamicImport }
predicate isSink(DataFlow::Node sink, FlowState state) {
sink instanceof WorkerThreads and state = TUrlConstructor()
}
predicate isBarrier(DataFlow::Node node) { node instanceof Barrier }
predicate isAdditionalFlowStep(
DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2
) {
exists(DataFlow::NewNode newUrl | succ = newUrl |
exists(DataFlow::NewNode newUrl | node2 = newUrl |
newUrl = DataFlow::globalVarRef("URL").getAnInstantiation() and
pred = newUrl.getArgument(0)
node1 = newUrl.getArgument(0)
) and
predlbl instanceof StandardFlowLabel and
succlbl instanceof UrlConstructorLabel
state1 = TTaint() and
state2 = TUrlConstructor()
}
}
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
module CodeInjectionFlow = TaintTracking::GlobalWithState<CodeInjectionConfig>;
import CodeInjectionFlow::PathGraph
from CodeInjectionFlow::PathNode source, CodeInjectionFlow::PathNode sink
where CodeInjectionFlow::flowPath(source, sink)
select sink.getNode(), source, sink, "This command line depends on a $@.", source.getNode(),
"user-provided value"

View File

@@ -11,33 +11,32 @@
*/
import javascript
import DataFlow::PathGraph
/** A taint tracking configuration for unsafe environment injection. */
class Configuration extends TaintTracking::Configuration {
Configuration() { this = "envInjection" }
module EnvValueAndKeyInjectionConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof ActiveThreatModelSource }
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
override predicate isSink(DataFlow::Node sink) {
predicate isSink(DataFlow::Node sink) {
sink = keyOfEnv() or
sink = valueOfEnv()
}
override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
exists(DataFlow::InvokeNode ikn |
ikn = DataFlow::globalVarRef("Object").getAMemberInvocation("keys")
|
pred = ikn.getArgument(0) and
node1 = ikn.getArgument(0) and
(
succ = ikn.getAChainedMethodCall(["filter", "map"]) or
succ = ikn or
succ = ikn.getAChainedMethodCall("forEach").getABoundCallbackParameter(0, 0)
node2 = ikn.getAChainedMethodCall(["filter", "map"]) or
node2 = ikn or
node2 = ikn.getAChainedMethodCall("forEach").getABoundCallbackParameter(0, 0)
)
)
}
}
module EnvValueAndKeyInjectionFlow = TaintTracking::Global<EnvValueAndKeyInjectionConfig>;
DataFlow::Node keyOfEnv() {
result =
NodeJSLib::process().getAPropertyRead("env").getAPropertyWrite().getPropertyNameExpr().flow()
@@ -56,13 +55,15 @@ private predicate readToProcessEnv(DataFlow::Node envKey, DataFlow::Node envValu
)
}
import EnvValueAndKeyInjectionFlow::PathGraph
from
Configuration cfgForValue, Configuration cfgForKey, DataFlow::PathNode source,
DataFlow::PathNode envKey, DataFlow::PathNode envValue
EnvValueAndKeyInjectionFlow::PathNode source, EnvValueAndKeyInjectionFlow::PathNode envKey,
EnvValueAndKeyInjectionFlow::PathNode envValue
where
cfgForValue.hasFlowPath(source, envKey) and
EnvValueAndKeyInjectionFlow::flowPath(source, envKey) and
envKey.getNode() = keyOfEnv() and
cfgForKey.hasFlowPath(source, envValue) and
EnvValueAndKeyInjectionFlow::flowPath(source, envValue) and
envValue.getNode() = valueOfEnv() and
readToProcessEnv(envKey.getNode(), envValue.getNode())
select envKey.getNode(), source, envKey, "arbitrary environment variable assignment from this $@.",

View File

@@ -11,20 +11,21 @@
*/
import javascript
import DataFlow::PathGraph
/** A taint tracking configuration for unsafe environment injection. */
class Configuration extends TaintTracking::Configuration {
Configuration() { this = "envInjection" }
module EnvValueInjectionConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
override predicate isSink(DataFlow::Node sink) {
predicate isSink(DataFlow::Node sink) {
sink = API::moduleImport("process").getMember("env").getAMember().asSink()
}
}
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
module EnvValueInjectionFlow = TaintTracking::Global<EnvValueInjectionConfig>;
import EnvValueInjectionFlow::PathGraph
from EnvValueInjectionFlow::PathNode source, EnvValueInjectionFlow::PathNode sink
where EnvValueInjectionFlow::flowPath(source, sink)
select sink.getNode(), source, sink, "this environment variable assignment is $@.",
source.getNode(), "user controllable"

View File

@@ -14,7 +14,6 @@
import javascript
import DataFlow
import DataFlow::PathGraph
class PredictableResultSource extends DataFlow::Node {
PredictableResultSource() {
@@ -38,14 +37,16 @@ class TokenAssignmentValueSink extends DataFlow::Node {
}
}
class TokenBuiltFromUuidConfig extends TaintTracking::Configuration {
TokenBuiltFromUuidConfig() { this = "TokenBuiltFromUuidConfig" }
module TokenBuiltFromUuidConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof PredictableResultSource }
override predicate isSource(DataFlow::Node source) { source instanceof PredictableResultSource }
override predicate isSink(DataFlow::Node sink) { sink instanceof TokenAssignmentValueSink }
predicate isSink(DataFlow::Node sink) { sink instanceof TokenAssignmentValueSink }
}
from DataFlow::PathNode source, DataFlow::PathNode sink, TokenBuiltFromUuidConfig config
where config.hasFlowPath(source, sink)
module TokenBuiltFromUuidFlow = TaintTracking::Global<TokenBuiltFromUuidConfig>;
import TokenBuiltFromUuidFlow::PathGraph
from TokenBuiltFromUuidFlow::PathNode source, TokenBuiltFromUuidFlow::PathNode sink
where TokenBuiltFromUuidFlow::flowPath(source, sink)
select sink.getNode(), source, sink, "Token built from $@.", source.getNode(), "predictable value"

View File

@@ -11,30 +11,29 @@
*/
import javascript
import DataFlow::PathGraph
import JWT
class ConfigurationUnverifiedDecode extends TaintTracking::Configuration {
ConfigurationUnverifiedDecode() { this = "jsonwebtoken without any signature verification" }
module UnverifiedDecodeConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof ActiveThreatModelSource }
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
override predicate isSink(DataFlow::Node sink) { sink = unverifiedDecode() }
predicate isSink(DataFlow::Node sink) { sink = unverifiedDecode() }
}
class ConfigurationVerifiedDecode extends TaintTracking::Configuration {
ConfigurationVerifiedDecode() { this = "jsonwebtoken with signature verification" }
module UnverifiedDecodeFlow = TaintTracking::Global<UnverifiedDecodeConfig>;
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
module VerifiedDecodeConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof ActiveThreatModelSource }
override predicate isSink(DataFlow::Node sink) { sink = verifiedDecode() }
predicate isSink(DataFlow::Node sink) { sink = verifiedDecode() }
}
from ConfigurationUnverifiedDecode cfg, DataFlow::PathNode source, DataFlow::PathNode sink
module VerifiedDecodeFlow = TaintTracking::Global<VerifiedDecodeConfig>;
import UnverifiedDecodeFlow::PathGraph
from UnverifiedDecodeFlow::PathNode source, UnverifiedDecodeFlow::PathNode sink
where
cfg.hasFlowPath(source, sink) and
not exists(ConfigurationVerifiedDecode cfg2 |
cfg2.hasFlowPath(any(DataFlow::PathNode p | p.getNode() = source.getNode()), _)
)
UnverifiedDecodeFlow::flowPath(source, sink) and
not VerifiedDecodeFlow::flow(source.getNode(), _)
select source.getNode(), source, sink, "Decoding JWT $@.", sink.getNode(),
"without signature verification"

View File

@@ -11,29 +11,25 @@
*/
import javascript
import DataFlow::PathGraph
import JWT
class Configuration extends TaintTracking::Configuration {
Configuration() { this = "jsonwebtoken without any signature verification" }
override predicate isSource(DataFlow::Node source) {
module DecodeWithoutVerificationConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
source = [unverifiedDecode(), verifiedDecode()].getALocalSource()
}
override predicate isSink(DataFlow::Node sink) {
predicate isSink(DataFlow::Node sink) {
sink = unverifiedDecode()
or
sink = verifiedDecode()
}
}
module DecodeWithoutVerificationFlow = TaintTracking::Global<DecodeWithoutVerificationConfig>;
/** Holds if `source` flows to the first parameter of jsonwebtoken.verify */
predicate isSafe(Configuration cfg, DataFlow::Node source) {
exists(DataFlow::Node sink |
cfg.hasFlow(source, sink) and
sink = verifiedDecode()
)
predicate isSafe(DataFlow::Node source) {
DecodeWithoutVerificationFlow::flow(source, verifiedDecode())
}
/**
@@ -41,15 +37,17 @@ predicate isSafe(Configuration cfg, DataFlow::Node source) {
* - `source` does not flow to the first parameter of `jsonwebtoken.verify`, and
* - `source` flows to the first parameter of `jsonwebtoken.decode`
*/
predicate isVulnerable(Configuration cfg, DataFlow::Node source, DataFlow::Node sink) {
not isSafe(cfg, source) and // i.e., source does not flow to a verify call
cfg.hasFlow(source, sink) and // but it does flow to something else
predicate isVulnerable(DataFlow::Node source, DataFlow::Node sink) {
not isSafe(source) and // i.e., source does not flow to a verify call
DecodeWithoutVerificationFlow::flow(source, sink) and // but it does flow to something else
sink = unverifiedDecode() // and that something else is a call to decode.
}
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
import DecodeWithoutVerificationFlow::PathGraph
from DecodeWithoutVerificationFlow::PathNode source, DecodeWithoutVerificationFlow::PathNode sink
where
cfg.hasFlowPath(source, sink) and
isVulnerable(cfg, source.getNode(), sink.getNode())
DecodeWithoutVerificationFlow::flowPath(source, sink) and
isVulnerable(source.getNode(), sink.getNode())
select source.getNode(), source, sink, "Decoding JWT $@.", sink.getNode(),
"without signature verification"

View File

@@ -12,24 +12,25 @@
*/
import javascript
import DataFlow::PathGraph
import DecompressionBombs
class BombConfiguration extends TaintTracking::Configuration {
BombConfiguration() { this = "DecompressionBombs" }
module DecompressionBombConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
predicate isSink(DataFlow::Node sink) { sink instanceof DecompressionBomb::Sink }
override predicate isSink(DataFlow::Node sink) { sink instanceof DecompressionBomb::Sink }
override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
exists(DecompressionBomb::AdditionalTaintStep addstep |
addstep.isAdditionalTaintStep(pred, succ)
addstep.isAdditionalTaintStep(node1, node2)
)
}
}
from BombConfiguration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
module DecompressionBombFlow = TaintTracking::Global<DecompressionBombConfig>;
import DecompressionBombFlow::PathGraph
from DecompressionBombFlow::PathNode source, DecompressionBombFlow::PathNode sink
where DecompressionBombFlow::flowPath(source, sink)
select sink.getNode(), source, sink, "This Decompression depends on a $@.", source.getNode(),
"potentially untrusted source"

View File

@@ -1,7 +1,6 @@
import javascript
import experimental.semmle.javascript.FormParsers
import experimental.semmle.javascript.ReadableStream
import DataFlow::PathGraph
module DecompressionBomb {
/**

View File

@@ -12,9 +12,9 @@
import javascript
import SSRF
import DataFlow::PathGraph
import SsrfFlow::PathGraph
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, DataFlow::Node request
from SsrfFlow::PathNode source, SsrfFlow::PathNode sink, DataFlow::Node request
where
cfg.hasFlowPath(source, sink) and request = sink.getNode().(RequestForgery::Sink).getARequest()
SsrfFlow::flowPath(source, sink) and request = sink.getNode().(RequestForgery::Sink).getARequest()
select sink, source, sink, "The URL of this request depends on a user-provided value."

View File

@@ -2,42 +2,41 @@ import javascript
import semmle.javascript.security.dataflow.RequestForgeryCustomizations
import semmle.javascript.security.dataflow.UrlConcatenation
class Configuration extends TaintTracking::Configuration {
Configuration() { this = "SSRF" }
module SsrfConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof RequestForgery::Source }
override predicate isSource(DataFlow::Node source) { source instanceof RequestForgery::Source }
predicate isSink(DataFlow::Node sink) { sink instanceof RequestForgery::Sink }
override predicate isSink(DataFlow::Node sink) { sink instanceof RequestForgery::Sink }
override predicate isSanitizer(DataFlow::Node node) {
super.isSanitizer(node) or
node instanceof RequestForgery::Sanitizer
predicate isBarrier(DataFlow::Node node) {
node instanceof RequestForgery::Sanitizer or
node = DataFlow::MakeBarrierGuard<BarrierGuard>::getABarrierNode()
}
private predicate hasSanitizingSubstring(DataFlow::Node nd) {
nd.getStringValue().regexpMatch(".*[?#].*")
or
this.hasSanitizingSubstring(StringConcatenation::getAnOperand(nd))
hasSanitizingSubstring(StringConcatenation::getAnOperand(nd))
or
this.hasSanitizingSubstring(nd.getAPredecessor())
hasSanitizingSubstring(nd.getAPredecessor())
}
private predicate strictSanitizingPrefixEdge(DataFlow::Node source, DataFlow::Node sink) {
exists(DataFlow::Node operator, int n |
StringConcatenation::taintStep(source, sink, operator, n) and
this.hasSanitizingSubstring(StringConcatenation::getOperand(operator, [0 .. n - 1]))
hasSanitizingSubstring(StringConcatenation::getOperand(operator, [0 .. n - 1]))
)
}
override predicate isSanitizerOut(DataFlow::Node node) {
this.strictSanitizingPrefixEdge(node, _)
}
predicate isBarrierOut(DataFlow::Node node) { strictSanitizingPrefixEdge(node, _) }
}
override predicate isSanitizerGuard(TaintTracking::SanitizerGuardNode nd) {
nd instanceof IntegerCheck or
nd instanceof ValidatorCheck or
nd instanceof TernaryOperatorSanitizerGuard
}
module SsrfFlow = TaintTracking::Global<SsrfConfig>;
/**
* DEPRECATED. Use the `SsrfFlow` module instead.
*/
deprecated class Configuration extends TaintTracking::Configuration {
Configuration() { this = "SSRF" }
}
/**
@@ -56,14 +55,14 @@ class Configuration extends TaintTracking::Configuration {
class TernaryOperatorSanitizer extends RequestForgery::Sanitizer {
TernaryOperatorSanitizer() {
exists(
TaintTracking::SanitizerGuardNode guard, IfStmt ifStmt, DataFlow::Node taintedInput,
TaintTracking::AdditionalBarrierGuard guard, IfStmt ifStmt, DataFlow::Node taintedInput,
boolean outcome, Stmt r, DataFlow::Node falseNode
|
ifStmt.getCondition().flow().getAPredecessor+() = guard and
ifStmt.getCondition().flow().getAPredecessor+() = falseNode and
falseNode.asExpr().(BooleanLiteral).mayHaveBooleanValue(false) and
not ifStmt.getCondition() instanceof LogicalBinaryExpr and
guard.sanitizes(outcome, taintedInput.asExpr()) and
guard.blocksExpr(outcome, taintedInput.asExpr()) and
(
outcome = true and r = ifStmt.getThen() and not ifStmt.getCondition() instanceof LogNotExpr
or
@@ -81,6 +80,12 @@ class TernaryOperatorSanitizer extends RequestForgery::Sanitizer {
}
}
/** A barrier guard for this SSRF query. */
abstract class BarrierGuard extends DataFlow::Node {
/** Holds if flow through `e` should be blocked, provided this evaluates to `outcome`. */
abstract predicate blocksExpr(boolean outcome, Expr e);
}
/**
* This sanitizer guard is another way of modeling the example from above
* In this case:
@@ -95,8 +100,8 @@ class TernaryOperatorSanitizer extends RequestForgery::Sanitizer {
* Thats why we model this sanitizer guard which says that
* the result of the ternary operator execution is a sanitizer guard.
*/
class TernaryOperatorSanitizerGuard extends TaintTracking::SanitizerGuardNode {
TaintTracking::SanitizerGuardNode originalGuard;
class TernaryOperatorSanitizerGuard extends BarrierGuard {
TaintTracking::AdditionalBarrierGuard originalGuard;
TernaryOperatorSanitizerGuard() {
this.getAPredecessor+().asExpr().(BooleanLiteral).mayHaveBooleanValue(false) and
@@ -104,13 +109,13 @@ class TernaryOperatorSanitizerGuard extends TaintTracking::SanitizerGuardNode {
not this.asExpr() instanceof LogicalBinaryExpr
}
override predicate sanitizes(boolean outcome, Expr e) {
override predicate blocksExpr(boolean outcome, Expr e) {
not this.asExpr() instanceof LogNotExpr and
originalGuard.sanitizes(outcome, e)
originalGuard.blocksExpr(outcome, e)
or
exists(boolean originalOutcome |
this.asExpr() instanceof LogNotExpr and
originalGuard.sanitizes(originalOutcome, e) and
originalGuard.blocksExpr(originalOutcome, e) and
(
originalOutcome = true and outcome = false
or
@@ -123,10 +128,10 @@ class TernaryOperatorSanitizerGuard extends TaintTracking::SanitizerGuardNode {
/**
* A call to Number.isInteger seen as a sanitizer guard because a number can't be used to exploit a SSRF.
*/
class IntegerCheck extends TaintTracking::SanitizerGuardNode, DataFlow::CallNode {
class IntegerCheck extends DataFlow::CallNode, BarrierGuard {
IntegerCheck() { this = DataFlow::globalVarRef("Number").getAMemberCall("isInteger") }
override predicate sanitizes(boolean outcome, Expr e) {
override predicate blocksExpr(boolean outcome, Expr e) {
outcome = true and
e = this.getArgument(0).asExpr()
}
@@ -137,7 +142,7 @@ class IntegerCheck extends TaintTracking::SanitizerGuardNode, DataFlow::CallNode
* validator is a library which has a variety of input-validation functions. We are interesed in
* checking that source is a number (any type of number) or an alphanumeric value.
*/
class ValidatorCheck extends TaintTracking::SanitizerGuardNode, DataFlow::CallNode {
class ValidatorCheck extends DataFlow::CallNode, BarrierGuard {
ValidatorCheck() {
exists(DataFlow::SourceNode mod, string method |
mod = DataFlow::moduleImport("validator") and
@@ -149,7 +154,7 @@ class ValidatorCheck extends TaintTracking::SanitizerGuardNode, DataFlow::CallNo
)
}
override predicate sanitizes(boolean outcome, Expr e) {
override predicate blocksExpr(boolean outcome, Expr e) {
outcome = true and
e = this.getArgument(0).asExpr()
}

View File

@@ -12,9 +12,10 @@
import javascript
import CorsPermissiveConfigurationQuery
import DataFlow::PathGraph
import CorsPermissiveConfigurationFlow::PathGraph
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
from
CorsPermissiveConfigurationFlow::PathNode source, CorsPermissiveConfigurationFlow::PathNode sink
where CorsPermissiveConfigurationFlow::flowPath(source, sink)
select sink.getNode(), source, sink, "CORS Origin misconfiguration due to a $@.", source.getNode(),
"too permissive or user controlled value"

View File

@@ -10,6 +10,45 @@ import Apollo::Apollo
/** Module containing sources, sinks, and sanitizers for overly permissive CORS configurations. */
module CorsPermissiveConfiguration {
private newtype TFlowState =
TTaint() or
TTrueOrNull() or
TWildcard()
/** A flow state to asociate with a tracked value. */
class FlowState extends TFlowState {
/** Gets a string representation of this flow state. */
string toString() {
this = TTaint() and result = "taint"
or
this = TTrueOrNull() and result = "true-or-null"
or
this = TWildcard() and result = "wildcard"
}
deprecated DataFlow::FlowLabel toFlowLabel() {
this = TTaint() and result.isTaint()
or
this = TTrueOrNull() and result instanceof TrueAndNull
or
this = TWildcard() and result instanceof Wildcard
}
}
/** Predicates for working with flow states. */
module FlowState {
deprecated FlowState fromFlowLabel(DataFlow::FlowLabel label) { result.toFlowLabel() = label }
/** A tainted value. */
FlowState taint() { result = TTaint() }
/** A `true` or `null` value. */
FlowState trueOrNull() { result = TTrueOrNull() }
/** A `"*"` value. */
FlowState wildcard() { result = TWildcard() }
}
/**
* A data flow source for permissive CORS configuration.
*/
@@ -38,18 +77,18 @@ module CorsPermissiveConfiguration {
}
/** A flow label representing `true` and `null` values. */
abstract class TrueAndNull extends DataFlow::FlowLabel {
abstract deprecated class TrueAndNull extends DataFlow::FlowLabel {
TrueAndNull() { this = "TrueAndNull" }
}
TrueAndNull truenullLabel() { any() }
deprecated TrueAndNull truenullLabel() { any() }
/** A flow label representing `*` value. */
abstract class Wildcard extends DataFlow::FlowLabel {
abstract deprecated class Wildcard extends DataFlow::FlowLabel {
Wildcard() { this = "Wildcard" }
}
Wildcard wildcardLabel() { any() }
deprecated Wildcard wildcardLabel() { any() }
/** An overly permissive value for `origin` (Apollo) */
class TrueNullValue extends Source {

View File

@@ -10,37 +10,58 @@
import javascript
import CorsPermissiveConfigurationCustomizations::CorsPermissiveConfiguration
private import CorsPermissiveConfigurationCustomizations::CorsPermissiveConfiguration as CorsPermissiveConfiguration
/**
* A data flow configuration for overly permissive CORS configuration.
*/
class Configuration extends TaintTracking::Configuration {
module CorsPermissiveConfigurationConfig implements DataFlow::StateConfigSig {
class FlowState = CorsPermissiveConfiguration::FlowState;
predicate isSource(DataFlow::Node source, FlowState state) {
source instanceof TrueNullValue and state = FlowState::trueOrNull()
or
source instanceof WildcardValue and state = FlowState::wildcard()
or
source instanceof RemoteFlowSource and state = FlowState::taint()
}
predicate isSink(DataFlow::Node sink, FlowState state) {
sink instanceof CorsApolloServer and state = [FlowState::taint(), FlowState::trueOrNull()]
or
sink instanceof ExpressCors and state = [FlowState::taint(), FlowState::wildcard()]
}
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
}
module CorsPermissiveConfigurationFlow =
TaintTracking::GlobalWithState<CorsPermissiveConfigurationConfig>;
/**
* DEPRECATED. Use the `CorsPermissiveConfigurationFlow` module instead.
*/
deprecated class Configuration extends TaintTracking::Configuration {
Configuration() { this = "CorsPermissiveConfiguration" }
override predicate isSource(DataFlow::Node source, DataFlow::FlowLabel label) {
source instanceof TrueNullValue and label = truenullLabel()
or
source instanceof WildcardValue and label = wildcardLabel()
or
source instanceof RemoteFlowSource and label = DataFlow::FlowLabel::taint()
CorsPermissiveConfigurationConfig::isSource(source, FlowState::fromFlowLabel(label))
}
override predicate isSink(DataFlow::Node sink, DataFlow::FlowLabel label) {
sink instanceof CorsApolloServer and label = [DataFlow::FlowLabel::taint(), truenullLabel()]
or
sink instanceof ExpressCors and label = [DataFlow::FlowLabel::taint(), wildcardLabel()]
CorsPermissiveConfigurationConfig::isSink(sink, FlowState::fromFlowLabel(label))
}
override predicate isSanitizer(DataFlow::Node node) {
super.isSanitizer(node) or
node instanceof Sanitizer
CorsPermissiveConfigurationConfig::isBarrier(node)
}
}
private class WildcardActivated extends DataFlow::FlowLabel, Wildcard {
deprecated private class WildcardActivated extends DataFlow::FlowLabel, Wildcard {
WildcardActivated() { this = this }
}
private class TrueAndNullActivated extends DataFlow::FlowLabel, TrueAndNull {
deprecated private class TrueAndNullActivated extends DataFlow::FlowLabel, TrueAndNull {
TrueAndNullActivated() { this = this }
}

View File

@@ -12,11 +12,15 @@
import javascript
import semmle.javascript.security.dataflow.ExternalAPIUsedWithUntrustedDataQuery
import DataFlow::PathGraph
import semmle.javascript.heuristics.AdditionalSources
import ExternalAPIUsedWithUntrustedDataFlow::PathGraph
from Configuration config, DataFlow::PathNode source, DataFlow::PathNode sink
where config.hasFlowPath(source, sink) and source.getNode() instanceof HeuristicSource
from
ExternalAPIUsedWithUntrustedDataFlow::PathNode source,
ExternalAPIUsedWithUntrustedDataFlow::PathNode sink
where
ExternalAPIUsedWithUntrustedDataFlow::flowPath(source, sink) and
source.getNode() instanceof HeuristicSource
select sink, source, sink,
"Call to " + sink.getNode().(Sink).getApiName() + " with untrusted data from $@.", source,
source.toString()

View File

@@ -16,17 +16,17 @@
import javascript
import semmle.javascript.security.dataflow.CommandInjectionQuery
import DataFlow::PathGraph
import semmle.javascript.heuristics.AdditionalSources
import CommandInjectionFlow::PathGraph
from
Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, DataFlow::Node highlight,
Source sourceNode
CommandInjectionFlow::PathNode source, CommandInjectionFlow::PathNode sink,
DataFlow::Node highlight, Source sourceNode
where
cfg.hasFlowPath(source, sink) and
CommandInjectionFlow::flowPath(source, sink) and
(
if cfg.isSinkWithHighlight(sink.getNode(), _)
then cfg.isSinkWithHighlight(sink.getNode(), highlight)
if isSinkWithHighlight(sink.getNode(), _)
then isSinkWithHighlight(sink.getNode(), highlight)
else highlight = sink.getNode()
) and
sourceNode = source.getNode() and

View File

@@ -15,11 +15,11 @@
import javascript
import semmle.javascript.security.dataflow.DomBasedXssQuery
import DataFlow::PathGraph
import semmle.javascript.heuristics.AdditionalSources
import DomBasedXssFlow::PathGraph
from DataFlow::Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink) and source.getNode() instanceof HeuristicSource
from DomBasedXssFlow::PathNode source, DomBasedXssFlow::PathNode sink
where DomBasedXssFlow::flowPath(source, sink) and source.getNode() instanceof HeuristicSource
select sink.getNode(), source, sink,
sink.getNode().(Sink).getVulnerabilityKind() + " vulnerability due to $@.", source.getNode(),
"user-provided value"

View File

@@ -15,18 +15,24 @@
*/
import javascript
import semmle.javascript.security.dataflow.SqlInjectionQuery as SqlInjection
import semmle.javascript.security.dataflow.NosqlInjectionQuery as NosqlInjection
import DataFlow::PathGraph
import semmle.javascript.security.dataflow.SqlInjectionQuery as Sql
import semmle.javascript.security.dataflow.NosqlInjectionQuery as Nosql
import semmle.javascript.heuristics.AdditionalSources
from DataFlow::Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, string type
module Merged =
DataFlow::MergePathGraph<Sql::SqlInjectionFlow::PathNode, Nosql::NosqlInjectionFlow::PathNode,
Sql::SqlInjectionFlow::PathGraph, Nosql::NosqlInjectionFlow::PathGraph>;
import DataFlow::DeduplicatePathGraph<Merged::PathNode, Merged::PathGraph>
from PathNode source, PathNode sink, string type
where
(
cfg instanceof SqlInjection::Configuration and type = "string"
or
cfg instanceof NosqlInjection::Configuration and type = "object"
) and
cfg.hasFlowPath(source, sink)
Sql::SqlInjectionFlow::flowPath(source.getAnOriginalPathNode().asPathNode1(),
sink.getAnOriginalPathNode().asPathNode1()) and
type = "string"
or
Nosql::NosqlInjectionFlow::flowPath(source.getAnOriginalPathNode().asPathNode2(),
sink.getAnOriginalPathNode().asPathNode2()) and
type = "object"
select sink.getNode(), source, sink, "This query " + type + " depends on a $@.", source.getNode(),
"user-provided value"

View File

@@ -17,10 +17,10 @@
import javascript
import semmle.javascript.security.dataflow.CodeInjectionQuery
import DataFlow::PathGraph
import CodeInjectionFlow::PathGraph
import semmle.javascript.heuristics.AdditionalSources
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink) and source.getNode() instanceof HeuristicSource
from CodeInjectionFlow::PathNode source, CodeInjectionFlow::PathNode sink
where CodeInjectionFlow::flowPath(source, sink) and source.getNode() instanceof HeuristicSource
select sink.getNode(), source, sink, sink.getNode().(Sink).getMessagePrefix() + " depends on a $@.",
source.getNode(), "user-provided value"

View File

@@ -13,11 +13,11 @@
*/
import javascript
import DataFlow::PathGraph
import semmle.javascript.security.dataflow.LogInjectionQuery
import semmle.javascript.heuristics.AdditionalSources
import LogInjectionFlow::PathGraph
from LogInjectionConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink
where config.hasFlowPath(source, sink) and source.getNode() instanceof HeuristicSource
from LogInjectionFlow::PathNode source, LogInjectionFlow::PathNode sink
where LogInjectionFlow::flowPath(source, sink) and source.getNode() instanceof HeuristicSource
select sink.getNode(), source, sink, "Log entry depends on a $@.", source.getNode(),
"user-provided value"

View File

@@ -13,10 +13,11 @@
import javascript
import semmle.javascript.security.dataflow.TaintedFormatStringQuery
import DataFlow::PathGraph
import TaintedFormatStringFlow::PathGraph
import semmle.javascript.heuristics.AdditionalSources
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink) and source.getNode() instanceof HeuristicSource
from TaintedFormatStringFlow::PathNode source, TaintedFormatStringFlow::PathNode sink
where
TaintedFormatStringFlow::flowPath(source, sink) and source.getNode() instanceof HeuristicSource
select sink.getNode(), source, sink, "Format string depends on a $@.", source.getNode(),
"user-provided value"

View File

@@ -15,11 +15,12 @@
import javascript
import semmle.javascript.security.dataflow.CorsMisconfigurationForCredentialsQuery
import DataFlow::PathGraph
import semmle.javascript.heuristics.AdditionalSources
import CorsMisconfigurationFlow::PathGraph
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink) and source.getNode() instanceof HeuristicSource
from CorsMisconfigurationFlow::PathNode source, CorsMisconfigurationFlow::PathNode sink
where
CorsMisconfigurationFlow::flowPath(source, sink) and source.getNode() instanceof HeuristicSource
select sink.getNode(), source, sink, "$@ leak vulnerability due to a $@.",
sink.getNode().(Sink).getCredentialsHeader(), "Credential", source.getNode(),
"misconfigured CORS header value"

View File

@@ -15,10 +15,12 @@
import javascript
import semmle.javascript.security.dataflow.RemotePropertyInjectionQuery
import DataFlow::PathGraph
import RemotePropertyInjectionFlow::PathGraph
import semmle.javascript.heuristics.AdditionalSources
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink) and source.getNode() instanceof HeuristicSource
from RemotePropertyInjectionFlow::PathNode source, RemotePropertyInjectionFlow::PathNode sink
where
RemotePropertyInjectionFlow::flowPath(source, sink) and
source.getNode() instanceof HeuristicSource
select sink.getNode(), source, sink, sink.getNode().(Sink).getMessage() + " depends on a $@.",
source.getNode(), "user-provided value"

View File

@@ -14,10 +14,11 @@
import javascript
import semmle.javascript.security.dataflow.UnsafeDeserializationQuery
import DataFlow::PathGraph
import UnsafeDeserializationFlow::PathGraph
import semmle.javascript.heuristics.AdditionalSources
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink) and source.getNode() instanceof HeuristicSource
from UnsafeDeserializationFlow::PathNode source, UnsafeDeserializationFlow::PathNode sink
where
UnsafeDeserializationFlow::flowPath(source, sink) and source.getNode() instanceof HeuristicSource
select sink.getNode(), source, sink, "Unsafe deserialization depends on a $@.", source.getNode(),
"user-provided value"

View File

@@ -15,11 +15,11 @@
import javascript
import semmle.javascript.security.dataflow.XxeQuery
import DataFlow::PathGraph
import XxeFlow::PathGraph
import semmle.javascript.heuristics.AdditionalSources
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink) and source.getNode() instanceof HeuristicSource
from XxeFlow::PathNode source, XxeFlow::PathNode sink
where XxeFlow::flowPath(source, sink) and source.getNode() instanceof HeuristicSource
select sink.getNode(), source, sink,
"XML parsing depends on a $@ without guarding against external entity expansion.",
source.getNode(), "user-provided value"

View File

@@ -14,10 +14,10 @@
import javascript
import semmle.javascript.security.dataflow.XpathInjectionQuery
import DataFlow::PathGraph
import XpathInjectionFlow::PathGraph
import semmle.javascript.heuristics.AdditionalSources
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink) and source.getNode() instanceof HeuristicSource
from XpathInjectionFlow::PathNode source, XpathInjectionFlow::PathNode sink
where XpathInjectionFlow::flowPath(source, sink) and source.getNode() instanceof HeuristicSource
select sink.getNode(), source, sink, "XPath expression depends on a $@.", source.getNode(),
"user-provided value"

View File

@@ -16,10 +16,10 @@
import javascript
import semmle.javascript.security.dataflow.RegExpInjectionQuery
import DataFlow::PathGraph
import RegExpInjectionFlow::PathGraph
import semmle.javascript.heuristics.AdditionalSources
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink) and source.getNode() instanceof HeuristicSource
from RegExpInjectionFlow::PathNode source, RegExpInjectionFlow::PathNode sink
where RegExpInjectionFlow::flowPath(source, sink) and source.getNode() instanceof HeuristicSource
select sink.getNode(), source, sink, "This regular expression is constructed from a $@.",
source.getNode(), "user-provided value"

View File

@@ -14,11 +14,11 @@
*/
import javascript
import DataFlow::PathGraph
import semmle.javascript.security.dataflow.ResourceExhaustionQuery
import semmle.javascript.heuristics.AdditionalSources
import ResourceExhaustionFlow::PathGraph
from Configuration dataflow, DataFlow::PathNode source, DataFlow::PathNode sink
where dataflow.hasFlowPath(source, sink) and source.getNode() instanceof HeuristicSource
from ResourceExhaustionFlow::PathNode source, ResourceExhaustionFlow::PathNode sink
where ResourceExhaustionFlow::flowPath(source, sink) and source.getNode() instanceof HeuristicSource
select sink, source, sink, sink.getNode().(Sink).getProblemDescription() + " from a $@.", source,
"user-provided value"

View File

@@ -15,11 +15,11 @@
import javascript
import semmle.javascript.security.dataflow.XmlBombQuery
import DataFlow::PathGraph
import XmlBombFlow::PathGraph
import semmle.javascript.heuristics.AdditionalSources
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink) and source.getNode() instanceof HeuristicSource
from XmlBombFlow::PathNode source, XmlBombFlow::PathNode sink
where XmlBombFlow::flowPath(source, sink) and source.getNode() instanceof HeuristicSource
select sink.getNode(), source, sink,
"XML parsing depends on a $@ without guarding against uncontrolled entity expansion.",
source.getNode(), "user-provided value"

View File

@@ -14,13 +14,15 @@
import javascript
import semmle.javascript.security.dataflow.ConditionalBypassQuery
import DataFlow::PathGraph
import semmle.javascript.heuristics.AdditionalSources
import ConditionalBypassFlow::PathGraph
from DataFlow::PathNode source, DataFlow::PathNode sink, SensitiveAction action
from
ConditionalBypassFlow::PathNode source, ConditionalBypassFlow::PathNode sink,
SensitiveAction action
where
isTaintedGuardForSensitiveAction(sink, source, action) and
not isEarlyAbortGuard(sink, action) and
isTaintedGuardNodeForSensitiveAction(sink, source, action) and
not isEarlyAbortGuardNode(sink, action) and
source.getNode() instanceof HeuristicSource
select sink.getNode(), source, sink, "This condition guards a sensitive $@, but a $@ controls it.",
action, "action", source.getNode(), "user-provided value"

View File

@@ -20,11 +20,15 @@
import javascript
import semmle.javascript.security.dataflow.PrototypePollutingAssignmentQuery
import DataFlow::PathGraph
import semmle.javascript.heuristics.AdditionalSources
import PrototypePollutingAssignmentFlow::PathGraph
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink) and source.getNode() instanceof HeuristicSource
from
PrototypePollutingAssignmentFlow::PathNode source, PrototypePollutingAssignmentFlow::PathNode sink
where
PrototypePollutingAssignmentFlow::flowPath(source, sink) and
not isIgnoredLibraryFlow(source.getNode(), sink.getNode()) and
source.getNode() instanceof HeuristicSource
select sink, source, sink,
"This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@.",
source.getNode(), source.getNode().(Source).describe()

View File

@@ -133,44 +133,6 @@ private module StandardPoIs {
override predicate is(Node l0) { l0 instanceof RemoteFlowSource }
}
/**
* A "source" for any active configuration.
*/
class SourcePoI extends PoI {
SourcePoI() { this = "SourcePoI" }
override predicate is(Node l0) {
exists(Configuration cfg | cfg.isSource(l0) or cfg.isSource(l0, _))
}
}
/**
* A "sink" for any active configuration.
*/
class SinkPoI extends PoI {
SinkPoI() { this = "SinkPoI" }
override predicate is(Node l0) {
exists(Configuration cfg | cfg.isSink(l0) or cfg.isSink(l0, _))
}
}
/**
* A "barrier" for any active configuration.
*/
class BarrierPoI extends PoI {
BarrierPoI() { this = "BarrierPoI" }
override predicate is(Node l0) {
exists(Configuration cfg |
cfg.isBarrier(l0) or
cfg.isBarrierEdge(l0, _) or
cfg.isBarrierEdge(l0, _, _) or
cfg.isLabeledBarrier(l0, _)
)
}
}
/**
* Provides groups of often used points of interest.
*/
@@ -185,16 +147,6 @@ private module StandardPoIs {
this instanceof UnpromotedRouteHandlerWithFlowPoI
}
}
/**
* A configuration-related point of interest.
*/
class DataFlowConfigurationPoI extends PoI {
DataFlowConfigurationPoI() {
this instanceof SourcePoI or
this instanceof SinkPoI
}
}
}
import StandardPoIGroups

View File

@@ -12,20 +12,20 @@
import javascript
import meta.internal.TaintMetrics
class BasicTaintConfiguration extends TaintTracking::Configuration {
BasicTaintConfiguration() { this = "BasicTaintConfiguration" }
module BasicTaintConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node node) { node = relevantTaintSource() }
override predicate isSource(DataFlow::Node node) { node = relevantTaintSource() }
override predicate isSink(DataFlow::Node node) {
predicate isSink(DataFlow::Node node) {
// To reduce noise from synthetic nodes, only count value nodes
node instanceof DataFlow::ValueNode and
not node.getFile() instanceof IgnoredFile
}
}
module BasicTaintFlow = TaintTracking::Global<BasicTaintConfig>;
// Avoid linking to the source as this would upset the statistics: nodes reachable
// from multiple sources would be counted multilpe times, and that's not what we intend to measure.
// from multiple sources would be counted multiple times, and that's not what we intend to measure.
from DataFlow::Node node
where any(BasicTaintConfiguration cfg).hasFlow(_, node)
where BasicTaintFlow::flowTo(node)
select node, "Tainted node"

View File

@@ -11,12 +11,12 @@
import javascript
import meta.internal.TaintMetrics
class BasicTaintConfiguration extends TaintTracking::Configuration {
BasicTaintConfiguration() { this = "BasicTaintConfiguration" }
module BasicTaintConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node node) { node = relevantTaintSource() }
override predicate isSource(DataFlow::Node node) { node = relevantTaintSource() }
override predicate isSink(DataFlow::Node node) { node = relevantSanitizerInput() }
predicate isSink(DataFlow::Node node) { node = relevantSanitizerInput() }
}
select projectRoot(), count(DataFlow::Node node | any(BasicTaintConfiguration cfg).hasFlow(_, node))
module BasicTaintFlow = TaintTracking::Global<BasicTaintConfig>;
select projectRoot(), count(DataFlow::Node node | BasicTaintFlow::flowTo(node))

View File

@@ -11,12 +11,12 @@
import javascript
import meta.internal.TaintMetrics
class BasicTaintConfiguration extends TaintTracking::Configuration {
BasicTaintConfiguration() { this = "BasicTaintConfiguration" }
module BasicTaintConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node node) { node = relevantSanitizerOutput() }
override predicate isSource(DataFlow::Node node) { node = relevantSanitizerOutput() }
override predicate isSink(DataFlow::Node node) { node = relevantTaintSink() }
predicate isSink(DataFlow::Node node) { node = relevantTaintSink() }
}
select projectRoot(), count(DataFlow::Node node | any(BasicTaintConfiguration cfg).hasFlow(_, node))
module BasicTaintFlow = TaintTracking::Global<BasicTaintConfig>;
select projectRoot(), count(DataFlow::Node node | BasicTaintFlow::flowTo(node))

View File

@@ -17,8 +17,6 @@ predicate relevantStep(DataFlow::Node pred, DataFlow::Node succ) {
or
DataFlow::SharedFlowStep::step(pred, succ)
or
DataFlow::SharedFlowStep::step(pred, succ, _, _)
or
DataFlow::SharedFlowStep::loadStep(pred, succ, _)
or
DataFlow::SharedFlowStep::storeStep(pred, succ, _)

View File

@@ -12,16 +12,16 @@
import javascript
import meta.internal.TaintMetrics
class BasicTaintConfiguration extends TaintTracking::Configuration {
BasicTaintConfiguration() { this = "BasicTaintConfiguration" }
module BasicTaintConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node node) { node = relevantTaintSource() }
override predicate isSource(DataFlow::Node node) { node = relevantTaintSource() }
override predicate isSink(DataFlow::Node node) {
predicate isSink(DataFlow::Node node) {
// To reduce noise from synthetic nodes, only count value nodes
node instanceof DataFlow::ValueNode and
not node.getFile() instanceof IgnoredFile
}
}
select projectRoot(), count(DataFlow::Node node | any(BasicTaintConfiguration cfg).hasFlow(_, node))
module BasicTaintFlow = TaintTracking::Global<BasicTaintConfig>;
select projectRoot(), count(DataFlow::Node node | BasicTaintFlow::flowTo(node))

View File

@@ -24,8 +24,6 @@ predicate unmodeled(API::Node callee, API::CallNode call, DataFlow::Node pred, D
or
DataFlow::SharedFlowStep::step(_, succ)
or
DataFlow::SharedFlowStep::step(_, succ, _, _)
or
DataFlow::SharedFlowStep::loadStep(_, succ, _)
or
DataFlow::SharedFlowStep::storeStep(_, succ, _)