Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit e5abfd0

Browse files
committed
Python: Modernise Security/ queries
1 parent 2802ac2 commit e5abfd0

16 files changed

Lines changed: 97 additions & 92 deletions

python/ql/src/Security/CWE-022/TarSlip.ql

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,21 +26,21 @@ class OpenTarFile extends TaintKind {
2626
name = "getmembers" and result.(SequenceKind).getItem() instanceof TarFileInfo
2727
}
2828

29-
override ClassValue getType() { result = Module::named("tarfile").attr("TarFile") }
29+
override ClassValue getType() { result = Value::named("tarfile.TarFile") }
3030

3131
override TaintKind getTaintForIteration() { result instanceof TarFileInfo }
3232
}
3333

3434
/** The source of open tarfile objects. That is, any call to `tarfile.open(...)` */
3535
class TarfileOpen extends TaintSource {
3636
TarfileOpen() {
37-
Module::named("tarfile").attr("open").getACall() = this and
37+
Value::named("tarfile.open").getACall() = this and
3838
/*
3939
* If argument refers to a string object, then it's a hardcoded path and
4040
* this tarfile is safe.
4141
*/
4242

43-
not this.(CallNode).getAnArg().refersTo(any(StringObject str)) and
43+
not this.(CallNode).getAnArg().pointsTo(Value::forString(_)) and
4444
/* Ignore opens within the tarfile module itself */
4545
not this.(ControlFlowNode).getLocation().getFile().getBaseName() = "tarfile.py"
4646
}

python/ql/src/Security/CWE-079/Jinja2WithoutEscaping.ql

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -12,36 +12,38 @@
1212

1313
import python
1414

15-
ClassObject jinja2EnvironmentOrTemplate() {
16-
exists(ModuleObject jinja2, string name |
17-
jinja2.getName() = "jinja2" and
18-
jinja2.attr(name) = result
19-
|
20-
name = "Environment" or
21-
name = "Template"
22-
)
15+
/* Jinja 2 Docs:
16+
* https://jinja.palletsprojects.com/en/2.11.x/api/#jinja2.Environment
17+
* https://jinja.palletsprojects.com/en/2.11.x/api/#jinja2.Template
18+
*
19+
* Although the docs doesn't say very clearly, autoescape is a valid arugment when constructing
20+
* a Template manually
21+
*
22+
* unsafe_tmpl = Template('Hello {{ name }}!')
23+
* safe1_tmpl = Template('Hello {{ name }}!', autoescape=True)
24+
*/
25+
26+
ClassValue jinja2EnvironmentOrTemplate() {
27+
28+
result = Value::named("jinja2.Environment")
29+
or
30+
result = Value::named("jinja2.Template")
2331
}
2432

2533
ControlFlowNode getAutoEscapeParameter(CallNode call) {
26-
exists(Object callable | call.getFunction().refersTo(callable) |
27-
callable = jinja2EnvironmentOrTemplate() and
28-
result = call.getArgByName("autoescape")
29-
)
34+
result = call.getArgByName("autoescape")
3035
}
3136

3237
from CallNode call
3338
where
39+
call.getFunction().pointsTo(jinja2EnvironmentOrTemplate()) and
3440
not exists(call.getNode().getStarargs()) and
3541
not exists(call.getNode().getKwargs()) and
3642
(
37-
not exists(getAutoEscapeParameter(call)) and
38-
exists(Object env |
39-
call.getFunction().refersTo(env) and
40-
env = jinja2EnvironmentOrTemplate()
41-
)
43+
not exists(getAutoEscapeParameter(call))
4244
or
43-
exists(Object isFalse |
44-
getAutoEscapeParameter(call).refersTo(isFalse) and isFalse.booleanValue() = false
45+
exists(Value isFalse |
46+
getAutoEscapeParameter(call).pointsTo(isFalse) and isFalse.booleanValue() = false
4547
)
4648
)
4749
select call, "Using jinja2 templates with autoescape=False can potentially allow XSS attacks."

python/ql/src/Security/CWE-089/SqlInjection.ql

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,10 @@ class SQLInjectionConfiguration extends TaintTracking::Configuration {
3030
override predicate isSink(TaintTracking::Sink sink) { sink instanceof SqlInjectionSink }
3131
}
3232

33-
/* Additional configuration to support tracking of DB objects. Connections, cursors, etc. */
33+
/* Additional configuration to support tracking of DB objects. Connections, cursors, etc.
34+
* Without this configuration (or the LegacyConfiguration), the pattern of
35+
* `any(MyTaintKind k).tains(control_flow_node)` used in DbConnectionExecuteArgument would not work.
36+
*/
3437
class DbConfiguration extends TaintTracking::Configuration {
3538
DbConfiguration() { this = "DB configuration" }
3639

python/ql/src/Security/CWE-215/FlaskDebug.ql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@
1313
import python
1414
import semmle.python.web.flask.General
1515

16-
from CallNode call, Object isTrue
16+
from CallNode call, Value isTrue
1717
where
1818
call = theFlaskClass().declaredAttribute("run").(FunctionValue).getACall() and
19-
call.getArgByName("debug").refersTo(isTrue) and
19+
call.getArgByName("debug").pointsTo(isTrue) and
2020
isTrue.booleanValue() = true
2121
select call,
2222
"A Flask app appears to be run in debug mode. This may allow an attacker to run arbitrary code through the debugger."

python/ql/src/Security/CWE-295/MissingHostKeyValidation.ql

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,26 +11,26 @@
1111

1212
import python
1313

14-
private ModuleObject theParamikoClientModule() { result = ModuleObject::named("paramiko.client") }
14+
private ModuleValue theParamikoClientModule() { result = Value::named("paramiko.client") }
1515

16-
private ClassObject theParamikoSSHClientClass() {
16+
private ClassValue theParamikoSSHClientClass() {
1717
result = theParamikoClientModule().attr("SSHClient")
1818
}
1919

20-
private ClassObject unsafe_paramiko_policy(string name) {
20+
private ClassValue unsafe_paramiko_policy(string name) {
2121
(name = "AutoAddPolicy" or name = "WarningPolicy") and
2222
result = theParamikoClientModule().attr(name)
2323
}
2424

2525
from CallNode call, ControlFlowNode arg, string name
2626
where
2727
call = theParamikoSSHClientClass()
28-
.lookupAttribute("set_missing_host_key_policy")
29-
.(FunctionObject)
28+
.lookup("set_missing_host_key_policy")
29+
.(FunctionValue)
3030
.getACall() and
3131
arg = call.getAnArg() and
3232
(
33-
arg.refersTo(unsafe_paramiko_policy(name)) or
34-
arg.refersTo(_, unsafe_paramiko_policy(name), _)
33+
arg.pointsTo(unsafe_paramiko_policy(name)) or
34+
arg.pointsTo().getClass() = unsafe_paramiko_policy(name)
3535
)
3636
select call, "Setting missing host key policy to " + name + " may be unsafe."

python/ql/src/Security/CWE-295/RequestWithoutValidation.ql

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,15 @@
1212
import python
1313
import semmle.python.web.Http
1414

15-
FunctionObject requestFunction() { result = ModuleObject::named("requests").attr(httpVerbLower()) }
15+
FunctionValue requestFunction() { result = Module::named("requests").attr(httpVerbLower()) }
1616

1717
/** requests treats None as the default and all other "falsey" values as False */
18-
predicate falseNotNone(Object o) { o.booleanValue() = false and not o = theNoneObject() }
18+
predicate falseNotNone(Value v) { v.booleanValue() = false and not v = Value::none_() }
1919

20-
from CallNode call, FunctionObject func, Object falsey, ControlFlowNode origin
20+
from CallNode call, FunctionValue func, Value falsey, ControlFlowNode origin
2121
where
2222
func = requestFunction() and
2323
func.getACall() = call and
2424
falseNotNone(falsey) and
25-
call.getArgByName("verify").refersTo(falsey, origin)
25+
call.getArgByName("verify").pointsTo(falsey, origin)
2626
select call, "Call to $@ with verify=$@", func, "requests." + func.getName(), origin, "False"

python/ql/src/Security/CWE-326/WeakCrypto.ql

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,60 +19,60 @@ int minimumSecureKeySize(string algo) {
1919
algo = "ECC" and result = 224
2020
}
2121

22-
predicate dsaRsaKeySizeArg(FunctionObject obj, string algorithm, string arg) {
23-
exists(ModuleObject mod | mod.attr(_) = obj |
22+
predicate dsaRsaKeySizeArg(FunctionValue func, string algorithm, string arg) {
23+
exists(ModuleValue mod | func = mod.attr(_) |
2424
algorithm = "DSA" and
2525
(
26-
mod.getName() = "cryptography.hazmat.primitives.asymmetric.dsa" and arg = "key_size"
26+
mod = Module::named("cryptography.hazmat.primitives.asymmetric.dsa") and arg = "key_size"
2727
or
28-
mod.getName() = "Crypto.PublicKey.DSA" and arg = "bits"
28+
mod = Module::named("Crypto.PublicKey.DSA") and arg = "bits"
2929
or
30-
mod.getName() = "Cryptodome.PublicKey.DSA" and arg = "bits"
30+
mod = Module::named("Cryptodome.PublicKey.DSA") and arg = "bits"
3131
)
3232
or
3333
algorithm = "RSA" and
3434
(
35-
mod.getName() = "cryptography.hazmat.primitives.asymmetric.rsa" and arg = "key_size"
35+
mod = Module::named("cryptography.hazmat.primitives.asymmetric.rsa") and arg = "key_size"
3636
or
37-
mod.getName() = "Crypto.PublicKey.RSA" and arg = "bits"
37+
mod = Module::named("Crypto.PublicKey.RSA") and arg = "bits"
3838
or
39-
mod.getName() = "Cryptodome.PublicKey.RSA" and arg = "bits"
39+
mod = Module::named("Cryptodome.PublicKey.RSA") and arg = "bits"
4040
)
4141
)
4242
}
4343

44-
predicate ecKeySizeArg(FunctionObject obj, string arg) {
45-
exists(ModuleObject mod | mod.attr(_) = obj |
46-
mod.getName() = "cryptography.hazmat.primitives.asymmetric.ec" and arg = "curve"
44+
predicate ecKeySizeArg(FunctionValue func, string arg) {
45+
exists(ModuleValue mod | func = mod.attr(_) |
46+
mod = Module::named("cryptography.hazmat.primitives.asymmetric.ec") and arg = "curve"
4747
)
4848
}
4949

50-
int keySizeFromCurve(ClassObject curveClass) {
51-
result = curveClass.declaredAttribute("key_size").(NumericObject).intValue()
50+
int keySizeFromCurve(ClassValue curveClass) {
51+
result = curveClass.declaredAttribute("key_size").(NumericValue).getIntValue()
5252
}
5353

5454
predicate algorithmAndKeysizeForCall(
5555
CallNode call, string algorithm, int keySize, ControlFlowNode keyOrigin
5656
) {
57-
exists(FunctionObject func, string argname, ControlFlowNode arg |
57+
exists(FunctionValue func, string argname, ControlFlowNode arg |
5858
arg = func.getNamedArgumentForCall(call, argname)
5959
|
60-
exists(NumericObject key |
61-
arg.refersTo(key, keyOrigin) and
60+
exists(NumericValue key |
61+
arg.pointsTo(key, keyOrigin) and
6262
dsaRsaKeySizeArg(func, algorithm, argname) and
63-
keySize = key.intValue()
63+
keySize = key.getIntValue()
6464
)
6565
or
66-
exists(ClassObject curve |
67-
arg.refersTo(_, curve, keyOrigin) and
68-
ecKeySizeArg(func, argname) and
66+
exists(ClassValue curveClass |
6967
algorithm = "ECC" and
70-
keySize = keySizeFromCurve(curve)
68+
ecKeySizeArg(func, argname) and
69+
arg.pointsTo(_, curveClass, keyOrigin) and
70+
keySize = keySizeFromCurve(curveClass)
7171
)
7272
)
7373
}
7474

75-
from CallNode call, ControlFlowNode origin, string algo, int keySize
75+
from CallNode call, string algo, int keySize, ControlFlowNode origin
7676
where
7777
algorithmAndKeysizeForCall(call, algo, keySize, origin) and
7878
keySize < minimumSecureKeySize(algo)

python/ql/src/Security/CWE-327/InsecureDefaultProtocol.ql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@
1212

1313
import python
1414

15-
FunctionObject ssl_wrap_socket() { result = ModuleObject::named("ssl").attr("wrap_socket") }
15+
FunctionValue ssl_wrap_socket() { result = Value::named("ssl.wrap_socket") }
1616

17-
ClassObject ssl_Context_class() { result = ModuleObject::named("ssl").attr("SSLContext") }
17+
ClassValue ssl_Context_class() { result = Value::named("ssl.SSLContext") }
1818

1919
CallNode unsafe_call(string method_name) {
2020
result = ssl_wrap_socket().getACall() and

python/ql/src/Security/CWE-327/InsecureProtocol.ql

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,17 @@
1111

1212
import python
1313

14-
FunctionObject ssl_wrap_socket() { result = the_ssl_module().attr("wrap_socket") }
14+
private ModuleValue the_ssl_module() { result = Module::named("ssl") }
1515

16-
ClassObject ssl_Context_class() { result = the_ssl_module().attr("SSLContext") }
16+
FunctionValue ssl_wrap_socket() { result = the_ssl_module().attr("wrap_socket") }
17+
18+
ClassValue ssl_Context_class() { result = the_ssl_module().attr("SSLContext") }
19+
20+
private ModuleValue the_pyOpenSSL_module() { result = Value::named("pyOpenSSL.SSL") }
21+
22+
ClassValue the_pyOpenSSL_Context_class() {
23+
result = Value::named("pyOpenSSL.SSL.Context")
24+
}
1725

1826
string insecure_version_name() {
1927
// For `pyOpenSSL.SSL`
@@ -29,10 +37,6 @@ string insecure_version_name() {
2937
result = "PROTOCOL_TLSv1"
3038
}
3139

32-
private ModuleObject the_ssl_module() { result = ModuleObject::named("ssl") }
33-
34-
private ModuleObject the_pyOpenSSL_module() { result = ModuleObject::named("pyOpenSSL.SSL") }
35-
3640
/*
3741
* A syntactic check for cases where points-to analysis cannot infer the presence of
3842
* a protocol constant, e.g. if it has been removed in later versions of the `ssl`
@@ -47,7 +51,7 @@ predicate probable_insecure_ssl_constant(
4751
arg = call.getArgByName(named_argument) or
4852
arg = call.getArg(0)
4953
|
50-
arg.(AttrNode).getObject(insecure_version).refersTo(the_ssl_module())
54+
arg.(AttrNode).getObject(insecure_version).pointsTo(the_ssl_module())
5155
or
5256
arg.(NameNode).getId() = insecure_version and
5357
exists(Import imp |
@@ -71,20 +75,16 @@ predicate unsafe_ssl_wrap_socket_call(
7175
) and
7276
insecure_version = insecure_version_name() and
7377
(
74-
call.getArgByName(named_argument).refersTo(the_ssl_module().attr(insecure_version))
78+
call.getArgByName(named_argument).pointsTo(the_ssl_module().attr(insecure_version))
7579
or
7680
probable_insecure_ssl_constant(call, insecure_version, named_argument)
7781
)
7882
}
7983

80-
ClassObject the_pyOpenSSL_Context_class() {
81-
result = ModuleObject::named("pyOpenSSL.SSL").attr("Context")
82-
}
83-
8484
predicate unsafe_pyOpenSSL_Context_call(CallNode call, string insecure_version) {
8585
call = the_pyOpenSSL_Context_class().getACall() and
8686
insecure_version = insecure_version_name() and
87-
call.getArg(0).refersTo(the_pyOpenSSL_module().attr(insecure_version))
87+
call.getArg(0).pointsTo(the_pyOpenSSL_module().attr(insecure_version))
8888
}
8989

9090
from CallNode call, string method_name, string insecure_version

python/ql/src/Security/CWE-377/InsecureTemporaryFile.ql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
import python
1414

15-
FunctionObject temporary_name_function(string mod, string function) {
15+
FunctionValue temporary_name_function(string mod, string function) {
1616
(
1717
mod = "tempfile" and function = "mktemp"
1818
or
@@ -23,7 +23,7 @@ FunctionObject temporary_name_function(string mod, string function) {
2323
function = "tempnam"
2424
)
2525
) and
26-
result = ModuleObject::named(mod).attr(function)
26+
result = Module::named(mod).attr(function)
2727
}
2828

2929
from Call c, string mod, string function

0 commit comments

Comments
 (0)