Merge pull request #1763 from markshannon/python-cwe-312

Python: Two new queries for CWE-312.
This commit is contained in:
Taus
2019-08-30 15:28:56 +02:00
committed by GitHub
30 changed files with 604 additions and 81 deletions

View File

@@ -0,0 +1,14 @@
# Improvements to Python analysis
## General improvements
## New queries
| **Query** | **Tags** | **Purpose** |
|-----------|----------|-------------|
| Clear-text logging of sensitive information (`py/clear-text-logging-sensitive-data`) | security, external/cwe/cwe-312 | Finds instances where sensitive information is logged without encryption or hashing. Results are shown on LGTM by default. |
| Clear-text storage of sensitive information (`py/clear-text-storage-sensitive-data`) | security, external/cwe/cwe-312 | Finds instances where sensitive information is stored without encryption or hashing. Results are shown on LGTM by default. |

View File

@@ -0,0 +1,5 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<include src="CleartextStorage.qhelp" /></qhelp>

View File

@@ -0,0 +1,42 @@
/**
* @name Clear-text logging of sensitive information
* @description Logging sensitive information without encryption or hashing can
* expose it to an attacker.
* @kind path-problem
* @problem.severity error
* @precision high
* @id py/clear-text-logging-sensitive-data
* @tags security
* external/cwe/cwe-312
* external/cwe/cwe-315
* external/cwe/cwe-359
*/
import python
import semmle.python.security.Paths
import semmle.python.security.TaintTracking
import semmle.python.security.SensitiveData
import semmle.python.security.ClearText
class CleartextLoggingConfiguration extends TaintTracking::Configuration {
CleartextLoggingConfiguration() { this = "ClearTextLogging" }
override predicate isSource(DataFlow::Node src, TaintKind kind) {
src.asCfgNode().(SensitiveData::Source).isSourceOf(kind)
}
override predicate isSink(DataFlow::Node sink, TaintKind kind) {
sink.asCfgNode() instanceof ClearTextLogging::Sink and
kind instanceof SensitiveData
}
}
from CleartextLoggingConfiguration config, TaintedPathSource source, TaintedPathSink sink
where config.hasFlowPath(source, sink)
select sink.getSink(), source, sink, "Sensitive data returned by $@ is logged here.",
source.getSource(), source.getCfgNode().(SensitiveData::Source).repr()

View File

@@ -0,0 +1,52 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
Sensitive information that is stored unencrypted is accessible to an attacker
who gains access to the storage. This is particularly important for cookies,
which are stored on the machine of the end-user.
</p>
</overview>
<recommendation>
<p>
Ensure that sensitive information is always encrypted before being stored.
If possible, avoid placing sensitive information in cookies altogether.
Instead, prefer storing, in the cookie, a key that can be used to look up the
sensitive information.
</p>
<p>
In general, decrypt sensitive information only at the point where it is
necessary for it to be used in cleartext.
</p>
<p>
Be aware that external processes often store the <code>standard
out</code> and <code>standard error</code> streams of the application,
causing logged sensitive information to be stored as well.
</p>
</recommendation>
<example>
<p>
The following example code stores user credentials (in this case, their password) in a cookie in plain text:
</p>
<sample src="examples/password_in_cookie.py"/>
<p>
Instead, the credentials should be encrypted, for instance by using the <code>cryptography</code> module, or not stored at all.
</p>
</example>
<references>
<li>M. Dowd, J. McDonald and J. Schuhm, <i>The Art of Software Security Assessment</i>, 1st Edition, Chapter 2 - 'Common Vulnerabilities of Encryption', p. 43. Addison Wesley, 2006.</li>
<li>M. Howard and D. LeBlanc, <i>Writing Secure Code</i>, 2nd Edition, Chapter 9 - 'Protecting Secret Data', p. 299. Microsoft, 2002.</li>
</references>
</qhelp>

View File

