diff --git a/java/lib/ghsl.qll b/java/lib/ghsl.qll new file mode 100644 index 00000000..01fe18fe --- /dev/null +++ b/java/lib/ghsl.qll @@ -0,0 +1,3 @@ +import ghsl.LocalSources +// Export utils +import ghsl.Utils \ No newline at end of file diff --git a/java/lib/ghsl/Utils.qll b/java/lib/ghsl/Utils.qll new file mode 100644 index 00000000..184fe4d6 --- /dev/null +++ b/java/lib/ghsl/Utils.qll @@ -0,0 +1,118 @@ +/** + * A collection of utility predicates and classes for the Java library. + */ + +private import semmle.code.java.dataflow.DataFlow +private import semmle.code.java.dataflow.ExternalFlow +private import semmle.code.java.dataflow.FlowSources +// Sinks +private import semmle.code.java.security.QueryInjection +private import semmle.code.java.security.CommandLineQuery +private import semmle.code.java.security.LdapInjection +private import semmle.code.java.security.LogInjection +private import semmle.code.java.security.OgnlInjection +private import semmle.code.java.security.RequestForgery +private import semmle.code.java.security.TemplateInjection + +/** + * Filter nodes by its location (relative path or base name). + */ +bindingset[relative_path] +predicate findByLocation(DataFlow::Node node, string relative_path, int linenumber) { + node.getLocation().getFile().getRelativePath().matches(relative_path) and + node.getLocation().getStartLine() = linenumber +} + +/** + * This will only show sinks that are callable (method calls) + */ +predicate isCallable(DataFlow::Node sink) { sink.asExpr() instanceof MethodCall } + +/** + * Check if the source node is a method parameter. + */ +predicate checkSource(DataFlow::Node source) { + exists(source.asParameter()) + or + source.asExpr() instanceof MethodCall +} + +/** + * Local sources + */ +class LocalSources = LocalUserInput; + +/** + * List of all the souces + */ +class AllSources extends DataFlow::Node { + private string threadmodel; + + AllSources() { + this instanceof LocalUserInput and + threadmodel = "local" + or + this instanceof RemoteFlowSource and + threadmodel = "remote" + or + this instanceof ActiveThreatModelSource + and + threadmodel = this.(SourceNode).getThreatModel() + } + + /** + * Gets the source threat model. + */ + string getThreatModel() { + result = threadmodel + } +} + +/** + * List of all the sinks that we want to check. + */ +class AllSinks extends DataFlow::Node { + private string sink; + + AllSinks() { + this instanceof QueryInjectionSink + and + sink = "QueryInjectionSink" + or + this instanceof CommandInjectionSink + and + sink = "CommandInjectionSink" + or + this instanceof LdapInjectionSink + and + sink = "LdapInjectionSink" + or + this instanceof LogInjectionSink + and + sink = "LogInjectionSink" + or + this instanceof OgnlInjectionSink + and + sink = "OgnlInjectionSink" + or + this instanceof RequestForgerySink + and + sink = "RequestForgerySink" + or + this instanceof TemplateInjectionSink + and + sink = "TemplateInjectionSink" + or + // All MaD sinks + sinkNode(this, _) + and + sink = "MaD" + } + + /** + * Gets the sink sink type. + */ + string sinkType() { + result = sink + } +} diff --git a/java/lib/qlpack.yml b/java/lib/qlpack.yml index 38bd3ea0..5480b5de 100644 --- a/java/lib/qlpack.yml +++ b/java/lib/qlpack.yml @@ -3,3 +3,4 @@ name: githubsecuritylab/codeql-java-libs version: 0.2.1 dependencies: codeql/java-all: '*' + githubsecuritylab/codeql-java-extensions: '0.2.1' diff --git a/java/src/debugging/PartialPathsFromSink.ql b/java/src/debugging/PartialPathsFromSink.ql new file mode 100644 index 00000000..a3f4499a --- /dev/null +++ b/java/src/debugging/PartialPathsFromSink.ql @@ -0,0 +1,41 @@ +/** + * @name Partial Path Query from Sink + * @kind path-problem + * @problem.severity warning + * @security-severity 1.0 + * @sub-severity low + * @precision low + * @id java/debugging/partial-path-from-sink + * @tags debugging + */ + +import java +import ghsl +import semmle.code.java.dataflow.DataFlow +import semmle.code.java.dataflow.FlowSources +import semmle.code.java.dataflow.TaintTracking + +// Partial Graph +private module RemoteFlowsConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { any() } + + predicate isSink(DataFlow::Node sink) { sink instanceof AllSinks } +} + +int explorationLimit() { result = 10 } + +private module RemoteFlows = DataFlow::Global; + +private module RemoteFlowsPartial = RemoteFlows::FlowExplorationRev; + +private import RemoteFlowsPartial::PartialPathGraph + +from RemoteFlowsPartial::PartialPathNode source, RemoteFlowsPartial::PartialPathNode sink +where + /// Only show sinks from a certain file + // findByLocation(sink.getNode(), "File.java", _) and + /// Only show sources that match our criteria + // checkSource(source.getNode()) and + /// Partical Path + RemoteFlowsPartial::partialFlow(source, sink, _) +select sink.getNode(), source, sink, "Partial Graph $@.", source.getNode(), "user-provided value" diff --git a/java/src/debugging/PartialPathsFromSource.ql b/java/src/debugging/PartialPathsFromSource.ql new file mode 100644 index 00000000..6fe9eb50 --- /dev/null +++ b/java/src/debugging/PartialPathsFromSource.ql @@ -0,0 +1,41 @@ +/** + * @name Partial Path Query from Source + * @kind path-problem + * @problem.severity warning + * @security-severity 1.0 + * @sub-severity low + * @precision low + * @id java/debugging/partial-path-from-source + * @tags debugging + */ + +import java +import ghsl +import semmle.code.java.dataflow.DataFlow +import semmle.code.java.dataflow.FlowSources +import semmle.code.java.dataflow.TaintTracking + +// Partial Graph +private module RemoteFlowsConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source instanceof AllSources } + + predicate isSink(DataFlow::Node sink) { none() } +} + +int explorationLimit() { result = 10 } + +private module RemoteFlows = DataFlow::Global; + +private module RemoteFlowsPartial = RemoteFlows::FlowExplorationFwd; + +private import RemoteFlowsPartial::PartialPathGraph + +from RemoteFlowsPartial::PartialPathNode source, RemoteFlowsPartial::PartialPathNode sink +where + /// Filter by file (line number) + // findByLocation(source.getNode(), "File.java", _) and + /// Filter by if the sink is callable + // isCallable(sink.getNode()) and + /// Perform Partial Flow query + RemoteFlowsPartial::partialFlow(source, sink, _) +select sink.getNode(), source, sink, "Partial Graph $@.", source.getNode(), "user-provided value" diff --git a/java/src/debugging/Sinks.ql b/java/src/debugging/Sinks.ql new file mode 100644 index 00000000..ffd51462 --- /dev/null +++ b/java/src/debugging/Sinks.ql @@ -0,0 +1,16 @@ +/** + * @name List of all known sinks + * @kind problem + * @problem.severity warning + * @security-severity 1.0 + * @sub-severity low + * @precision high + * @id java/debugging/sinks + * @tags debugging + */ + +import java +import ghsl + +from AllSinks sinks +select sinks, "sink[" + sinks.sinkType() + "]" diff --git a/java/src/debugging/Sources.ql b/java/src/debugging/Sources.ql new file mode 100644 index 00000000..b18cf12d --- /dev/null +++ b/java/src/debugging/Sources.ql @@ -0,0 +1,19 @@ +/** + * @name List of all known sources (remote, local, etc.) + * @kind problem + * @problem.severity warning + * @security-severity 1.0 + * @sub-severity low + * @precision high + * @id java/debugging/sources + * @tags debugging + */ + +import java +import ghsl + +from AllSources sources, string threatModel +where threatModel = sources.getThreatModel() +// Local sources +// sources.getThreatModel() = "local" +select sources, "source[" + threatModel + "]" diff --git a/java/src/suites/java-debugging.qls b/java/src/suites/java-debugging.qls new file mode 100644 index 00000000..0a48e6be --- /dev/null +++ b/java/src/suites/java-debugging.qls @@ -0,0 +1,19 @@ +- description: "GitHub's Community Packs Java/Kotlin Extended Suite" + +- queries: '.' + from: githubsecuritylab/codeql-java-queries + +- include: + kind: + - problem + - path-problem + precision: + - very-high + - high + tags contain: + - debugging + +# Remove local testing folders +- exclude: + query path: + - /testing\/.*/