mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
Merge pull request #73 from esben-semmle/js/cleartext-logging-query
Approved by xiemaisi
This commit is contained in:
@@ -85,6 +85,7 @@
|
||||
|
||||
| **Query** | **Tags** | **Purpose** |
|
||||
|-----------------------------|-----------|--------------------------------------------------------------------|
|
||||
| Clear-text logging of sensitive information (`js/clear-text-logging`) | security, external/cwe/cwe-312, external/cwe/cwe-315, external/cwe/cwe-359 | Highlights logging of sensitive information, indicating a violation of [CWE-312](https://cwe.mitre.org/data/definitions/312.html). Results shown on LGTM by default. |
|
||||
| Disabling Electron webSecurity (`js/disabling-electron-websecurity`) | security, frameworks/electron | Highlights Electron browser objects that are created with the `webSecurity` property set to false. Results shown on LGTM by default. |
|
||||
| Enabling Electron allowRunningInsecureContent (`js/enabling-electron-insecure-content`) | security, frameworks/electron | Highlights Electron browser objects that are created with the `allowRunningInsecureContent` property set to true. Results shown on LGTM by default. |
|
||||
| Use of externally-controlled format string (`js/tainted-format-string`) | security, external/cwe/cwe-134 | Highlights format strings containing user-provided data, indicating a violation of [CWE-134](https://cwe.mitre.org/data/definitions/134.html). Results shown on LGTM by default. |
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
+ semmlecode-javascript-queries/Security/CWE-134/TaintedFormatString.ql: /Security/CWE/CWE-134
|
||||
+ semmlecode-javascript-queries/Security/CWE-209/StackTraceExposure.ql: /Security/CWE/CWE-209
|
||||
+ semmlecode-javascript-queries/Security/CWE-312/CleartextStorage.ql: /Security/CWE/CWE-312
|
||||
+ semmlecode-javascript-queries/Security/CWE-312/CleartextLogging.ql: /Security/CWE/CWE-312
|
||||
+ semmlecode-javascript-queries/Security/CWE-313/PasswordInConfigurationFile.ql: /Security/CWE/CWE-313
|
||||
+ semmlecode-javascript-queries/Security/CWE-327/BrokenCryptoAlgorithm.ql: /Security/CWE/CWE-327
|
||||
+ semmlecode-javascript-queries/Security/CWE-338/InsecureRandomness.ql: /Security/CWE/CWE-338
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<include src="CleartextStorage.qhelp" /></qhelp>
|
||||
38
javascript/ql/src/Security/CWE-312/CleartextLogging.ql
Normal file
38
javascript/ql/src/Security/CWE-312/CleartextLogging.ql
Normal file
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* @name Clear-text logging of sensitive information
|
||||
* @description Logging sensitive information without encryption or hashing can
|
||||
* expose it to an attacker.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @id js/clear-text-logging
|
||||
* @tags security
|
||||
* external/cwe/cwe-312
|
||||
* external/cwe/cwe-315
|
||||
* external/cwe/cwe-359
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import semmle.javascript.security.dataflow.CleartextLogging::CleartextLogging
|
||||
|
||||
/**
|
||||
* Holds if `tl` is used in a browser environment.
|
||||
*/
|
||||
predicate inBrowserEnvironment(TopLevel tl) {
|
||||
tl instanceof InlineScript or
|
||||
tl instanceof CodeInAttribute or
|
||||
exists (GlobalVarAccess e |
|
||||
e.getTopLevel() = tl |
|
||||
e.getName() = "window"
|
||||
) or
|
||||
exists (Module m | inBrowserEnvironment(m) |
|
||||
tl = m.getAnImportedModule() or
|
||||
m = tl.(Module).getAnImportedModule()
|
||||
)
|
||||
}
|
||||
|
||||
from Configuration cfg, Source source, DataFlow::Node sink
|
||||
where cfg.hasFlow(source, sink) and
|
||||
// ignore logging to the browser console (even though it is not a good practice)
|
||||
not inBrowserEnvironment(sink.asExpr().getTopLevel())
|
||||
select sink, "Sensitive data returned by $@ is logged here.", source, source.describe()
|
||||
@@ -15,13 +15,22 @@ which are stored on the machine of the end-user.
|
||||
<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 lookup the
|
||||
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>
|
||||
|
||||
@@ -59,6 +59,7 @@ import semmle.javascript.frameworks.DigitalOcean
|
||||
import semmle.javascript.frameworks.Electron
|
||||
import semmle.javascript.frameworks.jQuery
|
||||
import semmle.javascript.frameworks.LodashUnderscore
|
||||
import semmle.javascript.frameworks.Logging
|
||||
import semmle.javascript.frameworks.HttpFrameworks
|
||||
import semmle.javascript.frameworks.NoSQL
|
||||
import semmle.javascript.frameworks.PkgCloud
|
||||
|
||||
@@ -316,13 +316,12 @@ module TaintTracking {
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint propagating data flow edge arising from string append and other string
|
||||
* operations defined in the standard library.
|
||||
* A taint propagating data flow edge arising from string concatenations.
|
||||
*
|
||||
* Note that since we cannot easily distinguish string append from addition, we consider
|
||||
* any `+` operation to propagate taint.
|
||||
* Note that since we cannot easily distinguish string append from addition,
|
||||
* we consider any `+` operation to propagate taint.
|
||||
*/
|
||||
private class StringManipulationTaintStep extends AdditionalTaintStep, DataFlow::ValueNode {
|
||||
class StringConcatenationTaintStep extends AdditionalTaintStep, DataFlow::ValueNode {
|
||||
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
succ = this and
|
||||
(
|
||||
@@ -336,8 +335,19 @@ module TaintTracking {
|
||||
or
|
||||
// templating propagates taint
|
||||
astNode.(TemplateLiteral).getAnElement() = pred.asExpr()
|
||||
or
|
||||
// other string operations that propagate taint
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint propagating data flow edge arising from string manipulation
|
||||
* functions defined in the standard library.
|
||||
*/
|
||||
private class StringManipulationTaintStep extends AdditionalTaintStep, DataFlow::ValueNode {
|
||||
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
succ = this and
|
||||
(
|
||||
// string operations that propagate taint
|
||||
exists (string name | name = astNode.(MethodCallExpr).getMethodName() |
|
||||
pred.asExpr() = astNode.(MethodCallExpr).getReceiver() and
|
||||
( // sorted, interesting, properties of String.prototype
|
||||
|
||||
139
javascript/ql/src/semmle/javascript/frameworks/Logging.qll
Normal file
139
javascript/ql/src/semmle/javascript/frameworks/Logging.qll
Normal file
@@ -0,0 +1,139 @@
|
||||
/**
|
||||
* Provides classes for working with logging libraries.
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
/**
|
||||
* A call to a logging mechanism.
|
||||
*/
|
||||
abstract class LoggerCall extends DataFlow::CallNode {
|
||||
|
||||
/**
|
||||
* Gets a node that contributes to the logged message.
|
||||
*/
|
||||
abstract DataFlow::Node getAMessageComponent();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a log level name that is used in RFC5424, `npm`, `console`.
|
||||
*/
|
||||
private string getAStandardLoggerMethodName() {
|
||||
result = "crit" or
|
||||
result = "debug" or
|
||||
result = "error" or
|
||||
result = "emerg" or
|
||||
result = "fatal" or
|
||||
result = "info" or
|
||||
result = "log" or
|
||||
result = "notice" or
|
||||
result = "silly" or
|
||||
result = "trace" or
|
||||
result = "warn"
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides classes for working the builtin Node.js/Browser `console`.
|
||||
*/
|
||||
private module Console {
|
||||
|
||||
/**
|
||||
* Gets a data flow source node for the console library.
|
||||
*/
|
||||
private DataFlow::SourceNode console() {
|
||||
result = DataFlow::moduleImport("console") or
|
||||
result = DataFlow::globalVarRef("console")
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to the console logging mechanism.
|
||||
*/
|
||||
class ConsoleLoggerCall extends LoggerCall {
|
||||
string name;
|
||||
|
||||
ConsoleLoggerCall() {
|
||||
(
|
||||
name = getAStandardLoggerMethodName() or
|
||||
name = "assert"
|
||||
) and
|
||||
this = console().getAMethodCall(name)
|
||||
}
|
||||
|
||||
override DataFlow::Node getAMessageComponent() {
|
||||
if name = "assert" then
|
||||
result = getArgument([1..getNumArgument()])
|
||||
else
|
||||
result = getAnArgument()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides classes for working with [loglevel](https://github.com/pimterry/loglevel).
|
||||
*/
|
||||
private module Loglevel {
|
||||
|
||||
/**
|
||||
* A call to the loglevel logging mechanism.
|
||||
*/
|
||||
class LoglevelLoggerCall extends LoggerCall {
|
||||
LoglevelLoggerCall() {
|
||||
this = DataFlow::moduleMember("loglevel", getAStandardLoggerMethodName()).getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAMessageComponent() {
|
||||
result = getAnArgument()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Provides classes for working with [winston](https://github.com/winstonjs/winston).
|
||||
*/
|
||||
private module Winston {
|
||||
|
||||
/**
|
||||
* A call to the winston logging mechanism.
|
||||
*/
|
||||
class WinstonLoggerCall extends LoggerCall, DataFlow::MethodCallNode {
|
||||
WinstonLoggerCall() {
|
||||
this = DataFlow::moduleMember("winston", "createLogger").getACall().getAMethodCall(getAStandardLoggerMethodName())
|
||||
}
|
||||
|
||||
override DataFlow::Node getAMessageComponent() {
|
||||
if getMethodName() = "log" then
|
||||
result = getOptionArgument(0, "message")
|
||||
else
|
||||
result = getAnArgument()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides classes for working with [log4js](https://github.com/log4js-node/log4js-node).
|
||||
*/
|
||||
private module log4js {
|
||||
|
||||
/**
|
||||
* A call to the log4js logging mechanism.
|
||||
*/
|
||||
class Log4jsLoggerCall extends LoggerCall {
|
||||
Log4jsLoggerCall() {
|
||||
this = DataFlow::moduleMember("log4js", "getLogger").getACall().getAMethodCall(getAStandardLoggerMethodName())
|
||||
}
|
||||
|
||||
override DataFlow::Node getAMessageComponent() {
|
||||
result = getAnArgument()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -11,27 +11,38 @@
|
||||
|
||||
import javascript
|
||||
|
||||
/** A regular expression that identifies strings that look like they represent secret data that are not passwords. */
|
||||
private string suspiciousNonPassword() {
|
||||
result = "(?is).*(secret|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()
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
private string nonSuspicious() {
|
||||
result = "(?is).*(hash|(?<!un)encrypted|\\bcrypt\\b).*"
|
||||
module HeuristicNames {
|
||||
|
||||
/** A regular expression that identifies strings that look like they represent secret data that are not passwords. */
|
||||
string suspiciousNonPassword() {
|
||||
result = "(?is).*(secret|account|accnt|(?<!un)trusted).*"
|
||||
}
|
||||
|
||||
/** A regular expression that identifies strings that look like they represent secret data that are passwords. */
|
||||
string suspiciousPassword() {
|
||||
result = "(?is).*(password|passwd).*"
|
||||
}
|
||||
|
||||
/** A regular expression that identifies strings that look like they represent secret data. */
|
||||
string suspicious() {
|
||||
result = suspiciousPassword() or result = suspiciousNonPassword()
|
||||
}
|
||||
|
||||
/**
|
||||
* A regular expression that identifies strings that look like they represent data that is
|
||||
* hashed or encrypted.
|
||||
*/
|
||||
string nonSuspicious() {
|
||||
result = "(?is).*(redact|censor|obfuscate|hash|md5|sha|((?<!un)(en))?(crypt|code)).*"
|
||||
}
|
||||
|
||||
}
|
||||
private import HeuristicNames
|
||||
|
||||
/** An expression that might contain sensitive data. */
|
||||
abstract class SensitiveExpr extends Expr {
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
/**
|
||||
* Provides a dataflow tracking configuration for reasoning about clear-text logging of sensitive information.
|
||||
*/
|
||||
import javascript
|
||||
private import semmle.javascript.dataflow.InferredTypes
|
||||
private import semmle.javascript.security.SensitiveActions::HeuristicNames
|
||||
|
||||
module CleartextLogging {
|
||||
/**
|
||||
* A data flow source for clear-text logging of sensitive information.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node {
|
||||
/** Gets a string that describes the type of this data flow source. */
|
||||
abstract string describe();
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow sink for clear-text logging of sensitive information.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A barrier for clear-text logging of sensitive information.
|
||||
*/
|
||||
abstract class Barrier extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A dataflow tracking configuration for clear-text logging of sensitive information.
|
||||
*
|
||||
* This configuration identifies flows from `Source`s, which are sources of
|
||||
* sensitive data, to `Sink`s, which is an abstract class representing all
|
||||
* the places sensitive data may be stored in clear-text. Additional sources or sinks can be
|
||||
* added either by extending the relevant class, or by subclassing this configuration itself,
|
||||
* and amending the sources and sinks.
|
||||
*/
|
||||
class Configuration extends DataFlow::Configuration {
|
||||
Configuration() { this = "CleartextLogging" }
|
||||
|
||||
override
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
source instanceof Source
|
||||
}
|
||||
|
||||
override
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
sink instanceof Sink
|
||||
}
|
||||
|
||||
override
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
node instanceof Barrier
|
||||
}
|
||||
|
||||
override predicate isAdditionalFlowStep(DataFlow::Node src, DataFlow::Node trg) {
|
||||
any (TaintTracking::StringConcatenationTaintStep s).step(src, trg)
|
||||
or
|
||||
exists (string name | name = "toString" or name = "valueOf" |
|
||||
src.(DataFlow::SourceNode).getAMethodCall(name) = trg
|
||||
)
|
||||
or
|
||||
// A taint propagating data flow edge through objects: a tainted write taints the entire object.
|
||||
exists (DataFlow::PropWrite write |
|
||||
write.getRhs() = src and
|
||||
trg.(DataFlow::SourceNode).flowsTo(write.getBase())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An argument to a logging mechanism.
|
||||
*/
|
||||
class LoggerSink extends Sink {
|
||||
LoggerSink() {
|
||||
this = any(LoggerCall log).getAMessageComponent()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow node that does not contain a clear-text password, according to its syntactic name.
|
||||
*/
|
||||
private class NameGuidedNonCleartextPassword extends NonCleartextPassword {
|
||||
|
||||
NameGuidedNonCleartextPassword() {
|
||||
exists (string name |
|
||||
name.regexpMatch(nonSuspicious()) |
|
||||
this.asExpr().(VarAccess).getName() = name
|
||||
or
|
||||
this.(DataFlow::PropRead).getPropertyName() = name
|
||||
or
|
||||
this.(DataFlow::InvokeNode).getCalleeName() = name
|
||||
)
|
||||
or
|
||||
// avoid i18n strings
|
||||
this.(DataFlow::PropRead).getBase().asExpr().(VarRef).getName().regexpMatch("(?is).*(messages|strings).*")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow node that is definitely not an object.
|
||||
*/
|
||||
private class NonObject extends NonCleartextPassword {
|
||||
|
||||
NonObject() {
|
||||
forall (AbstractValue v | v = analyze().getAValue() |
|
||||
not v.getType() = TTObject()
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow node that receives flow that is not a clear-text password.
|
||||
*/
|
||||
private class NonCleartextPasswordFlow extends NonCleartextPassword {
|
||||
|
||||
NonCleartextPasswordFlow() {
|
||||
any(NonCleartextPassword other).(DataFlow::SourceNode).flowsTo(this)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A call that might obfuscate a password, for example through hashing.
|
||||
*/
|
||||
private class ObfuscatorCall extends Barrier, DataFlow::InvokeNode {
|
||||
|
||||
ObfuscatorCall() {
|
||||
getCalleeName().regexpMatch(nonSuspicious())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow node that does not contain a clear-text password.
|
||||
*/
|
||||
private abstract class NonCleartextPassword extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* An object with a property that may contain password information
|
||||
*
|
||||
* This is a source since `console.log(obj)` will show the properties of `obj`.
|
||||
*/
|
||||
private class ObjectPasswordPropertySource extends DataFlow::ValueNode, Source {
|
||||
string name;
|
||||
|
||||
ObjectPasswordPropertySource() {
|
||||
exists (DataFlow::PropWrite write |
|
||||
name.regexpMatch(suspiciousPassword()) and
|
||||
not name.regexpMatch(nonSuspicious()) and
|
||||
write = this.(DataFlow::SourceNode).getAPropertyWrite(name) and
|
||||
// avoid safe values assigned to presumably unsafe names
|
||||
not write.getRhs() instanceof NonCleartextPassword
|
||||
)
|
||||
}
|
||||
|
||||
override string describe() {
|
||||
result = "an access to " + name
|
||||
}
|
||||
}
|
||||
|
||||
/** An access to a variable or property that might contain a password. */
|
||||
private class ReadPasswordSource extends DataFlow::ValueNode, Source {
|
||||
string name;
|
||||
|
||||
ReadPasswordSource() {
|
||||
// avoid safe values assigned to presumably unsafe names
|
||||
not this instanceof NonCleartextPassword and
|
||||
name.regexpMatch(suspiciousPassword()) and
|
||||
(
|
||||
this.asExpr().(VarAccess).getName() = name
|
||||
or
|
||||
exists (DataFlow::SourceNode base |
|
||||
this = base.getAPropertyRead(name) and
|
||||
// avoid safe values assigned to presumably unsafe names
|
||||
exists (DataFlow::SourceNode baseObj | baseObj.flowsTo(base) |
|
||||
not base.getAPropertyWrite(name).getRhs() instanceof NonCleartextPassword
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override string describe() {
|
||||
result = "an access to " + name
|
||||
}
|
||||
}
|
||||
|
||||
/** A call that might return a password. */
|
||||
private class CallPasswordSource extends DataFlow::ValueNode, DataFlow::InvokeNode, Source {
|
||||
string name;
|
||||
|
||||
CallPasswordSource() {
|
||||
name = getCalleeName() and
|
||||
name.regexpMatch("(?is)getPassword")
|
||||
}
|
||||
|
||||
override string describe() {
|
||||
result = "a call to " + name
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
| tst.js:3:1:3:26 | console ... ", arg) | tst.js:3:13:3:20 | "msg %s" |
|
||||
| tst.js:3:1:3:26 | console ... ", arg) | tst.js:3:23:3:25 | arg |
|
||||
| tst.js:4:1:4:28 | console ... ", arg) | tst.js:4:15:4:22 | "msg %s" |
|
||||
| tst.js:4:1:4:28 | console ... ", arg) | tst.js:4:25:4:27 | arg |
|
||||
| tst.js:5:1:5:28 | console ... ", arg) | tst.js:5:15:5:22 | "msg %s" |
|
||||
| tst.js:5:1:5:28 | console ... ", arg) | tst.js:5:25:5:27 | arg |
|
||||
| tst.js:7:1:7:34 | require ... ", arg) | tst.js:7:21:7:28 | "msg %s" |
|
||||
| tst.js:7:1:7:34 | require ... ", arg) | tst.js:7:31:7:33 | arg |
|
||||
| tst.js:9:1:9:38 | require ... ", arg) | tst.js:9:25:9:32 | "msg %s" |
|
||||
| tst.js:9:1:9:38 | require ... ", arg) | tst.js:9:35:9:37 | arg |
|
||||
| tst.js:11:1:11:73 | require ... other) | tst.js:11:50:11:63 | "msg with arg" |
|
||||
| tst.js:12:1:12:53 | require ... ", arg) | tst.js:12:40:12:47 | "msg %s" |
|
||||
| tst.js:12:1:12:53 | require ... ", arg) | tst.js:12:50:12:52 | arg |
|
||||
| tst.js:14:1:14:48 | require ... ", arg) | tst.js:14:35:14:42 | "msg %s" |
|
||||
| tst.js:14:1:14:48 | require ... ", arg) | tst.js:14:45:14:47 | arg |
|
||||
| tst.js:16:1:16:35 | console ... ", arg) | tst.js:16:22:16:29 | "msg %s" |
|
||||
| tst.js:16:1:16:35 | console ... ", arg) | tst.js:16:32:16:34 | arg |
|
||||
@@ -0,0 +1,4 @@
|
||||
import javascript
|
||||
|
||||
from LoggerCall log
|
||||
select log, log.getAMessageComponent()
|
||||
16
javascript/ql/test/library-tests/frameworks/Logging/tst.js
Normal file
16
javascript/ql/test/library-tests/frameworks/Logging/tst.js
Normal file
@@ -0,0 +1,16 @@
|
||||
var requiredConsole = require("console");
|
||||
|
||||
console.log("msg %s", arg);
|
||||
console.fatal("msg %s", arg);
|
||||
console.debug("msg %s", arg);
|
||||
|
||||
requiredConsole.log("msg %s", arg);
|
||||
|
||||
require("loglevel").log("msg %s", arg);
|
||||
|
||||
require("winston").createLogger().log({ message: "msg with arg" }, other);
|
||||
require("winston").createLogger().info("msg %s", arg);
|
||||
|
||||
require("log4js").getLogger().log("msg %s", arg);
|
||||
|
||||
console.assert(true, "msg %s", arg);
|
||||
@@ -0,0 +1,26 @@
|
||||
| passwords.js:2:17:2:24 | password | Sensitive data returned by $@ is logged here. | passwords.js:2:17:2:24 | password | an access to password |
|
||||
| passwords.js:3:17:3:26 | o.password | Sensitive data returned by $@ is logged here. | passwords.js:3:17:3:26 | o.password | an access to password |
|
||||
| passwords.js:4:17:4:29 | getPassword() | Sensitive data returned by $@ is logged here. | passwords.js:4:17:4:29 | getPassword() | a call to getPassword |
|
||||
| passwords.js:5:17:5:31 | o.getPassword() | Sensitive data returned by $@ is logged here. | passwords.js:5:17:5:31 | o.getPassword() | a call to getPassword |
|
||||
| passwords.js:8:21:8:21 | x | Sensitive data returned by $@ is logged here. | passwords.js:10:11:10:18 | password | an access to password |
|
||||
| passwords.js:12:18:12:25 | password | Sensitive data returned by $@ is logged here. | passwords.js:12:18:12:25 | password | an access to password |
|
||||
| passwords.js:14:17:14:38 | name + ... assword | Sensitive data returned by $@ is logged here. | passwords.js:14:31:14:38 | password | an access to password |
|
||||
| passwords.js:16:17:16:38 | `${name ... sword}` | Sensitive data returned by $@ is logged here. | passwords.js:16:29:16:36 | password | an access to password |
|
||||
| passwords.js:21:17:21:20 | obj1 | Sensitive data returned by $@ is logged here. | passwords.js:18:16:20:5 | {\\n ... x\\n } | an access to password |
|
||||
| passwords.js:26:17:26:20 | obj2 | Sensitive data returned by $@ is logged here. | passwords.js:24:12:24:19 | password | an access to password |
|
||||
| passwords.js:29:17:29:20 | obj3 | Sensitive data returned by $@ is logged here. | passwords.js:30:14:30:21 | password | an access to password |
|
||||
| passwords.js:78:17:78:38 | temp.en ... assword | Sensitive data returned by $@ is logged here. | passwords.js:77:37:77:53 | req.body.password | an access to password |
|
||||
| passwords.js:81:17:81:31 | `pw: ${secret}` | Sensitive data returned by $@ is logged here. | passwords.js:80:18:80:25 | password | an access to password |
|
||||
| passwords.js:93:21:93:46 | "Passwo ... assword | Sensitive data returned by $@ is logged here. | passwords.js:93:39:93:46 | password | an access to password |
|
||||
| passwords.js:98:21:98:46 | "Passwo ... assword | Sensitive data returned by $@ is logged here. | passwords.js:98:39:98:46 | password | an access to password |
|
||||
| passwords.js:105:21:105:46 | "Passwo ... assword | Sensitive data returned by $@ is logged here. | passwords.js:105:39:105:46 | password | an access to password |
|
||||
| passwords.js:110:21:110:46 | "Passwo ... assword | Sensitive data returned by $@ is logged here. | passwords.js:110:39:110:46 | password | an access to password |
|
||||
| passwords.js:114:25:114:50 | "Passwo ... assword | Sensitive data returned by $@ is logged here. | passwords.js:114:43:114:50 | password | an access to password |
|
||||
| passwords.js:119:21:119:46 | "Passwo ... assword | Sensitive data returned by $@ is logged here. | passwords.js:119:39:119:46 | password | an access to password |
|
||||
| passwords.js:122:17:122:49 | name + ... tring() | Sensitive data returned by $@ is logged here. | passwords.js:122:31:122:38 | password | an access to password |
|
||||
| passwords.js:123:17:123:48 | name + ... lueOf() | Sensitive data returned by $@ is logged here. | passwords.js:123:31:123:38 | password | an access to password |
|
||||
| passwords_in_server_1.js:6:13:6:20 | password | Sensitive data returned by $@ is logged here. | passwords_in_server_1.js:6:13:6:20 | password | an access to password |
|
||||
| passwords_in_server_2.js:3:13:3:20 | password | Sensitive data returned by $@ is logged here. | passwords_in_server_2.js:3:13:3:20 | password | an access to password |
|
||||
| passwords_in_server_3.js:2:13:2:20 | password | Sensitive data returned by $@ is logged here. | passwords_in_server_3.js:2:13:2:20 | password | an access to password |
|
||||
| passwords_in_server_4.js:2:13:2:20 | password | Sensitive data returned by $@ is logged here. | passwords_in_server_4.js:2:13:2:20 | password | an access to password |
|
||||
| passwords_in_server_5.js:8:17:8:17 | x | Sensitive data returned by $@ is logged here. | passwords_in_server_5.js:4:7:4:24 | req.query.password | an access to password |
|
||||
@@ -0,0 +1 @@
|
||||
Security/CWE-312/CleartextLogging.ql
|
||||
@@ -0,0 +1,2 @@
|
||||
import foo from "foo";
|
||||
window.location;
|
||||
124
javascript/ql/test/query-tests/Security/CWE-312/passwords.js
Normal file
124
javascript/ql/test/query-tests/Security/CWE-312/passwords.js
Normal file
@@ -0,0 +1,124 @@
|
||||
(function() {
|
||||
console.log(password); // NOT OK
|
||||
console.log(o.password); // NOT OK
|
||||
console.log(getPassword()); // NOT OK
|
||||
console.log(o.getPassword()); // NOT OK
|
||||
|
||||
function myLog(x) {
|
||||
console.log(x); // NOT OK
|
||||
}
|
||||
myLog(password);
|
||||
|
||||
console.info(password); // NOT OK
|
||||
|
||||
console.log(name + ", " + password); // NOT OK
|
||||
|
||||
console.log(`${name}, ${password}`); // NOT OK
|
||||
|
||||
var obj1 = {
|
||||
password: x
|
||||
};
|
||||
console.log(obj1); // NOT OK
|
||||
|
||||
var obj2 = {
|
||||
x: password
|
||||
};
|
||||
console.log(obj2); // NOT OK
|
||||
|
||||
var obj3 = {};
|
||||
console.log(obj3);
|
||||
obj3.x = password; // NOT OK
|
||||
|
||||
var fixed_password = "123";
|
||||
console.log(fixed_password); // OK
|
||||
|
||||
console.log(messages.IncorrectPasswordError); // OK
|
||||
|
||||
console.log(this.hashed_password); // OK
|
||||
console.log(login.wrappedJSObject.encryptedPassword); // OK
|
||||
console.log(HTML5QQ.encodedPassword); // OK
|
||||
|
||||
console.log({password: crypt(pw)}); // OK
|
||||
var actually_secure_password = crypt(password); // OK
|
||||
console.log(actually_secure_password); // OK
|
||||
|
||||
var user1 = {};
|
||||
user1.crypted_password = x();
|
||||
console.log(user1); // OK
|
||||
|
||||
var user2 = {};
|
||||
user2.password = hash();
|
||||
console.log(user2); // OK
|
||||
|
||||
var user3 = {
|
||||
password: encryptLib.encryptPassword(req.body.password)
|
||||
};
|
||||
console.log(user3); // OK
|
||||
|
||||
var actually_ok_password_1 = hashed1();
|
||||
console.log(actually_ok_password_1);
|
||||
var hashed2 = actually_ok_password_2();
|
||||
console.log(hashed2); // OK
|
||||
|
||||
console.log(this._passwordMD5); // OK
|
||||
console.log(this._password_sha); // OK
|
||||
|
||||
var utilityObject = {
|
||||
passwordSet: function(){}
|
||||
}
|
||||
console.log(utilityObject); // OK
|
||||
|
||||
var displayedPassword = password? null: "(hidden)";
|
||||
var config = {
|
||||
password: displayedPassword
|
||||
};
|
||||
console.log(config); // OK
|
||||
|
||||
var temp = { encryptedPassword: req.body.password };
|
||||
console.log(temp.encryptedPassword); // OK XXX
|
||||
|
||||
var secret = password;
|
||||
console.log(`pw: ${secret}`); // NOT OK
|
||||
|
||||
function redact(kind, value) {
|
||||
if (value && kind == 'password') {
|
||||
return "********"
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
console.log("Password is: " + redact('password', password));
|
||||
|
||||
if (environment.isTestEnv()) {
|
||||
console.log("Password is: " + password); // OK, but still flagged
|
||||
}
|
||||
|
||||
if (environment.is(TEST)) {
|
||||
// NB: for security reasons, we only log passwords in test environments
|
||||
console.log("Password is: " + password); // OK, but still flagged
|
||||
}
|
||||
|
||||
|
||||
if (x.test(y)) {
|
||||
f();
|
||||
// ...
|
||||
console.log("Password is: " + password); // NOT OK
|
||||
// ...
|
||||
}
|
||||
|
||||
if (environment.isTestEnv())
|
||||
console.log("Password is: " + password); // OK, but still flagged
|
||||
|
||||
if (x.test(y)) {
|
||||
if (f()) {
|
||||
console.log("Password is: " + password); // NOT OK
|
||||
}
|
||||
}
|
||||
|
||||
if (!environment.isProduction()) {
|
||||
console.log("Password is: " + password); // OK, but still flagged
|
||||
}
|
||||
|
||||
console.log(name + ", " + password.toString()); // NOT OK
|
||||
console.log(name + ", " + password.valueOf()); // NOT OK
|
||||
});
|
||||
@@ -0,0 +1,2 @@
|
||||
window.location;
|
||||
console.log(password);
|
||||
@@ -0,0 +1,2 @@
|
||||
import browser from "./browser";
|
||||
console.log(password);
|
||||
@@ -0,0 +1,6 @@
|
||||
var express = require('express');
|
||||
var app = express();
|
||||
app.get('/some/path', function() {
|
||||
})
|
||||
|
||||
console.log(password);
|
||||
@@ -0,0 +1,3 @@
|
||||
require("foo");
|
||||
(function (req, res){});
|
||||
console.log(password);
|
||||
@@ -0,0 +1,2 @@
|
||||
var server = require("./server");
|
||||
console.log(password);
|
||||
@@ -0,0 +1,2 @@
|
||||
require("foo");
|
||||
console.log(password);
|
||||
@@ -0,0 +1,9 @@
|
||||
var express = require('express');
|
||||
var app = express();
|
||||
app.get('/some/path', function() {
|
||||
f(req.query.password);
|
||||
})
|
||||
|
||||
function f(x) {
|
||||
console.log(x);
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
require("foo");
|
||||
(function (req, res){})
|
||||
@@ -0,0 +1,2 @@
|
||||
require("./server.js")
|
||||
require("./passwords_in_server_4.js")
|
||||
Reference in New Issue
Block a user