@@ -0,0 +1,41 @@
/**
* @name Clear-text storage of sensitive information
* @description Sensitive information stored without encryption or hashing can expose it to an
* attacker.
* @kind path-problem
* @problem.severity error
* @precision high
* @id py/clear-text-storage-sensitive-data
* @tags security
* external/cwe/cwe-312
* external/cwe/cwe-315
* external/cwe/cwe-359
*/
import python
import semmle.python.security.Paths
import semmle.python.security.TaintTracking
import semmle.python.security.SensitiveData
import semmle.python.security.ClearText
class CleartextStorageConfiguration extends TaintTracking::Configuration {
CleartextStorageConfiguration() { this = "ClearTextStorage" }
override predicate isSource(DataFlow::Node src, TaintKind kind) {
src.asCfgNode().(SensitiveData::Source).isSourceOf(kind)
}
override predicate isSink(DataFlow::Node sink, TaintKind kind) {
sink.asCfgNode() instanceof ClearTextStorage::Sink and
kind instanceof SensitiveData
}
}
from CleartextStorageConfiguration config, TaintedPathSource source, TaintedPathSink sink
where config.hasFlowPath(source, sink)
select sink.getSink(), source, sink, "Sensitive data from $@ is stored here.",
source.getSource(), source.getCfgNode().(SensitiveData::Source).repr()

View File

@@ -0,0 +1,10 @@
from flask import Flask, make_response, request
app = Flask("Leak password")
@app.route('/')
def index():
password = request.args.get("password")
resp = make_response(render_template(...))
resp.set_cookie("password", password)
return resp

View File

@@ -0,0 +1,26 @@
import python
import semmle.python.security.TaintTracking
class OpenFile extends TaintKind {
OpenFile() { this = "file.open" }
override string repr() { result = "an open file" }
}
class OpenFileConfiguration extends TaintTracking::Configuration {
OpenFileConfiguration() { this = "Open file configuration" }
override predicate isSource(DataFlow::Node src, TaintKind kind) {
theOpenFunction().(FunctionObject).getACall() = src.asCfgNode() and
kind instanceof OpenFile
}
override predicate isSink(DataFlow::Node sink, TaintKind kind) {
none()
}
}

View File

@@ -435,8 +435,11 @@ class AbsentModuleAttributeObjectInternal extends ObjectInternal, TAbsentModuleA
override predicate subscriptUnknown() { any() }
/* We know what this is called, but not its innate name */
override string getName() { none() }
/* We know what this is called, but not its innate name.
* However, if we are looking for things by name, this is a reasonable approximation */
override string getName() {
this = TAbsentModuleAttribute(_, result)
}
override predicate contextSensitiveCallee() { none() }

View File

@@ -0,0 +1,66 @@
import python
import semmle.python.security.TaintTracking
import semmle.python.security.SensitiveData
import semmle.python.dataflow.Files
import semmle.python.web.Http
module ClearTextStorage {
abstract class Sink extends TaintSink {
override predicate sinks(TaintKind kind) {
kind instanceof SensitiveData
}
}
class CookieStorageSink extends Sink {
CookieStorageSink() {
any(CookieSet cookie).getValue() = this
}
}
class FileStorageSink extends Sink {
FileStorageSink() {
exists(CallNode call, AttrNode meth, string name |
any(OpenFile fd).taints(meth.getObject(name)) and
call.getFunction() = meth and
call.getAnArg() = this |
name = "write"
)
}
}
}
module ClearTextLogging {
abstract class Sink extends TaintSink {
override predicate sinks(TaintKind kind) {
kind instanceof SensitiveData
}
}
class PrintSink extends Sink {
PrintSink() {
exists(CallNode call |
call.getAnArg() = this and
thePrintFunction().(FunctionObject).getACall() = call
)
}
}
class LoggingSink extends Sink {
LoggingSink() {
exists(CallNode call, AttrNode meth, string name |
call.getFunction() = meth and
meth.getObject(name).(NameNode).getId().matches("logg%") and
call.getAnArg() = this |
name = "error" or
name = "warn" or
name = "warning" or
name = "debug" or
name = "info"
)
}
}
}

View File

