Initial merge from main

This commit is contained in:
Dave Bartolomeo
2024-09-15 08:55:31 -04:00
2602 changed files with 114048 additions and 76664 deletions

View File

@@ -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.

View File

@@ -1,6 +0,0 @@
/**
* Deprecated. Use `semmle.python.regexp.RegexTreeView` instead.
*/
deprecated import regexp.RegexTreeView as Dep
import Dep

View File

@@ -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.
*

View File

@@ -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.
*

View File

@@ -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.
*

View File

@@ -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.
*

View File

@@ -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

View File

@@ -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() }
}

View File

@@ -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;

View File

@@ -1,4 +0,0 @@
import python
import semmle.python.dataflow.TaintTracking
abstract deprecated class SqlInjectionSink extends TaintSink { }

View File

@@ -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() {

View File

@@ -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() {

View File

@@ -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.

View 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")

View 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>

View 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."

View 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")

View File

@@ -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. */

View File

@@ -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")

View File

@@ -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. |

View File

@@ -0,0 +1 @@
experimental/Security/CWE-346/CorsBypass.ql

View File

@@ -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 | |

View File

@@ -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) |

View File

@@ -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):

View File

@@ -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) |

View File

@@ -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

View File

@@ -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) |

View File

@@ -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():