From b143e148e713d8b0ebe7bf1631a8a2a7ec7e528b Mon Sep 17 00:00:00 2001 From: Chad Bentz <1760475+felickz@users.noreply.github.com> Date: Wed, 3 Sep 2025 18:25:43 -0400 Subject: [PATCH 1/2] Intial support for partial queries for js --- javascript/lib/ghsl/Utils.qll | 3 + javascript/src/audit/explore/RemoteSources.ql | 60 +++++++++++++++++++ .../src/debugging/PartialPathsFromSink.ql | 39 ++++++++++++ .../src/debugging/PartialPathsFromSource.ql | 38 ++++++++++++ 4 files changed, 140 insertions(+) create mode 100644 javascript/src/audit/explore/RemoteSources.ql create mode 100644 javascript/src/debugging/PartialPathsFromSink.ql create mode 100644 javascript/src/debugging/PartialPathsFromSource.ql diff --git a/javascript/lib/ghsl/Utils.qll b/javascript/lib/ghsl/Utils.qll index 45d4df85..aa3cbb1a 100644 --- a/javascript/lib/ghsl/Utils.qll +++ b/javascript/lib/ghsl/Utils.qll @@ -6,6 +6,7 @@ private import semmle.javascript.security.dataflow.CommandInjectionCustomization private import semmle.javascript.security.dataflow.CodeInjectionCustomizations private import semmle.javascript.security.dataflow.LogInjectionQuery as LogInjection private import semmle.javascript.security.dataflow.NosqlInjectionCustomizations +private import semmle.javascript.security.dataflow.SqlInjectionCustomizations private import semmle.javascript.security.dataflow.Xss as Xss private import semmle.javascript.security.dataflow.XxeCustomizations @@ -77,6 +78,8 @@ class AllSinks extends DataFlow::Node { sink = "log-injection" or this instanceof NosqlInjection::Sink and sink = "nosql-injection" or + this instanceof SqlInjection::Sink and + sink = "sql-injection" or this instanceof Xss::Shared::Sink and sink = "xss" or this instanceof Xxe::Sink and diff --git a/javascript/src/audit/explore/RemoteSources.ql b/javascript/src/audit/explore/RemoteSources.ql new file mode 100644 index 00000000..8bc5620a --- /dev/null +++ b/javascript/src/audit/explore/RemoteSources.ql @@ -0,0 +1,60 @@ +/** + * @name Remote Sources + * @kind problem + * @problem.severity note + * @precision low + * @id js/debugging/remote-sources + * @tags debugging + */ + + import javascript + + // ========================================================================== +// Helper Predicates +// ========================================================================== + +/** + * Filter results to a specific file and line number + * + * **Examples:** + * + * ``` + * filterByLocation(sources, "db.js", 1) + * // or we don't care about the line numbers + * filterByLocation(sources, "db.js", _) + * ``` + */ +predicate filterByLocation(DataFlow::Node node, string relative_path, int linenumber) { + node.getLocation().getFile().getRelativePath() = relative_path and + node.getLocation().getStartLine() = linenumber + } + + + // ========================================================================== + // Sources + // ========================================================================== + + /** + * All Sources (Remote and Local) + */ + final class AllSources extends RemoteSources, LocalSources { } + + /** + * Remote Sources (HTTP frameworks, etc) + */ + class RemoteSources extends ThreatModelSource { + RemoteSources() { this.getThreatModel() = "remote" } + } + + /** + * Local Sources (CLI arguments, Filesystem, etc) + */ + class LocalSources extends ThreatModelSource { + LocalSources() { this.getThreatModel() = "local" } + } + + from RemoteSources sources +// where +// // Filter results to a specific file +// filterByLocation(sources, "app.js", _) + select sources, "Remote Sources" \ No newline at end of file diff --git a/javascript/src/debugging/PartialPathsFromSink.ql b/javascript/src/debugging/PartialPathsFromSink.ql new file mode 100644 index 00000000..f9592bbf --- /dev/null +++ b/javascript/src/debugging/PartialPathsFromSink.ql @@ -0,0 +1,39 @@ +/** + * @name Partial Path Query from Sink + * @kind path-problem + * @problem.severity warning + * @security-severity 1.0 + * @sub-severity low + * @precision low + * @id js/debugging/partial-path-from-sink + * @tags debugging + */ + +import javascript +import ghsl +import DataFlow + +// Partial Graph +module PartialFlowConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { any() } + + predicate isSink(DataFlow::Node sink) { sink instanceof AllSinks } +} + +int explorationLimit() { result = 10 } + +private module PartialFlows = DataFlow::Global; + +private module PartialFlowsGraph = PartialFlows::FlowExplorationRev; + +private import PartialFlowsGraph::PartialPathGraph + +from PartialFlowsGraph::PartialPathNode source, PartialFlowsGraph::PartialPathNode sink +where + /// Only show sinks from a certain file + //filterByLocation(sink.getNode(), "index.js", _) and + /// Only show sources that match our criteria + //checkSource(source.getNode()) and + /// Partial Path + PartialFlowsGraph::partialFlow(source, sink, _) +select sink.getNode(), source, sink, "Partial Graph $@.", source.getNode(), "user-provided value" diff --git a/javascript/src/debugging/PartialPathsFromSource.ql b/javascript/src/debugging/PartialPathsFromSource.ql new file mode 100644 index 00000000..b0f20c32 --- /dev/null +++ b/javascript/src/debugging/PartialPathsFromSource.ql @@ -0,0 +1,38 @@ +/** + * @name Partial Path Query from Source + * @kind path-problem + * @problem.severity warning + * @security-severity 1.0 + * @sub-severity low + * @precision low + * @id js/debugging/partial-path-from-source + * @tags debugging + */ + +import javascript +import ghsl +import DataFlow + +// Partial Graph +module PartialFlowConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + source instanceof AllSources + } + + predicate isSink(DataFlow::Node sink) { none() } +} + +int explorationLimit() { result = 10 } + +private module PartialFlows = DataFlow::Global; + +private module PartialFlowsGraph = PartialFlows::FlowExplorationFwd; + +private import PartialFlowsGraph::PartialPathGraph + +from PartialFlowsGraph::PartialPathNode source, PartialFlowsGraph::PartialPathNode sink +where + /// Filter by location + // filterByLocation(source.getNode(), "main.js", _) and + PartialFlowsGraph::partialFlow(source, sink, _) +select sink.getNode(), source, sink, "Partial Graph $@.", source.getNode(), "user-provided value" From 5c3f8d4438ec52110e56d36a8517311cb2c59e13 Mon Sep 17 00:00:00 2001 From: Chad Bentz <1760475+felickz@users.noreply.github.com> Date: Wed, 3 Sep 2025 18:27:09 -0400 Subject: [PATCH 2/2] Delete javascript/src/audit/explore/RemoteSources.ql --- javascript/src/audit/explore/RemoteSources.ql | 60 ------------------- 1 file changed, 60 deletions(-) delete mode 100644 javascript/src/audit/explore/RemoteSources.ql diff --git a/javascript/src/audit/explore/RemoteSources.ql b/javascript/src/audit/explore/RemoteSources.ql deleted file mode 100644 index 8bc5620a..00000000 --- a/javascript/src/audit/explore/RemoteSources.ql +++ /dev/null @@ -1,60 +0,0 @@ -/** - * @name Remote Sources - * @kind problem - * @problem.severity note - * @precision low - * @id js/debugging/remote-sources - * @tags debugging - */ - - import javascript - - // ========================================================================== -// Helper Predicates -// ========================================================================== - -/** - * Filter results to a specific file and line number - * - * **Examples:** - * - * ``` - * filterByLocation(sources, "db.js", 1) - * // or we don't care about the line numbers - * filterByLocation(sources, "db.js", _) - * ``` - */ -predicate filterByLocation(DataFlow::Node node, string relative_path, int linenumber) { - node.getLocation().getFile().getRelativePath() = relative_path and - node.getLocation().getStartLine() = linenumber - } - - - // ========================================================================== - // Sources - // ========================================================================== - - /** - * All Sources (Remote and Local) - */ - final class AllSources extends RemoteSources, LocalSources { } - - /** - * Remote Sources (HTTP frameworks, etc) - */ - class RemoteSources extends ThreatModelSource { - RemoteSources() { this.getThreatModel() = "remote" } - } - - /** - * Local Sources (CLI arguments, Filesystem, etc) - */ - class LocalSources extends ThreatModelSource { - LocalSources() { this.getThreatModel() = "local" } - } - - from RemoteSources sources -// where -// // Filter results to a specific file -// filterByLocation(sources, "app.js", _) - select sources, "Remote Sources" \ No newline at end of file