mirror of
https://github.com/github/codeql.git
synced 2025-12-18 09:43:15 +01:00
Merge branch 'main' of https://github.com/github/codeql into python-dataflow/flow-summaries-from-scratch
synced files have changed
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
## 0.4.1
|
||||
|
||||
## 0.4.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
0
python/ql/src/Exceptions/EmptyExcept.ql
Executable file → Normal file
0
python/ql/src/Exceptions/EmptyExcept.ql
Executable file → Normal file
0
python/ql/src/Exceptions/UnguardedNextInGenerator.ql
Executable file → Normal file
0
python/ql/src/Exceptions/UnguardedNextInGenerator.ql
Executable file → Normal file
0
python/ql/src/Resources/FileNotAlwaysClosed.ql
Executable file → Normal file
0
python/ql/src/Resources/FileNotAlwaysClosed.ql
Executable file → Normal file
@@ -35,7 +35,7 @@ private import semmle.python.objects.ObjectInternal
|
||||
// functionality into `BuiltinFunctionValue` and `BuiltinMethodValue`, but will
|
||||
// probably require some more work: for this query, it's totally ok to use
|
||||
// `builtins.open` for the code `open(f)`, but well, it requires a bit of thinking to
|
||||
// figure out if that is desireable in general. I simply skipped a corner here!
|
||||
// figure out if that is desirable in general. I simply skipped a corner here!
|
||||
// 4. TaintTrackingPrivate: Nothing else gives us access to `defaultAdditionalTaintStep` :(
|
||||
/**
|
||||
* A callable that is considered a "safe" external API from a security perspective.
|
||||
@@ -131,7 +131,9 @@ class UntrustedExternalApiDataNode extends ExternalApiDataNode {
|
||||
/** DEPRECATED: Alias for UntrustedExternalApiDataNode */
|
||||
deprecated class UntrustedExternalAPIDataNode = UntrustedExternalApiDataNode;
|
||||
|
||||
/** An external API which is used with untrusted data. */
|
||||
private newtype TExternalApi =
|
||||
/** An untrusted API method `m` where untrusted data is passed at `index`. */
|
||||
TExternalApiParameter(DataFlowPrivate::DataFlowCallable callable, int index) {
|
||||
exists(UntrustedExternalApiDataNode n |
|
||||
callable = n.getCallable() and
|
||||
|
||||
65
python/ql/src/Security/CWE-020/OverlyLargeRange.qhelp
Normal file
65
python/ql/src/Security/CWE-020/OverlyLargeRange.qhelp
Normal file
@@ -0,0 +1,65 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
It's easy to write a regular expression range that matches a wider range of characters than you intended.
|
||||
For example, <code>/[a-zA-z]/</code> matches all lowercase and all uppercase letters,
|
||||
as you would expect, but it also matches the characters: <code>[ \ ] ^ _ `</code>.
|
||||
</p>
|
||||
<p>
|
||||
Another common problem is failing to escape the dash character in a regular
|
||||
expression. An unescaped dash is interpreted
|
||||
as part of a range. For example, in the character class <code>[a-zA-Z0-9%=.,-_]</code>
|
||||
the last character range matches the 55 characters between
|
||||
<code>,</code> and <code>_</code> (both included), which overlaps with the
|
||||
range <code>[0-9]</code> and is clearly not intended by the writer.
|
||||
</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>
|
||||
Avoid any confusion about which characters are included in the range by
|
||||
writing unambiguous regular expressions.
|
||||
Always check that character ranges match only the expected characters.
|
||||
</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
|
||||
<p>
|
||||
The following example code is intended to check whether a string is a valid 6 digit hex color.
|
||||
</p>
|
||||
|
||||
<sample language="python">
|
||||
import re
|
||||
def is_valid_hex_color(color):
|
||||
return re.match(r'^#[0-9a-fA-f]{6}$', color) is not None
|
||||
</sample>
|
||||
|
||||
<p>
|
||||
However, the <code>A-f</code> range is overly large and matches every uppercase character.
|
||||
It would parse a "color" like <code>#XXYYZZ</code> as valid.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The fix is to use an uppercase <code>A-F</code> range instead.
|
||||
</p>
|
||||
|
||||
<sample language="python">
|
||||
import re
|
||||
def is_valid_hex_color(color):
|
||||
return re.match(r'^#[0-9a-fA-F]{6}$', color) is not None
|
||||
</sample>
|
||||
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>GitHub Advisory Database: <a href="https://github.com/advisories/GHSA-g4rg-993r-mgx7">CVE-2021-42740: Improper Neutralization of Special Elements used in a Command in Shell-quote</a></li>
|
||||
<li>wh0.github.io: <a href="https://wh0.github.io/2021/10/28/shell-quote-rce-exploiting.html">Exploiting CVE-2021-42740</a></li>
|
||||
<li>Yosuke Ota: <a href="https://ota-meshi.github.io/eslint-plugin-regexp/rules/no-obscure-range.html">no-obscure-range</a></li>
|
||||
<li>Paul Boyd: <a href="https://pboyd.io/posts/comma-dash-dot/">The regex [,-.]</a></li>
|
||||
</references>
|
||||
</qhelp>
|
||||
19
python/ql/src/Security/CWE-020/OverlyLargeRange.ql
Normal file
19
python/ql/src/Security/CWE-020/OverlyLargeRange.ql
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* @name Overly permissive regular expression range
|
||||
* @description Overly permissive regular expression ranges match a wider range of characters than intended.
|
||||
* This may allow an attacker to bypass a filter or sanitizer.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 5.0
|
||||
* @precision high
|
||||
* @id py/overly-large-range
|
||||
* @tags correctness
|
||||
* security
|
||||
* external/cwe/cwe-020
|
||||
*/
|
||||
|
||||
import semmle.python.security.OverlyLargeRangeQuery
|
||||
|
||||
from RegExpCharacterRange range, string reason
|
||||
where problem(range, reason)
|
||||
select range, "Suspicious character range that " + reason + "."
|
||||
0
python/ql/src/Security/CWE-078/CommandInjection.ql
Executable file → Normal file
0
python/ql/src/Security/CWE-078/CommandInjection.ql
Executable file → Normal file
@@ -19,14 +19,14 @@ private API::Node unsafe_paramiko_policy(string name) {
|
||||
result = API::moduleImport("paramiko").getMember("client").getMember(name)
|
||||
}
|
||||
|
||||
private API::Node paramikoSSHClientInstance() {
|
||||
private API::Node paramikoSshClientInstance() {
|
||||
result = API::moduleImport("paramiko").getMember("client").getMember("SSHClient").getReturn()
|
||||
}
|
||||
|
||||
from DataFlow::CallCfgNode call, DataFlow::Node arg, string name
|
||||
where
|
||||
// see http://docs.paramiko.org/en/stable/api/client.html#paramiko.client.SSHClient.set_missing_host_key_policy
|
||||
call = paramikoSSHClientInstance().getMember("set_missing_host_key_policy").getACall() and
|
||||
call = paramikoSshClientInstance().getMember("set_missing_host_key_policy").getACall() and
|
||||
arg in [call.getArg(0), call.getArgByName("policy")] and
|
||||
(
|
||||
arg = unsafe_paramiko_policy(name).getAValueReachableFromSource() or
|
||||
|
||||
@@ -29,7 +29,7 @@ The example shows two unsafe calls to <a href="https://semmle.com">semmle.com</a
|
||||
|
||||
<references>
|
||||
<li>
|
||||
Python requests documentation: <a href="http://docs.python-requests.org/en/master/user/advanced/#ssl-cert-verification">SSL Cert Verification</a>.
|
||||
Python requests documentation: <a href="https://requests.readthedocs.io/en/latest/user/advanced/#ssl-cert-verification">SSL Cert Verification</a>.
|
||||
</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
|
||||
@@ -7,13 +7,13 @@ private import python
|
||||
private import semmle.python.ApiGraphs
|
||||
import TlsLibraryModel
|
||||
|
||||
class PyOpenSSLContextCreation extends ContextCreation, DataFlow::CallCfgNode {
|
||||
PyOpenSSLContextCreation() {
|
||||
class PyOpenSslContextCreation extends ContextCreation, DataFlow::CallCfgNode {
|
||||
PyOpenSslContextCreation() {
|
||||
this = API::moduleImport("OpenSSL").getMember("SSL").getMember("Context").getACall()
|
||||
}
|
||||
|
||||
override string getProtocol() {
|
||||
exists(DataFlow::Node protocolArg, PyOpenSSL pyo |
|
||||
exists(DataFlow::Node protocolArg, PyOpenSsl pyo |
|
||||
protocolArg in [this.getArg(0), this.getArgByName("method")]
|
||||
|
|
||||
protocolArg in [
|
||||
@@ -51,12 +51,12 @@ class SetOptionsCall extends ProtocolRestriction, DataFlow::CallCfgNode {
|
||||
}
|
||||
}
|
||||
|
||||
class UnspecificPyOpenSSLContextCreation extends PyOpenSSLContextCreation, UnspecificContextCreation {
|
||||
UnspecificPyOpenSSLContextCreation() { library instanceof PyOpenSSL }
|
||||
class UnspecificPyOpenSslContextCreation extends PyOpenSslContextCreation, UnspecificContextCreation {
|
||||
UnspecificPyOpenSslContextCreation() { library instanceof PyOpenSsl }
|
||||
}
|
||||
|
||||
class PyOpenSSL extends TlsLibrary {
|
||||
PyOpenSSL() { this = "pyOpenSSL" }
|
||||
class PyOpenSsl extends TlsLibrary {
|
||||
PyOpenSsl() { this = "pyOpenSSL" }
|
||||
|
||||
override string specific_version_name(ProtocolVersion version) { result = version + "_METHOD" }
|
||||
|
||||
@@ -70,7 +70,7 @@ class PyOpenSSL extends TlsLibrary {
|
||||
override ContextCreation default_context_creation() { none() }
|
||||
|
||||
override ContextCreation specific_context_creation() {
|
||||
result instanceof PyOpenSSLContextCreation
|
||||
result instanceof PyOpenSslContextCreation
|
||||
}
|
||||
|
||||
override DataFlow::Node insecure_connection_creation(ProtocolVersion version) { none() }
|
||||
@@ -80,6 +80,6 @@ class PyOpenSSL extends TlsLibrary {
|
||||
override ProtocolRestriction protocol_restriction() { result instanceof SetOptionsCall }
|
||||
|
||||
override ProtocolUnrestriction protocol_unrestriction() {
|
||||
result instanceof UnspecificPyOpenSSLContextCreation
|
||||
result instanceof UnspecificPyOpenSslContextCreation
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@ private import python
|
||||
private import semmle.python.ApiGraphs
|
||||
import TlsLibraryModel
|
||||
|
||||
class SSLContextCreation extends ContextCreation, DataFlow::CallCfgNode {
|
||||
SSLContextCreation() { this = API::moduleImport("ssl").getMember("SSLContext").getACall() }
|
||||
class SslContextCreation extends ContextCreation, DataFlow::CallCfgNode {
|
||||
SslContextCreation() { this = API::moduleImport("ssl").getMember("SSLContext").getACall() }
|
||||
|
||||
override string getProtocol() {
|
||||
exists(DataFlow::Node protocolArg, Ssl ssl |
|
||||
@@ -27,8 +27,8 @@ class SSLContextCreation extends ContextCreation, DataFlow::CallCfgNode {
|
||||
}
|
||||
}
|
||||
|
||||
class SSLDefaultContextCreation extends ContextCreation {
|
||||
SSLDefaultContextCreation() {
|
||||
class SslDefaultContextCreation extends ContextCreation {
|
||||
SslDefaultContextCreation() {
|
||||
this = API::moduleImport("ssl").getMember("create_default_context").getACall()
|
||||
}
|
||||
|
||||
@@ -161,8 +161,8 @@ class ContextSetVersion extends ProtocolRestriction, ProtocolUnrestriction, Data
|
||||
}
|
||||
}
|
||||
|
||||
class UnspecificSSLContextCreation extends SSLContextCreation, UnspecificContextCreation {
|
||||
UnspecificSSLContextCreation() { library instanceof Ssl }
|
||||
class UnspecificSslContextCreation extends SslContextCreation, UnspecificContextCreation {
|
||||
UnspecificSslContextCreation() { library instanceof Ssl }
|
||||
|
||||
override ProtocolVersion getUnrestriction() {
|
||||
result = UnspecificContextCreation.super.getUnrestriction() and
|
||||
@@ -172,7 +172,7 @@ class UnspecificSSLContextCreation extends SSLContextCreation, UnspecificContext
|
||||
}
|
||||
}
|
||||
|
||||
class UnspecificSSLDefaultContextCreation extends SSLDefaultContextCreation, ProtocolUnrestriction {
|
||||
class UnspecificSslDefaultContextCreation extends SslDefaultContextCreation, ProtocolUnrestriction {
|
||||
override DataFlow::Node getContext() { result = this }
|
||||
|
||||
// see https://docs.python.org/3/library/ssl.html#ssl.create_default_context
|
||||
@@ -195,10 +195,10 @@ class Ssl extends TlsLibrary {
|
||||
override API::Node version_constants() { result = API::moduleImport("ssl") }
|
||||
|
||||
override ContextCreation default_context_creation() {
|
||||
result instanceof SSLDefaultContextCreation
|
||||
result instanceof SslDefaultContextCreation
|
||||
}
|
||||
|
||||
override ContextCreation specific_context_creation() { result instanceof SSLContextCreation }
|
||||
override ContextCreation specific_context_creation() { result instanceof SslContextCreation }
|
||||
|
||||
override DataFlow::CallCfgNode insecure_connection_creation(ProtocolVersion version) {
|
||||
result = API::moduleImport("ssl").getMember("wrap_socket").getACall() and
|
||||
@@ -220,8 +220,8 @@ class Ssl extends TlsLibrary {
|
||||
or
|
||||
result instanceof ContextSetVersion
|
||||
or
|
||||
result instanceof UnspecificSSLContextCreation
|
||||
result instanceof UnspecificSslContextCreation
|
||||
or
|
||||
result instanceof UnspecificSSLDefaultContextCreation
|
||||
result instanceof UnspecificSslDefaultContextCreation
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
*/
|
||||
|
||||
import python
|
||||
import semmle.python.security.performance.SuperlinearBackTracking
|
||||
import semmle.python.security.regexp.SuperlinearBackTracking
|
||||
import semmle.python.security.dataflow.PolynomialReDoSQuery
|
||||
import DataFlow::PathGraph
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
*/
|
||||
|
||||
import python
|
||||
import semmle.python.security.performance.ExponentialBackTracking
|
||||
import semmle.python.security.regexp.ExponentialBackTracking
|
||||
|
||||
from RegExpTerm t, string pump, State s, string prefixMsg
|
||||
where
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
category: newQuery
|
||||
---
|
||||
* Added a new query, `py/suspicious-regexp-range`, to detect character ranges in regular expressions that seem to match
|
||||
too many characters.
|
||||
1
python/ql/src/change-notes/released/0.4.1.md
Normal file
1
python/ql/src/change-notes/released/0.4.1.md
Normal file
@@ -0,0 +1 @@
|
||||
## 0.4.1
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.4.0
|
||||
lastReleaseVersion: 0.4.1
|
||||
|
||||
@@ -17,8 +17,8 @@ import semmle.python.web.HttpRequest
|
||||
/* Sinks */
|
||||
import experimental.semmle.python.security.injection.XSLT
|
||||
|
||||
class XSLTInjectionConfiguration extends TaintTracking::Configuration {
|
||||
XSLTInjectionConfiguration() { this = "XSLT injection configuration" }
|
||||
class XsltInjectionConfiguration extends TaintTracking::Configuration {
|
||||
XsltInjectionConfiguration() { this = "XSLT injection configuration" }
|
||||
|
||||
deprecated override predicate isSource(TaintTracking::Source source) {
|
||||
source instanceof HttpRequestTaintSource
|
||||
@@ -29,7 +29,7 @@ class XSLTInjectionConfiguration extends TaintTracking::Configuration {
|
||||
}
|
||||
}
|
||||
|
||||
from XSLTInjectionConfiguration config, TaintedPathSource src, TaintedPathSink sink
|
||||
from XsltInjectionConfiguration config, TaintedPathSource src, TaintedPathSink sink
|
||||
where config.hasFlowPath(src, sink)
|
||||
select sink.getSink(), src, sink, "This XSLT query depends on $@.", src.getSource(),
|
||||
"a user-provided value"
|
||||
|
||||
@@ -2,7 +2,6 @@ private import python
|
||||
private import semmle.python.Concepts
|
||||
private import semmle.python.ApiGraphs
|
||||
private import semmle.python.dataflow.new.RemoteFlowSources
|
||||
private import semmle.python.dataflow.new.DataFlow
|
||||
|
||||
/**
|
||||
* A data flow source of the client ip obtained according to the remote endpoint identifier specified
|
||||
|
||||
@@ -182,7 +182,10 @@ module LdapBind {
|
||||
/**
|
||||
* Holds if the binding process use SSL.
|
||||
*/
|
||||
abstract predicate useSSL();
|
||||
abstract predicate useSsl();
|
||||
|
||||
/** DEPRECATED: Alias for useSsl */
|
||||
deprecated predicate useSSL() { useSsl() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,7 +216,10 @@ class LdapBind extends DataFlow::Node {
|
||||
/**
|
||||
* Holds if the binding process use SSL.
|
||||
*/
|
||||
predicate useSSL() { range.useSSL() }
|
||||
predicate useSsl() { range.useSsl() }
|
||||
|
||||
/** DEPRECATED: Alias for useSsl */
|
||||
deprecated predicate useSSL() { useSsl() }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for LdapBind */
|
||||
|
||||
@@ -12,13 +12,13 @@ private import semmle.python.ApiGraphs
|
||||
/**
|
||||
* Provides models for Python's ldap-related libraries.
|
||||
*/
|
||||
private module LDAP {
|
||||
private module Ldap {
|
||||
/**
|
||||
* Provides models for the `python-ldap` PyPI package (imported as `ldap`).
|
||||
*
|
||||
* See https://www.python-ldap.org/en/python-ldap-3.3.0/index.html
|
||||
*/
|
||||
private module LDAP2 {
|
||||
private module Ldap2 {
|
||||
/** Gets a reference to the `ldap` module. */
|
||||
API::Node ldap() { result = API::moduleImport("ldap") }
|
||||
|
||||
@@ -38,8 +38,8 @@ private module LDAP {
|
||||
*
|
||||
* See https://www.python-ldap.org/en/python-ldap-3.3.0/reference/ldap.html#functions
|
||||
*/
|
||||
private class LDAP2QueryMethods extends string {
|
||||
LDAP2QueryMethods() {
|
||||
private class Ldap2QueryMethods extends string {
|
||||
Ldap2QueryMethods() {
|
||||
this in ["search", "search_s", "search_st", "search_ext", "search_ext_s"]
|
||||
}
|
||||
}
|
||||
@@ -52,7 +52,7 @@ private module LDAP {
|
||||
/** Gets a reference to a `ldap` query. */
|
||||
private DataFlow::Node ldapQuery() {
|
||||
result = ldapOperation() and
|
||||
result.(DataFlow::AttrRead).getAttributeName() instanceof LDAP2QueryMethods
|
||||
result.(DataFlow::AttrRead).getAttributeName() instanceof Ldap2QueryMethods
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -60,8 +60,8 @@ private module LDAP {
|
||||
*
|
||||
* See `LDAP2QueryMethods`
|
||||
*/
|
||||
private class LDAP2Query extends DataFlow::CallCfgNode, LdapQuery::Range {
|
||||
LDAP2Query() { this.getFunction() = ldapQuery() }
|
||||
private class Ldap2Query extends DataFlow::CallCfgNode, LdapQuery::Range {
|
||||
Ldap2Query() { this.getFunction() = ldapQuery() }
|
||||
|
||||
override DataFlow::Node getQuery() {
|
||||
result in [this.getArg(0), this.getArg(2), this.getArgByName("filterstr")]
|
||||
@@ -73,8 +73,8 @@ private module LDAP {
|
||||
*
|
||||
* See https://www.python-ldap.org/en/python-ldap-3.3.0/reference/ldap.html#functions
|
||||
*/
|
||||
private class LDAP2BindMethods extends string {
|
||||
LDAP2BindMethods() {
|
||||
private class Ldap2BindMethods extends string {
|
||||
Ldap2BindMethods() {
|
||||
this in [
|
||||
"bind", "bind_s", "simple_bind", "simple_bind_s", "sasl_interactive_bind_s",
|
||||
"sasl_non_interactive_bind_s", "sasl_external_bind_s", "sasl_gssapi_bind_s"
|
||||
@@ -85,12 +85,12 @@ private module LDAP {
|
||||
/** Gets a reference to a `ldap` bind. */
|
||||
private DataFlow::Node ldapBind() {
|
||||
result = ldapOperation() and
|
||||
result.(DataFlow::AttrRead).getAttributeName() instanceof LDAP2BindMethods
|
||||
result.(DataFlow::AttrRead).getAttributeName() instanceof Ldap2BindMethods
|
||||
}
|
||||
|
||||
/**List of SSL-demanding options */
|
||||
private class LDAPSSLOptions extends DataFlow::Node {
|
||||
LDAPSSLOptions() {
|
||||
private class LdapSslOptions extends DataFlow::Node {
|
||||
LdapSslOptions() {
|
||||
this = ldap().getMember("OPT_X_TLS_" + ["DEMAND", "HARD"]).getAValueReachableFromSource()
|
||||
}
|
||||
}
|
||||
@@ -100,8 +100,8 @@ private module LDAP {
|
||||
*
|
||||
* See `LDAP2BindMethods`
|
||||
*/
|
||||
private class LDAP2Bind extends DataFlow::CallCfgNode, LdapBind::Range {
|
||||
LDAP2Bind() { this.getFunction() = ldapBind() }
|
||||
private class Ldap2Bind extends DataFlow::CallCfgNode, LdapBind::Range {
|
||||
Ldap2Bind() { this.getFunction() = ldapBind() }
|
||||
|
||||
override DataFlow::Node getPassword() {
|
||||
result in [this.getArg(1), this.getArgByName("cred")]
|
||||
@@ -115,11 +115,11 @@ private module LDAP {
|
||||
)
|
||||
}
|
||||
|
||||
override predicate useSSL() {
|
||||
override predicate useSsl() {
|
||||
// use initialize to correlate `this` and so avoid FP in several instances
|
||||
exists(DataFlow::CallCfgNode initialize |
|
||||
// ldap.set_option(ldap.OPT_X_TLS_%s)
|
||||
ldap().getMember("set_option").getACall().getArg(_) instanceof LDAPSSLOptions
|
||||
ldap().getMember("set_option").getACall().getArg(_) instanceof LdapSslOptions
|
||||
or
|
||||
this.getFunction().(DataFlow::AttrRead).getObject().getALocalSource() = initialize and
|
||||
initialize = ldapInitialize().getACall() and
|
||||
@@ -136,7 +136,7 @@ private module LDAP {
|
||||
setOption.getFunction().(DataFlow::AttrRead).getObject().getALocalSource() =
|
||||
initialize and
|
||||
setOption.getFunction().(DataFlow::AttrRead).getAttributeName() = "set_option" and
|
||||
setOption.getArg(0) instanceof LDAPSSLOptions and
|
||||
setOption.getArg(0) instanceof LdapSslOptions and
|
||||
not DataFlow::exprNode(any(False falseExpr))
|
||||
.(DataFlow::LocalSourceNode)
|
||||
.flowsTo(setOption.getArg(1))
|
||||
@@ -144,6 +144,9 @@ private module LDAP {
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for useSsl */
|
||||
deprecated override predicate useSSL() { this.useSsl() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -151,8 +154,8 @@ private module LDAP {
|
||||
*
|
||||
* See https://github.com/python-ldap/python-ldap/blob/7ce471e238cdd9a4dd8d17baccd1c9e05e6f894a/Lib/ldap/dn.py#L17
|
||||
*/
|
||||
private class LDAP2EscapeDNCall extends DataFlow::CallCfgNode, LdapEscape::Range {
|
||||
LDAP2EscapeDNCall() { this = ldap().getMember("dn").getMember("escape_dn_chars").getACall() }
|
||||
private class Ldap2EscapeDNCall extends DataFlow::CallCfgNode, LdapEscape::Range {
|
||||
Ldap2EscapeDNCall() { this = ldap().getMember("dn").getMember("escape_dn_chars").getACall() }
|
||||
|
||||
override DataFlow::Node getAnInput() { result = this.getArg(0) }
|
||||
}
|
||||
@@ -162,8 +165,8 @@ private module LDAP {
|
||||
*
|
||||
* See https://www.python-ldap.org/en/python-ldap-3.3.0/reference/ldap-filter.html#ldap.filter.escape_filter_chars
|
||||
*/
|
||||
private class LDAP2EscapeFilterCall extends DataFlow::CallCfgNode, LdapEscape::Range {
|
||||
LDAP2EscapeFilterCall() {
|
||||
private class Ldap2EscapeFilterCall extends DataFlow::CallCfgNode, LdapEscape::Range {
|
||||
Ldap2EscapeFilterCall() {
|
||||
this = ldap().getMember("filter").getMember("escape_filter_chars").getACall()
|
||||
}
|
||||
|
||||
@@ -176,7 +179,7 @@ private module LDAP {
|
||||
*
|
||||
* See https://pypi.org/project/ldap3/
|
||||
*/
|
||||
private module LDAP3 {
|
||||
private module Ldap3 {
|
||||
/** Gets a reference to the `ldap3` module. */
|
||||
API::Node ldap3() { result = API::moduleImport("ldap3") }
|
||||
|
||||
@@ -192,8 +195,8 @@ private module LDAP {
|
||||
/**
|
||||
* A class to find `ldap3` methods executing a query.
|
||||
*/
|
||||
private class LDAP3Query extends DataFlow::CallCfgNode, LdapQuery::Range {
|
||||
LDAP3Query() {
|
||||
private class Ldap3Query extends DataFlow::CallCfgNode, LdapQuery::Range {
|
||||
Ldap3Query() {
|
||||
this.getFunction().(DataFlow::AttrRead).getObject().getALocalSource() =
|
||||
ldap3Connection().getACall() and
|
||||
this.getFunction().(DataFlow::AttrRead).getAttributeName() = "search"
|
||||
@@ -205,8 +208,8 @@ private module LDAP {
|
||||
/**
|
||||
* A class to find `ldap3` methods binding a connection.
|
||||
*/
|
||||
class LDAP3Bind extends DataFlow::CallCfgNode, LdapBind::Range {
|
||||
LDAP3Bind() { this = ldap3Connection().getACall() }
|
||||
class Ldap3Bind extends DataFlow::CallCfgNode, LdapBind::Range {
|
||||
Ldap3Bind() { this = ldap3Connection().getACall() }
|
||||
|
||||
override DataFlow::Node getPassword() {
|
||||
result in [this.getArg(2), this.getArgByName("password")]
|
||||
@@ -220,7 +223,7 @@ private module LDAP {
|
||||
)
|
||||
}
|
||||
|
||||
override predicate useSSL() {
|
||||
override predicate useSsl() {
|
||||
exists(DataFlow::CallCfgNode serverCall |
|
||||
serverCall = ldap3Server().getACall() and
|
||||
this.getArg(0).getALocalSource() = serverCall and
|
||||
@@ -236,6 +239,9 @@ private module LDAP {
|
||||
startTLS.getObject().getALocalSource() = this
|
||||
)
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for useSsl */
|
||||
deprecated override predicate useSSL() { this.useSsl() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -243,8 +249,8 @@ private module LDAP {
|
||||
*
|
||||
* See https://github.com/cannatag/ldap3/blob/4d33166f0869b929f59c6e6825a1b9505eb99967/ldap3/utils/dn.py#L390
|
||||
*/
|
||||
private class LDAP3EscapeDNCall extends DataFlow::CallCfgNode, LdapEscape::Range {
|
||||
LDAP3EscapeDNCall() { this = ldap3Utils().getMember("dn").getMember("escape_rdn").getACall() }
|
||||
private class Ldap3EscapeDNCall extends DataFlow::CallCfgNode, LdapEscape::Range {
|
||||
Ldap3EscapeDNCall() { this = ldap3Utils().getMember("dn").getMember("escape_rdn").getACall() }
|
||||
|
||||
override DataFlow::Node getAnInput() { result = this.getArg(0) }
|
||||
}
|
||||
@@ -254,8 +260,8 @@ private module LDAP {
|
||||
*
|
||||
* See https://github.com/cannatag/ldap3/blob/4d33166f0869b929f59c6e6825a1b9505eb99967/ldap3/utils/conv.py#L91
|
||||
*/
|
||||
private class LDAP3EscapeFilterCall extends DataFlow::CallCfgNode, LdapEscape::Range {
|
||||
LDAP3EscapeFilterCall() {
|
||||
private class Ldap3EscapeFilterCall extends DataFlow::CallCfgNode, LdapEscape::Range {
|
||||
Ldap3EscapeFilterCall() {
|
||||
this = ldap3Utils().getMember("conv").getMember("escape_filter_chars").getACall()
|
||||
}
|
||||
|
||||
|
||||
@@ -122,7 +122,7 @@ class LdapInsecureAuthConfig extends TaintTracking::Configuration {
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(LdapBind ldapBind | not ldapBind.useSSL() and sink = ldapBind.getHost())
|
||||
exists(LdapBind ldapBind | not ldapBind.useSsl() and sink = ldapBind.getHost())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,12 +11,15 @@ import semmle.python.dataflow.TaintTracking
|
||||
import semmle.python.web.HttpRequest
|
||||
|
||||
/** Models XSLT Injection related classes and functions */
|
||||
module XSLTInjection {
|
||||
module XsltInjection {
|
||||
/** Returns a class value which refers to `lxml.etree` */
|
||||
Value etree() { result = Value::named("lxml.etree") }
|
||||
|
||||
/** A generic taint sink that is vulnerable to XSLT injection. */
|
||||
abstract class XSLTInjectionSink extends TaintSink { }
|
||||
abstract class XsltInjectionSink extends TaintSink { }
|
||||
|
||||
/** DEPRECATED: Alias for XsltInjectionSink */
|
||||
deprecated class XSLTInjectionSink = XsltInjectionSink;
|
||||
|
||||
/**
|
||||
* A kind of "taint", representing an untrusted XML string
|
||||
@@ -73,10 +76,10 @@ module XSLTInjection {
|
||||
* root = etree.XML("<xmlContent>")
|
||||
* find_text = etree.XSLT("`sink`")
|
||||
*/
|
||||
private class EtreeXSLTArgument extends XSLTInjectionSink {
|
||||
private class EtreeXsltArgument extends XsltInjectionSink {
|
||||
override string toString() { result = "lxml.etree.XSLT" }
|
||||
|
||||
EtreeXSLTArgument() {
|
||||
EtreeXsltArgument() {
|
||||
exists(CallNode call | call.getFunction().(AttrNode).getObject("XSLT").pointsTo(etree()) |
|
||||
call.getArg(0) = this
|
||||
)
|
||||
@@ -94,10 +97,10 @@ module XSLTInjection {
|
||||
* tree = etree.parse(f)
|
||||
* result_tree = tree.xslt(`sink`)
|
||||
*/
|
||||
private class ParseXSLTArgument extends XSLTInjectionSink {
|
||||
private class ParseXsltArgument extends XsltInjectionSink {
|
||||
override string toString() { result = "lxml.etree.parse.xslt" }
|
||||
|
||||
ParseXSLTArgument() {
|
||||
ParseXsltArgument() {
|
||||
exists(
|
||||
CallNode parseCall, CallNode xsltCall, ControlFlowNode obj, Variable var, AssignStmt assign
|
||||
|
|
||||
@@ -113,3 +116,6 @@ module XSLTInjection {
|
||||
override predicate sinks(TaintKind kind) { kind instanceof ExternalXmlKind }
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for XsltInjection */
|
||||
deprecated module XSLTInjection = XsltInjection;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/python-queries
|
||||
version: 0.4.1-dev
|
||||
version: 0.4.2-dev
|
||||
groups:
|
||||
- python
|
||||
- queries
|
||||
|
||||
Reference in New Issue
Block a user