mirror of
https://github.com/github/codeql.git
synced 2026-04-28 18:25:24 +02:00
Merge branch 'main' into timing-attack-py
This commit is contained in:
@@ -1,3 +1,19 @@
|
||||
## 0.3.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* Contextual queries and the query libraries they depend on have been moved to the `codeql/python-all` package.
|
||||
|
||||
## 0.2.0
|
||||
|
||||
### Major Analysis Improvements
|
||||
|
||||
* Improved library modeling for the query "Request without certificate validation" (`py/request-without-cert-validation`), so it now also covers `httpx`, `aiohttp.client`, and `urllib3`.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The query "Use of a broken or weak cryptographic algorithm" (`py/weak-cryptographic-algorithm`) now reports if a cryptographic operation is potentially insecure due to use of a weak block mode.
|
||||
|
||||
## 0.1.4
|
||||
|
||||
## 0.1.3
|
||||
|
||||
@@ -53,7 +53,7 @@ predicate matchesBeginningOfString(RegExpTerm term) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the given sequence contains top-level domain preceded by a dot, such as `.com`,
|
||||
* Holds if the given sequence `seq` contains top-level domain preceded by a dot, such as `.com`,
|
||||
* excluding cases where this is at the very beginning of the regexp.
|
||||
*
|
||||
* `i` is bound to the index of the last child in the top-level domain part.
|
||||
|
||||
@@ -13,170 +13,10 @@
|
||||
*/
|
||||
|
||||
import python
|
||||
import semmle.python.security.Paths
|
||||
import semmle.python.dataflow.TaintTracking
|
||||
import semmle.python.security.strings.Basic
|
||||
import semmle.python.security.dataflow.TarSlipQuery
|
||||
import DataFlow::PathGraph
|
||||
|
||||
/** A TaintKind to represent open tarfile objects. That is, the result of calling `tarfile.open(...)` */
|
||||
class OpenTarFile extends TaintKind {
|
||||
OpenTarFile() { this = "tarfile.open" }
|
||||
|
||||
override TaintKind getTaintOfMethodResult(string name) {
|
||||
name = "getmember" and result instanceof TarFileInfo
|
||||
or
|
||||
name = "getmembers" and result.(SequenceKind).getItem() instanceof TarFileInfo
|
||||
}
|
||||
|
||||
override ClassValue getType() { result = Value::named("tarfile.TarFile") }
|
||||
|
||||
override TaintKind getTaintForIteration() { result instanceof TarFileInfo }
|
||||
}
|
||||
|
||||
/** The source of open tarfile objects. That is, any call to `tarfile.open(...)` */
|
||||
class TarfileOpen extends TaintSource {
|
||||
TarfileOpen() {
|
||||
Value::named("tarfile.open").getACall() = this and
|
||||
/*
|
||||
* If argument refers to a string object, then it's a hardcoded path and
|
||||
* this tarfile is safe.
|
||||
*/
|
||||
|
||||
not this.(CallNode).getAnArg().pointsTo(any(StringValue str)) and
|
||||
/* Ignore opens within the tarfile module itself */
|
||||
not this.(ControlFlowNode).getLocation().getFile().getBaseName() = "tarfile.py"
|
||||
}
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) { kind instanceof OpenTarFile }
|
||||
}
|
||||
|
||||
class TarFileInfo extends TaintKind {
|
||||
TarFileInfo() { this = "tarfile.entry" }
|
||||
|
||||
override TaintKind getTaintOfMethodResult(string name) { name = "next" and result = this }
|
||||
|
||||
override TaintKind getTaintOfAttribute(string name) {
|
||||
name = "name" and result instanceof TarFileInfo
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* For efficiency we don't want to track the flow of taint
|
||||
* around the tarfile module.
|
||||
*/
|
||||
|
||||
class ExcludeTarFilePy extends Sanitizer {
|
||||
ExcludeTarFilePy() { this = "Tar sanitizer" }
|
||||
|
||||
override predicate sanitizingNode(TaintKind taint, ControlFlowNode node) {
|
||||
node.getLocation().getFile().getBaseName() = "tarfile.py" and
|
||||
(
|
||||
taint instanceof OpenTarFile
|
||||
or
|
||||
taint instanceof TarFileInfo
|
||||
or
|
||||
taint.(SequenceKind).getItem() instanceof TarFileInfo
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/* Any call to an extractall method */
|
||||
class ExtractAllSink extends TaintSink {
|
||||
ExtractAllSink() {
|
||||
exists(CallNode call |
|
||||
this = call.getFunction().(AttrNode).getObject("extractall") and
|
||||
not exists(call.getAnArg())
|
||||
)
|
||||
}
|
||||
|
||||
override predicate sinks(TaintKind kind) { kind instanceof OpenTarFile }
|
||||
}
|
||||
|
||||
/* Argument to extract method */
|
||||
class ExtractSink extends TaintSink {
|
||||
CallNode call;
|
||||
|
||||
ExtractSink() {
|
||||
call.getFunction().(AttrNode).getName() = "extract" and
|
||||
this = call.getArg(0)
|
||||
}
|
||||
|
||||
override predicate sinks(TaintKind kind) { kind instanceof TarFileInfo }
|
||||
}
|
||||
|
||||
/* Members argument to extract method */
|
||||
class ExtractMembersSink extends TaintSink {
|
||||
CallNode call;
|
||||
|
||||
ExtractMembersSink() {
|
||||
call.getFunction().(AttrNode).getName() = "extractall" and
|
||||
(this = call.getArg(0) or this = call.getArgByName("members"))
|
||||
}
|
||||
|
||||
override predicate sinks(TaintKind kind) {
|
||||
kind.(SequenceKind).getItem() instanceof TarFileInfo
|
||||
or
|
||||
kind instanceof OpenTarFile
|
||||
}
|
||||
}
|
||||
|
||||
class TarFileInfoSanitizer extends Sanitizer {
|
||||
TarFileInfoSanitizer() { this = "TarInfo sanitizer" }
|
||||
|
||||
/* The test `if <path_sanitizing_test>:` clears taint on its `false` edge. */
|
||||
override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) {
|
||||
taint instanceof TarFileInfo and
|
||||
clears_taint_on_false_edge(test.getTest(), test.getSense())
|
||||
}
|
||||
|
||||
private predicate clears_taint_on_false_edge(ControlFlowNode test, boolean sense) {
|
||||
path_sanitizing_test(test) and
|
||||
sense = false
|
||||
or
|
||||
// handle `not` (also nested)
|
||||
test.(UnaryExprNode).getNode().getOp() instanceof Not and
|
||||
clears_taint_on_false_edge(test.(UnaryExprNode).getOperand(), sense.booleanNot())
|
||||
}
|
||||
}
|
||||
|
||||
private predicate path_sanitizing_test(ControlFlowNode test) {
|
||||
/* Assume that any test with "path" in it is a sanitizer */
|
||||
test.getAChild+().(AttrNode).getName().matches("%path")
|
||||
or
|
||||
test.getAChild+().(NameNode).getId().matches("%path")
|
||||
}
|
||||
|
||||
class TarSlipConfiguration extends TaintTracking::Configuration {
|
||||
TarSlipConfiguration() { this = "TarSlip configuration" }
|
||||
|
||||
override predicate isSource(TaintTracking::Source source) { source instanceof TarfileOpen }
|
||||
|
||||
override predicate isSink(TaintTracking::Sink sink) {
|
||||
sink instanceof ExtractSink or
|
||||
sink instanceof ExtractAllSink or
|
||||
sink instanceof ExtractMembersSink
|
||||
}
|
||||
|
||||
override predicate isSanitizer(Sanitizer sanitizer) {
|
||||
sanitizer instanceof TarFileInfoSanitizer
|
||||
or
|
||||
sanitizer instanceof ExcludeTarFilePy
|
||||
}
|
||||
|
||||
override predicate isBarrier(DataFlow::Node node) {
|
||||
// Avoid flow into the tarfile module
|
||||
exists(ParameterDefinition def |
|
||||
node.asVariable().getDefinition() = def
|
||||
or
|
||||
node.asCfgNode() = def.getDefiningNode()
|
||||
|
|
||||
def.getScope() = Value::named("tarfile.open").(CallableValue).getScope()
|
||||
or
|
||||
def.isSelf() and def.getScope().getEnclosingModule().getName() = "tarfile"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
from TarSlipConfiguration config, TaintedPathSource src, TaintedPathSink sink
|
||||
where config.hasFlowPath(src, sink)
|
||||
select sink.getSink(), src, sink, "Extraction of tarfile from $@", src.getSource(),
|
||||
from Configuration config, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where config.hasFlowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "Extraction of tarfile from $@", source.getNode(),
|
||||
"a potentially untrusted source"
|
||||
|
||||
@@ -42,7 +42,7 @@ where
|
||||
not exists(call.getArgByName("autoescape"))
|
||||
or
|
||||
call.getKeywordParameter("autoescape")
|
||||
.getAValueReachingRhs()
|
||||
.getAValueReachingSink()
|
||||
.asExpr()
|
||||
.(ImmutableLiteral)
|
||||
.booleanValue() = false
|
||||
|
||||
@@ -18,9 +18,9 @@ import semmle.python.dataflow.new.TaintTracking
|
||||
API::Node libPam() {
|
||||
exists(API::CallNode findLibCall, API::CallNode cdllCall |
|
||||
findLibCall = API::moduleImport("ctypes").getMember("util").getMember("find_library").getACall() and
|
||||
findLibCall.getParameter(0).getAValueReachingRhs().asExpr().(StrConst).getText() = "pam" and
|
||||
findLibCall.getParameter(0).getAValueReachingSink().asExpr().(StrConst).getText() = "pam" and
|
||||
cdllCall = API::moduleImport("ctypes").getMember("CDLL").getACall() and
|
||||
cdllCall.getParameter(0).getAValueReachingRhs() = findLibCall
|
||||
cdllCall.getParameter(0).getAValueReachingSink() = findLibCall
|
||||
|
|
||||
result = cdllCall.getReturn()
|
||||
)
|
||||
|
||||
@@ -29,7 +29,7 @@ where
|
||||
call = paramikoSSHClientInstance().getMember("set_missing_host_key_policy").getACall() and
|
||||
arg in [call.getArg(0), call.getArgByName("policy")] and
|
||||
(
|
||||
arg = unsafe_paramiko_policy(name).getAUse() or
|
||||
arg = unsafe_paramiko_policy(name).getReturn().getAUse()
|
||||
arg = unsafe_paramiko_policy(name).getAValueReachableFromSource() or
|
||||
arg = unsafe_paramiko_policy(name).getReturn().getAValueReachableFromSource()
|
||||
)
|
||||
select call, "Setting missing host key policy to " + name + " may be unsafe."
|
||||
|
||||
@@ -17,7 +17,8 @@ class PyOpenSSLContextCreation extends ContextCreation, DataFlow::CallCfgNode {
|
||||
protocolArg in [this.getArg(0), this.getArgByName("method")]
|
||||
|
|
||||
protocolArg in [
|
||||
pyo.specific_version(result).getAUse(), pyo.unspecific_version(result).getAUse()
|
||||
pyo.specific_version(result).getAValueReachableFromSource(),
|
||||
pyo.unspecific_version(result).getAValueReachableFromSource()
|
||||
]
|
||||
)
|
||||
}
|
||||
@@ -43,9 +44,10 @@ class SetOptionsCall extends ProtocolRestriction, DataFlow::CallCfgNode {
|
||||
}
|
||||
|
||||
override ProtocolVersion getRestriction() {
|
||||
API::moduleImport("OpenSSL").getMember("SSL").getMember("OP_NO_" + result).getAUse() in [
|
||||
this.getArg(0), this.getArgByName("options")
|
||||
]
|
||||
API::moduleImport("OpenSSL")
|
||||
.getMember("SSL")
|
||||
.getMember("OP_NO_" + result)
|
||||
.getAValueReachableFromSource() in [this.getArg(0), this.getArgByName("options")]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,10 @@ class SSLContextCreation extends ContextCreation, DataFlow::CallCfgNode {
|
||||
protocolArg in [this.getArg(0), this.getArgByName("protocol")]
|
||||
|
|
||||
protocolArg =
|
||||
[ssl.specific_version(result).getAUse(), ssl.unspecific_version(result).getAUse()]
|
||||
[
|
||||
ssl.specific_version(result).getAValueReachableFromSource(),
|
||||
ssl.unspecific_version(result).getAValueReachableFromSource()
|
||||
]
|
||||
)
|
||||
or
|
||||
not exists(this.getArg(_)) and
|
||||
@@ -54,7 +57,11 @@ class OptionsAugOr extends ProtocolRestriction, DataFlow::CfgNode {
|
||||
aa.getTarget() = attr.getNode() and
|
||||
attr.getName() = "options" and
|
||||
attr.getObject() = node and
|
||||
flag = API::moduleImport("ssl").getMember("OP_NO_" + restriction).getAUse().asExpr() and
|
||||
flag =
|
||||
API::moduleImport("ssl")
|
||||
.getMember("OP_NO_" + restriction)
|
||||
.getAValueReachableFromSource()
|
||||
.asExpr() and
|
||||
(
|
||||
aa.getValue() = flag
|
||||
or
|
||||
@@ -79,7 +86,11 @@ class OptionsAugAndNot extends ProtocolUnrestriction, DataFlow::CfgNode {
|
||||
attr.getObject() = node and
|
||||
notFlag.getOp() instanceof Invert and
|
||||
notFlag.getOperand() = flag and
|
||||
flag = API::moduleImport("ssl").getMember("OP_NO_" + restriction).getAUse().asExpr() and
|
||||
flag =
|
||||
API::moduleImport("ssl")
|
||||
.getMember("OP_NO_" + restriction)
|
||||
.getAValueReachableFromSource()
|
||||
.asExpr() and
|
||||
(
|
||||
aa.getValue() = notFlag
|
||||
or
|
||||
@@ -134,7 +145,10 @@ class ContextSetVersion extends ProtocolRestriction, ProtocolUnrestriction, Data
|
||||
this = aw.getObject() and
|
||||
aw.getAttributeName() = "minimum_version" and
|
||||
aw.getValue() =
|
||||
API::moduleImport("ssl").getMember("TLSVersion").getMember(restriction).getAUse()
|
||||
API::moduleImport("ssl")
|
||||
.getMember("TLSVersion")
|
||||
.getMember(restriction)
|
||||
.getAValueReachableFromSource()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -188,7 +202,8 @@ class Ssl extends TlsLibrary {
|
||||
|
||||
override DataFlow::CallCfgNode insecure_connection_creation(ProtocolVersion version) {
|
||||
result = API::moduleImport("ssl").getMember("wrap_socket").getACall() and
|
||||
this.specific_version(version).getAUse() = result.getArgByName("ssl_version") and
|
||||
this.specific_version(version).getAValueReachableFromSource() =
|
||||
result.getArgByName("ssl_version") and
|
||||
version.isInsecure()
|
||||
}
|
||||
|
||||
|
||||
@@ -36,13 +36,13 @@ string permissive_permission(int p) {
|
||||
|
||||
predicate chmod_call(API::CallNode call, string name, int mode) {
|
||||
call = API::moduleImport("os").getMember("chmod").getACall() and
|
||||
mode = call.getParameter(1, "mode").getAValueReachingRhs().asExpr().(IntegerLiteral).getValue() and
|
||||
mode = call.getParameter(1, "mode").getAValueReachingSink().asExpr().(IntegerLiteral).getValue() and
|
||||
name = "chmod"
|
||||
}
|
||||
|
||||
predicate open_call(API::CallNode call, string name, int mode) {
|
||||
call = API::moduleImport("os").getMember("open").getACall() and
|
||||
mode = call.getParameter(2, "mode").getAValueReachingRhs().asExpr().(IntegerLiteral).getValue() and
|
||||
mode = call.getParameter(2, "mode").getAValueReachingSink().asExpr().(IntegerLiteral).getValue() and
|
||||
name = "open"
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
import python
|
||||
import DefinitionTracking
|
||||
import analysis.DefinitionTracking
|
||||
|
||||
predicate uniqueness_error(int number, string what, string problem) {
|
||||
what in [
|
||||
|
||||
@@ -1,516 +0,0 @@
|
||||
/**
|
||||
* Definition tracking for jump-to-defn query.
|
||||
*/
|
||||
|
||||
import python
|
||||
import semmle.python.pointsto.PointsTo
|
||||
import IDEContextual
|
||||
|
||||
private newtype TDefinition =
|
||||
TLocalDefinition(AstNode a) { a instanceof Expr or a instanceof Stmt or a instanceof Module }
|
||||
|
||||
/** A definition for the purposes of jump-to-definition. */
|
||||
class Definition extends TLocalDefinition {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = "Definition " + this.getAstNode().getLocation().toString() }
|
||||
|
||||
AstNode getAstNode() { this = TLocalDefinition(result) }
|
||||
|
||||
Module getModule() { result = this.getAstNode().getScope().getEnclosingModule() }
|
||||
|
||||
Location getLocation() { result = this.getAstNode().getLocation() }
|
||||
}
|
||||
|
||||
private predicate jump_to_defn(ControlFlowNode use, Definition defn) {
|
||||
exists(EssaVariable var |
|
||||
use = var.getASourceUse() and
|
||||
ssa_variable_defn(var, defn)
|
||||
)
|
||||
or
|
||||
exists(string name |
|
||||
use.isLoad() and
|
||||
jump_to_defn_attribute(use.(AttrNode).getObject(name), name, defn)
|
||||
)
|
||||
or
|
||||
exists(PythonModuleObject mod |
|
||||
use.(ImportExprNode).refersTo(mod) and
|
||||
defn.getAstNode() = mod.getModule()
|
||||
)
|
||||
or
|
||||
exists(PythonModuleObject mod, string name |
|
||||
use.(ImportMemberNode).getModule(name).refersTo(mod) and
|
||||
scope_jump_to_defn_attribute(mod.getModule(), name, defn)
|
||||
)
|
||||
or
|
||||
exists(PackageObject package |
|
||||
use.(ImportExprNode).refersTo(package) and
|
||||
defn.getAstNode() = package.getInitModule().getModule()
|
||||
)
|
||||
or
|
||||
exists(PackageObject package, string name |
|
||||
use.(ImportMemberNode).getModule(name).refersTo(package) and
|
||||
scope_jump_to_defn_attribute(package.getInitModule().getModule(), name, defn)
|
||||
)
|
||||
or
|
||||
(use instanceof PyFunctionObject or use instanceof ClassObject) and
|
||||
defn.getAstNode() = use.getNode()
|
||||
}
|
||||
|
||||
/* Prefer class and functions to class-expressions and function-expressions. */
|
||||
private predicate preferred_jump_to_defn(Expr use, Definition def) {
|
||||
not use instanceof ClassExpr and
|
||||
not use instanceof FunctionExpr and
|
||||
jump_to_defn(use.getAFlowNode(), def)
|
||||
}
|
||||
|
||||
private predicate unique_jump_to_defn(Expr use, Definition def) {
|
||||
preferred_jump_to_defn(use, def) and
|
||||
not exists(Definition other |
|
||||
other != def and
|
||||
preferred_jump_to_defn(use, other)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate ssa_variable_defn(EssaVariable var, Definition defn) {
|
||||
ssa_defn_defn(var.getDefinition(), defn)
|
||||
}
|
||||
|
||||
/** Holds if the phi-function `phi` refers to (`value`, `cls`, `origin`) given the context `context`. */
|
||||
private predicate ssa_phi_defn(PhiFunction phi, Definition defn) {
|
||||
ssa_variable_defn(phi.getAnInput(), defn)
|
||||
}
|
||||
|
||||
/** Holds if the ESSA defn `def` refers to (`value`, `cls`, `origin`) given the context `context`. */
|
||||
private predicate ssa_defn_defn(EssaDefinition def, Definition defn) {
|
||||
ssa_phi_defn(def, defn)
|
||||
or
|
||||
ssa_node_defn(def, defn)
|
||||
or
|
||||
ssa_filter_defn(def, defn)
|
||||
or
|
||||
ssa_node_refinement_defn(def, defn)
|
||||
}
|
||||
|
||||
/** Holds if ESSA edge refinement, `def`, is defined by `defn` */
|
||||
predicate ssa_filter_defn(PyEdgeRefinement def, Definition defn) {
|
||||
ssa_variable_defn(def.getInput(), defn)
|
||||
}
|
||||
|
||||
/** Holds if ESSA defn, `uniphi`,is defined by `defn` */
|
||||
predicate uni_edged_phi_defn(SingleSuccessorGuard uniphi, Definition defn) {
|
||||
ssa_variable_defn(uniphi.getInput(), defn)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate ssa_node_defn(EssaNodeDefinition def, Definition defn) {
|
||||
assignment_jump_to_defn(def, defn)
|
||||
or
|
||||
parameter_defn(def, defn)
|
||||
or
|
||||
delete_defn(def, defn)
|
||||
or
|
||||
scope_entry_defn(def, defn)
|
||||
or
|
||||
implicit_submodule_defn(def, defn)
|
||||
}
|
||||
|
||||
/* Definition for normal assignments `def = ...` */
|
||||
private predicate assignment_jump_to_defn(AssignmentDefinition def, Definition defn) {
|
||||
defn = TLocalDefinition(def.getValue().getNode())
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate ssa_node_refinement_defn(EssaNodeRefinement def, Definition defn) {
|
||||
method_callsite_defn(def, defn)
|
||||
or
|
||||
import_star_defn(def, defn)
|
||||
or
|
||||
attribute_assignment_defn(def, defn)
|
||||
or
|
||||
callsite_defn(def, defn)
|
||||
or
|
||||
argument_defn(def, defn)
|
||||
or
|
||||
attribute_delete_defn(def, defn)
|
||||
or
|
||||
uni_edged_phi_defn(def, defn)
|
||||
}
|
||||
|
||||
/* Definition for parameter. `def foo(param): ...` */
|
||||
private predicate parameter_defn(ParameterDefinition def, Definition defn) {
|
||||
defn.getAstNode() = def.getDefiningNode().getNode()
|
||||
}
|
||||
|
||||
/* Definition for deletion: `del name` */
|
||||
private predicate delete_defn(DeletionDefinition def, Definition defn) { none() }
|
||||
|
||||
/* Implicit "defn" of the names of submodules at the start of an `__init__.py` file. */
|
||||
private predicate implicit_submodule_defn(ImplicitSubModuleDefinition def, Definition defn) {
|
||||
exists(PackageObject package, ModuleObject mod |
|
||||
package.getInitModule().getModule() = def.getDefiningNode().getScope() and
|
||||
mod = package.submodule(def.getSourceVariable().getName()) and
|
||||
defn.getAstNode() = mod.getModule()
|
||||
)
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper for scope_entry_value_transfer(...).
|
||||
* Transfer of values from the callsite to the callee, for enclosing variables, but not arguments/parameters
|
||||
*/
|
||||
|
||||
private predicate scope_entry_value_transfer_at_callsite(
|
||||
EssaVariable pred_var, ScopeEntryDefinition succ_def
|
||||
) {
|
||||
exists(CallNode callsite, FunctionObject f |
|
||||
f.getACall() = callsite and
|
||||
pred_var.getSourceVariable() = succ_def.getSourceVariable() and
|
||||
pred_var.getAUse() = callsite and
|
||||
succ_def.getDefiningNode() = f.getFunction().getEntryNode()
|
||||
)
|
||||
}
|
||||
|
||||
/* Model the transfer of values at scope-entry points. Transfer from `pred_var, pred_context` to `succ_def, succ_context` */
|
||||
private predicate scope_entry_value_transfer(EssaVariable pred_var, ScopeEntryDefinition succ_def) {
|
||||
BaseFlow::scope_entry_value_transfer_from_earlier(pred_var, _, succ_def, _)
|
||||
or
|
||||
scope_entry_value_transfer_at_callsite(pred_var, succ_def)
|
||||
or
|
||||
class_entry_value_transfer(pred_var, succ_def)
|
||||
}
|
||||
|
||||
/* Helper for scope_entry_value_transfer */
|
||||
private predicate class_entry_value_transfer(EssaVariable pred_var, ScopeEntryDefinition succ_def) {
|
||||
exists(ImportTimeScope scope, ControlFlowNode class_def |
|
||||
class_def = pred_var.getAUse() and
|
||||
scope.entryEdge(class_def, succ_def.getDefiningNode()) and
|
||||
pred_var.getSourceVariable() = succ_def.getSourceVariable()
|
||||
)
|
||||
}
|
||||
|
||||
/* Definition for implicit variable declarations at scope-entry. */
|
||||
pragma[noinline]
|
||||
private predicate scope_entry_defn(ScopeEntryDefinition def, Definition defn) {
|
||||
/* Transfer from another scope */
|
||||
exists(EssaVariable var |
|
||||
scope_entry_value_transfer(var, def) and
|
||||
ssa_variable_defn(var, defn)
|
||||
)
|
||||
}
|
||||
|
||||
/*
|
||||
* Definition for a variable (possibly) redefined by a call:
|
||||
* Just assume that call does not define variable
|
||||
*/
|
||||
|
||||
pragma[noinline]
|
||||
private predicate callsite_defn(CallsiteRefinement def, Definition defn) {
|
||||
ssa_variable_defn(def.getInput(), defn)
|
||||
}
|
||||
|
||||
/* Pass through for `self` for the implicit re-defn of `self` in `self.foo()` */
|
||||
private predicate method_callsite_defn(MethodCallsiteRefinement def, Definition defn) {
|
||||
/* The value of self remains the same, only the attributes may change */
|
||||
ssa_variable_defn(def.getInput(), defn)
|
||||
}
|
||||
|
||||
/** Helpers for import_star_defn */
|
||||
pragma[noinline]
|
||||
private predicate module_and_name_for_import_star(
|
||||
ModuleObject mod, string name, ImportStarRefinement def
|
||||
) {
|
||||
exists(ImportStarNode im_star |
|
||||
module_and_name_for_import_star_helper(mod, name, im_star, def) and
|
||||
mod.exports(name)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate module_and_name_for_import_star_helper(
|
||||
ModuleObject mod, string name, ImportStarNode im_star, ImportStarRefinement def
|
||||
) {
|
||||
im_star = def.getDefiningNode() and
|
||||
im_star.getModule().refersTo(mod) and
|
||||
name = def.getSourceVariable().getName()
|
||||
}
|
||||
|
||||
/** Holds if `def` is technically a defn of `var`, but the `from ... import *` does not in fact define `var` */
|
||||
pragma[noinline]
|
||||
private predicate variable_not_redefined_by_import_star(EssaVariable var, ImportStarRefinement def) {
|
||||
var = def.getInput() and
|
||||
exists(ModuleObject mod |
|
||||
def.getDefiningNode().(ImportStarNode).getModule().refersTo(mod) and
|
||||
not mod.exports(var.getSourceVariable().getName())
|
||||
)
|
||||
}
|
||||
|
||||
/* Definition for `from ... import *` */
|
||||
private predicate import_star_defn(ImportStarRefinement def, Definition defn) {
|
||||
exists(ModuleObject mod, string name | module_and_name_for_import_star(mod, name, def) |
|
||||
/* Attribute from imported module */
|
||||
scope_jump_to_defn_attribute(mod.getModule(), name, defn)
|
||||
)
|
||||
or
|
||||
exists(EssaVariable var |
|
||||
/* Retain value held before import */
|
||||
variable_not_redefined_by_import_star(var, def) and
|
||||
ssa_variable_defn(var, defn)
|
||||
)
|
||||
}
|
||||
|
||||
/** Attribute assignments have no effect as far as defn tracking is concerned */
|
||||
private predicate attribute_assignment_defn(AttributeAssignment def, Definition defn) {
|
||||
ssa_variable_defn(def.getInput(), defn)
|
||||
}
|
||||
|
||||
/** Ignore the effects of calls on their arguments. This is an approximation, but attempting to improve accuracy would be very expensive for very little gain. */
|
||||
private predicate argument_defn(ArgumentRefinement def, Definition defn) {
|
||||
ssa_variable_defn(def.getInput(), defn)
|
||||
}
|
||||
|
||||
/** Attribute deletions have no effect as far as value tracking is concerned. */
|
||||
pragma[noinline]
|
||||
private predicate attribute_delete_defn(EssaAttributeDeletion def, Definition defn) {
|
||||
ssa_variable_defn(def.getInput(), defn)
|
||||
}
|
||||
|
||||
/*
|
||||
* Definition flow for attributes. These mirror the "normal" defn predicates.
|
||||
* For each defn predicate `xxx_defn(XXX def, Definition defn)`
|
||||
* There is an equivalent predicate that tracks the values in attributes:
|
||||
* `xxx_jump_to_defn_attribute(XXX def, string name, Definition defn)`
|
||||
*/
|
||||
|
||||
/**
|
||||
* INTERNAL -- Public for testing only.
|
||||
* Holds if the attribute `name` of the ssa variable `var` refers to (`value`, `cls`, `origin`)
|
||||
*/
|
||||
predicate ssa_variable_jump_to_defn_attribute(EssaVariable var, string name, Definition defn) {
|
||||
ssa_defn_jump_to_defn_attribute(var.getDefinition(), name, defn)
|
||||
}
|
||||
|
||||
/** Helper for ssa_variable_jump_to_defn_attribute */
|
||||
private predicate ssa_defn_jump_to_defn_attribute(EssaDefinition def, string name, Definition defn) {
|
||||
ssa_phi_jump_to_defn_attribute(def, name, defn)
|
||||
or
|
||||
ssa_node_jump_to_defn_attribute(def, name, defn)
|
||||
or
|
||||
ssa_node_refinement_jump_to_defn_attribute(def, name, defn)
|
||||
or
|
||||
ssa_filter_jump_to_defn_attribute(def, name, defn)
|
||||
}
|
||||
|
||||
/** Holds if ESSA edge refinement, `def`, is defined by `defn` of `priority` */
|
||||
predicate ssa_filter_jump_to_defn_attribute(PyEdgeRefinement def, string name, Definition defn) {
|
||||
ssa_variable_jump_to_defn_attribute(def.getInput(), name, defn)
|
||||
}
|
||||
|
||||
/** Holds if the attribute `name` of the ssa phi-function defn `phi` refers to (`value`, `cls`, `origin`) */
|
||||
private predicate ssa_phi_jump_to_defn_attribute(PhiFunction phi, string name, Definition defn) {
|
||||
ssa_variable_jump_to_defn_attribute(phi.getAnInput(), name, defn)
|
||||
}
|
||||
|
||||
/** Helper for ssa_defn_jump_to_defn_attribute */
|
||||
pragma[noinline]
|
||||
private predicate ssa_node_jump_to_defn_attribute(
|
||||
EssaNodeDefinition def, string name, Definition defn
|
||||
) {
|
||||
assignment_jump_to_defn_attribute(def, name, defn)
|
||||
or
|
||||
self_parameter_jump_to_defn_attribute(def, name, defn)
|
||||
or
|
||||
scope_entry_jump_to_defn_attribute(def, name, defn)
|
||||
}
|
||||
|
||||
/** Helper for ssa_defn_jump_to_defn_attribute */
|
||||
pragma[noinline]
|
||||
private predicate ssa_node_refinement_jump_to_defn_attribute(
|
||||
EssaNodeRefinement def, string name, Definition defn
|
||||
) {
|
||||
attribute_assignment_jump_to_defn_attribute(def, name, defn)
|
||||
or
|
||||
argument_jump_to_defn_attribute(def, name, defn)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate scope_entry_jump_to_defn_attribute(
|
||||
ScopeEntryDefinition def, string name, Definition defn
|
||||
) {
|
||||
exists(EssaVariable var |
|
||||
scope_entry_value_transfer(var, def) and
|
||||
ssa_variable_jump_to_defn_attribute(var, name, defn)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate scope_jump_to_defn_attribute(ImportTimeScope s, string name, Definition defn) {
|
||||
exists(EssaVariable var |
|
||||
BaseFlow::reaches_exit(var) and
|
||||
var.getScope() = s and
|
||||
var.getName() = name
|
||||
|
|
||||
ssa_variable_defn(var, defn)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate jump_to_defn_attribute(ControlFlowNode use, string name, Definition defn) {
|
||||
/* Local attribute */
|
||||
exists(EssaVariable var |
|
||||
use = var.getASourceUse() and
|
||||
ssa_variable_jump_to_defn_attribute(var, name, defn)
|
||||
)
|
||||
or
|
||||
/* Instance attributes */
|
||||
exists(ClassObject cls | use.refersTo(_, cls, _) |
|
||||
scope_jump_to_defn_attribute(cls.getPyClass(), name, defn)
|
||||
)
|
||||
or
|
||||
/* Super attributes */
|
||||
exists(AttrNode f, SuperBoundMethod sbm, Object function |
|
||||
use = f.getObject(name) and
|
||||
f.refersTo(sbm) and
|
||||
function = sbm.getFunction(_) and
|
||||
function.getOrigin() = defn.getAstNode()
|
||||
)
|
||||
or
|
||||
/* Class or module attribute */
|
||||
exists(Object obj, Scope scope |
|
||||
use.refersTo(obj) and
|
||||
scope_jump_to_defn_attribute(scope, name, defn)
|
||||
|
|
||||
obj.(ClassObject).getPyClass() = scope
|
||||
or
|
||||
obj.(PythonModuleObject).getModule() = scope
|
||||
or
|
||||
obj.(PackageObject).getInitModule().getModule() = scope
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate assignment_jump_to_defn_attribute(
|
||||
AssignmentDefinition def, string name, Definition defn
|
||||
) {
|
||||
jump_to_defn_attribute(def.getValue(), name, defn)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate attribute_assignment_jump_to_defn_attribute(
|
||||
AttributeAssignment def, string name, Definition defn
|
||||
) {
|
||||
defn.getAstNode() = def.getDefiningNode().getNode() and name = def.getName()
|
||||
or
|
||||
ssa_variable_jump_to_defn_attribute(def.getInput(), name, defn) and not name = def.getName()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `def` defines the attribute `name`
|
||||
* `def` takes the form `setattr(use, "name")` where `use` is the input to the defn.
|
||||
*/
|
||||
private predicate sets_attribute(ArgumentRefinement def, string name) {
|
||||
exists(CallNode call |
|
||||
call = def.getDefiningNode() and
|
||||
call.getFunction().refersTo(Object::builtin("setattr")) and
|
||||
def.getInput().getAUse() = call.getArg(0) and
|
||||
call.getArg(1).getNode().(StrConst).getText() = name
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate argument_jump_to_defn_attribute(
|
||||
ArgumentRefinement def, string name, Definition defn
|
||||
) {
|
||||
if sets_attribute(def, name)
|
||||
then jump_to_defn(def.getDefiningNode().(CallNode).getArg(2), defn)
|
||||
else ssa_variable_jump_to_defn_attribute(def.getInput(), name, defn)
|
||||
}
|
||||
|
||||
/** Gets the (temporally) preceding variable for "self", e.g. `def` is in method foo() and `result` is in `__init__()`. */
|
||||
private EssaVariable preceding_self_variable(ParameterDefinition def) {
|
||||
def.isSelf() and
|
||||
exists(Function preceding, Function method |
|
||||
method = def.getScope() and
|
||||
// Only methods
|
||||
preceding.isMethod() and
|
||||
preceding.precedes(method) and
|
||||
BaseFlow::reaches_exit(result) and
|
||||
result.getSourceVariable().(Variable).isSelf() and
|
||||
result.getScope() = preceding
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate self_parameter_jump_to_defn_attribute(
|
||||
ParameterDefinition def, string name, Definition defn
|
||||
) {
|
||||
ssa_variable_jump_to_defn_attribute(preceding_self_variable(def), name, defn)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a definition for 'use'.
|
||||
* This exists primarily for testing use `getPreferredDefinition()` instead.
|
||||
*/
|
||||
Definition getADefinition(Expr use) {
|
||||
jump_to_defn(use.getAFlowNode(), result) and
|
||||
not use instanceof Call and
|
||||
not use.isArtificial() and
|
||||
// Not the use itself
|
||||
not result = TLocalDefinition(use)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the unique definition for 'use', if one can be found.
|
||||
* Helper for the jump-to-definition query.
|
||||
*/
|
||||
Definition getUniqueDefinition(Expr use) {
|
||||
unique_jump_to_defn(use, result) and
|
||||
not use instanceof Call and
|
||||
not use.isArtificial() and
|
||||
// Not the use itself
|
||||
not result = TLocalDefinition(use)
|
||||
}
|
||||
|
||||
/** A helper class to get suitable locations for attributes */
|
||||
class NiceLocationExpr extends Expr {
|
||||
/** Gets a textual representation of this element. */
|
||||
override string toString() { result = this.(Expr).toString() }
|
||||
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `bc` of line `bl` to
|
||||
* column `ec` of line `el` in file `f`.
|
||||
* For more information, see
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
predicate hasLocationInfo(string f, int bl, int bc, int el, int ec) {
|
||||
/* Attribute location for x.y is that of 'y' so that url does not overlap with that of 'x' */
|
||||
exists(int abl, int abc | this.(Attribute).getLocation().hasLocationInfo(f, abl, abc, el, ec) |
|
||||
bl = el and bc = ec - this.(Attribute).getName().length() + 1
|
||||
)
|
||||
or
|
||||
this.(Name).getLocation().hasLocationInfo(f, bl, bc, el, ec)
|
||||
or
|
||||
// Show xxx for `xxx` in `from xxx import y` or
|
||||
// for `import xxx` or for `import xxx as yyy`.
|
||||
this.(ImportExpr).getLocation().hasLocationInfo(f, bl, bc, el, ec)
|
||||
or
|
||||
/* Show y for `y` in `from xxx import y` */
|
||||
exists(string name |
|
||||
name = this.(ImportMember).getName() and
|
||||
this.(ImportMember).getLocation().hasLocationInfo(f, _, _, el, ec) and
|
||||
bl = el and
|
||||
bc = ec - name.length() + 1
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the definition (of kind `kind`) for the expression `use`, if one can be found.
|
||||
*/
|
||||
cached
|
||||
Definition definitionOf(NiceLocationExpr use, string kind) {
|
||||
exists(string f, int l |
|
||||
result = getUniqueDefinition(use) and
|
||||
kind = "Definition" and
|
||||
use.hasLocationInfo(f, l, _, _, _) and
|
||||
// Ignore if the definition is on the same line as the use
|
||||
not result.getLocation().hasLocationInfo(f, l, _, _, _)
|
||||
)
|
||||
}
|
||||
@@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
import python
|
||||
import DefinitionTracking
|
||||
import analysis.DefinitionTracking
|
||||
|
||||
from NiceLocationExpr use, Definition defn, string kind
|
||||
where defn = definitionOf(use, kind)
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
/**
|
||||
* Provides shared predicates related to contextual queries in the code viewer.
|
||||
*/
|
||||
|
||||
import semmle.files.FileSystem
|
||||
|
||||
/**
|
||||
* Returns the `File` matching the given source file name as encoded by the VS
|
||||
* Code extension.
|
||||
*/
|
||||
cached
|
||||
File getFileBySourceArchiveName(string name) {
|
||||
// The name provided for a file in the source archive by the VS Code extension
|
||||
// has some differences from the absolute path in the database:
|
||||
// 1. colons are replaced by underscores
|
||||
// 2. there's a leading slash, even for Windows paths: "C:/foo/bar" ->
|
||||
// "/C_/foo/bar"
|
||||
// 3. double slashes in UNC prefixes are replaced with a single slash
|
||||
// We can handle 2 and 3 together by unconditionally adding a leading slash
|
||||
// before replacing double slashes.
|
||||
name = ("/" + result.getAbsolutePath().replaceAll(":", "_")).replaceAll("//", "/")
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
/**
|
||||
* @name Jump-to-definition links
|
||||
* @description Generates use-definition pairs that provide the data
|
||||
* for jump-to-definition in the code viewer.
|
||||
* @kind definitions
|
||||
* @id py/ide-jump-to-definition
|
||||
* @tags ide-contextual-queries/local-definitions
|
||||
*/
|
||||
|
||||
import python
|
||||
import DefinitionTracking
|
||||
|
||||
external string selectedSourceFile();
|
||||
|
||||
from NiceLocationExpr use, Definition defn, string kind, string f
|
||||
where
|
||||
defn = definitionOf(use, kind) and
|
||||
use.hasLocationInfo(f, _, _, _, _) and
|
||||
getFileBySourceArchiveName(selectedSourceFile()).getAbsolutePath() = f
|
||||
select use, defn, kind
|
||||
@@ -1,19 +0,0 @@
|
||||
/**
|
||||
* @name Find-references links
|
||||
* @description Generates use-definition pairs that provide the data
|
||||
* for find-references in the code viewer.
|
||||
* @kind definitions
|
||||
* @id py/ide-find-references
|
||||
* @tags ide-contextual-queries/local-references
|
||||
*/
|
||||
|
||||
import python
|
||||
import DefinitionTracking
|
||||
|
||||
external string selectedSourceFile();
|
||||
|
||||
from NiceLocationExpr use, Definition defn, string kind
|
||||
where
|
||||
defn = definitionOf(use, kind) and
|
||||
defn.getLocation().getFile() = getFileBySourceArchiveName(selectedSourceFile())
|
||||
select use, defn, kind
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import python
|
||||
import DefinitionTracking
|
||||
import analysis.DefinitionTracking
|
||||
|
||||
predicate want_to_have_definition(Expr e) {
|
||||
/* not builtin object like len, tuple, etc. */
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The query "Use of a broken or weak cryptographic algorithm" (`py/weak-cryptographic-algorithm`) now report if a cryptographic operation is potentially insecure due to use of a weak block mode.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: majorAnalysis
|
||||
---
|
||||
* Improved library modeling for the query "Request without certificate validation" (`py/request-without-cert-validation`), so it now also covers `httpx`, `aiohttp.client`, and `urllib3`.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
category: breaking
|
||||
---
|
||||
* Contextual queries and the query libraries they depend on have been moved to the `codeql/python-all` package.
|
||||
|
||||
9
python/ql/src/change-notes/released/0.2.0.md
Normal file
9
python/ql/src/change-notes/released/0.2.0.md
Normal file
@@ -0,0 +1,9 @@
|
||||
## 0.2.0
|
||||
|
||||
### Major Analysis Improvements
|
||||
|
||||
* Improved library modeling for the query "Request without certificate validation" (`py/request-without-cert-validation`), so it now also covers `httpx`, `aiohttp.client`, and `urllib3`.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The query "Use of a broken or weak cryptographic algorithm" (`py/weak-cryptographic-algorithm`) now reports if a cryptographic operation is potentially insecure due to use of a weak block mode.
|
||||
5
python/ql/src/change-notes/released/0.3.0.md
Normal file
5
python/ql/src/change-notes/released/0.3.0.md
Normal file
@@ -0,0 +1,5 @@
|
||||
## 0.3.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* Contextual queries and the query libraries they depend on have been moved to the `codeql/python-all` package.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.1.4
|
||||
lastReleaseVersion: 0.3.0
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
blob_client = blob_service_client.get_blob_client(container=container_name, blob=blob_name)
|
||||
blob_client.require_encryption = True
|
||||
blob_client.key_encryption_key = kek
|
||||
# GOOD: Must use `encryption_version` set to `2.0`
|
||||
blob_client.encryption_version = '2.0' # Use Version 2.0!
|
||||
with open("decryptedcontentfile.txt", "rb") as stream:
|
||||
blob_client.upload_blob(stream, overwrite=OVERWRITE_EXISTING)
|
||||
@@ -0,0 +1,29 @@
|
||||
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>Azure Storage .NET, Java, and Python SDKs support encryption on the client with a customer-managed key that is maintained in Azure Key Vault or another key store.</p>
|
||||
<p>Current release versions of the Azure Storage SDKs use cipher block chaining (CBC mode) for client-side encryption (referred to as <code>v1</code>).</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>Consider switching to <code>v2</code> client-side encryption.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
|
||||
<sample src="UnsafeUsageOfClientSideEncryptionVersion.py" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
<li>
|
||||
<a href="http://aka.ms/azstorageclientencryptionblog">Azure Storage Client Encryption Blog.</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-30187">CVE-2022-30187</a>
|
||||
</li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* @name Unsafe usage of v1 version of Azure Storage client-side encryption.
|
||||
* @description Using version v1 of Azure Storage client-side encryption is insecure, and may enable an attacker to decrypt encrypted data
|
||||
* @kind problem
|
||||
* @tags security
|
||||
* cryptography
|
||||
* external/cwe/cwe-327
|
||||
* @id py/azure-storage/unsafe-client-side-encryption-in-use
|
||||
* @problem.severity error
|
||||
* @precision medium
|
||||
*/
|
||||
|
||||
import python
|
||||
import semmle.python.ApiGraphs
|
||||
|
||||
predicate isUnsafeClientSideAzureStorageEncryptionViaAttributes(Call call, AttrNode node) {
|
||||
exists(
|
||||
API::Node n, API::Node n2, Attribute a, AssignStmt astmt, API::Node uploadBlob,
|
||||
ControlFlowNode ctrlFlowNode, string s
|
||||
|
|
||||
s in ["key_encryption_key", "key_resolver_function"] and
|
||||
n =
|
||||
API::moduleImport("azure")
|
||||
.getMember("storage")
|
||||
.getMember("blob")
|
||||
.getMember("BlobClient")
|
||||
.getReturn()
|
||||
.getMember(s) and
|
||||
n2 =
|
||||
API::moduleImport("azure")
|
||||
.getMember("storage")
|
||||
.getMember("blob")
|
||||
.getMember("BlobClient")
|
||||
.getReturn()
|
||||
.getMember("upload_blob") and
|
||||
n.getAValueReachableFromSource().asExpr() = a and
|
||||
astmt.getATarget() = a and
|
||||
a.getAFlowNode() = node and
|
||||
uploadBlob =
|
||||
API::moduleImport("azure")
|
||||
.getMember("storage")
|
||||
.getMember("blob")
|
||||
.getMember("BlobClient")
|
||||
.getReturn()
|
||||
.getMember("upload_blob") and
|
||||
uploadBlob.getACall().asExpr() = call and
|
||||
ctrlFlowNode = call.getAFlowNode() and
|
||||
node.strictlyReaches(ctrlFlowNode) and
|
||||
node != ctrlFlowNode and
|
||||
not exists(
|
||||
AssignStmt astmt2, Attribute a2, AttrNode encryptionVersionSet, StrConst uc,
|
||||
API::Node encryptionVersion
|
||||
|
|
||||
uc = astmt2.getValue() and
|
||||
uc.getText() in ["'2.0'", "2.0"] and
|
||||
encryptionVersion =
|
||||
API::moduleImport("azure")
|
||||
.getMember("storage")
|
||||
.getMember("blob")
|
||||
.getMember("BlobClient")
|
||||
.getReturn()
|
||||
.getMember("encryption_version") and
|
||||
encryptionVersion.getAValueReachableFromSource().asExpr() = a2 and
|
||||
astmt2.getATarget() = a2 and
|
||||
a2.getAFlowNode() = encryptionVersionSet and
|
||||
encryptionVersionSet.strictlyReaches(ctrlFlowNode)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
predicate isUnsafeClientSideAzureStorageEncryptionViaObjectCreation(Call call, ControlFlowNode node) {
|
||||
exists(API::Node c, string s, Keyword k | k.getAFlowNode() = node |
|
||||
c.getACall().asExpr() = call and
|
||||
c = API::moduleImport("azure").getMember("storage").getMember("blob").getMember(s) and
|
||||
s in ["ContainerClient", "BlobClient", "BlobServiceClient"] and
|
||||
k.getArg() = "key_encryption_key" and
|
||||
k = call.getANamedArg() and
|
||||
not k.getValue() instanceof None and
|
||||
not exists(Keyword k2 | k2 = call.getANamedArg() |
|
||||
k2.getArg() = "encryption_version" and
|
||||
k2.getValue().(StrConst).getText() in ["'2.0'", "2.0"]
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
from Call call, ControlFlowNode node
|
||||
where
|
||||
isUnsafeClientSideAzureStorageEncryptionViaAttributes(call, node) or
|
||||
isUnsafeClientSideAzureStorageEncryptionViaObjectCreation(call, node)
|
||||
select node, "Unsafe usage of v1 version of Azure Storage client-side encryption."
|
||||
@@ -86,11 +86,13 @@ private module ExperimentalPrivateDjango {
|
||||
t.start() and
|
||||
(
|
||||
exists(SubscriptNode subscript |
|
||||
subscript.getObject() = baseClassRef().getReturn().getAUse().asCfgNode() and
|
||||
subscript.getObject() =
|
||||
baseClassRef().getReturn().getAValueReachableFromSource().asCfgNode() and
|
||||
result.asCfgNode() = subscript
|
||||
)
|
||||
or
|
||||
result.(DataFlow::AttrRead).getObject() = baseClassRef().getReturn().getAUse()
|
||||
result.(DataFlow::AttrRead).getObject() =
|
||||
baseClassRef().getReturn().getAValueReachableFromSource()
|
||||
)
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = headerInstance(t2).track(t2, t))
|
||||
|
||||
@@ -29,7 +29,11 @@ module ExperimentalFlask {
|
||||
|
||||
/** Gets a reference to a header instance. */
|
||||
private DataFlow::LocalSourceNode headerInstance() {
|
||||
result = [Flask::Response::classRef(), flaskMakeResponse()].getReturn().getAMember().getAUse()
|
||||
result =
|
||||
[Flask::Response::classRef(), flaskMakeResponse()]
|
||||
.getReturn()
|
||||
.getAMember()
|
||||
.getAValueReachableFromSource()
|
||||
}
|
||||
|
||||
/** Gets a reference to a header instance call/subscript */
|
||||
|
||||
@@ -90,7 +90,9 @@ private module LDAP {
|
||||
|
||||
/**List of SSL-demanding options */
|
||||
private class LDAPSSLOptions extends DataFlow::Node {
|
||||
LDAPSSLOptions() { this = ldap().getMember("OPT_X_TLS_" + ["DEMAND", "HARD"]).getAUse() }
|
||||
LDAPSSLOptions() {
|
||||
this = ldap().getMember("OPT_X_TLS_" + ["DEMAND", "HARD"]).getAValueReachableFromSource()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -50,11 +50,11 @@ private module NoSql {
|
||||
t.start() and
|
||||
(
|
||||
exists(SubscriptNode subscript |
|
||||
subscript.getObject() = mongoClientInstance().getAUse().asCfgNode() and
|
||||
subscript.getObject() = mongoClientInstance().getAValueReachableFromSource().asCfgNode() and
|
||||
result.asCfgNode() = subscript
|
||||
)
|
||||
or
|
||||
result.(DataFlow::AttrRead).getObject() = mongoClientInstance().getAUse()
|
||||
result.(DataFlow::AttrRead).getObject() = mongoClientInstance().getAValueReachableFromSource()
|
||||
or
|
||||
result = mongoEngine().getMember(["get_db", "connect"]).getACall()
|
||||
or
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
/**
|
||||
* @name Print AST
|
||||
* @description Outputs a representation of a file's Abstract Syntax Tree. This
|
||||
* query is used by the VS Code extension.
|
||||
* @id py/print-ast
|
||||
* @kind graph
|
||||
* @tags ide-contextual-queries/print-ast
|
||||
*/
|
||||
|
||||
import python
|
||||
import semmle.python.PrintAst
|
||||
import analysis.DefinitionTracking
|
||||
|
||||
/**
|
||||
* Gets the source file that will be used to generate the AST.
|
||||
*/
|
||||
external string selectedSourceFile();
|
||||
|
||||
class PrintAstConfigurationOverride extends PrintAstConfiguration {
|
||||
/**
|
||||
* Holds if the location matches the selected file in the VS Code extension and
|
||||
* the element is not a synthetic constructor.
|
||||
*/
|
||||
override predicate shouldPrint(AstNode e, Location l) {
|
||||
super.shouldPrint(e, l) and
|
||||
l.getFile() = getFileBySourceArchiveName(selectedSourceFile())
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/python-queries
|
||||
version: 0.2.0-dev
|
||||
version: 0.3.1-dev
|
||||
groups:
|
||||
- python
|
||||
- queries
|
||||
|
||||
Reference in New Issue
Block a user