@@ -11,93 +11,184 @@
import python
import semmle.python.security.TaintTracking
/** A regular expression that identifies strings that look like they represent secret data that are not passwords. */
private string suspiciousNonPassword() {
result = "(?is).*(account|accnt|(?<!un)trusted).*"
}
/** A regular expression that identifies strings that look like they represent secret data that are passwords. */
private string suspiciousPassword() {
result = "(?is).*(password|passwd).*"
}
/** A regular expression that identifies strings that look like they represent secret data. */
private string suspicious() {
result = suspiciousPassword() or result = suspiciousNonPassword()
}
import semmle.python.web.HttpRequest
/**
* A string for `match` that identifies strings that look like they represent secret data that is
* hashed or encrypted.
* Provides heuristics for identifying names related to sensitive information.
*
* INTERNAL: Do not use directly.
* This is copied from the javascript library, but should be language independent.
*/
private string nonSuspicious() {
result = "(?is).*(hash|(?<!un)encrypted|\\bcrypt\\b).*"
private module HeuristicNames {
/**
* Gets a regular expression that identifies strings that may indicate the presence of secret
* or trusted data.
*/
string maybeSecret() { result = "(?is).*((?<!is)secret|(?<!un|is)trusted).*" }
/**
* Gets a regular expression that identifies strings that may indicate the presence of
* user names or other account information.
*/
string maybeAccountInfo() {
result = "(?is).*acc(ou)?nt.*" or
result = "(?is).*(puid|username|userid).*"
}
/**
* Gets a regular expression that identifies strings that may indicate the presence of
* a password or an authorization key.
*/
string maybePassword() {
result = "(?is).*pass(wd|word|code|phrase)(?!.*question).*" or
result = "(?is).*(auth(entication|ori[sz]ation)?)key.*"
}
/**
* Gets a regular expression that identifies strings that may indicate the presence of
* a certificate.
*/
string maybeCertificate() { result = "(?is).*(cert)(?!.*(format|name)).*" }
/**
* Gets a regular expression that identifies strings that may indicate the presence
* of sensitive data, with `classification` describing the kind of sensitive data involved.
*/
string maybeSensitive(SensitiveData data) {
result = maybeSecret() and data instanceof SensitiveData::Secret
or
result = maybeAccountInfo() and data instanceof SensitiveData::Id
or
result = maybePassword() and data instanceof SensitiveData::Password
or
result = maybeCertificate() and data instanceof SensitiveData::Certificate
}
/**
* Gets a regular expression that identifies strings that may indicate the presence of data
* that is hashed or encrypted, and hence rendered non-sensitive.
*/
string notSensitive() {
result = "(?is).*(redact|censor|obfuscate|hash|md5|sha|((?<!un)(en))?(crypt|code)).*"
}
bindingset[name]
SensitiveData getSensitiveDataForName(string name) {
name.regexpMatch(HeuristicNames::maybeSensitive(result)) and
not name.regexpMatch(HeuristicNames::notSensitive())
}
}
/** An expression that might contain sensitive data. */
abstract class SensitiveExpr extends Expr { }
abstract class SensitiveData extends TaintKind {
/** A method access that might produce sensitive data. */
class SensitiveCall extends SensitiveExpr, Call {
SensitiveCall() {
exists(string name |
name = this.getFunc().(Name).getId() or
name = this.getFunc().(Attribute).getName() or
exists(StringObject s |
this.getAnArg().refersTo(s) |
name = s.getText()
)
|
name.regexpMatch(suspicious()) and
not name.regexpMatch(nonSuspicious())
bindingset[this]
SensitiveData() { this = this }
}
module SensitiveData {
class Secret extends SensitiveData {
Secret() { this = "sensitive.data.secret" }
override string repr() { result = "a secret" }
}
class Id extends SensitiveData {
Id() { this = "sensitive.data.id" }
override string repr() { result = "an ID" }
}
class Password extends SensitiveData {
Password() { this = "sensitive.data.password" }
override string repr() { result = "a password" }
}
class Certificate extends SensitiveData {
Certificate() { this = "sensitive.data.certificate" }
override string repr() { result = "a certificate or key" }
}
private SensitiveData fromFunction(Value func) {
result = HeuristicNames::getSensitiveDataForName(func.getName())
or
// This is particularly to pick up methods with an argument like "password", which
// may indicate a lookup.
exists(string name | name = func.(PythonFunctionValue).getScope().getAnArg().asName().getId() |
result = HeuristicNames::getSensitiveDataForName(name)
)
}
}
/** An access to a variable or property that might contain sensitive data. */
abstract class SensitiveVariableAccess extends SensitiveExpr {
abstract class Source extends TaintSource {
string name;
abstract string repr();
SensitiveVariableAccess() {
this.(Name).getId() = name or
this.(Attribute).getName() = name
}
}
}
private class SensitiveCallSource extends Source {
/** An access to a variable or property that might contain sensitive data. */
private class BasicSensitiveVariableAccess extends SensitiveVariableAccess {
SensitiveData data;
BasicSensitiveVariableAccess() {
name.regexpMatch(suspicious()) and not name.regexpMatch(nonSuspicious())
}
SensitiveCallSource() {
exists(Value callee |
callee.getACall() = this |
data = fromFunction(callee)
)
}
}
override predicate isSourceOf(TaintKind kind) {
kind = data
}
class SensitiveData extends TaintKind {
override string repr() {
result = "a call returning " + data.repr()
}
}
/** An access to a variable or property that might contain sensitive data. */
private class SensitiveVariableAccess extends SensitiveData::Source {
SensitiveData data;
SensitiveVariableAccess() {
data = HeuristicNames::getSensitiveDataForName(this.(AttrNode).getName())
}
override predicate isSourceOf(TaintKind kind) {
kind = data
}
override string repr() {
result = "an attribute or property containing " + data.repr()
}
}
private class SensitiveRequestParameter extends SensitiveData::Source {
SensitiveData data;
SensitiveRequestParameter() {
this.(CallNode).getFunction().(AttrNode).getName() = "get" and
exists(string sensitive |
this.(CallNode).getAnArg().refersTo(any(StringObject s | s.getText() = sensitive)) and
data = HeuristicNames::getSensitiveDataForName(sensitive)
)
}
override predicate isSourceOf(TaintKind kind) {
kind = data
}
override string repr() {
result = "a request parameter containing " + data.repr()
}
SensitiveData() {
this = "sensitive.data"
}
}
class SensitiveDataSource extends TaintSource {
SensitiveDataSource() {
this.(ControlFlowNode).getNode() instanceof SensitiveExpr
}
override string toString() {
result = "sensitive.data.source"
}
override predicate isSourceOf(TaintKind kind) {
kind instanceof SensitiveData
}
}
//Backwards compatibility
class SensitiveDataSource = SensitiveData::Source;

View File

@@ -72,6 +72,19 @@ class UntrustedCookie extends TaintKind {
}
abstract class CookieOperation extends @py_flow_node {
abstract string toString();
abstract ControlFlowNode getKey();
abstract ControlFlowNode getValue();
}
abstract class CookieGet extends CookieOperation {}
abstract class CookieSet extends CookieOperation {}
/** Generic taint sink in a http response */
abstract class HttpResponseTaintSink extends TaintSink {

View File

@@ -56,3 +56,17 @@ class BottleHandlerFunctionResult extends HttpResponseTaintSink {
}
class BottleCookieSet extends CookieSet, CallNode {
BottleCookieSet() {
any(BottleResponse r).taints(this.getFunction().(AttrNode).getObject("set_cookie"))
}
override string toString() { result = this.(CallNode).toString() }
override ControlFlowNode getKey() { result = this.getArg(0) }
override ControlFlowNode getValue() { result = this.getArg(1) }
}

View File

@@ -83,5 +83,16 @@ class DjangoResponseContent extends HttpResponseTaintSink {
}
class DjangoCookieSet extends CookieSet, CallNode {
DjangoCookieSet() {
any(DjangoResponse r).taints(this.getFunction().(AttrNode).getObject("set_cookie"))
}
override string toString() { result = this.(CallNode).toString() }
override ControlFlowNode getKey() { result = this.getArg(0) }
override ControlFlowNode getValue() { result = this.getArg(1) }
}

View File

@@ -115,3 +115,18 @@ private class AsView extends TaintSource {
}
class FlaskCookieSet extends CookieSet, CallNode {
FlaskCookieSet() {
this.getFunction().(AttrNode).getObject("set_cookie").refersTo(_, theFlaskReponseClass(), _)
}
override string toString() { result = this.(CallNode).toString() }
override ControlFlowNode getKey() { result = this.getArg(0) }
override ControlFlowNode getValue() { result = this.getArg(1) }
}

View File

@@ -3,6 +3,7 @@ import python
import semmle.python.security.TaintTracking
import semmle.python.security.strings.Basic
import semmle.python.web.Http
private import semmle.python.web.pyramid.View
private import semmle.python.web.Http
@@ -27,3 +28,21 @@ class PyramidRoutedResponse extends HttpResponseTaintSink {
}
}
class PyramidCookieSet extends CookieSet, CallNode {
PyramidCookieSet() {
exists(ControlFlowNode f |
f = this.getFunction().(AttrNode).getObject("set_cookie") and
f.refersTo(_, ModuleObject::named("pyramid").attr("Response"), _)
)
}
override string toString() { result = this.(CallNode).toString() }
override ControlFlowNode getKey() { result = this.getArg(0) }
override ControlFlowNode getValue() { result = this.getArg(1) }
}

View File

@@ -1,6 +1,7 @@
import python
import semmle.python.security.TaintTracking
import semmle.python.web.Http
private ClassObject theTornadoRequestHandlerClass() {
result = ModuleObject::named("tornado.web").attr("RequestHandler")
@@ -32,4 +33,22 @@ predicate isTornadoRequestHandlerInstance(ControlFlowNode node) {
CallNode callToNamedTornadoRequestHandlerMethod(string name) {
isTornadoRequestHandlerInstance(result.getFunction().(AttrNode).getObject(name))
}
}
class TornadoCookieSet extends CookieSet, CallNode {
TornadoCookieSet() {
exists(ControlFlowNode f |
f = this.getFunction().(AttrNode).getObject("set_cookie") and
isTornadoRequestHandlerInstance(f)
)
}
override string toString() { result = this.(CallNode).toString() }
override ControlFlowNode getKey() { result = this.getArg(0) }
override ControlFlowNode getValue() { result = this.getArg(1) }
}

View File

@@ -0,0 +1,5 @@
| test.py:16:1:16:14 | test.py:16 | a call returning a password |
| test.py:17:1:17:12 | test.py:17 | a call returning a password |
| test.py:18:1:18:12 | test.py:18 | a call returning a secret |
| test.py:19:1:19:19 | test.py:19 | a call returning a certificate or key |
| test.py:20:1:20:12 | test.py:20 | a call returning an ID |

View File

@@ -0,0 +1,7 @@
import python
import semmle.python.security.SensitiveData
from SensitiveData::Source src
select src.getLocation(), src.repr()

View File

@@ -0,0 +1,21 @@
from not_found import get_passwd, account_id
def get_password():
pass
def get_secret():
pass
def fetch_certificate():
pass
def encrypt_password(pwd):
pass
get_password()
get_passwd()
get_secret()
fetch_certificate()
account_id()
safe_to_store = encrypt_password(pwd)

View File

@@ -0,0 +1,6 @@
edges
| test.py:7:16:7:29 | a password | test.py:8:35:8:42 | a password |
#select
| test.py:8:35:8:42 | password | test.py:7:16:7:29 | a password | test.py:8:35:8:42 | a password | Sensitive data returned by $@ is logged here. | test.py:7:16:7:29 | get_password() | a call returning a password |
| test.py:14:30:14:39 | get_cert() | test.py:14:30:14:39 | a certificate or key | test.py:14:30:14:39 | a certificate or key | Sensitive data returned by $@ is logged here. | test.py:14:30:14:39 | get_cert() | a call returning a certificate or key |
| test.py:17:11:17:24 | get_password() | test.py:17:11:17:24 | a password | test.py:17:11:17:24 | a password | Sensitive data returned by $@ is logged here. | test.py:17:11:17:24 | get_password() | a call returning a password |

View File

@@ -0,0 +1 @@
Security/CWE-312/CleartextLogging.ql

View File

@@ -0,0 +1,6 @@
edges
| password_in_cookie.py:7:16:7:43 | a password | password_in_cookie.py:9:33:9:40 | a password |
| test.py:20:12:20:21 | a certificate or key | test.py:22:20:22:23 | a certificate or key |
#select
| password_in_cookie.py:9:33:9:40 | password | password_in_cookie.py:7:16:7:43 | a password | password_in_cookie.py:9:33:9:40 | a password | Sensitive data from $@ is stored here. | password_in_cookie.py:7:16:7:43 | Attribute() | a request parameter containing a password |
| test.py:22:20:22:23 | cert | test.py:20:12:20:21 | a certificate or key | test.py:22:20:22:23 | a certificate or key | Sensitive data from $@ is stored here. | test.py:20:12:20:21 | get_cert() | a call returning a certificate or key |

View File

@@ -0,0 +1 @@
Security/CWE-312/CleartextStorage.ql

View File

@@ -0,0 +1 @@
semmle-extractor-options: -p ../lib/ --max-import-depth=3

View File

@@ -0,0 +1,10 @@
from flask import Flask, make_response, request
app = Flask("Leak password")
@app.route('/')
def index():
password = request.args.get("password")
resp = make_response(render_template(...))
resp.set_cookie("password", password)
return resp

View File

@@ -0,0 +1,22 @@
#Don't import logging; it transitively imports a lot of stuff
def get_password():
pass
def log_password():
password = get_password()
logging.info("Password '%s'", password)
def get_cert():
pass
def log_cert():
logging.debug("Cert=%s", get_cert())
def print_password():
print(get_password())
def write_cert(filename):
cert = get_cert()
with open(filename, "w") as file:
file.write(cert)

View File

@@ -1,2 +1,2 @@
| test_cryptography.py:7:29:7:37 | Use of weak crypto algorithm | Sensitive data from $@ is used in a broken or weak cryptographic algorithm. | test_cryptography.py:4:17:4:30 | sensitive.data.source | sensitive.data.source |
| test_pycrypto.py:6:27:6:35 | Use of weak crypto algorithm ARC4 | Sensitive data from $@ is used in a broken or weak cryptographic algorithm. | test_pycrypto.py:4:17:4:30 | sensitive.data.source | sensitive.data.source |
| test_cryptography.py:8:29:8:37 | Use of weak crypto algorithm | Sensitive data from $@ is used in a broken or weak cryptographic algorithm. | test_cryptography.py:5:17:5:30 | Taint source | Taint source |
| test_pycrypto.py:7:27:7:35 | Use of weak crypto algorithm ARC4 | Sensitive data from $@ is used in a broken or weak cryptographic algorithm. | test_pycrypto.py:5:17:5:30 | Taint source | Taint source |

View File

@@ -1,7 +1,7 @@
| Taint Crypto.Cipher.ARC4 | test_pycrypto.py:5:14:5:27 | test_pycrypto.py:5 | test_pycrypto.py:5:14:5:27 | Attribute() | |
| Taint Crypto.Cipher.ARC4 | test_pycrypto.py:6:12:6:17 | test_pycrypto.py:6 | test_pycrypto.py:6:12:6:17 | cipher | |
| Taint cryptography.Cipher.RC4 | test_cryptography.py:5:14:5:47 | test_cryptography.py:5 | test_cryptography.py:5:14:5:47 | Cipher() | |
| Taint cryptography.Cipher.RC4 | test_cryptography.py:6:17:6:22 | test_cryptography.py:6 | test_cryptography.py:6:17:6:22 | cipher | |
| Taint cryptography.encryptor.RC4 | test_cryptography.py:6:17:6:34 | test_cryptography.py:6 | test_cryptography.py:6:17:6:34 | Attribute() | |
| Taint cryptography.encryptor.RC4 | test_cryptography.py:7:12:7:20 | test_cryptography.py:7 | test_cryptography.py:7:12:7:20 | encryptor | |
| Taint cryptography.encryptor.RC4 | test_cryptography.py:7:42:7:50 | test_cryptography.py:7 | test_cryptography.py:7:42:7:50 | encryptor | |
| Taint Crypto.Cipher.ARC4 | test_pycrypto.py:6:14:6:27 | test_pycrypto.py:6 | test_pycrypto.py:6:14:6:27 | Attribute() | |
| Taint Crypto.Cipher.ARC4 | test_pycrypto.py:7:12:7:17 | test_pycrypto.py:7 | test_pycrypto.py:7:12:7:17 | cipher | |
| Taint cryptography.Cipher.RC4 | test_cryptography.py:6:14:6:47 | test_cryptography.py:6 | test_cryptography.py:6:14:6:47 | Cipher() | |
| Taint cryptography.Cipher.RC4 | test_cryptography.py:7:17:7:22 | test_cryptography.py:7 | test_cryptography.py:7:17:7:22 | cipher | |
| Taint cryptography.encryptor.RC4 | test_cryptography.py:7:17:7:34 | test_cryptography.py:7 | test_cryptography.py:7:17:7:34 | Attribute() | |
| Taint cryptography.encryptor.RC4 | test_cryptography.py:8:12:8:20 | test_cryptography.py:8 | test_cryptography.py:8:12:8:20 | encryptor | |
| Taint cryptography.encryptor.RC4 | test_cryptography.py:8:42:8:50 | test_cryptography.py:8 | test_cryptography.py:8:42:8:50 | encryptor | |

View File

@@ -1,4 +1,5 @@
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms
from secrets_store import get_password
def get_badly_encrypted_password():
dangerous = get_password()

View File

@@ -1,4 +1,5 @@
from Crypto.Cipher import ARC4
from secrets_store import get_password
def get_badly_encrypted_password():
dangerous = get_password()