mirror of
https://github.com/github/codeql.git
synced 2025-12-21 19:26:31 +01:00
Merge branch 'main' of github.com:github/codeql into htmlReg
This commit is contained in:
@@ -47,11 +47,7 @@ module ClearTextLogging {
|
||||
meth.getObject(name).(NameNode).getId().matches("logg%") and
|
||||
call.getAnArg() = this
|
||||
|
|
||||
name = "error" or
|
||||
name = "warn" or
|
||||
name = "warning" or
|
||||
name = "debug" or
|
||||
name = "info"
|
||||
name = ["error", "warn", "warning", "debug", "info"]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,13 +74,10 @@ class ExceptionInfoSequence extends SequenceKind {
|
||||
class CallToTracebackFunction extends ErrorInfoSource {
|
||||
CallToTracebackFunction() {
|
||||
exists(string name |
|
||||
name = "extract_tb" or
|
||||
name = "extract_stack" or
|
||||
name = "format_list" or
|
||||
name = "format_exception_only" or
|
||||
name = "format_exception" or
|
||||
name = "format_tb" or
|
||||
name = "format_stack"
|
||||
name in [
|
||||
"extract_tb", "extract_stack", "format_list", "format_exception_only", "format_exception",
|
||||
"format_tb", "format_stack"
|
||||
]
|
||||
|
|
||||
this = traceback_function(name).getACall()
|
||||
)
|
||||
|
||||
@@ -46,7 +46,7 @@ class CustomPathNode extends TCustomPathNode {
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
|
||||
@@ -60,8 +60,8 @@ module PolynomialReDoS {
|
||||
RegExpTerm t;
|
||||
|
||||
RegexExecutionAsSink() {
|
||||
exists(CompiledRegexes::RegexExecution re |
|
||||
re.getRegexNode().asExpr() = t.getRegex() and
|
||||
exists(RegexExecution re |
|
||||
re.getRegex().asExpr() = t.getRegex() and
|
||||
this = re.getString()
|
||||
) and
|
||||
t.isRootTerm()
|
||||
@@ -76,137 +76,3 @@ module PolynomialReDoS {
|
||||
*/
|
||||
class StringConstCompareAsSanitizerGuard extends SanitizerGuard, StringConstCompare { }
|
||||
}
|
||||
|
||||
/** Helper module for tracking compiled regexes. */
|
||||
private module CompiledRegexes {
|
||||
// TODO: This module should be refactored and merged with the experimental work done on detecting
|
||||
// regex injections, such that this can be expressed from just using a concept.
|
||||
/** A configuration for finding uses of compiled regexes. */
|
||||
class RegexDefinitionConfiguration extends DataFlow2::Configuration {
|
||||
RegexDefinitionConfiguration() { this = "RegexDefinitionConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RegexDefinitonSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof RegexDefinitionSink }
|
||||
}
|
||||
|
||||
/** A regex compilation. */
|
||||
class RegexDefinitonSource extends DataFlow::CallCfgNode {
|
||||
DataFlow::Node regexNode;
|
||||
|
||||
RegexDefinitonSource() {
|
||||
this = API::moduleImport("re").getMember("compile").getACall() and
|
||||
regexNode in [this.getArg(0), this.getArgByName("pattern")]
|
||||
}
|
||||
|
||||
/** Gets the regex that is being compiled by this node. */
|
||||
RegExpTerm getRegExp() { result.getRegex() = regexNode.asExpr() and result.isRootTerm() }
|
||||
|
||||
/** Gets the data flow node for the regex being compiled by this node. */
|
||||
DataFlow::Node getRegexNode() { result = regexNode }
|
||||
}
|
||||
|
||||
/** A use of a compiled regex. */
|
||||
class RegexDefinitionSink extends DataFlow::Node {
|
||||
RegexExecutionMethod method;
|
||||
DataFlow::CallCfgNode executingCall;
|
||||
|
||||
RegexDefinitionSink() {
|
||||
exists(DataFlow::AttrRead reMethod |
|
||||
executingCall.getFunction() = reMethod and
|
||||
reMethod.getAttributeName() = method and
|
||||
this = reMethod.getObject()
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the method used to execute the regex. */
|
||||
RegexExecutionMethod getMethod() { result = method }
|
||||
|
||||
/** Gets the data flow node for the executing call. */
|
||||
DataFlow::CallCfgNode getExecutingCall() { result = executingCall }
|
||||
}
|
||||
|
||||
/** A data flow node executing a regex. */
|
||||
abstract class RegexExecution extends DataFlow::Node {
|
||||
/** Gets the data flow node for the regex being compiled by this node. */
|
||||
abstract DataFlow::Node getRegexNode();
|
||||
|
||||
/** Gets a dataflow node for the string to be searched or matched against. */
|
||||
abstract DataFlow::Node getString();
|
||||
}
|
||||
|
||||
private class RegexExecutionMethod extends string {
|
||||
RegexExecutionMethod() {
|
||||
this in ["match", "fullmatch", "search", "split", "findall", "finditer", "sub", "subn"]
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets the index of the argument representing the string to be searched by a regex. */
|
||||
int stringArg(RegexExecutionMethod method) {
|
||||
method in ["match", "fullmatch", "search", "split", "findall", "finditer"] and
|
||||
result = 1
|
||||
or
|
||||
method in ["sub", "subn"] and
|
||||
result = 2
|
||||
}
|
||||
|
||||
/**
|
||||
* A class to find `re` methods immediately executing an expression.
|
||||
*
|
||||
* See `RegexExecutionMethods`
|
||||
*/
|
||||
class DirectRegex extends DataFlow::CallCfgNode, RegexExecution {
|
||||
RegexExecutionMethod method;
|
||||
|
||||
DirectRegex() { this = API::moduleImport("re").getMember(method).getACall() }
|
||||
|
||||
override DataFlow::Node getRegexNode() {
|
||||
result in [this.getArg(0), this.getArgByName("pattern")]
|
||||
}
|
||||
|
||||
override DataFlow::Node getString() {
|
||||
result in [this.getArg(stringArg(method)), this.getArgByName("string")]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A class to find `re` methods immediately executing a compiled expression by `re.compile`.
|
||||
*
|
||||
* Given the following example:
|
||||
*
|
||||
* ```py
|
||||
* pattern = re.compile(input)
|
||||
* pattern.match(s)
|
||||
* ```
|
||||
*
|
||||
* This class will identify that `re.compile` compiles `input` and afterwards
|
||||
* executes `re`'s `match`. As a result, `this` will refer to `pattern.match(s)`
|
||||
* and `this.getRegexNode()` will return the node for `input` (`re.compile`'s first argument)
|
||||
*
|
||||
*
|
||||
* See `RegexExecutionMethods`
|
||||
*
|
||||
* See https://docs.python.org/3/library/re.html#regular-expression-objects
|
||||
*/
|
||||
private class CompiledRegex extends DataFlow::CallCfgNode, RegexExecution {
|
||||
DataFlow::Node regexNode;
|
||||
RegexExecutionMethod method;
|
||||
|
||||
CompiledRegex() {
|
||||
exists(
|
||||
RegexDefinitionConfiguration conf, RegexDefinitonSource source, RegexDefinitionSink sink
|
||||
|
|
||||
conf.hasFlow(source, sink) and
|
||||
regexNode = source.getRegexNode() and
|
||||
method = sink.getMethod() and
|
||||
this = sink.getExecutingCall()
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getRegexNode() { result = regexNode }
|
||||
|
||||
override DataFlow::Node getString() {
|
||||
result in [this.getArg(stringArg(method) - 1), this.getArgByName("string")]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ module ReflectedXSS {
|
||||
class HtmlEscapingAsSanitizer extends Sanitizer {
|
||||
HtmlEscapingAsSanitizer() {
|
||||
// TODO: For now, since there is not an `isSanitizingStep` member-predicate part of a
|
||||
// `TaintTracking::Configuration`, we use treat the output is a taint-sanitizer. This
|
||||
// `TaintTracking::Configuration`, we treat the output as a taint-sanitizer. This
|
||||
// is slightly imprecise, which you can see in the `m_unsafe + SAFE` test-case in
|
||||
// python/ql/test/library-tests/frameworks/markupsafe/taint_test.py
|
||||
//
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
/**
|
||||
* Provides default sources, sinks and sanitizers for detecting
|
||||
* "SQLAlchemy TextClause injection"
|
||||
* vulnerabilities, as well as extension points for adding your own.
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.dataflow.new.RemoteFlowSources
|
||||
private import semmle.python.dataflow.new.BarrierGuards
|
||||
private import semmle.python.frameworks.SqlAlchemy
|
||||
|
||||
/**
|
||||
* Provides default sources, sinks and sanitizers for detecting
|
||||
* "SQLAlchemy TextClause injection"
|
||||
* vulnerabilities, as well as extension points for adding your own.
|
||||
*/
|
||||
module SQLAlchemyTextClause {
|
||||
/**
|
||||
* A data flow source for "SQLAlchemy TextClause injection" vulnerabilities.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A data flow sink for "SQLAlchemy TextClause injection" vulnerabilities.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A sanitizer for "SQLAlchemy TextClause injection" vulnerabilities.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A sanitizer guard for "SQLAlchemy TextClause injection" vulnerabilities.
|
||||
*/
|
||||
abstract class SanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
|
||||
/**
|
||||
* A source of remote user input, considered as a flow source.
|
||||
*/
|
||||
class RemoteFlowSourceAsSource extends Source, RemoteFlowSource { }
|
||||
|
||||
/**
|
||||
* The text argument of a SQLAlchemy TextClause construction, considered as a flow sink.
|
||||
*/
|
||||
class TextArgAsSink extends Sink {
|
||||
TextArgAsSink() { this = any(SqlAlchemy::TextClause::TextClauseConstruction tcc).getTextArg() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A comparison with a constant string, considered as a sanitizer-guard.
|
||||
*/
|
||||
class StringConstCompareAsSanitizerGuard extends SanitizerGuard, StringConstCompare { }
|
||||
}
|
||||
@@ -9,6 +9,7 @@ private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.dataflow.new.RemoteFlowSources
|
||||
private import semmle.python.dataflow.new.BarrierGuards
|
||||
private import semmle.python.frameworks.SqlAlchemy
|
||||
|
||||
/**
|
||||
* Provides default sources, sinks and sanitizers for detecting
|
||||
@@ -48,6 +49,13 @@ module SqlInjection {
|
||||
SqlExecutionAsSink() { this = any(SqlExecution e).getSql() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The text argument of a SQLAlchemy TextClause construction, considered as a flow sink.
|
||||
*/
|
||||
class TextArgAsSink extends Sink {
|
||||
TextArgAsSink() { this = any(SqlAlchemy::TextClause::TextClauseConstruction tcc).getTextArg() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A comparison with a constant string, considered as a sanitizer-guard.
|
||||
*/
|
||||
|
||||
@@ -13,18 +13,11 @@ import semmle.python.security.strings.Untrusted
|
||||
/** Abstract taint sink that is potentially vulnerable to malicious shell commands. */
|
||||
abstract class CommandSink extends TaintSink { }
|
||||
|
||||
private ModuleObject osOrPopenModule() {
|
||||
result.getName() = "os" or
|
||||
result.getName() = "popen2"
|
||||
}
|
||||
private ModuleObject osOrPopenModule() { result.getName() = ["os", "popen2"] }
|
||||
|
||||
private Object makeOsCall() {
|
||||
exists(string name | result = ModuleObject::named("subprocess").attr(name) |
|
||||
name = "Popen" or
|
||||
name = "call" or
|
||||
name = "check_call" or
|
||||
name = "check_output" or
|
||||
name = "run"
|
||||
name = ["Popen", "call", "check_call", "check_output", "run"]
|
||||
)
|
||||
}
|
||||
|
||||
@@ -65,8 +58,7 @@ class ShellCommand extends CommandSink {
|
||||
call.getAnArg() = this and
|
||||
call.getFunction().refersTo(osOrPopenModule().attr(name))
|
||||
|
|
||||
name = "system" or
|
||||
name = "popen" or
|
||||
name = ["system", "popen"] or
|
||||
name.matches("popen_")
|
||||
)
|
||||
or
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
/**
|
||||
* Provides a taint-tracking configuration for detecting "SQLAlchemy TextClause injection" vulnerabilities.
|
||||
* Provides a taint-tracking configuration for detecting regular expression injection
|
||||
* vulnerabilities.
|
||||
*
|
||||
* Note, for performance reasons: only import this file if
|
||||
* `SQLAlchemyTextClause::Configuration` is needed, otherwise
|
||||
* `SQLAlchemyTextClauseCustomizations` should be imported instead.
|
||||
* `RegexInjection::Configuration` is needed, otherwise
|
||||
* `RegexInjectionCustomizations` should be imported instead.
|
||||
*/
|
||||
|
||||
private import python
|
||||
@@ -11,16 +12,17 @@ import semmle.python.dataflow.new.DataFlow
|
||||
import semmle.python.dataflow.new.TaintTracking
|
||||
|
||||
/**
|
||||
* Provides a taint-tracking configuration for detecting "SQLAlchemy TextClause injection" vulnerabilities.
|
||||
* Provides a taint-tracking configuration for detecting regular expression injection
|
||||
* vulnerabilities.
|
||||
*/
|
||||
module SQLAlchemyTextClause {
|
||||
import SQLAlchemyTextClauseCustomizations::SQLAlchemyTextClause
|
||||
module RegexInjection {
|
||||
import RegexInjectionCustomizations::RegexInjection
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for detecting "SQLAlchemy TextClause injection" vulnerabilities.
|
||||
* A taint-tracking configuration for detecting "reflected server-side cross-site scripting" vulnerabilities.
|
||||
*/
|
||||
class Configuration extends TaintTracking::Configuration {
|
||||
Configuration() { this = "SQLAlchemyTextClause" }
|
||||
Configuration() { this = "RegexInjection" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* Provides default sources, sinks and sanitizers for detecting
|
||||
* "regular expression injection"
|
||||
* vulnerabilities, as well as extension points for adding your own.
|
||||
*/
|
||||
|
||||
private import python
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.dataflow.new.TaintTracking
|
||||
private import semmle.python.dataflow.new.RemoteFlowSources
|
||||
|
||||
/**
|
||||
* Provides default sources, sinks and sanitizers for detecting
|
||||
* "regular expression injection"
|
||||
* vulnerabilities, as well as extension points for adding your own.
|
||||
*/
|
||||
module RegexInjection {
|
||||
/**
|
||||
* A data flow source for "regular expression injection" vulnerabilities.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A sink for "regular expression injection" vulnerabilities is the execution of a regular expression.
|
||||
* If you have a custom way to execute regular expressions, you can extend `RegexExecution::Range`.
|
||||
*/
|
||||
class Sink extends DataFlow::Node {
|
||||
RegexExecution regexExecution;
|
||||
|
||||
Sink() { this = regexExecution.getRegex() }
|
||||
|
||||
/** Gets the call that executes the regular expression marked by this sink. */
|
||||
RegexExecution getRegexExecution() { result = regexExecution }
|
||||
}
|
||||
|
||||
/**
|
||||
* A sanitizer for "regular expression injection" vulnerabilities.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A sanitizer guard for "regular expression injection" vulnerabilities.
|
||||
*/
|
||||
abstract class SanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
|
||||
/**
|
||||
* A source of remote user input, considered as a flow source.
|
||||
*/
|
||||
class RemoteFlowSourceAsSource extends Source, RemoteFlowSource { }
|
||||
|
||||
/**
|
||||
* A regex escaping, considered as a sanitizer.
|
||||
*/
|
||||
class RegexEscapingAsSanitizer extends Sanitizer {
|
||||
RegexEscapingAsSanitizer() {
|
||||
// Due to use-use flow, we want the output rather than an input
|
||||
// (so the input can still flow to other sinks).
|
||||
this = any(RegexEscaping esc).getOutput()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -139,8 +139,6 @@ class RegExpRoot extends RegExpTerm {
|
||||
predicate isRelevant() {
|
||||
// there is at least one repetition
|
||||
getRoot(any(InfiniteRepetitionQuantifier q)) = this and
|
||||
// there are no lookbehinds
|
||||
not exists(RegExpLookbehind lbh | getRoot(lbh) = this) and
|
||||
// is actually used as a RegExp
|
||||
isUsedAsRegExp() and
|
||||
// not excluded for library specific reasons
|
||||
@@ -479,7 +477,7 @@ private module CharacterClasses {
|
||||
result = ["0", "9"]
|
||||
or
|
||||
cc.getValue() = "s" and
|
||||
result = [" "]
|
||||
result = " "
|
||||
or
|
||||
cc.getValue() = "w" and
|
||||
result = ["a", "Z", "_", "0", "9"]
|
||||
@@ -492,7 +490,7 @@ private module CharacterClasses {
|
||||
result = "9"
|
||||
or
|
||||
cc.getValue() = "s" and
|
||||
result = [" "]
|
||||
result = " "
|
||||
or
|
||||
cc.getValue() = "w" and
|
||||
result = "a"
|
||||
|
||||
Reference in New Issue
Block a user