mirror of
https://github.com/github/codeql.git
synced 2026-04-25 16:55:19 +02:00
Initial merge from main
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
---
|
||||
category: breaking
|
||||
---
|
||||
* Deleted the deprecated `explorationLimit` predicate from `DataFlow::Configuration`, use `FlowExploration<explorationLimit>` instead.
|
||||
* Deleted the deprecated `semmle.python.RegexTreeView` module, use `semmle.python.regexp.RegexTreeView` instead.
|
||||
* Deleted the deprecated `RegexString` class from `regex.qll`.
|
||||
* Deleted the deprecated `Regex` class, use `RegExp` instead.
|
||||
* Deleted the deprecated `semmle/python/security/SQL.qll` file.
|
||||
* Deleted the deprecated `useSSL` predicates from the LDAP libraries, use `useSsl` instead.
|
||||
@@ -1,6 +0,0 @@
|
||||
/**
|
||||
* Deprecated. Use `semmle.python.regexp.RegexTreeView` instead.
|
||||
*/
|
||||
|
||||
deprecated import regexp.RegexTreeView as Dep
|
||||
import Dep
|
||||
@@ -168,14 +168,6 @@ abstract deprecated class Configuration extends string {
|
||||
*/
|
||||
predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `FlowExploration<explorationLimit>` instead.
|
||||
*
|
||||
* Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev`
|
||||
* measured in approximate number of interprocedural steps.
|
||||
*/
|
||||
deprecated int explorationLimit() { none() }
|
||||
|
||||
/**
|
||||
* Holds if hidden nodes should be included in the data flow graph.
|
||||
*
|
||||
|
||||
@@ -168,14 +168,6 @@ abstract deprecated class Configuration extends string {
|
||||
*/
|
||||
predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `FlowExploration<explorationLimit>` instead.
|
||||
*
|
||||
* Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev`
|
||||
* measured in approximate number of interprocedural steps.
|
||||
*/
|
||||
deprecated int explorationLimit() { none() }
|
||||
|
||||
/**
|
||||
* Holds if hidden nodes should be included in the data flow graph.
|
||||
*
|
||||
|
||||
@@ -168,14 +168,6 @@ abstract deprecated class Configuration extends string {
|
||||
*/
|
||||
predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `FlowExploration<explorationLimit>` instead.
|
||||
*
|
||||
* Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev`
|
||||
* measured in approximate number of interprocedural steps.
|
||||
*/
|
||||
deprecated int explorationLimit() { none() }
|
||||
|
||||
/**
|
||||
* Holds if hidden nodes should be included in the data flow graph.
|
||||
*
|
||||
|
||||
@@ -168,14 +168,6 @@ abstract deprecated class Configuration extends string {
|
||||
*/
|
||||
predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `FlowExploration<explorationLimit>` instead.
|
||||
*
|
||||
* Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev`
|
||||
* measured in approximate number of interprocedural steps.
|
||||
*/
|
||||
deprecated int explorationLimit() { none() }
|
||||
|
||||
/**
|
||||
* Holds if hidden nodes should be included in the data flow graph.
|
||||
*
|
||||
|
||||
@@ -664,14 +664,6 @@ module DataFlow {
|
||||
}
|
||||
}
|
||||
|
||||
deprecated private class DataFlowType extends TaintKind {
|
||||
// this only exists to avoid an empty recursion error in the type checker
|
||||
DataFlowType() {
|
||||
this = "Data flow" and
|
||||
1 = 2
|
||||
}
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate dict_construct(ControlFlowNode itemnode, ControlFlowNode dictnode) {
|
||||
dictnode.(DictNode).getAValue() = itemnode
|
||||
|
||||
@@ -14,8 +14,3 @@ RegExpTerm getTermForExecution(Concepts::RegexExecution exec) {
|
||||
result.isRootTerm()
|
||||
)
|
||||
}
|
||||
|
||||
/** A StringLiteral used as a regular expression */
|
||||
deprecated class RegexString extends Regex {
|
||||
RegexString() { this = RegExpTracking::regExpSource(_).asExpr() }
|
||||
}
|
||||
|
||||
@@ -100,11 +100,6 @@ private module FindRegexMode {
|
||||
private string mode_from_node(DataFlow::Node node) { node = re_flag_tracker(result) }
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `RegExp` instead.
|
||||
*/
|
||||
deprecated class Regex = RegExp;
|
||||
|
||||
/** A StringLiteral used as a regular expression */
|
||||
class RegExp extends Expr instanceof StringLiteral {
|
||||
DataFlow::Node use;
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
import python
|
||||
import semmle.python.dataflow.TaintTracking
|
||||
|
||||
abstract deprecated class SqlInjectionSink extends TaintSink { }
|
||||
@@ -41,7 +41,9 @@ module CleartextLogging {
|
||||
*/
|
||||
class SensitiveDataSourceAsSource extends Source, SensitiveDataSource {
|
||||
SensitiveDataSourceAsSource() {
|
||||
not SensitiveDataSource.super.getClassification() = SensitiveDataClassification::id()
|
||||
not SensitiveDataSource.super.getClassification() in [
|
||||
SensitiveDataClassification::id(), SensitiveDataClassification::certificate()
|
||||
]
|
||||
}
|
||||
|
||||
override SensitiveDataClassification getClassification() {
|
||||
|
||||
@@ -40,7 +40,9 @@ module CleartextStorage {
|
||||
*/
|
||||
class SensitiveDataSourceAsSource extends Source, SensitiveDataSource {
|
||||
SensitiveDataSourceAsSource() {
|
||||
not SensitiveDataSource.super.getClassification() = SensitiveDataClassification::id()
|
||||
not SensitiveDataSource.super.getClassification() in [
|
||||
SensitiveDataClassification::id(), SensitiveDataClassification::certificate()
|
||||
]
|
||||
}
|
||||
|
||||
override SensitiveDataClassification getClassification() {
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The `py/clear-text-logging-sensitive-data` and `py/clear-text-storage-sensitive-data` queries have been updated to exclude the `certificate` classification of sensitive sources, which often do not contain sensitive data.
|
||||
9
python/ql/src/experimental/Security/CWE-346/CorsBad.py
Normal file
9
python/ql/src/experimental/Security/CWE-346/CorsBad.py
Normal file
@@ -0,0 +1,9 @@
|
||||
import cherrypy
|
||||
|
||||
def bad():
|
||||
request = cherrypy.request
|
||||
validCors = "domain.com"
|
||||
if request.method in ['POST', 'PUT', 'PATCH', 'DELETE']:
|
||||
origin = request.headers.get('Origin', None)
|
||||
if origin.startswith(validCors):
|
||||
print("Origin Valid")
|
||||
28
python/ql/src/experimental/Security/CWE-346/CorsBypass.qhelp
Normal file
28
python/ql/src/experimental/Security/CWE-346/CorsBypass.qhelp
Normal file
@@ -0,0 +1,28 @@
|
||||
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>Cross-origin resource sharing policy may be bypassed due to incorrect checks like the <code>string.startswith</code> call.</p>
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>Use a more stronger check to test for CORS policy bypass.</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>Most Python frameworks provide a mechanism for testing origins and performing CORS checks.
|
||||
For example, consider the code snippet below, <code>origin</code> is compared using a <code>
|
||||
startswith</code> call against a list of whitelisted origins. This check can be bypassed
|
||||
easily by origin like <code>domain.com.baddomain.com</code>
|
||||
</p>
|
||||
<sample src="CorsBad.py" />
|
||||
<p>This can be prevented by comparing the origin in a manner shown below.
|
||||
</p>
|
||||
<sample src="CorsGood.py" />
|
||||
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>PortsSwigger : <a href="https://portswigger.net/web-security/cors"></a>Cross-origin resource
|
||||
sharing (CORS)</li>
|
||||
<li>Related CVE: <a href="https://github.com/advisories/GHSA-824x-jcxf-hpfg">CVE-2022-3457</a>.</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
97
python/ql/src/experimental/Security/CWE-346/CorsBypass.ql
Normal file
97
python/ql/src/experimental/Security/CWE-346/CorsBypass.ql
Normal file
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* @name Cross Origin Resource Sharing(CORS) Policy Bypass
|
||||
* @description Checking user supplied origin headers using weak comparators like 'string.startswith' may lead to CORS policy bypass.
|
||||
* @kind path-problem
|
||||
* @problem.severity warning
|
||||
* @id py/cors-bypass
|
||||
* @tags security
|
||||
* externa/cwe/CWE-346
|
||||
*/
|
||||
|
||||
import python
|
||||
import semmle.python.ApiGraphs
|
||||
import semmle.python.dataflow.new.TaintTracking
|
||||
import semmle.python.Flow
|
||||
import semmle.python.dataflow.new.RemoteFlowSources
|
||||
|
||||
/**
|
||||
* Returns true if the control flow node may be useful in the current context.
|
||||
*
|
||||
* Ideally for more completeness, we should alert on every `startswith` call and every remote flow source which gets partailly checked. But, as this can lead to lots of FPs, we apply heuristics to filter some calls. This predicate provides logic for this filteration.
|
||||
*/
|
||||
private predicate maybeInteresting(ControlFlowNode c) {
|
||||
// Check if the name of the variable which calls the function matches the heuristic.
|
||||
// This would typically occur at the sink.
|
||||
// This should deal with cases like
|
||||
// `origin.startswith("bla")`
|
||||
heuristics(c.(CallNode).getFunction().(AttrNode).getObject().(NameNode).getId())
|
||||
or
|
||||
// Check if the name of the variable passed as an argument to the functions matches the heuristic. This would typically occur at the sink.
|
||||
// This should deal with cases like
|
||||
// `bla.startswith(origin)`
|
||||
heuristics(c.(CallNode).getArg(0).(NameNode).getId())
|
||||
or
|
||||
// Check if the value gets written to any interesting variable. This would typically occur at the source.
|
||||
// This should deal with cases like
|
||||
// `origin = request.headers.get('My-custom-header')`
|
||||
exists(Variable v | heuristics(v.getId()) | c.getASuccessor*().getNode() = v.getAStore())
|
||||
}
|
||||
|
||||
private class StringStartswithCall extends ControlFlowNode {
|
||||
StringStartswithCall() { this.(CallNode).getFunction().(AttrNode).getName() = "startswith" }
|
||||
}
|
||||
|
||||
bindingset[s]
|
||||
predicate heuristics(string s) { s.matches(["%origin%", "%cors%"]) }
|
||||
|
||||
/**
|
||||
* A member of the `cherrypy.request` class taken as a `RemoteFlowSource`.
|
||||
*/
|
||||
class CherryPyRequest extends RemoteFlowSource::Range {
|
||||
CherryPyRequest() {
|
||||
this =
|
||||
API::moduleImport("cherrypy")
|
||||
.getMember("request")
|
||||
.getMember([
|
||||
"charset", "content_type", "filename", "fp", "name", "params", "headers", "length",
|
||||
])
|
||||
.asSource()
|
||||
}
|
||||
|
||||
override string getSourceType() { result = "cherrypy.request" }
|
||||
}
|
||||
|
||||
module CorsBypassConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node node) { node instanceof RemoteFlowSource }
|
||||
|
||||
predicate isSink(DataFlow::Node node) {
|
||||
exists(StringStartswithCall s |
|
||||
node.asCfgNode() = s.(CallNode).getArg(0) or
|
||||
node.asCfgNode() = s.(CallNode).getFunction().(AttrNode).getObject()
|
||||
)
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
exists(API::CallNode c, API::Node n |
|
||||
n = API::moduleImport("cherrypy").getMember("request").getMember("headers") and
|
||||
c = n.getMember("get").getACall()
|
||||
|
|
||||
c.getReturn().asSource() = node2 and n.asSource() = node1
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module CorsFlow = TaintTracking::Global<CorsBypassConfig>;
|
||||
|
||||
import CorsFlow::PathGraph
|
||||
|
||||
from CorsFlow::PathNode source, CorsFlow::PathNode sink
|
||||
where
|
||||
CorsFlow::flowPath(source, sink) and
|
||||
(
|
||||
maybeInteresting(source.getNode().asCfgNode())
|
||||
or
|
||||
maybeInteresting(sink.getNode().asCfgNode())
|
||||
)
|
||||
select sink, source, sink,
|
||||
"Potentially incorrect string comparison which could lead to a CORS bypass."
|
||||
9
python/ql/src/experimental/Security/CWE-346/CorsGood.py
Normal file
9
python/ql/src/experimental/Security/CWE-346/CorsGood.py
Normal file
@@ -0,0 +1,9 @@
|
||||
import cherrypy
|
||||
|
||||
def good():
|
||||
request = cherrypy.request
|
||||
validOrigin = "domain.com"
|
||||
if request.method in ['POST', 'PUT', 'PATCH', 'DELETE']:
|
||||
origin = request.headers.get('Origin', None)
|
||||
if origin == validOrigin:
|
||||
print("Origin Valid")
|
||||
@@ -188,9 +188,6 @@ module LdapBind {
|
||||
* Holds if the binding process use SSL.
|
||||
*/
|
||||
abstract predicate useSsl();
|
||||
|
||||
/** DEPRECATED: Alias for useSsl */
|
||||
deprecated predicate useSSL() { this.useSsl() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,9 +212,6 @@ class LdapBind extends DataFlow::Node instanceof LdapBind::Range {
|
||||
* Holds if the binding process use SSL.
|
||||
*/
|
||||
predicate useSsl() { super.useSsl() }
|
||||
|
||||
/** DEPRECATED: Alias for useSsl */
|
||||
deprecated predicate useSSL() { this.useSsl() }
|
||||
}
|
||||
|
||||
/** Provides classes for modeling SQL sanitization libraries. */
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import cherrypy
|
||||
|
||||
def bad():
|
||||
request = cherrypy.request
|
||||
validCors = "domain.com"
|
||||
if request.method in ['POST', 'PUT', 'PATCH', 'DELETE']:
|
||||
origin = request.headers.get('Origin', None)
|
||||
if origin.startswith(validCors):
|
||||
print("Origin Valid")
|
||||
|
||||
def good():
|
||||
request = cherrypy.request
|
||||
validOrigin = "domain.com"
|
||||
if request.method in ['POST', 'PUT', 'PATCH', 'DELETE']:
|
||||
origin = request.headers.get('Origin', None)
|
||||
if origin == validOrigin:
|
||||
print("Origin Valid")
|
||||
@@ -0,0 +1,13 @@
|
||||
edges
|
||||
| Cors.py:7:9:7:14 | ControlFlowNode for origin | Cors.py:8:12:8:17 | ControlFlowNode for origin | provenance | |
|
||||
| Cors.py:7:18:7:32 | ControlFlowNode for Attribute | Cors.py:7:18:7:52 | ControlFlowNode for Attribute() | provenance | Config |
|
||||
| Cors.py:7:18:7:32 | ControlFlowNode for Attribute | Cors.py:7:18:7:52 | ControlFlowNode for Attribute() | provenance | dict.get |
|
||||
| Cors.py:7:18:7:52 | ControlFlowNode for Attribute() | Cors.py:7:9:7:14 | ControlFlowNode for origin | provenance | |
|
||||
nodes
|
||||
| Cors.py:7:9:7:14 | ControlFlowNode for origin | semmle.label | ControlFlowNode for origin |
|
||||
| Cors.py:7:18:7:32 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||
| Cors.py:7:18:7:52 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| Cors.py:8:12:8:17 | ControlFlowNode for origin | semmle.label | ControlFlowNode for origin |
|
||||
subpaths
|
||||
#select
|
||||
| Cors.py:8:12:8:17 | ControlFlowNode for origin | Cors.py:7:18:7:32 | ControlFlowNode for Attribute | Cors.py:8:12:8:17 | ControlFlowNode for origin | Potentially incorrect string comparison which could lead to a CORS bypass. |
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Security/CWE-346/CorsBypass.ql
|
||||
@@ -16,7 +16,6 @@ edges
|
||||
| testapp/orm_security_tests.py:42:13:42:18 | ControlFlowNode for person [Attribute name] | testapp/orm_security_tests.py:43:49:43:54 | ControlFlowNode for person [Attribute name] | provenance | |
|
||||
| testapp/orm_security_tests.py:42:23:42:42 | ControlFlowNode for Attribute() [List element, Attribute age] | testapp/orm_security_tests.py:42:13:42:18 | ControlFlowNode for person [Attribute age] | provenance | |
|
||||
| testapp/orm_security_tests.py:42:23:42:42 | ControlFlowNode for Attribute() [List element, Attribute name] | testapp/orm_security_tests.py:42:13:42:18 | ControlFlowNode for person [Attribute name] | provenance | |
|
||||
| testapp/orm_security_tests.py:43:13:43:21 | ControlFlowNode for resp_text | testapp/orm_security_tests.py:43:13:43:21 | ControlFlowNode for resp_text | provenance | |
|
||||
| testapp/orm_security_tests.py:43:13:43:21 | ControlFlowNode for resp_text | testapp/orm_security_tests.py:44:29:44:37 | ControlFlowNode for resp_text | provenance | |
|
||||
| testapp/orm_security_tests.py:43:49:43:54 | ControlFlowNode for person [Attribute name] | testapp/orm_security_tests.py:43:49:43:59 | ControlFlowNode for Attribute | provenance | |
|
||||
| testapp/orm_security_tests.py:43:49:43:59 | ControlFlowNode for Attribute | testapp/orm_security_tests.py:43:13:43:21 | ControlFlowNode for resp_text | provenance | |
|
||||
|
||||
@@ -30,7 +30,6 @@ nodes
|
||||
| test.py:23:58:23:65 | ControlFlowNode for password | semmle.label | ControlFlowNode for password |
|
||||
| test.py:27:40:27:47 | ControlFlowNode for password | semmle.label | ControlFlowNode for password |
|
||||
| test.py:30:58:30:65 | ControlFlowNode for password | semmle.label | ControlFlowNode for password |
|
||||
| test.py:34:30:34:39 | ControlFlowNode for get_cert() | semmle.label | ControlFlowNode for get_cert() |
|
||||
| test.py:37:11:37:24 | ControlFlowNode for get_password() | semmle.label | ControlFlowNode for get_password() |
|
||||
| test.py:39:22:39:35 | ControlFlowNode for get_password() | semmle.label | ControlFlowNode for get_password() |
|
||||
| test.py:40:22:40:35 | ControlFlowNode for get_password() | semmle.label | ControlFlowNode for get_password() |
|
||||
@@ -73,7 +72,6 @@ subpaths
|
||||
| test.py:23:58:23:65 | ControlFlowNode for password | test.py:19:16:19:29 | ControlFlowNode for get_password() | test.py:23:58:23:65 | ControlFlowNode for password | This expression logs $@ as clear text. | test.py:19:16:19:29 | ControlFlowNode for get_password() | sensitive data (password) |
|
||||
| test.py:27:40:27:47 | ControlFlowNode for password | test.py:19:16:19:29 | ControlFlowNode for get_password() | test.py:27:40:27:47 | ControlFlowNode for password | This expression logs $@ as clear text. | test.py:19:16:19:29 | ControlFlowNode for get_password() | sensitive data (password) |
|
||||
| test.py:30:58:30:65 | ControlFlowNode for password | test.py:19:16:19:29 | ControlFlowNode for get_password() | test.py:30:58:30:65 | ControlFlowNode for password | This expression logs $@ as clear text. | test.py:19:16:19:29 | ControlFlowNode for get_password() | sensitive data (password) |
|
||||
| test.py:34:30:34:39 | ControlFlowNode for get_cert() | test.py:34:30:34:39 | ControlFlowNode for get_cert() | test.py:34:30:34:39 | ControlFlowNode for get_cert() | This expression logs $@ as clear text. | test.py:34:30:34:39 | ControlFlowNode for get_cert() | sensitive data (certificate) |
|
||||
| test.py:37:11:37:24 | ControlFlowNode for get_password() | test.py:37:11:37:24 | ControlFlowNode for get_password() | test.py:37:11:37:24 | ControlFlowNode for get_password() | This expression logs $@ as clear text. | test.py:37:11:37:24 | ControlFlowNode for get_password() | sensitive data (password) |
|
||||
| test.py:39:22:39:35 | ControlFlowNode for get_password() | test.py:39:22:39:35 | ControlFlowNode for get_password() | test.py:39:22:39:35 | ControlFlowNode for get_password() | This expression logs $@ as clear text. | test.py:39:22:39:35 | ControlFlowNode for get_password() | sensitive data (password) |
|
||||
| test.py:40:22:40:35 | ControlFlowNode for get_password() | test.py:40:22:40:35 | ControlFlowNode for get_password() | test.py:40:22:40:35 | ControlFlowNode for get_password() | This expression logs $@ as clear text. | test.py:40:22:40:35 | ControlFlowNode for get_password() | sensitive data (password) |
|
||||
|
||||
@@ -31,7 +31,7 @@ def log_password():
|
||||
|
||||
|
||||
def log_cert():
|
||||
logging.debug("Cert=%s", get_cert()) # NOT OK
|
||||
logging.debug("Cert=%s", get_cert()) # OK
|
||||
|
||||
def print_password():
|
||||
print(get_password()) # NOT OK
|
||||
@@ -52,8 +52,8 @@ def log_private():
|
||||
print(passportNo) # NOT OK
|
||||
|
||||
def log2(post_code, zipCode, home_address):
|
||||
print(post_code) # NOT OK, but NOT FOUND - "code" is treated as enxrypted and thus not sensitive
|
||||
print(zipCode) # NOT OK, but NOT FOUND - "code" is treated as enxrypted and thus not sensitive
|
||||
print(post_code) # NOT OK, but NOT FOUND - "code" is treated as encrypted and thus not sensitive
|
||||
print(zipCode) # NOT OK, but NOT FOUND - "code" is treated as encrypted and thus not sensitive
|
||||
print(home_address) # NOT OK
|
||||
|
||||
def log3(user_latitude, user_longitude):
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
edges
|
||||
| test.py:9:5:9:8 | ControlFlowNode for cert | test.py:12:21:12:24 | ControlFlowNode for cert | provenance | |
|
||||
| test.py:9:5:9:8 | ControlFlowNode for cert | test.py:13:22:13:41 | ControlFlowNode for Attribute() | provenance | |
|
||||
| test.py:9:5:9:8 | ControlFlowNode for cert | test.py:15:26:15:29 | ControlFlowNode for cert | provenance | |
|
||||
| test.py:9:12:9:21 | ControlFlowNode for get_cert() | test.py:9:5:9:8 | ControlFlowNode for cert | provenance | |
|
||||
| test.py:9:5:9:12 | ControlFlowNode for password | test.py:12:21:12:28 | ControlFlowNode for password | provenance | |
|
||||
| test.py:9:5:9:12 | ControlFlowNode for password | test.py:13:22:13:45 | ControlFlowNode for Attribute() | provenance | |
|
||||
| test.py:9:5:9:12 | ControlFlowNode for password | test.py:15:26:15:33 | ControlFlowNode for password | provenance | |
|
||||
| test.py:9:16:9:29 | ControlFlowNode for get_password() | test.py:9:5:9:12 | ControlFlowNode for password | provenance | |
|
||||
nodes
|
||||
| test.py:9:5:9:8 | ControlFlowNode for cert | semmle.label | ControlFlowNode for cert |
|
||||
| test.py:9:12:9:21 | ControlFlowNode for get_cert() | semmle.label | ControlFlowNode for get_cert() |
|
||||
| test.py:12:21:12:24 | ControlFlowNode for cert | semmle.label | ControlFlowNode for cert |
|
||||
| test.py:13:22:13:41 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| test.py:15:26:15:29 | ControlFlowNode for cert | semmle.label | ControlFlowNode for cert |
|
||||
| test.py:9:5:9:12 | ControlFlowNode for password | semmle.label | ControlFlowNode for password |
|
||||
| test.py:9:16:9:29 | ControlFlowNode for get_password() | semmle.label | ControlFlowNode for get_password() |
|
||||
| test.py:12:21:12:28 | ControlFlowNode for password | semmle.label | ControlFlowNode for password |
|
||||
| test.py:13:22:13:45 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| test.py:15:26:15:33 | ControlFlowNode for password | semmle.label | ControlFlowNode for password |
|
||||
subpaths
|
||||
#select
|
||||
| test.py:12:21:12:24 | ControlFlowNode for cert | test.py:9:12:9:21 | ControlFlowNode for get_cert() | test.py:12:21:12:24 | ControlFlowNode for cert | This expression stores $@ as clear text. | test.py:9:12:9:21 | ControlFlowNode for get_cert() | sensitive data (certificate) |
|
||||
| test.py:13:22:13:41 | ControlFlowNode for Attribute() | test.py:9:12:9:21 | ControlFlowNode for get_cert() | test.py:13:22:13:41 | ControlFlowNode for Attribute() | This expression stores $@ as clear text. | test.py:9:12:9:21 | ControlFlowNode for get_cert() | sensitive data (certificate) |
|
||||
| test.py:15:26:15:29 | ControlFlowNode for cert | test.py:9:12:9:21 | ControlFlowNode for get_cert() | test.py:15:26:15:29 | ControlFlowNode for cert | This expression stores $@ as clear text. | test.py:9:12:9:21 | ControlFlowNode for get_cert() | sensitive data (certificate) |
|
||||
| test.py:12:21:12:28 | ControlFlowNode for password | test.py:9:16:9:29 | ControlFlowNode for get_password() | test.py:12:21:12:28 | ControlFlowNode for password | This expression stores $@ as clear text. | test.py:9:16:9:29 | ControlFlowNode for get_password() | sensitive data (password) |
|
||||
| test.py:13:22:13:45 | ControlFlowNode for Attribute() | test.py:9:16:9:29 | ControlFlowNode for get_password() | test.py:13:22:13:45 | ControlFlowNode for Attribute() | This expression stores $@ as clear text. | test.py:9:16:9:29 | ControlFlowNode for get_password() | sensitive data (password) |
|
||||
| test.py:15:26:15:33 | ControlFlowNode for password | test.py:9:16:9:29 | ControlFlowNode for get_password() | test.py:15:26:15:33 | ControlFlowNode for password | This expression stores $@ as clear text. | test.py:9:16:9:29 | ControlFlowNode for get_password() | sensitive data (password) |
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import pathlib
|
||||
|
||||
|
||||
def get_cert():
|
||||
return "<CERT>"
|
||||
def get_password():
|
||||
return "password"
|
||||
|
||||
|
||||
def write_password(filename):
|
||||
cert = get_cert()
|
||||
password = get_password()
|
||||
|
||||
path = pathlib.Path(filename)
|
||||
path.write_text(cert) # NOT OK
|
||||
path.write_bytes(cert.encode("utf-8")) # NOT OK
|
||||
path.write_text(password) # NOT OK
|
||||
path.write_bytes(password.encode("utf-8")) # NOT OK
|
||||
|
||||
path.open("w").write(cert) # NOT OK
|
||||
path.open("w").write(password) # NOT OK
|
||||
|
||||
@@ -3,10 +3,10 @@ edges
|
||||
| password_in_cookie.py:7:16:7:43 | ControlFlowNode for Attribute() | password_in_cookie.py:7:5:7:12 | ControlFlowNode for password | provenance | |
|
||||
| password_in_cookie.py:14:5:14:12 | ControlFlowNode for password | password_in_cookie.py:16:33:16:40 | ControlFlowNode for password | provenance | |
|
||||
| password_in_cookie.py:14:16:14:43 | ControlFlowNode for Attribute() | password_in_cookie.py:14:5:14:12 | ControlFlowNode for password | provenance | |
|
||||
| test.py:6:5:6:8 | ControlFlowNode for cert | test.py:8:20:8:23 | ControlFlowNode for cert | provenance | |
|
||||
| test.py:6:5:6:8 | ControlFlowNode for cert | test.py:9:9:9:13 | ControlFlowNode for lines | provenance | |
|
||||
| test.py:6:12:6:21 | ControlFlowNode for get_cert() | test.py:6:5:6:8 | ControlFlowNode for cert | provenance | |
|
||||
| test.py:9:9:9:13 | ControlFlowNode for lines | test.py:10:25:10:29 | ControlFlowNode for lines | provenance | |
|
||||
| test.py:15:5:15:12 | ControlFlowNode for password | test.py:17:20:17:27 | ControlFlowNode for password | provenance | |
|
||||
| test.py:15:5:15:12 | ControlFlowNode for password | test.py:18:9:18:13 | ControlFlowNode for lines | provenance | |
|
||||
| test.py:15:16:15:29 | ControlFlowNode for get_password() | test.py:15:5:15:12 | ControlFlowNode for password | provenance | |
|
||||
| test.py:18:9:18:13 | ControlFlowNode for lines | test.py:19:25:19:29 | ControlFlowNode for lines | provenance | |
|
||||
nodes
|
||||
| password_in_cookie.py:7:5:7:12 | ControlFlowNode for password | semmle.label | ControlFlowNode for password |
|
||||
| password_in_cookie.py:7:16:7:43 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
@@ -14,14 +14,14 @@ nodes
|
||||
| password_in_cookie.py:14:5:14:12 | ControlFlowNode for password | semmle.label | ControlFlowNode for password |
|
||||
| password_in_cookie.py:14:16:14:43 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||
| password_in_cookie.py:16:33:16:40 | ControlFlowNode for password | semmle.label | ControlFlowNode for password |
|
||||
| test.py:6:5:6:8 | ControlFlowNode for cert | semmle.label | ControlFlowNode for cert |
|
||||
| test.py:6:12:6:21 | ControlFlowNode for get_cert() | semmle.label | ControlFlowNode for get_cert() |
|
||||
| test.py:8:20:8:23 | ControlFlowNode for cert | semmle.label | ControlFlowNode for cert |
|
||||
| test.py:9:9:9:13 | ControlFlowNode for lines | semmle.label | ControlFlowNode for lines |
|
||||
| test.py:10:25:10:29 | ControlFlowNode for lines | semmle.label | ControlFlowNode for lines |
|
||||
| test.py:15:5:15:12 | ControlFlowNode for password | semmle.label | ControlFlowNode for password |
|
||||
| test.py:15:16:15:29 | ControlFlowNode for get_password() | semmle.label | ControlFlowNode for get_password() |
|
||||
| test.py:17:20:17:27 | ControlFlowNode for password | semmle.label | ControlFlowNode for password |
|
||||
| test.py:18:9:18:13 | ControlFlowNode for lines | semmle.label | ControlFlowNode for lines |
|
||||
| test.py:19:25:19:29 | ControlFlowNode for lines | semmle.label | ControlFlowNode for lines |
|
||||
subpaths
|
||||
#select
|
||||
| password_in_cookie.py:9:33:9:40 | ControlFlowNode for password | password_in_cookie.py:7:16:7:43 | ControlFlowNode for Attribute() | password_in_cookie.py:9:33:9:40 | ControlFlowNode for password | This expression stores $@ as clear text. | password_in_cookie.py:7:16:7:43 | ControlFlowNode for Attribute() | sensitive data (password) |
|
||||
| password_in_cookie.py:16:33:16:40 | ControlFlowNode for password | password_in_cookie.py:14:16:14:43 | ControlFlowNode for Attribute() | password_in_cookie.py:16:33:16:40 | ControlFlowNode for password | This expression stores $@ as clear text. | password_in_cookie.py:14:16:14:43 | ControlFlowNode for Attribute() | sensitive data (password) |
|
||||
| test.py:8:20:8:23 | ControlFlowNode for cert | test.py:6:12:6:21 | ControlFlowNode for get_cert() | test.py:8:20:8:23 | ControlFlowNode for cert | This expression stores $@ as clear text. | test.py:6:12:6:21 | ControlFlowNode for get_cert() | sensitive data (certificate) |
|
||||
| test.py:10:25:10:29 | ControlFlowNode for lines | test.py:6:12:6:21 | ControlFlowNode for get_cert() | test.py:10:25:10:29 | ControlFlowNode for lines | This expression stores $@ as clear text. | test.py:6:12:6:21 | ControlFlowNode for get_cert() | sensitive data (certificate) |
|
||||
| test.py:17:20:17:27 | ControlFlowNode for password | test.py:15:16:15:29 | ControlFlowNode for get_password() | test.py:17:20:17:27 | ControlFlowNode for password | This expression stores $@ as clear text. | test.py:15:16:15:29 | ControlFlowNode for get_password() | sensitive data (password) |
|
||||
| test.py:19:25:19:29 | ControlFlowNode for lines | test.py:15:16:15:29 | ControlFlowNode for get_password() | test.py:19:25:19:29 | ControlFlowNode for lines | This expression stores $@ as clear text. | test.py:15:16:15:29 | ControlFlowNode for get_password() | sensitive data (password) |
|
||||
|
||||
@@ -1,12 +1,21 @@
|
||||
def get_cert():
|
||||
return "<CERT>"
|
||||
|
||||
def get_password():
|
||||
return "password"
|
||||
|
||||
def write_cert(filename):
|
||||
cert = get_cert()
|
||||
with open(filename, "w") as file:
|
||||
file.write(cert) # NOT OK
|
||||
file.write(cert) # OK
|
||||
lines = [cert + "\n"]
|
||||
file.writelines(lines) # OK
|
||||
|
||||
def write_password(filename):
|
||||
password = get_password()
|
||||
with open(filename, "w") as file:
|
||||
file.write(password) # NOT OK
|
||||
lines = [password + "\n"]
|
||||
file.writelines(lines) # NOT OK
|
||||
|
||||
def FPs():
|
||||
|
||||
Reference in New Issue
Block a user