mirror of
https://github.com/github/codeql.git
synced 2026-04-27 09:45:15 +02:00
Merge branch 'main' into redsun82/update-fmt
This commit is contained in:
4
cpp/ql/lib/change-notes/2025-11-19-content.md
Normal file
4
cpp/ql/lib/change-notes/2025-11-19-content.md
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The class `DataFlow::FieldContent` now covers both `union` and `struct`/`class` types. A new predicate `FieldContent.getAField` has been added to access the union members associated with the `FieldContent`. The old `FieldContent` has been renamed to `NonUnionFieldContent`.
|
||||
@@ -97,7 +97,7 @@ class Compilation extends @compilation {
|
||||
/**
|
||||
* Gets an expanded argument passed to the extractor on this invocation.
|
||||
*/
|
||||
string getAnExpandedArgument() { result = this.getArgument(_) }
|
||||
string getAnExpandedArgument() { result = this.getExpandedArgument(_) }
|
||||
|
||||
/**
|
||||
* Gets the `i`th expanded argument passed to the extractor on this
|
||||
@@ -107,7 +107,11 @@ class Compilation extends @compilation {
|
||||
* includes the arguments from that file, rather than just taking the
|
||||
* argument literally.
|
||||
*/
|
||||
string getExpandedArgument(int i) { compilation_expanded_args(this, i, result) }
|
||||
string getExpandedArgument(int i) {
|
||||
if exists(string arg | compilation_expanded_args(this, _, arg))
|
||||
then compilation_expanded_args(this, i, result)
|
||||
else result = this.getArgument(i)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the total amount of CPU time spent processing all the files in the
|
||||
|
||||
@@ -861,6 +861,10 @@ predicate jumpStep(Node n1, Node n2) {
|
||||
n2.(FlowSummaryNode).getSummaryNode())
|
||||
}
|
||||
|
||||
bindingset[c]
|
||||
pragma[inline_late]
|
||||
private int getIndirectionIndexLate(Content c) { result = c.getIndirectionIndex() }
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` via an assignment to `f`.
|
||||
* Thus, `node2` references an object with a field `f` that contains the
|
||||
@@ -873,23 +877,17 @@ predicate jumpStep(Node n1, Node n2) {
|
||||
predicate storeStepImpl(Node node1, Content c, Node node2, boolean certain) {
|
||||
exists(
|
||||
PostFieldUpdateNode postFieldUpdate, int indirectionIndex1, int numberOfLoads,
|
||||
StoreInstruction store
|
||||
StoreInstruction store, FieldContent fc
|
||||
|
|
||||
postFieldUpdate = node2 and
|
||||
nodeHasInstruction(node1, store, pragma[only_bind_into](indirectionIndex1)) and
|
||||
fc = c and
|
||||
nodeHasInstruction(node1, pragma[only_bind_into](store),
|
||||
pragma[only_bind_into](indirectionIndex1)) and
|
||||
postFieldUpdate.getIndirectionIndex() = 1 and
|
||||
numberOfLoadsFromOperand(postFieldUpdate.getFieldAddress(),
|
||||
store.getDestinationAddressOperand(), numberOfLoads, certain)
|
||||
|
|
||||
exists(FieldContent fc | fc = c |
|
||||
fc.getField() = postFieldUpdate.getUpdatedField() and
|
||||
fc.getIndirectionIndex() = 1 + indirectionIndex1 + numberOfLoads
|
||||
)
|
||||
or
|
||||
exists(UnionContent uc | uc = c |
|
||||
uc.getAField() = postFieldUpdate.getUpdatedField() and
|
||||
uc.getIndirectionIndex() = 1 + indirectionIndex1 + numberOfLoads
|
||||
)
|
||||
store.getDestinationAddressOperand(), numberOfLoads, certain) and
|
||||
fc.getAField() = postFieldUpdate.getUpdatedField() and
|
||||
getIndirectionIndexLate(fc) = 1 + indirectionIndex1 + numberOfLoads
|
||||
)
|
||||
or
|
||||
// models-as-data summarized flow
|
||||
@@ -965,22 +963,17 @@ predicate nodeHasInstruction(Node node, Instruction instr, int indirectionIndex)
|
||||
* `node2`.
|
||||
*/
|
||||
predicate readStep(Node node1, ContentSet c, Node node2) {
|
||||
exists(FieldAddress fa1, Operand operand, int numberOfLoads, int indirectionIndex2 |
|
||||
exists(
|
||||
FieldAddress fa1, Operand operand, int numberOfLoads, int indirectionIndex2, FieldContent fc
|
||||
|
|
||||
fc = c and
|
||||
nodeHasOperand(node2, operand, indirectionIndex2) and
|
||||
// The `1` here matches the `node2.getIndirectionIndex() = 1` conjunct
|
||||
// in `storeStep`.
|
||||
nodeHasOperand(node1, fa1.getObjectAddressOperand(), 1) and
|
||||
numberOfLoadsFromOperand(fa1, operand, numberOfLoads, _)
|
||||
|
|
||||
exists(FieldContent fc | fc = c |
|
||||
fc.getField() = fa1.getField() and
|
||||
fc.getIndirectionIndex() = indirectionIndex2 + numberOfLoads
|
||||
)
|
||||
or
|
||||
exists(UnionContent uc | uc = c |
|
||||
uc.getAField() = fa1.getField() and
|
||||
uc.getIndirectionIndex() = indirectionIndex2 + numberOfLoads
|
||||
)
|
||||
numberOfLoadsFromOperand(fa1, operand, numberOfLoads, _) and
|
||||
fc.getAField() = fa1.getField() and
|
||||
getIndirectionIndexLate(fc) = indirectionIndex2 + numberOfLoads
|
||||
)
|
||||
or
|
||||
// models-as-data summarized flow
|
||||
@@ -1574,7 +1567,7 @@ pragma[inline]
|
||||
ContentApprox getContentApprox(Content c) {
|
||||
exists(string prefix, Field f |
|
||||
prefix = result.(FieldApproxContent).getPrefix() and
|
||||
f = c.(FieldContent).getField() and
|
||||
f = c.(NonUnionFieldContent).getField() and
|
||||
fieldHasApproxName(f, prefix)
|
||||
)
|
||||
or
|
||||
|
||||
@@ -2093,8 +2093,8 @@ private Field getAFieldWithSize(Union u, int bytes) {
|
||||
|
||||
cached
|
||||
private newtype TContent =
|
||||
TFieldContent(Field f, int indirectionIndex) {
|
||||
// the indirection index for field content starts at 1 (because `TFieldContent` is thought of as
|
||||
TNonUnionContent(Field f, int indirectionIndex) {
|
||||
// the indirection index for field content starts at 1 (because `TNonUnionContent` is thought of as
|
||||
// the address of the field, `FieldAddress` in the IR).
|
||||
indirectionIndex = [1 .. SsaImpl::getMaxIndirectionsForType(f.getUnspecifiedType())] and
|
||||
// Reads and writes of union fields are tracked using `UnionContent`.
|
||||
@@ -2124,14 +2124,14 @@ private newtype TContent =
|
||||
*/
|
||||
class Content extends TContent {
|
||||
/** Gets a textual representation of this element. */
|
||||
abstract string toString();
|
||||
string toString() { none() } // overridden in subclasses
|
||||
|
||||
predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
|
||||
path = "" and sl = 0 and sc = 0 and el = 0 and ec = 0
|
||||
}
|
||||
|
||||
/** Gets the indirection index of this `Content`. */
|
||||
abstract int getIndirectionIndex();
|
||||
int getIndirectionIndex() { none() } // overridden in subclasses
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
@@ -2142,7 +2142,7 @@ class Content extends TContent {
|
||||
* For example, a write to a field `f` implies that any content of
|
||||
* the form `*f` is also cleared.
|
||||
*/
|
||||
abstract predicate impliesClearOf(Content c);
|
||||
predicate impliesClearOf(Content c) { none() } // overridden in subclasses
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2162,22 +2162,42 @@ private module ContentStars {
|
||||
|
||||
private import ContentStars
|
||||
|
||||
/** A reference through a non-union instance field. */
|
||||
private class TFieldContent = TNonUnionContent or TUnionContent;
|
||||
|
||||
/**
|
||||
* A `Content` that references a `Field`. This may be a field of a `struct`,
|
||||
* `class`, or `union`. In the case of a `union` there may be multiple fields
|
||||
* associated with the same `Content`.
|
||||
*/
|
||||
class FieldContent extends Content, TFieldContent {
|
||||
/** Gets a `Field` of this `Content`. */
|
||||
Field getAField() { none() }
|
||||
|
||||
/**
|
||||
* Gets the field associated with this `Content`, if a unique one exists.
|
||||
*/
|
||||
final Field getField() { result = unique( | | this.getAField()) }
|
||||
|
||||
override int getIndirectionIndex() { none() } // overridden in subclasses
|
||||
|
||||
override string toString() { none() } // overridden in subclasses
|
||||
|
||||
override predicate impliesClearOf(Content c) { none() } // overridden in subclasses
|
||||
}
|
||||
|
||||
/** A reference through a non-union instance field. */
|
||||
class NonUnionFieldContent extends FieldContent, TNonUnionContent {
|
||||
private Field f;
|
||||
private int indirectionIndex;
|
||||
|
||||
FieldContent() { this = TFieldContent(f, indirectionIndex) }
|
||||
NonUnionFieldContent() { this = TNonUnionContent(f, indirectionIndex) }
|
||||
|
||||
override string toString() { result = contentStars(this) + f.toString() }
|
||||
|
||||
Field getField() { result = f }
|
||||
override Field getAField() { result = f }
|
||||
|
||||
/** Gets the indirection index of this `FieldContent`. */
|
||||
pragma[inline]
|
||||
override int getIndirectionIndex() {
|
||||
pragma[only_bind_into](result) = pragma[only_bind_out](indirectionIndex)
|
||||
}
|
||||
override int getIndirectionIndex() { result = indirectionIndex }
|
||||
|
||||
override predicate impliesClearOf(Content c) {
|
||||
exists(FieldContent fc |
|
||||
@@ -2191,7 +2211,7 @@ class FieldContent extends Content, TFieldContent {
|
||||
}
|
||||
|
||||
/** A reference through an instance field of a union. */
|
||||
class UnionContent extends Content, TUnionContent {
|
||||
class UnionContent extends FieldContent, TUnionContent {
|
||||
private Union u;
|
||||
private int indirectionIndex;
|
||||
private int bytes;
|
||||
@@ -2201,16 +2221,13 @@ class UnionContent extends Content, TUnionContent {
|
||||
override string toString() { result = contentStars(this) + u.toString() }
|
||||
|
||||
/** Gets a field of the underlying union of this `UnionContent`, if any. */
|
||||
Field getAField() { result = u.getAField() and getFieldSize(result) = bytes }
|
||||
override Field getAField() { result = u.getAField() and getFieldSize(result) = bytes }
|
||||
|
||||
/** Gets the underlying union of this `UnionContent`. */
|
||||
Union getUnion() { result = u }
|
||||
|
||||
/** Gets the indirection index of this `UnionContent`. */
|
||||
pragma[inline]
|
||||
override int getIndirectionIndex() {
|
||||
pragma[only_bind_into](result) = pragma[only_bind_out](indirectionIndex)
|
||||
}
|
||||
override int getIndirectionIndex() { result = indirectionIndex }
|
||||
|
||||
override predicate impliesClearOf(Content c) {
|
||||
exists(UnionContent uc |
|
||||
@@ -2234,10 +2251,7 @@ class ElementContent extends Content, TElementContent {
|
||||
|
||||
ElementContent() { this = TElementContent(indirectionIndex) }
|
||||
|
||||
pragma[inline]
|
||||
override int getIndirectionIndex() {
|
||||
pragma[only_bind_into](result) = pragma[only_bind_out](indirectionIndex)
|
||||
}
|
||||
override int getIndirectionIndex() { result = indirectionIndex }
|
||||
|
||||
override predicate impliesClearOf(Content c) { none() }
|
||||
|
||||
|
||||
@@ -190,7 +190,7 @@ module ModelGeneratorCommonInput implements ModelGeneratorCommonInputSig<Cpp::Lo
|
||||
predicate isRelevantType(Type t) { any() }
|
||||
|
||||
Type getUnderlyingContentType(DataFlow::ContentSet c) {
|
||||
result = c.(DataFlow::FieldContent).getField().getUnspecifiedType() or
|
||||
result = c.(DataFlow::NonUnionFieldContent).getField().getUnspecifiedType() or
|
||||
result = c.(DataFlow::UnionContent).getUnion().getUnspecifiedType()
|
||||
}
|
||||
|
||||
@@ -340,12 +340,7 @@ private module SummaryModelGeneratorInput implements SummaryModelGeneratorInputS
|
||||
)
|
||||
}
|
||||
|
||||
predicate isField(DataFlow::ContentSet cs) {
|
||||
exists(DataFlow::Content c | cs.isSingleton(c) |
|
||||
c instanceof DataFlow::FieldContent or
|
||||
c instanceof DataFlow::UnionContent
|
||||
)
|
||||
}
|
||||
predicate isField(DataFlow::ContentSet cs) { cs.isSingleton(any(DataFlow::FieldContent fc)) }
|
||||
|
||||
predicate isCallback(DataFlow::ContentSet c) { none() }
|
||||
|
||||
|
||||
@@ -18,7 +18,9 @@ ql/go/ql/src/Security/CWE-295/DisabledCertificateCheck.ql
|
||||
ql/go/ql/src/Security/CWE-312/CleartextLogging.ql
|
||||
ql/go/ql/src/Security/CWE-322/InsecureHostKeyCallback.ql
|
||||
ql/go/ql/src/Security/CWE-326/InsufficientKeySize.ql
|
||||
ql/go/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.ql
|
||||
ql/go/ql/src/Security/CWE-327/InsecureTLS.ql
|
||||
ql/go/ql/src/Security/CWE-327/WeakSensitiveDataHashing.ql
|
||||
ql/go/ql/src/Security/CWE-338/InsecureRandomness.ql
|
||||
ql/go/ql/src/Security/CWE-347/MissingJwtSignatureCheck.ql
|
||||
ql/go/ql/src/Security/CWE-352/ConstantOauth2State.ql
|
||||
|
||||
@@ -41,7 +41,9 @@ ql/go/ql/src/Security/CWE-295/DisabledCertificateCheck.ql
|
||||
ql/go/ql/src/Security/CWE-312/CleartextLogging.ql
|
||||
ql/go/ql/src/Security/CWE-322/InsecureHostKeyCallback.ql
|
||||
ql/go/ql/src/Security/CWE-326/InsufficientKeySize.ql
|
||||
ql/go/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.ql
|
||||
ql/go/ql/src/Security/CWE-327/InsecureTLS.ql
|
||||
ql/go/ql/src/Security/CWE-327/WeakSensitiveDataHashing.ql
|
||||
ql/go/ql/src/Security/CWE-338/InsecureRandomness.ql
|
||||
ql/go/ql/src/Security/CWE-347/MissingJwtSignatureCheck.ql
|
||||
ql/go/ql/src/Security/CWE-352/ConstantOauth2State.ql
|
||||
|
||||
@@ -19,7 +19,9 @@ ql/go/ql/src/Security/CWE-295/DisabledCertificateCheck.ql
|
||||
ql/go/ql/src/Security/CWE-312/CleartextLogging.ql
|
||||
ql/go/ql/src/Security/CWE-322/InsecureHostKeyCallback.ql
|
||||
ql/go/ql/src/Security/CWE-326/InsufficientKeySize.ql
|
||||
ql/go/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.ql
|
||||
ql/go/ql/src/Security/CWE-327/InsecureTLS.ql
|
||||
ql/go/ql/src/Security/CWE-327/WeakSensitiveDataHashing.ql
|
||||
ql/go/ql/src/Security/CWE-338/InsecureRandomness.ql
|
||||
ql/go/ql/src/Security/CWE-347/MissingJwtSignatureCheck.ql
|
||||
ql/go/ql/src/Security/CWE-352/ConstantOauth2State.ql
|
||||
|
||||
@@ -14,7 +14,6 @@ ql/go/ql/src/experimental/CWE-203/Timing.ql
|
||||
ql/go/ql/src/experimental/CWE-285/PamAuthBypass.ql
|
||||
ql/go/ql/src/experimental/CWE-287/ImproperLdapAuth.ql
|
||||
ql/go/ql/src/experimental/CWE-321-V2/HardCodedKeys.ql
|
||||
ql/go/ql/src/experimental/CWE-327/WeakCryptoAlgorithm.ql
|
||||
ql/go/ql/src/experimental/CWE-369/DivideByZero.ql
|
||||
ql/go/ql/src/experimental/CWE-400/DatabaseCallInLoop.ql
|
||||
ql/go/ql/src/experimental/CWE-522-DecompressionBombs/DecompressionBombs.ql
|
||||
|
||||
@@ -33,6 +33,7 @@ import semmle.go.frameworks.AwsLambda
|
||||
import semmle.go.frameworks.Beego
|
||||
import semmle.go.frameworks.BeegoOrm
|
||||
import semmle.go.frameworks.Bun
|
||||
import semmle.go.frameworks.CryptoLibraries
|
||||
import semmle.go.frameworks.RsCors
|
||||
import semmle.go.frameworks.Couchbase
|
||||
import semmle.go.frameworks.Echo
|
||||
|
||||
@@ -6,6 +6,7 @@ extractor: go
|
||||
library: true
|
||||
upgrades: upgrades
|
||||
dependencies:
|
||||
codeql/concepts: ${workspace}
|
||||
codeql/dataflow: ${workspace}
|
||||
codeql/mad: ${workspace}
|
||||
codeql/threat-models: ${workspace}
|
||||
|
||||
@@ -8,6 +8,10 @@ import go
|
||||
import semmle.go.dataflow.FunctionInputsAndOutputs
|
||||
import semmle.go.concepts.HTTP
|
||||
import semmle.go.concepts.GeneratedFile
|
||||
private import codeql.concepts.ConceptsShared
|
||||
private import semmle.go.dataflow.internal.DataFlowImplSpecific
|
||||
|
||||
private module ConceptsShared = ConceptsMake<Location, GoDataFlow>;
|
||||
|
||||
/**
|
||||
* A data-flow node that executes an operating system command,
|
||||
@@ -505,3 +509,98 @@ module UnmarshalingFunction {
|
||||
abstract string getFormat();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides models for cryptographic things.
|
||||
*/
|
||||
module Cryptography {
|
||||
private import ConceptsShared::Cryptography as SC
|
||||
|
||||
/**
|
||||
* A data-flow node that is an application of a cryptographic algorithm. For example,
|
||||
* encryption, decryption, signature-validation.
|
||||
*
|
||||
* Extend this class to refine existing API models. If you want to model new APIs,
|
||||
* extend `CryptographicOperation::Range` instead.
|
||||
*/
|
||||
class CryptographicOperation extends SC::CryptographicOperation { }
|
||||
|
||||
class EncryptionAlgorithm = SC::EncryptionAlgorithm;
|
||||
|
||||
class HashingAlgorithm = SC::HashingAlgorithm;
|
||||
|
||||
class PasswordHashingAlgorithm = SC::PasswordHashingAlgorithm;
|
||||
|
||||
module CryptographicOperation = SC::CryptographicOperation;
|
||||
|
||||
class BlockMode = SC::BlockMode;
|
||||
|
||||
class CryptographicAlgorithm = SC::CryptographicAlgorithm;
|
||||
|
||||
/** A data flow node that initializes a hash algorithm. */
|
||||
abstract class HashAlgorithmInit extends DataFlow::Node {
|
||||
/** Gets the hash algorithm being initialized. */
|
||||
abstract HashingAlgorithm getAlgorithm();
|
||||
}
|
||||
|
||||
/** A data flow node that is an application of a hash algorithm. */
|
||||
abstract class HashOperation extends CryptographicOperation::Range {
|
||||
override BlockMode getBlockMode() { none() }
|
||||
}
|
||||
|
||||
/** A data flow node that initializes an encryption algorithm. */
|
||||
abstract class EncryptionAlgorithmInit extends DataFlow::Node {
|
||||
/** Gets the encryption algorithm being initialized. */
|
||||
abstract EncryptionAlgorithm getAlgorithm();
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow node that initializes a block cipher mode of operation, and
|
||||
* may also propagate taint for encryption algorithms.
|
||||
*/
|
||||
abstract class BlockModeInit extends DataFlow::CallNode {
|
||||
/** Gets the block cipher mode of operation being initialized. */
|
||||
abstract BlockMode getMode();
|
||||
|
||||
/** Gets a step propagating the encryption algorithm through this call. */
|
||||
abstract predicate step(DataFlow::Node node1, DataFlow::Node node2);
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow node that is an application of an encryption algorithm, where
|
||||
* the encryption algorithm and the block cipher mode of operation (if there
|
||||
* is one) have been initialized separately.
|
||||
*/
|
||||
abstract class EncryptionOperation extends CryptographicOperation::Range {
|
||||
DataFlow::Node encryptionFlowTarget;
|
||||
DataFlow::Node inputNode;
|
||||
|
||||
override DataFlow::Node getInitialization() {
|
||||
EncryptionFlow::flow(result, encryptionFlowTarget)
|
||||
}
|
||||
|
||||
override EncryptionAlgorithm getAlgorithm() {
|
||||
result = this.getInitialization().(EncryptionAlgorithmInit).getAlgorithm()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAnInput() { result = inputNode }
|
||||
|
||||
override BlockMode getBlockMode() {
|
||||
result = this.getInitialization().(BlockModeInit).getMode()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An `EncryptionOperation` which is a method call where the encryption
|
||||
* algorithm and block cipher mode of operation (if there is one) flow to the
|
||||
* receiver and the input is an argument.
|
||||
*/
|
||||
abstract class EncryptionMethodCall extends EncryptionOperation instanceof DataFlow::CallNode {
|
||||
int inputArg;
|
||||
|
||||
EncryptionMethodCall() {
|
||||
encryptionFlowTarget = super.getReceiver() and
|
||||
inputNode = super.getArgument(inputArg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
484
go/ql/lib/semmle/go/frameworks/CryptoLibraries.qll
Normal file
484
go/ql/lib/semmle/go/frameworks/CryptoLibraries.qll
Normal file
@@ -0,0 +1,484 @@
|
||||
/**
|
||||
* Provides classes for modeling cryptographic libraries.
|
||||
*/
|
||||
|
||||
import go
|
||||
import semmle.go.Concepts::Cryptography
|
||||
private import codeql.concepts.internal.CryptoAlgorithmNames
|
||||
|
||||
/**
|
||||
* A data flow call node that is an application of a hash operation where the
|
||||
* hash algorithm is defined in any earlier initialization node, and the input
|
||||
* is the first argument of the call.
|
||||
*/
|
||||
abstract class DirectHashOperation extends HashOperation instanceof DataFlow::CallNode {
|
||||
override DataFlow::Node getInitialization() { result = this }
|
||||
|
||||
override DataFlow::Node getAnInput() { result = super.getArgument(0) }
|
||||
}
|
||||
|
||||
private module HashConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof HashAlgorithmInit }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { any() }
|
||||
}
|
||||
|
||||
/** Tracks the flow of hash algorithms. */
|
||||
module HashFlow = DataFlow::Global<HashConfig>;
|
||||
|
||||
/**
|
||||
* A data flow node that initializes a block mode and propagates the encryption
|
||||
* algorithm from the first argument to the receiver.
|
||||
*/
|
||||
abstract class StdLibNewEncrypter extends BlockModeInit {
|
||||
override predicate step(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
node1 = this.getArgument(0) and
|
||||
node2 = this.getResult(0)
|
||||
}
|
||||
}
|
||||
|
||||
private module EncryptionConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
source instanceof EncryptionAlgorithmInit or
|
||||
source instanceof BlockModeInit
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { any() }
|
||||
|
||||
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
any(BlockModeInit nbcm).step(node1, node2)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tracks algorithms and block cipher modes of operation used for encryption.
|
||||
*/
|
||||
module EncryptionFlow = DataFlow::Global<EncryptionConfig>;
|
||||
|
||||
private module Crypto {
|
||||
private module Aes {
|
||||
private class NewCipher extends EncryptionAlgorithmInit {
|
||||
NewCipher() {
|
||||
exists(Function f | this = f.getACall().getResult(0) |
|
||||
f.hasQualifiedName("crypto/aes", "NewCipher")
|
||||
)
|
||||
}
|
||||
|
||||
override EncryptionAlgorithm getAlgorithm() { result.matchesName("AES") }
|
||||
}
|
||||
}
|
||||
|
||||
private module Des {
|
||||
private class NewCipher extends EncryptionAlgorithmInit {
|
||||
NewCipher() {
|
||||
exists(Function f | this = f.getACall().getResult(0) |
|
||||
f.hasQualifiedName("crypto/des", "NewCipher")
|
||||
)
|
||||
}
|
||||
|
||||
override EncryptionAlgorithm getAlgorithm() { result.matchesName("DES") }
|
||||
}
|
||||
|
||||
private class NewTripleDESCipher extends EncryptionAlgorithmInit {
|
||||
NewTripleDESCipher() {
|
||||
exists(Function f | this = f.getACall().getResult(0) |
|
||||
f.hasQualifiedName("crypto/des", "NewTripleDESCipher")
|
||||
)
|
||||
}
|
||||
|
||||
override EncryptionAlgorithm getAlgorithm() { result.matchesName("TRIPLEDES") }
|
||||
}
|
||||
}
|
||||
|
||||
private module Md5 {
|
||||
private class Sum extends DirectHashOperation instanceof DataFlow::CallNode {
|
||||
Sum() { this.getTarget().hasQualifiedName("crypto/md5", "Sum") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("MD5") }
|
||||
}
|
||||
|
||||
private class New extends HashAlgorithmInit instanceof DataFlow::CallNode {
|
||||
New() { this.getTarget().hasQualifiedName("crypto/md5", "New") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("MD5") }
|
||||
}
|
||||
}
|
||||
|
||||
private module Rc4 {
|
||||
private class CipherXorKeyStream extends CryptographicOperation::Range instanceof DataFlow::CallNode
|
||||
{
|
||||
CipherXorKeyStream() {
|
||||
this.(DataFlow::MethodCallNode)
|
||||
.getTarget()
|
||||
.hasQualifiedName("crypto/rc4", "Cipher", "XORKeyStream")
|
||||
}
|
||||
|
||||
override DataFlow::Node getInitialization() { result = this }
|
||||
|
||||
override EncryptionAlgorithm getAlgorithm() { result.matchesName("RC4") }
|
||||
|
||||
override DataFlow::Node getAnInput() { result = super.getArgument(1) }
|
||||
|
||||
override BlockMode getBlockMode() { none() }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cryptographic operations from the `crypto/sha1` package.
|
||||
*/
|
||||
private module Sha1 {
|
||||
private class Sum extends DirectHashOperation instanceof DataFlow::CallNode {
|
||||
Sum() { this.getTarget().hasQualifiedName("crypto/sha1", "Sum") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("SHA1") }
|
||||
}
|
||||
|
||||
private class New extends HashAlgorithmInit instanceof DataFlow::CallNode {
|
||||
New() { this.getTarget().hasQualifiedName("crypto/sha1", "New") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("SHA1") }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cryptographic operations from the `crypto/sha256` package.
|
||||
*/
|
||||
private module Sha256 {
|
||||
private class Sum256 extends DirectHashOperation instanceof DataFlow::CallNode {
|
||||
Sum256() { this.getTarget().hasQualifiedName("crypto/sha256", "Sum256") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("SHA256") }
|
||||
}
|
||||
|
||||
private class Sum224 extends DirectHashOperation instanceof DataFlow::CallNode {
|
||||
Sum224() { this.getTarget().hasQualifiedName("crypto/sha256", "Sum224") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("SHA224") }
|
||||
}
|
||||
|
||||
private class New extends HashAlgorithmInit instanceof DataFlow::CallNode {
|
||||
New() { this.getTarget().hasQualifiedName("crypto/sha256", "New") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("SHA256") }
|
||||
}
|
||||
|
||||
private class New224 extends HashAlgorithmInit instanceof DataFlow::CallNode {
|
||||
New224() { this.getTarget().hasQualifiedName("crypto/sha256", "New224") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("SHA224") }
|
||||
}
|
||||
}
|
||||
|
||||
private module Sha3 {
|
||||
private class Sum224 extends DirectHashOperation instanceof DataFlow::CallNode {
|
||||
Sum224() { this.getTarget().hasQualifiedName("crypto/sha3", "Sum224") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("SHA3224") }
|
||||
}
|
||||
|
||||
private class Sum256 extends DirectHashOperation instanceof DataFlow::CallNode {
|
||||
Sum256() { this.getTarget().hasQualifiedName("crypto/sha3", "Sum256") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("SHA3256") }
|
||||
}
|
||||
|
||||
private class Sum384 extends DirectHashOperation instanceof DataFlow::CallNode {
|
||||
Sum384() { this.getTarget().hasQualifiedName("crypto/sha3", "Sum384") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("SHA3384") }
|
||||
}
|
||||
|
||||
private class Sum512 extends DirectHashOperation instanceof DataFlow::CallNode {
|
||||
Sum512() { this.getTarget().hasQualifiedName("crypto/sha3", "Sum512") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("SHA3512") }
|
||||
}
|
||||
|
||||
private class SumShake128 extends DirectHashOperation instanceof DataFlow::CallNode {
|
||||
SumShake128() { this.getTarget().hasQualifiedName("crypto/sha3", "SumSHAKE128") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("SHAKE128") }
|
||||
}
|
||||
|
||||
private class SumShake256 extends DirectHashOperation instanceof DataFlow::CallNode {
|
||||
SumShake256() { this.getTarget().hasQualifiedName("crypto/sha3", "SumSHAKE256") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("SHAKE256") }
|
||||
}
|
||||
|
||||
private class New224 extends HashAlgorithmInit instanceof DataFlow::CallNode {
|
||||
New224() { this.getTarget().hasQualifiedName("crypto/sha3", "New224") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("SHA3224") }
|
||||
}
|
||||
|
||||
private class New256 extends HashAlgorithmInit instanceof DataFlow::CallNode {
|
||||
New256() { this.getTarget().hasQualifiedName("crypto/sha3", "New256") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("SHA3256") }
|
||||
}
|
||||
|
||||
private class New384 extends HashAlgorithmInit instanceof DataFlow::CallNode {
|
||||
New384() { this.getTarget().hasQualifiedName("crypto/sha3", "New384") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("SHA3384") }
|
||||
}
|
||||
|
||||
private class New512 extends HashAlgorithmInit instanceof DataFlow::CallNode {
|
||||
New512() { this.getTarget().hasQualifiedName("crypto/sha3", "New512") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("SHA3512") }
|
||||
}
|
||||
|
||||
private class NewShake128 extends HashAlgorithmInit instanceof DataFlow::CallNode {
|
||||
NewShake128() {
|
||||
this.getTarget().hasQualifiedName("crypto/sha3", ["NewCSHAKE128", "NewSHAKE128"])
|
||||
}
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("SHAKE128") }
|
||||
}
|
||||
|
||||
private class NewShake256 extends HashAlgorithmInit instanceof DataFlow::CallNode {
|
||||
NewShake256() {
|
||||
this.getTarget().hasQualifiedName("crypto/sha3", ["NewCSHAKE256", "NewSHAKE256"])
|
||||
}
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("SHAKE256") }
|
||||
}
|
||||
|
||||
private class ShakeWrite extends HashOperation instanceof DataFlow::MethodCallNode {
|
||||
ShakeWrite() { this.getTarget().hasQualifiedName("crypto/sha3", "SHAKE", "Write") }
|
||||
|
||||
override HashAlgorithmInit getInitialization() { HashFlow::flow(result, super.getReceiver()) }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result = this.getInitialization().getAlgorithm() }
|
||||
|
||||
override DataFlow::Node getAnInput() { result = super.getArgument(0) }
|
||||
}
|
||||
}
|
||||
|
||||
private module Sha512 {
|
||||
private class Sum384 extends DirectHashOperation instanceof DataFlow::CallNode {
|
||||
Sum384() { this.getTarget().hasQualifiedName("crypto/sha512", "Sum384") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("SHA384") }
|
||||
}
|
||||
|
||||
private class Sum512 extends DirectHashOperation instanceof DataFlow::CallNode {
|
||||
Sum512() { this.getTarget().hasQualifiedName("crypto/sha512", "Sum512") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("SHA512") }
|
||||
}
|
||||
|
||||
private class Sum512_224 extends DirectHashOperation instanceof DataFlow::CallNode {
|
||||
Sum512_224() { this.getTarget().hasQualifiedName("crypto/sha512", "Sum512_224") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("SHA512224") }
|
||||
}
|
||||
|
||||
private class Sum512_256 extends DirectHashOperation instanceof DataFlow::CallNode {
|
||||
Sum512_256() { this.getTarget().hasQualifiedName("crypto/sha512", "Sum512_256") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("SHA512256") }
|
||||
}
|
||||
|
||||
private class New extends HashAlgorithmInit instanceof DataFlow::CallNode {
|
||||
New() { this.getTarget().hasQualifiedName("crypto/sha512", "New") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("SHA512") }
|
||||
}
|
||||
|
||||
private class New384 extends HashAlgorithmInit instanceof DataFlow::CallNode {
|
||||
New384() { this.getTarget().hasQualifiedName("crypto/sha512", "New384") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("SHA384") }
|
||||
}
|
||||
|
||||
private class New512_224 extends HashAlgorithmInit instanceof DataFlow::CallNode {
|
||||
New512_224() { this.getTarget().hasQualifiedName("crypto/sha512", "New512_224") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("SHA512224") }
|
||||
}
|
||||
|
||||
private class New512_256 extends HashAlgorithmInit instanceof DataFlow::CallNode {
|
||||
New512_256() { this.getTarget().hasQualifiedName("crypto/sha512", "New512_256") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("SHA512256") }
|
||||
}
|
||||
}
|
||||
|
||||
private module Cipher {
|
||||
private class NewCbcEncrypter extends StdLibNewEncrypter {
|
||||
NewCbcEncrypter() { this.getTarget().hasQualifiedName("crypto/cipher", "NewCBCEncrypter") }
|
||||
|
||||
override BlockMode getMode() { result = "CBC" }
|
||||
}
|
||||
|
||||
private class NewCfbEncrypter extends StdLibNewEncrypter {
|
||||
NewCfbEncrypter() { this.getTarget().hasQualifiedName("crypto/cipher", "NewCFBEncrypter") }
|
||||
|
||||
override BlockMode getMode() { result = "CFB" }
|
||||
}
|
||||
|
||||
private class NewCtr extends StdLibNewEncrypter {
|
||||
NewCtr() { this.getTarget().hasQualifiedName("crypto/cipher", "NewCTR") }
|
||||
|
||||
override BlockMode getMode() { result = "CTR" }
|
||||
}
|
||||
|
||||
private class NewGcm extends StdLibNewEncrypter {
|
||||
NewGcm() {
|
||||
exists(string name | this.getTarget().hasQualifiedName("crypto/cipher", name) |
|
||||
name = ["NewGCM", "NewGCMWithNonceSize", "NewGCMWithRandomNonce", "NewGCMWithTagSize"]
|
||||
)
|
||||
}
|
||||
|
||||
override BlockMode getMode() { result = "GCM" }
|
||||
}
|
||||
|
||||
private class NewOfb extends StdLibNewEncrypter {
|
||||
NewOfb() { this.getTarget().hasQualifiedName("crypto/cipher", "NewOFB") }
|
||||
|
||||
override BlockMode getMode() { result = "OFB" }
|
||||
}
|
||||
|
||||
private class AeadSeal extends EncryptionMethodCall {
|
||||
AeadSeal() {
|
||||
this.(DataFlow::MethodCallNode)
|
||||
.getTarget()
|
||||
.hasQualifiedName("crypto/cipher", "AEAD", "Seal") and
|
||||
inputArg = 2
|
||||
}
|
||||
}
|
||||
|
||||
private class BlockEncrypt extends EncryptionMethodCall {
|
||||
BlockEncrypt() {
|
||||
this.(DataFlow::MethodCallNode)
|
||||
.getTarget()
|
||||
.hasQualifiedName("crypto/cipher", "Block", "Encrypt") and
|
||||
inputArg = 1
|
||||
}
|
||||
}
|
||||
|
||||
private class BlockModeCryptBlocks extends EncryptionMethodCall {
|
||||
BlockModeCryptBlocks() {
|
||||
this.(DataFlow::MethodCallNode)
|
||||
.getTarget()
|
||||
.hasQualifiedName("crypto/cipher", "BlockMode", "CryptBlocks") and
|
||||
inputArg = 1
|
||||
}
|
||||
}
|
||||
|
||||
private class StreamXorKeyStream extends EncryptionMethodCall {
|
||||
StreamXorKeyStream() {
|
||||
this.(DataFlow::MethodCallNode)
|
||||
.getTarget()
|
||||
.hasQualifiedName("crypto/cipher", "Stream", "XORKeyStream") and
|
||||
inputArg = 1
|
||||
}
|
||||
}
|
||||
|
||||
private class StreamReader extends EncryptionOperation {
|
||||
StreamReader() {
|
||||
lookThroughPointerType(this.getType()).hasQualifiedName("crypto/cipher", "StreamReader") and
|
||||
exists(DataFlow::Write w, DataFlow::Node base, Field f |
|
||||
f.hasQualifiedName("crypto/cipher", "StreamReader", "S") and
|
||||
w.writesField(base, f, encryptionFlowTarget) and
|
||||
DataFlow::localFlow(base, this)
|
||||
) and
|
||||
exists(DataFlow::Write w, DataFlow::Node base, Field f |
|
||||
f.hasQualifiedName("crypto/cipher", "StreamReader", "R") and
|
||||
w.writesField(base, f, inputNode) and
|
||||
DataFlow::localFlow(base, this)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Limitation: StreamWriter wraps a Writer, so we need to look forward to
|
||||
* where the Writer is written to. Currently this is done using local flow,
|
||||
* so it only works within one function.
|
||||
*/
|
||||
private class StreamWriter extends EncryptionOperation {
|
||||
StreamWriter() {
|
||||
lookThroughPointerType(this.getType()).hasQualifiedName("crypto/cipher", "StreamWriter") and
|
||||
inputNode = this and
|
||||
exists(DataFlow::Write w, DataFlow::Node base, Field f |
|
||||
w.writesField(base, f, encryptionFlowTarget) and
|
||||
f.hasQualifiedName("crypto/cipher", "StreamWriter", "S")
|
||||
|
|
||||
base = this or
|
||||
TaintTracking::localTaint(base, this.(DataFlow::PostUpdateNode).getPreUpdateNode())
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private module Hash {
|
||||
private class HashSum extends HashOperation instanceof DataFlow::MethodCallNode {
|
||||
HashSum() { this.getTarget().implements("hash", "Hash", "Sum") }
|
||||
|
||||
override HashAlgorithmInit getInitialization() { HashFlow::flow(result, super.getReceiver()) }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result = this.getInitialization().getAlgorithm() }
|
||||
|
||||
override DataFlow::Node getAnInput() { result = super.getArgument(0) }
|
||||
}
|
||||
}
|
||||
|
||||
private DataFlow::Node getANonIoWriterPredecessor(DataFlow::Node node) {
|
||||
node.getType().implements("io", "Writer") and
|
||||
exists(DataFlow::Node pre | TaintTracking::localTaintStep(pre, node) |
|
||||
if pre.getType().implements("io", "Writer")
|
||||
then result = getANonIoWriterPredecessor(pre)
|
||||
else result = pre
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Taint flowing to an `io.Writer` (such as `hash.Hash` or `*sha3.SHAKE`) via
|
||||
* its implementation of the `io.Writer` interface.
|
||||
*/
|
||||
private class FlowToIoWriter extends HashOperation instanceof DataFlow::Node {
|
||||
HashAlgorithmInit init;
|
||||
DataFlow::Node input;
|
||||
|
||||
FlowToIoWriter() {
|
||||
this.getType().implements("io", "Writer") and
|
||||
HashFlow::flow(init, this) and
|
||||
// If we have `h.Write(taint)` or `io.WriteString(h, taint)` then it's
|
||||
// the post-update node of `h` that gets tainted.
|
||||
exists(DataFlow::PostUpdateNode pun | pun.getPreUpdateNode() = this |
|
||||
input = getANonIoWriterPredecessor(pun)
|
||||
)
|
||||
}
|
||||
|
||||
override HashAlgorithmInit getInitialization() { result = init }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result = this.getInitialization().getAlgorithm() }
|
||||
|
||||
override DataFlow::Node getAnInput() { result = input }
|
||||
}
|
||||
|
||||
/**
|
||||
* Currently only weak algorithms from the `golang.org/x/crypto` module are
|
||||
* modeled here.
|
||||
*/
|
||||
private module GolangOrgXCrypto {
|
||||
private module Md4 {
|
||||
private class New extends HashAlgorithmInit instanceof DataFlow::CallNode {
|
||||
New() { this.getTarget().hasQualifiedName("golang.org/x/crypto/md4", "New") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("MD4") }
|
||||
}
|
||||
}
|
||||
|
||||
private module Ripemd160 {
|
||||
private class New extends HashAlgorithmInit instanceof DataFlow::CallNode {
|
||||
New() { this.getTarget().hasQualifiedName("golang.org/x/crypto/ripemd160", "New") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("RIPEMD160") }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
/**
|
||||
* Provides default sources, sinks and sanitizers for detecting "use of a
|
||||
* broken or weak cryptographic hashing algorithm on sensitive data"
|
||||
* vulnerabilities, as well as extension points for adding your own. This is
|
||||
* divided into two general cases:
|
||||
* - hashing sensitive data
|
||||
* - hashing passwords (which requires the hashing algorithm to be
|
||||
* sufficiently computationally expensive in addition to other requirements)
|
||||
*/
|
||||
|
||||
import go
|
||||
private import semmle.go.security.SensitiveActions
|
||||
|
||||
/**
|
||||
* Provides default sources, sinks and sanitizers for detecting "use of a broken or weak
|
||||
* cryptographic hashing algorithm on sensitive data" vulnerabilities on sensitive data that does
|
||||
* NOT require computationally expensive hashing, as well as extension points for adding your own.
|
||||
*
|
||||
* Also see the `ComputationallyExpensiveHashFunction` module.
|
||||
*/
|
||||
module NormalHashFunction {
|
||||
/**
|
||||
* A data flow source for "use of a broken or weak cryptographic hashing algorithm on sensitive
|
||||
* data" vulnerabilities that does not require computationally expensive hashing. That is, a
|
||||
* piece of sensitive data that is not a password.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node {
|
||||
Source() { not this instanceof ComputationallyExpensiveHashFunction::Source }
|
||||
|
||||
/**
|
||||
* Gets the classification of the sensitive data.
|
||||
*/
|
||||
abstract string getClassification();
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow sink for "use of a broken or weak cryptographic hashing algorithm on sensitive
|
||||
* data" vulnerabilities that applies to data that does not require computationally expensive
|
||||
* hashing. That is, a broken or weak hashing algorithm.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::Node {
|
||||
/**
|
||||
* Gets the name of the weak hashing algorithm.
|
||||
*/
|
||||
abstract string getAlgorithmName();
|
||||
}
|
||||
|
||||
/**
|
||||
* A barrier for "use of a broken or weak cryptographic hashing algorithm on sensitive data"
|
||||
* vulnerabilities that applies to data that does not require computationally expensive hashing.
|
||||
*/
|
||||
abstract class Barrier extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A flow source modeled by the `SensitiveData` library.
|
||||
*/
|
||||
class SensitiveDataAsSource extends Source {
|
||||
SensitiveExpr::Classification classification;
|
||||
|
||||
SensitiveDataAsSource() {
|
||||
classification = this.asExpr().(SensitiveExpr).getClassification() and
|
||||
not classification = SensitiveExpr::password() and // (covered in ComputationallyExpensiveHashFunction)
|
||||
not classification = SensitiveExpr::id() // (not accurate enough)
|
||||
}
|
||||
|
||||
override SensitiveExpr::Classification getClassification() { result = classification }
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow sink modeled by the `Cryptography` module.
|
||||
*/
|
||||
class WeakHashingOperationInputAsSink extends Sink {
|
||||
Cryptography::HashingAlgorithm algorithm;
|
||||
|
||||
WeakHashingOperationInputAsSink() {
|
||||
exists(Cryptography::CryptographicOperation operation |
|
||||
algorithm.isWeak() and
|
||||
algorithm = operation.getAlgorithm() and
|
||||
this = operation.getAnInput()
|
||||
)
|
||||
}
|
||||
|
||||
override string getAlgorithmName() { result = algorithm.getName() }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides default sources, sinks and sanitizers for detecting "use of a broken or weak
|
||||
* cryptographic hashing algorithm on sensitive data" vulnerabilities on sensitive data that DOES
|
||||
* require computationally expensive hashing, as well as extension points for adding your own.
|
||||
*
|
||||
* Also see the `NormalHashFunction` module.
|
||||
*/
|
||||
module ComputationallyExpensiveHashFunction {
|
||||
/**
|
||||
* A data flow source for "use of a broken or weak cryptographic hashing algorithm on sensitive
|
||||
* data" vulnerabilities that does require computationally expensive hashing. That is, a
|
||||
* password.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node {
|
||||
/**
|
||||
* Gets the classification of the sensitive data.
|
||||
*/
|
||||
abstract string getClassification();
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow sink for "use of a broken or weak cryptographic hashing algorithm on sensitive
|
||||
* data" vulnerabilities that applies to data that does require computationally expensive
|
||||
* hashing. That is, a broken or weak hashing algorithm or one that is not computationally
|
||||
* expensive enough for password hashing.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::Node {
|
||||
/**
|
||||
* Gets the name of the weak hashing algorithm.
|
||||
*/
|
||||
abstract string getAlgorithmName();
|
||||
|
||||
/**
|
||||
* Holds if this sink is for a computationally expensive hash function (meaning that hash
|
||||
* function is just weak in some other regard.
|
||||
*/
|
||||
abstract predicate isComputationallyExpensive();
|
||||
}
|
||||
|
||||
/**
|
||||
* A barrier for "use of a broken or weak cryptographic hashing algorithm on sensitive data"
|
||||
* vulnerabilities that applies to data that does require computationally expensive hashing.
|
||||
*/
|
||||
abstract class Barrier extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A flow source modeled by the `SensitiveData` library.
|
||||
*/
|
||||
class PasswordAsSource extends Source {
|
||||
SensitiveExpr::Classification classification;
|
||||
|
||||
PasswordAsSource() {
|
||||
classification = this.asExpr().(SensitiveExpr).getClassification() and
|
||||
classification = SensitiveExpr::password()
|
||||
}
|
||||
|
||||
override SensitiveExpr::Classification getClassification() { result = classification }
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow sink modeled by the `Cryptography` module.
|
||||
*/
|
||||
class WeakPasswordHashingOperationInputSink extends Sink {
|
||||
Cryptography::CryptographicAlgorithm algorithm;
|
||||
|
||||
WeakPasswordHashingOperationInputSink() {
|
||||
exists(Cryptography::CryptographicOperation operation |
|
||||
(
|
||||
algorithm instanceof Cryptography::PasswordHashingAlgorithm and
|
||||
algorithm.isWeak()
|
||||
or
|
||||
algorithm instanceof Cryptography::HashingAlgorithm // Note that HashingAlgorithm and PasswordHashingAlgorithm are disjoint
|
||||
) and
|
||||
algorithm = operation.getAlgorithm() and
|
||||
this = operation.getAnInput()
|
||||
)
|
||||
}
|
||||
|
||||
override string getAlgorithmName() { result = algorithm.getName() }
|
||||
|
||||
override predicate isComputationallyExpensive() {
|
||||
algorithm instanceof Cryptography::PasswordHashingAlgorithm
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,10 +28,10 @@
|
||||
<example>
|
||||
|
||||
<p>
|
||||
The following code uses the different packages to encrypt/hash
|
||||
some secret data. The first few examples uses DES, MD5, RC4, and SHA1,
|
||||
which are older algorithms that are now considered weak. The following
|
||||
examples use AES and SHA256, which are stronger, more modern algorithms.
|
||||
The following code uses the different packages to encrypt
|
||||
some secret data. The first example uses DES,
|
||||
which is an older algorithm that is now considered weak. The following
|
||||
example uses AES, which is a stronger, more modern algorithm.
|
||||
</p>
|
||||
|
||||
<sample src="examples/Crypto.go" />
|
||||
33
go/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.ql
Normal file
33
go/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.ql
Normal file
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* @name Use of a broken or weak cryptographic algorithm
|
||||
* @description Using broken or weak cryptographic algorithms can compromise security.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 7.5
|
||||
* @precision high
|
||||
* @id go/weak-cryptographic-algorithm
|
||||
* @tags security
|
||||
* external/cwe/cwe-327
|
||||
* external/cwe/cwe-328
|
||||
*/
|
||||
|
||||
import go
|
||||
|
||||
from Cryptography::CryptographicOperation operation, string msgPrefix, DataFlow::Node init
|
||||
where
|
||||
init = operation.getInitialization() and
|
||||
// `init` may be a `BlockModeInit`, a `EncryptionAlgorithmInit`, or `operation` itself.
|
||||
(
|
||||
not init instanceof BlockModeInit and
|
||||
exists(Cryptography::CryptographicAlgorithm algorithm |
|
||||
algorithm = operation.getAlgorithm() and
|
||||
algorithm.isWeak() and
|
||||
msgPrefix = "The cryptographic algorithm " + algorithm.getName() and
|
||||
not algorithm instanceof Cryptography::HashingAlgorithm
|
||||
)
|
||||
or
|
||||
not init instanceof EncryptionAlgorithmInit and
|
||||
operation.getBlockMode().isWeak() and
|
||||
msgPrefix = "The block mode " + operation.getBlockMode()
|
||||
)
|
||||
select operation, "$@ is broken or weak, and should not be used.", init, msgPrefix
|
||||
97
go/ql/src/Security/CWE-327/WeakSensitiveDataHashing.qhelp
Normal file
97
go/ql/src/Security/CWE-327/WeakSensitiveDataHashing.qhelp
Normal file
@@ -0,0 +1,97 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>
|
||||
Using a broken or weak cryptographic hash function can leave data
|
||||
vulnerable, and should not be used in security related code.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
A strong cryptographic hash function should be resistant to:
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
pre-image attacks: if you know a hash value <code>h(x)</code>,
|
||||
you should not be able to easily find the input <code>x</code>.
|
||||
</li>
|
||||
<li>
|
||||
collision attacks: if you know a hash value <code>h(x)</code>,
|
||||
you should not be able to easily find a different input <code>y</code>
|
||||
with the same hash value <code>h(x) = h(y)</code>.
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
In cases with a limited input space, such as for passwords, the hash
|
||||
function also needs to be computationally expensive to be resistant to
|
||||
brute-force attacks. Passwords should also have an unique salt applied
|
||||
before hashing, but that is not considered by this query.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
As an example, both MD5 and SHA-1 are known to be vulnerable to collision attacks.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Since it's OK to use a weak cryptographic hash function in a non-security
|
||||
context, this query only alerts when these are used to hash sensitive
|
||||
data (such as passwords, certificates, usernames).
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Use of broken or weak cryptographic algorithms that are not hashing algorithms, is
|
||||
handled by the <code>rb/weak-cryptographic-algorithm</code> query.
|
||||
</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>
|
||||
Ensure that you use a strong, modern cryptographic hash function:
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
such as Argon2, scrypt, bcrypt, or PBKDF2 for passwords and other data with limited input space.
|
||||
</li>
|
||||
<li>
|
||||
such as SHA-2, or SHA-3 in other cases.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
|
||||
<p>
|
||||
The following example shows two functions for checking whether the hash
|
||||
of a secret matches a known value.
|
||||
|
||||
The first function uses SHA-1 that is known to be vulnerable to collision attacks.
|
||||
|
||||
The second function uses SHA-256 that is a strong cryptographic hashing function.
|
||||
</p>
|
||||
|
||||
<sample src="examples/WeakSecretHashing.go" />
|
||||
|
||||
</example>
|
||||
<example>
|
||||
<p>
|
||||
The following example shows two functions for hashing passwords.
|
||||
|
||||
The first example uses SHA-256 to hash passwords. Although
|
||||
SHA-256 is a strong cryptographic hash function, it is not suitable for password
|
||||
hashing since it is not computationally expensive.
|
||||
|
||||
The second function uses PBKDF2, which is a strong password hashing algorithm.
|
||||
</p>
|
||||
|
||||
<sample src="examples/WeakPasswordHashing.go" />
|
||||
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>OWASP: <a href="https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html">Password Storage Cheat Sheet</a></li>
|
||||
</references>
|
||||
|
||||
</qhelp>
|
||||
118
go/ql/src/Security/CWE-327/WeakSensitiveDataHashing.ql
Normal file
118
go/ql/src/Security/CWE-327/WeakSensitiveDataHashing.ql
Normal file
@@ -0,0 +1,118 @@
|
||||
/**
|
||||
* @name Use of a broken or weak cryptographic hashing algorithm on sensitive data
|
||||
* @description Using broken or weak cryptographic hashing algorithms can compromise security.
|
||||
* @kind path-problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 7.5
|
||||
* @precision high
|
||||
* @id go/weak-sensitive-data-hashing
|
||||
* @tags security
|
||||
* external/cwe/cwe-327
|
||||
* external/cwe/cwe-328
|
||||
* external/cwe/cwe-916
|
||||
*/
|
||||
|
||||
import go
|
||||
import semmle.go.security.WeakSensitiveDataHashingCustomizations
|
||||
|
||||
/**
|
||||
* Provides a taint-tracking configuration for detecting use of a broken or weak
|
||||
* cryptographic hash function on sensitive data, that does NOT require a
|
||||
* computationally expensive hash function.
|
||||
*/
|
||||
module NormalHashFunctionFlow {
|
||||
import NormalHashFunction
|
||||
|
||||
private module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) { node instanceof Barrier }
|
||||
|
||||
predicate isBarrierIn(DataFlow::Node node) {
|
||||
// make sources barriers so that we only report the closest instance
|
||||
isSource(node)
|
||||
}
|
||||
|
||||
predicate isBarrierOut(DataFlow::Node node) {
|
||||
// make sinks barriers so that we only report the closest instance
|
||||
isSink(node)
|
||||
}
|
||||
|
||||
predicate observeDiffInformedIncrementalMode() { any() }
|
||||
}
|
||||
|
||||
import TaintTracking::Global<Config>
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a taint-tracking configuration for detecting use of a broken or weak
|
||||
* cryptographic hashing algorithm on passwords.
|
||||
*
|
||||
* Passwords has stricter requirements on the hashing algorithm used (must be
|
||||
* computationally expensive to prevent brute-force attacks).
|
||||
*/
|
||||
module ComputationallyExpensiveHashFunctionFlow {
|
||||
import ComputationallyExpensiveHashFunction
|
||||
|
||||
private module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) { node instanceof Barrier }
|
||||
|
||||
predicate isBarrierIn(DataFlow::Node node) {
|
||||
// make sources barriers so that we only report the closest instance
|
||||
isSource(node)
|
||||
}
|
||||
|
||||
predicate isBarrierOut(DataFlow::Node node) {
|
||||
// make sinks barriers so that we only report the closest instance
|
||||
isSink(node)
|
||||
}
|
||||
|
||||
predicate observeDiffInformedIncrementalMode() { any() }
|
||||
}
|
||||
|
||||
import TaintTracking::Global<Config>
|
||||
}
|
||||
|
||||
/**
|
||||
* Global taint-tracking for detecting both variants of "use of a broken or weak
|
||||
* cryptographic hashing algorithm on sensitive data" vulnerabilities. The two configurations are
|
||||
* merged to generate a combined path graph.
|
||||
*/
|
||||
module WeakSensitiveDataHashingFlow =
|
||||
DataFlow::MergePathGraph<NormalHashFunctionFlow::PathNode,
|
||||
ComputationallyExpensiveHashFunctionFlow::PathNode, NormalHashFunctionFlow::PathGraph,
|
||||
ComputationallyExpensiveHashFunctionFlow::PathGraph>;
|
||||
|
||||
import WeakSensitiveDataHashingFlow::PathGraph
|
||||
|
||||
from
|
||||
WeakSensitiveDataHashingFlow::PathNode source, WeakSensitiveDataHashingFlow::PathNode sink,
|
||||
string ending, string algorithmName, string classification
|
||||
where
|
||||
NormalHashFunctionFlow::flowPath(source.asPathNode1(), sink.asPathNode1()) and
|
||||
algorithmName = sink.getNode().(NormalHashFunction::Sink).getAlgorithmName() and
|
||||
classification = source.getNode().(NormalHashFunction::Source).getClassification() and
|
||||
ending = "."
|
||||
or
|
||||
ComputationallyExpensiveHashFunctionFlow::flowPath(source.asPathNode2(), sink.asPathNode2()) and
|
||||
algorithmName = sink.getNode().(ComputationallyExpensiveHashFunction::Sink).getAlgorithmName() and
|
||||
classification =
|
||||
source.getNode().(ComputationallyExpensiveHashFunction::Source).getClassification() and
|
||||
(
|
||||
sink.getNode().(ComputationallyExpensiveHashFunction::Sink).isComputationallyExpensive() and
|
||||
ending = "."
|
||||
or
|
||||
not sink.getNode().(ComputationallyExpensiveHashFunction::Sink).isComputationallyExpensive() and
|
||||
ending =
|
||||
" for " + classification +
|
||||
" hashing, since it is not a computationally expensive hash function."
|
||||
)
|
||||
select sink.getNode(), source, sink,
|
||||
"$@ is used in a hashing algorithm (" + algorithmName + ") that is insecure" + ending,
|
||||
source.getNode(), "Sensitive data (" + classification + ")"
|
||||
20
go/ql/src/Security/CWE-327/examples/Crypto.go
Normal file
20
go/ql/src/Security/CWE-327/examples/Crypto.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/des"
|
||||
)
|
||||
|
||||
func EncryptMessageWeak(key []byte, message []byte) (dst []byte) {
|
||||
// BAD, DES is a weak crypto algorithm
|
||||
block, _ := des.NewCipher(key)
|
||||
block.Encrypt(dst, message)
|
||||
return
|
||||
}
|
||||
|
||||
func EncryptMessageStrong(key []byte, message []byte) (dst []byte) {
|
||||
// GOOD, AES is a weak crypto algorithm
|
||||
block, _ := aes.NewCipher(key)
|
||||
block.Encrypt(dst, message)
|
||||
return
|
||||
}
|
||||
21
go/ql/src/Security/CWE-327/examples/WeakPasswordHashing.go
Normal file
21
go/ql/src/Security/CWE-327/examples/WeakPasswordHashing.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/pbkdf2"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
)
|
||||
|
||||
func GetPasswordHashBad(password string) [32]byte {
|
||||
// BAD, SHA256 is a strong hashing algorithm but it is not computationally expensive
|
||||
return sha256.Sum256([]byte(password))
|
||||
}
|
||||
|
||||
func GetPasswordHashGood(password string) []byte {
|
||||
// GOOD, PBKDF2 is a strong hashing algorithm and it is computationally expensive
|
||||
salt := make([]byte, 16)
|
||||
rand.Read(salt)
|
||||
key, _ := pbkdf2.Key(sha512.New, password, salt, 4096, 32)
|
||||
return key
|
||||
}
|
||||
19
go/ql/src/Security/CWE-327/examples/WeakSecretHashing.go
Normal file
19
go/ql/src/Security/CWE-327/examples/WeakSecretHashing.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"slices"
|
||||
)
|
||||
|
||||
func SecretMatchesKnownHashBad(secret []byte, known_hash []byte) bool {
|
||||
// BAD, SHA1 is a weak crypto algorithm and secret is sensitive data
|
||||
h := sha1.New()
|
||||
return slices.Equal(h.Sum(secret), known_hash)
|
||||
}
|
||||
|
||||
func SecretMatchesKnownHashGood(secret []byte, known_hash []byte) bool {
|
||||
// GOOD, SHA256 is a strong hashing algorithm
|
||||
h := sha256.New()
|
||||
return slices.Equal(h.Sum(secret), known_hash)
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
category: newQuery
|
||||
---
|
||||
* Added a new query, `go/weak-crypto-algorithm`, to detect the use of a broken or weak cryptographic algorithm. A very simple version of this query was originally contributed as an [experimental query by @dilanbhalla](https://github.com/github/codeql-go/pull/284).
|
||||
* Added a new query, `go/weak-sensitive-data-hashing`, to detect the use of a broken or weak cryptographic hash algorithm on sensitive data.
|
||||
@@ -1,213 +0,0 @@
|
||||
/**
|
||||
* Provides classes for modeling cryptographic libraries.
|
||||
*/
|
||||
|
||||
import go
|
||||
|
||||
/**
|
||||
* Names of cryptographic algorithms, separated into strong and weak variants.
|
||||
*
|
||||
* The names are normalized: upper-case, no spaces, dashes or underscores.
|
||||
*
|
||||
* The names are inspired by the names used in real world crypto libraries.
|
||||
*
|
||||
* The classification into strong and weak are based on OWASP and Wikipedia (2020).
|
||||
*
|
||||
* Sources (more links in qhelp file):
|
||||
* https://en.wikipedia.org/wiki/Strong_cryptography#Cryptographically_strong_algorithms
|
||||
* https://en.wikipedia.org/wiki/Strong_cryptography#Examples
|
||||
* https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html
|
||||
*/
|
||||
private module AlgorithmNames {
|
||||
predicate isStrongHashingAlgorithm(string name) {
|
||||
name =
|
||||
[
|
||||
"DSA", "ED25519", "SHA256", "SHA384", "SHA512", "SHA3", "ES256", "ECDSA256", "ES384",
|
||||
"ECDSA384", "ES512", "ECDSA512", "SHA2", "SHA224"
|
||||
]
|
||||
}
|
||||
|
||||
predicate isWeakHashingAlgorithm(string name) {
|
||||
name =
|
||||
[
|
||||
"HAVEL128", "MD2", "SHA1", "MD4", "MD5", "PANAMA", "RIPEMD", "RIPEMD128", "RIPEMD256",
|
||||
"RIPEMD320", "SHA0"
|
||||
]
|
||||
}
|
||||
|
||||
predicate isStrongEncryptionAlgorithm(string name) {
|
||||
name = ["AES", "AES128", "AES192", "AES256", "AES512", "RSA", "RABBIT", "BLOWFISH"]
|
||||
}
|
||||
|
||||
predicate isWeakEncryptionAlgorithm(string name) {
|
||||
name =
|
||||
[
|
||||
"DES", "3DES", "ARC5", "RC5", "TRIPLEDES", "TDEA", "TRIPLEDEA", "ARC2", "RC2", "ARC4",
|
||||
"RC4", "ARCFOUR"
|
||||
]
|
||||
}
|
||||
|
||||
predicate isStrongPasswordHashingAlgorithm(string name) {
|
||||
name = ["ARGON2", "PBKDF2", "BCRYPT", "SCRYPT"]
|
||||
}
|
||||
|
||||
predicate isWeakPasswordHashingAlgorithm(string name) { none() }
|
||||
}
|
||||
|
||||
private import AlgorithmNames
|
||||
|
||||
/**
|
||||
* A cryptographic algorithm.
|
||||
*/
|
||||
private newtype TCryptographicAlgorithm =
|
||||
MkHashingAlgorithm(string name, boolean isWeak) {
|
||||
isStrongHashingAlgorithm(name) and isWeak = false
|
||||
or
|
||||
isWeakHashingAlgorithm(name) and isWeak = true
|
||||
} or
|
||||
MkEncryptionAlgorithm(string name, boolean isWeak) {
|
||||
isStrongEncryptionAlgorithm(name) and isWeak = false
|
||||
or
|
||||
isWeakEncryptionAlgorithm(name) and isWeak = true
|
||||
} or
|
||||
MkPasswordHashingAlgorithm(string name, boolean isWeak) {
|
||||
isStrongPasswordHashingAlgorithm(name) and isWeak = false
|
||||
or
|
||||
isWeakPasswordHashingAlgorithm(name) and isWeak = true
|
||||
}
|
||||
|
||||
/**
|
||||
* A cryptographic algorithm.
|
||||
*/
|
||||
abstract class CryptographicAlgorithm extends TCryptographicAlgorithm {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = this.getName() }
|
||||
|
||||
/**
|
||||
* Gets the name of this algorithm.
|
||||
*/
|
||||
abstract string getName();
|
||||
|
||||
/**
|
||||
* Holds if the name of this algorithm matches `name` modulo case,
|
||||
* white space, dashes and underscores.
|
||||
*/
|
||||
bindingset[name]
|
||||
predicate matchesName(string name) {
|
||||
exists(name.regexpReplaceAll("[-_]", "").regexpFind("(?i)\\Q" + this.getName() + "\\E", _, _))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this algorithm is weak.
|
||||
*/
|
||||
abstract predicate isWeak();
|
||||
}
|
||||
|
||||
/**
|
||||
* A hashing algorithm such as `MD5` or `SHA512`.
|
||||
*/
|
||||
class HashingAlgorithm extends MkHashingAlgorithm, CryptographicAlgorithm {
|
||||
string name;
|
||||
boolean isWeak;
|
||||
|
||||
HashingAlgorithm() { this = MkHashingAlgorithm(name, isWeak) }
|
||||
|
||||
override string getName() { result = name }
|
||||
|
||||
override predicate isWeak() { isWeak = true }
|
||||
}
|
||||
|
||||
/**
|
||||
* An encryption algorithm such as `DES` or `AES512`.
|
||||
*/
|
||||
class EncryptionAlgorithm extends MkEncryptionAlgorithm, CryptographicAlgorithm {
|
||||
string name;
|
||||
boolean isWeak;
|
||||
|
||||
EncryptionAlgorithm() { this = MkEncryptionAlgorithm(name, isWeak) }
|
||||
|
||||
override string getName() { result = name }
|
||||
|
||||
override predicate isWeak() { isWeak = true }
|
||||
}
|
||||
|
||||
/**
|
||||
* A password hashing algorithm such as `PBKDF2` or `SCRYPT`.
|
||||
*/
|
||||
class PasswordHashingAlgorithm extends MkPasswordHashingAlgorithm, CryptographicAlgorithm {
|
||||
string name;
|
||||
boolean isWeak;
|
||||
|
||||
PasswordHashingAlgorithm() { this = MkPasswordHashingAlgorithm(name, isWeak) }
|
||||
|
||||
override string getName() { result = name }
|
||||
|
||||
override predicate isWeak() { isWeak = true }
|
||||
}
|
||||
|
||||
/**
|
||||
* An application of a cryptographic algorithm.
|
||||
*/
|
||||
abstract class CryptographicOperation extends DataFlow::Node {
|
||||
/**
|
||||
* Gets the input the algorithm is used on, e.g. the plain text input to be encrypted.
|
||||
*/
|
||||
abstract Expr getInput();
|
||||
|
||||
/**
|
||||
* Gets the applied algorithm.
|
||||
*/
|
||||
abstract CryptographicAlgorithm getAlgorithm();
|
||||
}
|
||||
|
||||
/**
|
||||
* A cryptographic operation from the `crypto/md5` package.
|
||||
*/
|
||||
class Md5 extends CryptographicOperation, DataFlow::CallNode {
|
||||
Md5() { this.getTarget().hasQualifiedName("crypto/md5", ["New", "Sum"]) }
|
||||
|
||||
override Expr getInput() { result = this.getArgument(0).asExpr() }
|
||||
|
||||
override CryptographicAlgorithm getAlgorithm() {
|
||||
result.matchesName(this.getTarget().getPackage().getName())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A cryptographic operation from the `crypto/sha1` package.
|
||||
*/
|
||||
class Sha1 extends CryptographicOperation, DataFlow::CallNode {
|
||||
Sha1() { this.getTarget().hasQualifiedName("crypto/sha1", ["New", "Sum"]) }
|
||||
|
||||
override Expr getInput() { result = this.getArgument(0).asExpr() }
|
||||
|
||||
override CryptographicAlgorithm getAlgorithm() {
|
||||
result.matchesName(this.getTarget().getPackage().getName())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A cryptographic operation from the `crypto/des` package.
|
||||
*/
|
||||
class Des extends CryptographicOperation, DataFlow::CallNode {
|
||||
Des() { this.getTarget().hasQualifiedName("crypto/des", ["NewCipher", "NewTripleDESCipher"]) }
|
||||
|
||||
override Expr getInput() { result = this.getArgument(0).asExpr() }
|
||||
|
||||
override CryptographicAlgorithm getAlgorithm() {
|
||||
result.matchesName(this.getTarget().getPackage().getName())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A cryptographic operation from the `crypto/rc4` package.
|
||||
*/
|
||||
class Rc4 extends CryptographicOperation, DataFlow::CallNode {
|
||||
Rc4() { this.getTarget().hasQualifiedName("crypto/rc4", "NewCipher") }
|
||||
|
||||
override Expr getInput() { result = this.getArgument(0).asExpr() }
|
||||
|
||||
override CryptographicAlgorithm getAlgorithm() {
|
||||
result.matchesName(this.getTarget().getPackage().getName())
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
/**
|
||||
* @name Use of a weak cryptographic algorithm
|
||||
* @description Using weak cryptographic algorithms can allow an attacker to compromise security.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @id go/weak-crypto-algorithm
|
||||
* @tags security
|
||||
* experimental
|
||||
* external/cwe/cwe-327
|
||||
* external/cwe/cwe-328
|
||||
*/
|
||||
|
||||
import go
|
||||
import WeakCryptoAlgorithmCustomizations
|
||||
import WeakCryptoAlgorithm::Flow::PathGraph
|
||||
|
||||
from WeakCryptoAlgorithm::Flow::PathNode source, WeakCryptoAlgorithm::Flow::PathNode sink
|
||||
where WeakCryptoAlgorithm::Flow::flowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "$@ is used in a weak cryptographic algorithm.",
|
||||
source.getNode(), "Sensitive data"
|
||||
@@ -1,66 +0,0 @@
|
||||
/**
|
||||
* Provides default sources, sinks and sanitizers for reasoning about
|
||||
* sensitive information in weak cryptographic algorithms,
|
||||
* as well as extension points for adding your own.
|
||||
*/
|
||||
|
||||
import go
|
||||
private import semmle.go.security.SensitiveActions
|
||||
private import CryptoLibraries
|
||||
|
||||
/**
|
||||
* Provides default sources, sinks and sanitizers for reasoning about
|
||||
* sensitive information in weak cryptographic algorithms,
|
||||
* as well as extension points for adding your own.
|
||||
*/
|
||||
module WeakCryptoAlgorithm {
|
||||
/**
|
||||
* A data flow source for sensitive information in weak cryptographic algorithms.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A data flow sink for sensitive information in weak cryptographic algorithms.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A sanitizer for sensitive information in weak cryptographic algorithms.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A sensitive source.
|
||||
*/
|
||||
class SensitiveSource extends Source {
|
||||
SensitiveSource() { this.asExpr() instanceof SensitiveExpr }
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression used by a weak cryptographic algorithm.
|
||||
*/
|
||||
class WeakCryptographicOperationSink extends Sink {
|
||||
WeakCryptographicOperationSink() {
|
||||
exists(CryptographicOperation application |
|
||||
application.getAlgorithm().isWeak() and
|
||||
this.asExpr() = application.getInput()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
|
||||
predicate observeDiffInformedIncrementalMode() { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Tracks taint flow from sensitive information to weak cryptographic
|
||||
* algorithms.
|
||||
*/
|
||||
module Flow = TaintTracking::Global<Config>;
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/des"
|
||||
"crypto/md5"
|
||||
"crypto/rc4"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
)
|
||||
|
||||
func main() {
|
||||
public := []byte("hello")
|
||||
|
||||
password := []byte("123456")
|
||||
buf := password // testing dataflow by passing into different variable
|
||||
|
||||
// BAD, des is a weak crypto algorithm and password is sensitive data
|
||||
des.NewTripleDESCipher(buf)
|
||||
|
||||
// BAD, md5 is a weak crypto algorithm and password is sensitive data
|
||||
md5.Sum(buf)
|
||||
|
||||
// BAD, rc4 is a weak crypto algorithm and password is sensitive data
|
||||
rc4.NewCipher(buf)
|
||||
|
||||
// BAD, sha1 is a weak crypto algorithm and password is sensitive data
|
||||
sha1.Sum(buf)
|
||||
|
||||
// GOOD, password is sensitive data but aes is a strong crypto algorithm
|
||||
aes.NewCipher(buf)
|
||||
|
||||
// GOOD, password is sensitive data but sha256 is a strong crypto algorithm
|
||||
sha256.Sum256(buf)
|
||||
|
||||
// GOOD, des is a weak crypto algorithm but public is not sensitive data
|
||||
des.NewTripleDESCipher(public)
|
||||
|
||||
// GOOD, md5 is a weak crypto algorithm but public is not sensitive data
|
||||
md5.Sum(public)
|
||||
|
||||
// GOOD, rc4 is a weak crypto algorithm but public is not sensitive data
|
||||
rc4.NewCipher(public)
|
||||
|
||||
// GOOD, sha1 is a weak crypto algorithm but public is not sensitive data
|
||||
sha1.Sum(public)
|
||||
|
||||
// GOOD, aes is a strong crypto algorithm and public is not sensitive data
|
||||
aes.NewCipher(public)
|
||||
|
||||
// GOOD, sha256 is a strong crypto algorithm and public is not sensitive data
|
||||
sha256.Sum256(public)
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
| encryption.go:30:2:30:36 | call to Encrypt | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | ... := ...[0] | The cryptographic algorithm DES |
|
||||
| encryption.go:34:2:34:42 | call to Seal | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | ... := ...[0] | The cryptographic algorithm DES |
|
||||
| encryption.go:38:2:38:42 | call to Seal | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | ... := ...[0] | The cryptographic algorithm DES |
|
||||
| encryption.go:42:2:42:42 | call to Seal | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | ... := ...[0] | The cryptographic algorithm DES |
|
||||
| encryption.go:46:2:46:42 | call to Seal | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | ... := ...[0] | The cryptographic algorithm DES |
|
||||
| encryption.go:50:2:50:47 | call to CryptBlocks | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | ... := ...[0] | The cryptographic algorithm DES |
|
||||
| encryption.go:54:2:54:45 | call to XORKeyStream | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | ... := ...[0] | The cryptographic algorithm DES |
|
||||
| encryption.go:56:22:56:91 | struct literal | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | ... := ...[0] | The cryptographic algorithm DES |
|
||||
| encryption.go:59:21:59:68 | &... [postupdate] | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | ... := ...[0] | The cryptographic algorithm DES |
|
||||
| encryption.go:59:22:59:68 | struct literal | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | ... := ...[0] | The cryptographic algorithm DES |
|
||||
| encryption.go:59:22:59:68 | struct literal [postupdate] | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | ... := ...[0] | The cryptographic algorithm DES |
|
||||
| encryption.go:60:10:60:24 | ctrStreamWriter [postupdate] | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | ... := ...[0] | The cryptographic algorithm DES |
|
||||
| encryption.go:65:2:65:45 | call to XORKeyStream | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | ... := ...[0] | The cryptographic algorithm DES |
|
||||
| encryption.go:69:2:69:45 | call to XORKeyStream | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | ... := ...[0] | The cryptographic algorithm DES |
|
||||
| encryption.go:76:2:76:32 | call to Encrypt | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | ... := ...[0] | The cryptographic algorithm TRIPLEDES |
|
||||
| encryption.go:80:2:80:38 | call to Seal | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | ... := ...[0] | The cryptographic algorithm TRIPLEDES |
|
||||
| encryption.go:84:2:84:38 | call to Seal | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | ... := ...[0] | The cryptographic algorithm TRIPLEDES |
|
||||
| encryption.go:88:2:88:42 | call to Seal | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | ... := ...[0] | The cryptographic algorithm TRIPLEDES |
|
||||
| encryption.go:92:2:92:42 | call to Seal | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | ... := ...[0] | The cryptographic algorithm TRIPLEDES |
|
||||
| encryption.go:96:2:96:43 | call to CryptBlocks | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | ... := ...[0] | The cryptographic algorithm TRIPLEDES |
|
||||
| encryption.go:100:2:100:41 | call to XORKeyStream | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | ... := ...[0] | The cryptographic algorithm TRIPLEDES |
|
||||
| encryption.go:102:22:102:87 | struct literal | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | ... := ...[0] | The cryptographic algorithm TRIPLEDES |
|
||||
| encryption.go:105:21:105:68 | &... [postupdate] | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | ... := ...[0] | The cryptographic algorithm TRIPLEDES |
|
||||
| encryption.go:105:22:105:68 | struct literal | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | ... := ...[0] | The cryptographic algorithm TRIPLEDES |
|
||||
| encryption.go:105:22:105:68 | struct literal [postupdate] | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | ... := ...[0] | The cryptographic algorithm TRIPLEDES |
|
||||
| encryption.go:106:10:106:24 | ctrStreamWriter [postupdate] | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | ... := ...[0] | The cryptographic algorithm TRIPLEDES |
|
||||
| encryption.go:111:2:111:45 | call to XORKeyStream | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | ... := ...[0] | The cryptographic algorithm TRIPLEDES |
|
||||
| encryption.go:115:2:115:45 | call to XORKeyStream | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | ... := ...[0] | The cryptographic algorithm TRIPLEDES |
|
||||
| encryption.go:166:2:166:33 | call to XORKeyStream | $@ is broken or weak, and should not be used. | encryption.go:166:2:166:33 | call to XORKeyStream | The cryptographic algorithm RC4 |
|
||||
@@ -0,0 +1,4 @@
|
||||
query: Security/CWE-327/BrokenCryptoAlgorithm.ql
|
||||
postprocess:
|
||||
- utils/test/PrettyPrintModels.ql
|
||||
- utils/test/InlineExpectationsTestQuery.ql
|
||||
@@ -1,53 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/des"
|
||||
"crypto/md5"
|
||||
"crypto/rc4"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
)
|
||||
|
||||
func crypto() {
|
||||
public := []byte("hello")
|
||||
|
||||
password := []byte("123456")
|
||||
buf := password // testing dataflow by passing into different variable
|
||||
|
||||
// BAD, des is a weak crypto algorithm and password is sensitive data
|
||||
des.NewTripleDESCipher(buf)
|
||||
|
||||
// BAD, md5 is a weak crypto algorithm and password is sensitive data
|
||||
md5.Sum(buf)
|
||||
|
||||
// BAD, rc4 is a weak crypto algorithm and password is sensitive data
|
||||
rc4.NewCipher(buf)
|
||||
|
||||
// BAD, sha1 is a weak crypto algorithm and password is sensitive data
|
||||
sha1.Sum(buf)
|
||||
|
||||
// GOOD, password is sensitive data but aes is a strong crypto algorithm
|
||||
aes.NewCipher(buf)
|
||||
|
||||
// GOOD, password is sensitive data but sha256 is a strong crypto algorithm
|
||||
sha256.Sum256(buf)
|
||||
|
||||
// GOOD, des is a weak crypto algorithm but public is not sensitive data
|
||||
des.NewTripleDESCipher(public)
|
||||
|
||||
// GOOD, md5 is a weak crypto algorithm but public is not sensitive data
|
||||
md5.Sum(public)
|
||||
|
||||
// GOOD, rc4 is a weak crypto algorithm but public is not sensitive data
|
||||
rc4.NewCipher(public)
|
||||
|
||||
// GOOD, sha1 is a weak crypto algorithm but public is not sensitive data
|
||||
sha1.Sum(public)
|
||||
|
||||
// GOOD, aes is a strong crypto algorithm and public is not sensitive data
|
||||
aes.NewCipher(public)
|
||||
|
||||
// GOOD, sha256 is a strong crypto algorithm and public is not sensitive data
|
||||
sha256.Sum256(public)
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
testFailures
|
||||
invalidModelRow
|
||||
39
go/ql/test/query-tests/Security/CWE-327/CryptoAlgorithm.ql
Normal file
39
go/ql/test/query-tests/Security/CWE-327/CryptoAlgorithm.ql
Normal file
@@ -0,0 +1,39 @@
|
||||
import go
|
||||
import ModelValidation
|
||||
import utils.test.InlineExpectationsTest
|
||||
|
||||
module Test implements TestSig {
|
||||
string getARelevantTag() { result = "CryptographicOperation" }
|
||||
|
||||
predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
tag = "CryptographicOperation" and
|
||||
exists(
|
||||
CryptographicOperation::Range ho, string algorithm, string initialization, string blockMode
|
||||
|
|
||||
algorithm = ho.getAlgorithm().toString() + "." and
|
||||
(
|
||||
blockMode = " blockMode: " + ho.getBlockMode().toString() + "."
|
||||
or
|
||||
not exists(ho.getBlockMode()) and blockMode = ""
|
||||
) and
|
||||
exists(int c | c = count(ho.getInitialization()) |
|
||||
c = 0 and initialization = ""
|
||||
or
|
||||
c > 0 and
|
||||
initialization =
|
||||
" init from " +
|
||||
strictconcat(DataFlow::Node init, int n |
|
||||
init = ho.getInitialization() and
|
||||
n = ho.getStartLine() - init.getStartLine()
|
||||
|
|
||||
n.toString(), ","
|
||||
) + " lines above."
|
||||
) and
|
||||
ho.getLocation() = location and
|
||||
element = ho.toString() and
|
||||
value = "\"" + algorithm + blockMode + initialization + "\""
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import MakeTest<Test>
|
||||
@@ -1,17 +0,0 @@
|
||||
edges
|
||||
| Crypto.go:16:9:16:16 | password | Crypto.go:19:25:19:27 | buf | provenance | |
|
||||
| Crypto.go:16:9:16:16 | password | Crypto.go:22:10:22:12 | buf | provenance | |
|
||||
| Crypto.go:16:9:16:16 | password | Crypto.go:25:16:25:18 | buf | provenance | |
|
||||
| Crypto.go:16:9:16:16 | password | Crypto.go:28:11:28:13 | buf | provenance | |
|
||||
nodes
|
||||
| Crypto.go:16:9:16:16 | password | semmle.label | password |
|
||||
| Crypto.go:19:25:19:27 | buf | semmle.label | buf |
|
||||
| Crypto.go:22:10:22:12 | buf | semmle.label | buf |
|
||||
| Crypto.go:25:16:25:18 | buf | semmle.label | buf |
|
||||
| Crypto.go:28:11:28:13 | buf | semmle.label | buf |
|
||||
subpaths
|
||||
#select
|
||||
| Crypto.go:19:25:19:27 | buf | Crypto.go:16:9:16:16 | password | Crypto.go:19:25:19:27 | buf | $@ is used in a weak cryptographic algorithm. | Crypto.go:16:9:16:16 | password | Sensitive data |
|
||||
| Crypto.go:22:10:22:12 | buf | Crypto.go:16:9:16:16 | password | Crypto.go:22:10:22:12 | buf | $@ is used in a weak cryptographic algorithm. | Crypto.go:16:9:16:16 | password | Sensitive data |
|
||||
| Crypto.go:25:16:25:18 | buf | Crypto.go:16:9:16:16 | password | Crypto.go:25:16:25:18 | buf | $@ is used in a weak cryptographic algorithm. | Crypto.go:16:9:16:16 | password | Sensitive data |
|
||||
| Crypto.go:28:11:28:13 | buf | Crypto.go:16:9:16:16 | password | Crypto.go:28:11:28:13 | buf | $@ is used in a weak cryptographic algorithm. | Crypto.go:16:9:16:16 | password | Sensitive data |
|
||||
@@ -1 +0,0 @@
|
||||
experimental/CWE-327/WeakCryptoAlgorithm.ql
|
||||
@@ -0,0 +1,24 @@
|
||||
#select
|
||||
| hashing.go:22:8:22:22 | secretByteSlice | hashing.go:22:8:22:22 | secretByteSlice | hashing.go:22:8:22:22 | secretByteSlice | $@ is used in a hashing algorithm (MD5) that is insecure. | hashing.go:22:8:22:22 | secretByteSlice | Sensitive data (secret) |
|
||||
| hashing.go:23:10:23:24 | secretByteSlice | hashing.go:23:10:23:24 | secretByteSlice | hashing.go:23:10:23:24 | secretByteSlice | $@ is used in a hashing algorithm (MD5) that is insecure. | hashing.go:23:10:23:24 | secretByteSlice | Sensitive data (secret) |
|
||||
| hashing.go:24:20:24:31 | secretString | hashing.go:24:20:24:31 | secretString | hashing.go:24:20:24:31 | secretString | $@ is used in a hashing algorithm (MD5) that is insecure. | hashing.go:24:20:24:31 | secretString | Sensitive data (secret) |
|
||||
| hashing.go:25:10:25:24 | secretByteSlice | hashing.go:25:10:25:24 | secretByteSlice | hashing.go:25:10:25:24 | secretByteSlice | $@ is used in a hashing algorithm (MD5) that is insecure. | hashing.go:25:10:25:24 | secretByteSlice | Sensitive data (secret) |
|
||||
| hashing.go:27:17:27:31 | secretByteSlice | hashing.go:27:17:27:31 | secretByteSlice | hashing.go:27:17:27:31 | secretByteSlice | $@ is used in a hashing algorithm (SHA1) that is insecure. | hashing.go:27:17:27:31 | secretByteSlice | Sensitive data (secret) |
|
||||
| hashing.go:28:11:28:25 | secretByteSlice | hashing.go:28:11:28:25 | secretByteSlice | hashing.go:28:11:28:25 | secretByteSlice | $@ is used in a hashing algorithm (SHA1) that is insecure. | hashing.go:28:11:28:25 | secretByteSlice | Sensitive data (secret) |
|
||||
| hashing.go:30:16:30:30 | secretByteSlice | hashing.go:30:16:30:30 | secretByteSlice | hashing.go:30:16:30:30 | secretByteSlice | $@ is used in a hashing algorithm (MD4) that is insecure. | hashing.go:30:16:30:30 | secretByteSlice | Sensitive data (secret) |
|
||||
| hashing.go:31:22:31:36 | secretByteSlice | hashing.go:31:22:31:36 | secretByteSlice | hashing.go:31:22:31:36 | secretByteSlice | $@ is used in a hashing algorithm (RIPEMD160) that is insecure. | hashing.go:31:22:31:36 | secretByteSlice | Sensitive data (secret) |
|
||||
| hashing.go:82:23:82:38 | type conversion | hashing.go:82:30:82:37 | password | hashing.go:82:23:82:38 | type conversion | $@ is used in a hashing algorithm (SHA256) that is insecure for password hashing, since it is not a computationally expensive hash function. | hashing.go:82:30:82:37 | password | Sensitive data (password) |
|
||||
edges
|
||||
| hashing.go:82:30:82:37 | password | hashing.go:82:23:82:38 | type conversion | provenance | |
|
||||
nodes
|
||||
| hashing.go:22:8:22:22 | secretByteSlice | semmle.label | secretByteSlice |
|
||||
| hashing.go:23:10:23:24 | secretByteSlice | semmle.label | secretByteSlice |
|
||||
| hashing.go:24:20:24:31 | secretString | semmle.label | secretString |
|
||||
| hashing.go:25:10:25:24 | secretByteSlice | semmle.label | secretByteSlice |
|
||||
| hashing.go:27:17:27:31 | secretByteSlice | semmle.label | secretByteSlice |
|
||||
| hashing.go:28:11:28:25 | secretByteSlice | semmle.label | secretByteSlice |
|
||||
| hashing.go:30:16:30:30 | secretByteSlice | semmle.label | secretByteSlice |
|
||||
| hashing.go:31:22:31:36 | secretByteSlice | semmle.label | secretByteSlice |
|
||||
| hashing.go:82:23:82:38 | type conversion | semmle.label | type conversion |
|
||||
| hashing.go:82:30:82:37 | password | semmle.label | password |
|
||||
subpaths
|
||||
@@ -0,0 +1,4 @@
|
||||
query: Security/CWE-327/WeakSensitiveDataHashing.ql
|
||||
postprocess:
|
||||
- utils/test/PrettyPrintModels.ql
|
||||
- utils/test/InlineExpectationsTestQuery.ql
|
||||
167
go/ql/test/query-tests/Security/CWE-327/encryption.go
Normal file
167
go/ql/test/query-tests/Security/CWE-327/encryption.go
Normal file
@@ -0,0 +1,167 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/des"
|
||||
"crypto/rc4"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
var dst []byte = make([]byte, 16)
|
||||
var secretByteSlice []byte = []byte("")
|
||||
|
||||
const secretString string = ""
|
||||
|
||||
var public []byte = []byte("")
|
||||
|
||||
func getUserID() []byte {
|
||||
return []byte("")
|
||||
}
|
||||
|
||||
// Note that we do not alert on decryption as we may need to decrypt legacy formats
|
||||
|
||||
func BlockCipherDes() {
|
||||
// BAD, des is a weak crypto algorithm
|
||||
block, _ := des.NewCipher(nil)
|
||||
|
||||
block.Encrypt(dst, secretByteSlice) // $ Alert[go/weak-cryptographic-algorithm] CryptographicOperation="DES. init from 2 lines above."
|
||||
block.Decrypt(dst, secretByteSlice)
|
||||
|
||||
gcm1, _ := cipher.NewGCM(block)
|
||||
gcm1.Seal(nil, nil, secretByteSlice, nil) // $ Alert[go/weak-cryptographic-algorithm] CryptographicOperation="DES. init from 6 lines above."
|
||||
gcm1.Open(nil, nil, secretByteSlice, nil)
|
||||
|
||||
gcm2, _ := cipher.NewGCMWithNonceSize(block, 12)
|
||||
gcm2.Seal(nil, nil, secretByteSlice, nil) // $ Alert[go/weak-cryptographic-algorithm] CryptographicOperation="DES. init from 10 lines above."
|
||||
gcm2.Open(nil, nil, secretByteSlice, nil)
|
||||
|
||||
gcm3, _ := cipher.NewGCMWithRandomNonce(block)
|
||||
gcm3.Seal(nil, nil, secretByteSlice, nil) // $ Alert[go/weak-cryptographic-algorithm] CryptographicOperation="DES. init from 14 lines above."
|
||||
gcm3.Open(nil, nil, secretByteSlice, nil)
|
||||
|
||||
gcm4, _ := cipher.NewGCMWithTagSize(block, 12)
|
||||
gcm4.Seal(nil, nil, secretByteSlice, nil) // $ Alert[go/weak-cryptographic-algorithm] CryptographicOperation="DES. init from 18 lines above."
|
||||
gcm4.Open(nil, nil, secretByteSlice, nil)
|
||||
|
||||
cbcEncrypter := cipher.NewCBCEncrypter(block, nil)
|
||||
cbcEncrypter.CryptBlocks(dst, secretByteSlice) // $ Alert[go/weak-cryptographic-algorithm] CryptographicOperation="DES. blockMode: CBC. init from 1,22 lines above."
|
||||
cipher.NewCBCDecrypter(block, nil).CryptBlocks(dst, secretByteSlice)
|
||||
|
||||
ctrStream := cipher.NewCTR(block, nil)
|
||||
ctrStream.XORKeyStream(dst, secretByteSlice) // $ Alert[go/weak-cryptographic-algorithm] CryptographicOperation="DES. blockMode: CTR. init from 1,26 lines above."
|
||||
|
||||
ctrStreamReader := &cipher.StreamReader{S: ctrStream, R: bytes.NewReader(secretByteSlice)} // $ Alert[go/weak-cryptographic-algorithm] CryptographicOperation="DES. blockMode: CTR. init from 28,3 lines above."
|
||||
io.Copy(os.Stdout, ctrStreamReader)
|
||||
|
||||
ctrStreamWriter := &cipher.StreamWriter{S: ctrStream, W: os.Stdout} // $ Alert[go/weak-cryptographic-algorithm] CryptographicOperation="DES. blockMode: CTR. init from 31,6 lines above."
|
||||
io.Copy(ctrStreamWriter, bytes.NewReader(secretByteSlice)) // $ Alert[go/weak-cryptographic-algorithm] CryptographicOperation="DES. blockMode: CTR. init from 32,7 lines above."
|
||||
|
||||
// deprecated
|
||||
|
||||
cfbStream := cipher.NewCFBEncrypter(block, nil)
|
||||
cfbStream.XORKeyStream(dst, secretByteSlice) // $ Alert[go/weak-cryptographic-algorithm] CryptographicOperation="DES. blockMode: CFB. init from 1,37 lines above."
|
||||
cipher.NewCFBDecrypter(block, nil).XORKeyStream(dst, secretByteSlice)
|
||||
|
||||
ofbStream := cipher.NewOFB(block, nil)
|
||||
ofbStream.XORKeyStream(dst, secretByteSlice) // $ Alert[go/weak-cryptographic-algorithm] CryptographicOperation="DES. blockMode: OFB. init from 1,41 lines above."
|
||||
}
|
||||
|
||||
func BlockCipherTripleDes() {
|
||||
// BAD, triple des is a weak crypto algorithm and secretByteSlice is sensitive data
|
||||
block, _ := des.NewTripleDESCipher(nil)
|
||||
|
||||
block.Encrypt(dst, getUserID()) // $ Alert[go/weak-cryptographic-algorithm] CryptographicOperation="TRIPLEDES. init from 2 lines above."
|
||||
block.Decrypt(dst, getUserID())
|
||||
|
||||
gcm1, _ := cipher.NewGCM(block)
|
||||
gcm1.Seal(nil, nil, getUserID(), nil) // $ Alert[go/weak-cryptographic-algorithm] CryptographicOperation="TRIPLEDES. init from 6 lines above."
|
||||
gcm1.Open(nil, nil, getUserID(), nil)
|
||||
|
||||
gcm2, _ := cipher.NewGCMWithNonceSize(block, 12)
|
||||
gcm2.Seal(nil, nil, getUserID(), nil) // $ Alert[go/weak-cryptographic-algorithm] CryptographicOperation="TRIPLEDES. init from 10 lines above."
|
||||
gcm2.Open(nil, nil, getUserID(), nil)
|
||||
|
||||
gcm3, _ := cipher.NewGCMWithRandomNonce(block)
|
||||
gcm3.Seal(nil, nil, secretByteSlice, nil) // $ Alert[go/weak-cryptographic-algorithm] CryptographicOperation="TRIPLEDES. init from 14 lines above."
|
||||
gcm3.Open(nil, nil, secretByteSlice, nil)
|
||||
|
||||
gcm4, _ := cipher.NewGCMWithTagSize(block, 12)
|
||||
gcm4.Seal(nil, nil, secretByteSlice, nil) // $ Alert[go/weak-cryptographic-algorithm] CryptographicOperation="TRIPLEDES. init from 18 lines above."
|
||||
gcm4.Open(nil, nil, secretByteSlice, nil)
|
||||
|
||||
cbcEncrypter := cipher.NewCBCEncrypter(block, nil)
|
||||
cbcEncrypter.CryptBlocks(dst, getUserID()) // $ Alert[go/weak-cryptographic-algorithm] CryptographicOperation="TRIPLEDES. blockMode: CBC. init from 1,22 lines above."
|
||||
cipher.NewCBCDecrypter(block, nil).CryptBlocks(dst, getUserID())
|
||||
|
||||
ctrStream := cipher.NewCTR(block, nil)
|
||||
ctrStream.XORKeyStream(dst, getUserID()) // $ Alert[go/weak-cryptographic-algorithm] CryptographicOperation="TRIPLEDES. blockMode: CTR. init from 1,26 lines above."
|
||||
|
||||
ctrStreamReader := &cipher.StreamReader{S: ctrStream, R: bytes.NewReader(getUserID())} // $ Alert[go/weak-cryptographic-algorithm] CryptographicOperation="TRIPLEDES. blockMode: CTR. init from 28,3 lines above."
|
||||
io.Copy(os.Stdout, ctrStreamReader)
|
||||
|
||||
ctrStreamWriter := &cipher.StreamWriter{S: ctrStream, W: os.Stdout} // $ Alert[go/weak-cryptographic-algorithm] CryptographicOperation="TRIPLEDES. blockMode: CTR. init from 31,6 lines above."
|
||||
io.Copy(ctrStreamWriter, bytes.NewReader(getUserID())) // $ Alert[go/weak-cryptographic-algorithm] CryptographicOperation="TRIPLEDES. blockMode: CTR. init from 32,7 lines above."
|
||||
|
||||
// deprecated
|
||||
|
||||
cfbStream := cipher.NewCFBEncrypter(block, nil)
|
||||
cfbStream.XORKeyStream(dst, secretByteSlice) // $ Alert[go/weak-cryptographic-algorithm] CryptographicOperation="TRIPLEDES. blockMode: CFB. init from 1,37 lines above."
|
||||
cipher.NewCFBDecrypter(block, nil).XORKeyStream(dst, secretByteSlice)
|
||||
|
||||
ofbStream := cipher.NewOFB(block, nil)
|
||||
ofbStream.XORKeyStream(dst, secretByteSlice) // $ Alert[go/weak-cryptographic-algorithm] CryptographicOperation="TRIPLEDES. blockMode: OFB. init from 1,41 lines above."
|
||||
}
|
||||
|
||||
func BlockCipherAes() {
|
||||
// GOOD, aes is a strong crypto algorithm
|
||||
block, _ := aes.NewCipher(nil)
|
||||
|
||||
block.Encrypt(dst, secretByteSlice) // $ CryptographicOperation="AES. init from 2 lines above."
|
||||
block.Decrypt(dst, secretByteSlice)
|
||||
|
||||
gcm1, _ := cipher.NewGCM(block)
|
||||
gcm1.Seal(nil, nil, secretByteSlice, nil) // $ CryptographicOperation="AES. init from 6 lines above."
|
||||
gcm1.Open(nil, nil, secretByteSlice, nil)
|
||||
|
||||
gcm2, _ := cipher.NewGCMWithNonceSize(block, 12)
|
||||
gcm2.Seal(nil, nil, secretByteSlice, nil) // $ CryptographicOperation="AES. init from 10 lines above."
|
||||
gcm2.Open(nil, nil, secretByteSlice, nil)
|
||||
|
||||
gcm3, _ := cipher.NewGCMWithRandomNonce(block)
|
||||
gcm3.Seal(nil, nil, secretByteSlice, nil) // $ CryptographicOperation="AES. init from 14 lines above."
|
||||
gcm3.Open(nil, nil, secretByteSlice, nil)
|
||||
|
||||
gcm4, _ := cipher.NewGCMWithTagSize(block, 12)
|
||||
gcm4.Seal(nil, nil, secretByteSlice, nil) // $ CryptographicOperation="AES. init from 18 lines above."
|
||||
gcm4.Open(nil, nil, secretByteSlice, nil)
|
||||
|
||||
cbcEncrypter := cipher.NewCBCEncrypter(block, nil)
|
||||
cbcEncrypter.CryptBlocks(dst, secretByteSlice) // $ CryptographicOperation="AES. blockMode: CBC. init from 1,22 lines above."
|
||||
cipher.NewCBCDecrypter(block, nil).CryptBlocks(dst, secretByteSlice)
|
||||
|
||||
ctrStream := cipher.NewCTR(block, nil)
|
||||
ctrStream.XORKeyStream(dst, secretByteSlice) // $ CryptographicOperation="AES. blockMode: CTR. init from 1,26 lines above."
|
||||
|
||||
ctrStreamReader := &cipher.StreamReader{S: ctrStream, R: bytes.NewReader(secretByteSlice)} // $ CryptographicOperation="AES. blockMode: CTR. init from 28,3 lines above."
|
||||
io.Copy(os.Stdout, ctrStreamReader)
|
||||
|
||||
ctrStreamWriter := &cipher.StreamWriter{S: ctrStream, W: os.Stdout} // $ CryptographicOperation="AES. blockMode: CTR. init from 31,6 lines above."
|
||||
io.Copy(ctrStreamWriter, bytes.NewReader(secretByteSlice)) // $ CryptographicOperation="AES. blockMode: CTR. init from 32,7 lines above."
|
||||
|
||||
// deprecated
|
||||
|
||||
cfbStream := cipher.NewCFBEncrypter(block, nil)
|
||||
cfbStream.XORKeyStream(dst, secretByteSlice) // $ CryptographicOperation="AES. blockMode: CFB. init from 1,37 lines above."
|
||||
cipher.NewCFBDecrypter(block, nil).XORKeyStream(dst, secretByteSlice)
|
||||
|
||||
ofbStream := cipher.NewOFB(block, nil)
|
||||
ofbStream.XORKeyStream(dst, secretByteSlice) // $ CryptographicOperation="AES. blockMode: OFB. init from 1,41 lines above."
|
||||
}
|
||||
|
||||
func CipherRc4() {
|
||||
c, _ := rc4.NewCipher(nil)
|
||||
c.XORKeyStream(dst, getUserID()) // $ Alert[go/weak-cryptographic-algorithm] CryptographicOperation="RC4. init from 0 lines above."
|
||||
}
|
||||
5
go/ql/test/query-tests/Security/CWE-327/go.mod
Normal file
5
go/ql/test/query-tests/Security/CWE-327/go.mod
Normal file
@@ -0,0 +1,5 @@
|
||||
module test
|
||||
|
||||
go 1.24.0
|
||||
|
||||
require golang.org/x/crypto v0.43.0
|
||||
91
go/ql/test/query-tests/Security/CWE-327/hashing.go
Normal file
91
go/ql/test/query-tests/Security/CWE-327/hashing.go
Normal file
@@ -0,0 +1,91 @@
|
||||
package main
|
||||
|
||||
//go:generate depstubber -vendor golang.org/x/crypto/md4 "" New
|
||||
//go:generate depstubber -vendor golang.org/x/crypto/ripemd160 "" New
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"crypto/pbkdf2"
|
||||
"crypto/rand"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"crypto/sha3"
|
||||
"crypto/sha512"
|
||||
"io"
|
||||
|
||||
"golang.org/x/crypto/md4"
|
||||
"golang.org/x/crypto/ripemd160"
|
||||
)
|
||||
|
||||
func WeakHashes() {
|
||||
h := md5.New()
|
||||
h.Sum(secretByteSlice) // $ Alert[go/weak-sensitive-data-hashing] CryptographicOperation="MD5. init from 1 lines above."
|
||||
h.Write(secretByteSlice) // $ Alert[go/weak-sensitive-data-hashing] CryptographicOperation="MD5. init from 2 lines above."
|
||||
io.WriteString(h, secretString) // $ Alert[go/weak-sensitive-data-hashing] CryptographicOperation="MD5. init from 3 lines above."
|
||||
md5.Sum(secretByteSlice) // $ Alert[go/weak-sensitive-data-hashing] CryptographicOperation="MD5. init from 0 lines above."
|
||||
|
||||
sha1.New().Sum(secretByteSlice) // $ Alert[go/weak-sensitive-data-hashing] CryptographicOperation="SHA1. init from 0 lines above."
|
||||
sha1.Sum(secretByteSlice) // $ Alert[go/weak-sensitive-data-hashing] CryptographicOperation="SHA1. init from 0 lines above."
|
||||
|
||||
md4.New().Sum(secretByteSlice) // $ Alert[go/weak-sensitive-data-hashing] CryptographicOperation="MD4. init from 0 lines above."
|
||||
ripemd160.New().Sum(secretByteSlice) // $ Alert[go/weak-sensitive-data-hashing] CryptographicOperation="RIPEMD160. init from 0 lines above."
|
||||
|
||||
// Only alert when sensitive data is hashed.
|
||||
md5.New().Sum(public) // $ CryptographicOperation="MD5. init from 0 lines above."
|
||||
md5.Sum(public) // $ CryptographicOperation="MD5. init from 0 lines above."
|
||||
sha1.New().Sum(public) // $ CryptographicOperation="SHA1. init from 0 lines above."
|
||||
sha1.Sum(public) // $ CryptographicOperation="SHA1. init from 0 lines above."
|
||||
}
|
||||
|
||||
func StrongHashes() {
|
||||
sha256.New224().Sum(secretByteSlice) // $ CryptographicOperation="SHA224. init from 0 lines above."
|
||||
sha256.Sum224(secretByteSlice) // $ CryptographicOperation="SHA224. init from 0 lines above."
|
||||
|
||||
sha256.New().Sum(secretByteSlice) // $ CryptographicOperation="SHA256. init from 0 lines above."
|
||||
sha256.Sum256(secretByteSlice) // $ CryptographicOperation="SHA256. init from 0 lines above."
|
||||
|
||||
sha512.New().Sum(secretByteSlice) // $ CryptographicOperation="SHA512. init from 0 lines above."
|
||||
sha512.Sum512(secretByteSlice) // $ CryptographicOperation="SHA512. init from 0 lines above."
|
||||
|
||||
sha512.New384().Sum(secretByteSlice) // $ CryptographicOperation="SHA384. init from 0 lines above."
|
||||
sha512.Sum384(secretByteSlice) // $ CryptographicOperation="SHA384. init from 0 lines above."
|
||||
|
||||
sha512.New512_224().Sum(secretByteSlice) // $ CryptographicOperation="SHA512224. init from 0 lines above."
|
||||
sha512.Sum512_224(secretByteSlice) // $ CryptographicOperation="SHA512224. init from 0 lines above."
|
||||
|
||||
sha512.New512_256().Sum(secretByteSlice) // $ CryptographicOperation="SHA512256. init from 0 lines above."
|
||||
sha512.Sum512_256(secretByteSlice) // $ CryptographicOperation="SHA512256. init from 0 lines above."
|
||||
|
||||
sha3.New224().Sum(secretByteSlice) // $ CryptographicOperation="SHA3224. init from 0 lines above."
|
||||
sha3.Sum224(secretByteSlice) // $ CryptographicOperation="SHA3224. init from 0 lines above."
|
||||
|
||||
sha3.New256().Sum(secretByteSlice) // $ CryptographicOperation="SHA3256. init from 0 lines above."
|
||||
sha3.Sum256(secretByteSlice) // $ CryptographicOperation="SHA3256. init from 0 lines above."
|
||||
|
||||
sha3.New384().Sum(secretByteSlice) // $ CryptographicOperation="SHA3384. init from 0 lines above."
|
||||
sha3.Sum384(secretByteSlice) // $ CryptographicOperation="SHA3384. init from 0 lines above."
|
||||
|
||||
sha3.New512().Sum(secretByteSlice) // $ CryptographicOperation="SHA3512. init from 0 lines above."
|
||||
sha3.Sum512(secretByteSlice) // $ CryptographicOperation="SHA3512. init from 0 lines above."
|
||||
|
||||
sha3.NewSHAKE128().Write(secretByteSlice) // $ CryptographicOperation="SHAKE128. init from 0 lines above."
|
||||
sha3.NewCSHAKE128(nil, nil).Write(secretByteSlice) // $ CryptographicOperation="SHAKE128. init from 0 lines above."
|
||||
sha3.SumSHAKE128(secretByteSlice, 100) // $ CryptographicOperation="SHAKE128. init from 0 lines above."
|
||||
|
||||
sha3.NewSHAKE256().Write(secretByteSlice) // $ CryptographicOperation="SHAKE256. init from 0 lines above."
|
||||
sha3.NewCSHAKE256(nil, nil).Write(secretByteSlice) // $ CryptographicOperation="SHAKE256. init from 0 lines above."
|
||||
sha3.SumSHAKE256(secretByteSlice, 100) // $ CryptographicOperation="SHAKE256. init from 0 lines above."
|
||||
}
|
||||
|
||||
func GetPasswordHashBad(password string) [32]byte {
|
||||
// BAD, SHA256 is a strong hashing algorithm but it is not computationally expensive
|
||||
return sha256.Sum256([]byte(password)) // $ Alert[go/weak-sensitive-data-hashing] CryptographicOperation="SHA256. init from 0 lines above."
|
||||
}
|
||||
|
||||
func GetPasswordHashGood(password string) []byte {
|
||||
// GOOD, PBKDF2 is a strong hashing algorithm and it is computationally expensive
|
||||
salt := make([]byte, 16)
|
||||
rand.Read(salt)
|
||||
key, _ := pbkdf2.Key(sha512.New, password, salt, 4096, 32)
|
||||
return key
|
||||
}
|
||||
16
go/ql/test/query-tests/Security/CWE-327/vendor/golang.org/x/crypto/md4/stub.go
generated
vendored
Normal file
16
go/ql/test/query-tests/Security/CWE-327/vendor/golang.org/x/crypto/md4/stub.go
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
// Code generated by depstubber. DO NOT EDIT.
|
||||
// This is a simple stub for golang.org/x/crypto/md4, strictly for use in testing.
|
||||
|
||||
// See the LICENSE file for information about the licensing of the original library.
|
||||
// Source: golang.org/x/crypto/md4 (exports: ; functions: New)
|
||||
|
||||
// Package md4 is a stub of golang.org/x/crypto/md4, generated by depstubber.
|
||||
package md4
|
||||
|
||||
import (
|
||||
hash "hash"
|
||||
)
|
||||
|
||||
func New() hash.Hash {
|
||||
return nil
|
||||
}
|
||||
16
go/ql/test/query-tests/Security/CWE-327/vendor/golang.org/x/crypto/ripemd160/stub.go
generated
vendored
Normal file
16
go/ql/test/query-tests/Security/CWE-327/vendor/golang.org/x/crypto/ripemd160/stub.go
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
// Code generated by depstubber. DO NOT EDIT.
|
||||
// This is a simple stub for golang.org/x/crypto/ripemd160, strictly for use in testing.
|
||||
|
||||
// See the LICENSE file for information about the licensing of the original library.
|
||||
// Source: golang.org/x/crypto/ripemd160 (exports: ; functions: New)
|
||||
|
||||
// Package ripemd160 is a stub of golang.org/x/crypto/ripemd160, generated by depstubber.
|
||||
package ripemd160
|
||||
|
||||
import (
|
||||
hash "hash"
|
||||
)
|
||||
|
||||
func New() hash.Hash {
|
||||
return nil
|
||||
}
|
||||
4
go/ql/test/query-tests/Security/CWE-327/vendor/modules.txt
vendored
Normal file
4
go/ql/test/query-tests/Security/CWE-327/vendor/modules.txt
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
# golang.org/x/crypto v0.43.0
|
||||
## explicit
|
||||
golang.org/x/crypto/md4
|
||||
golang.org/x/crypto/ripemd160
|
||||
@@ -9,6 +9,7 @@ private import semmle.code.java.dataflow.SSA as Ssa
|
||||
private import semmle.code.java.dataflow.RangeUtils as RU
|
||||
|
||||
class SsaVariable extends Ssa::SsaDefinition {
|
||||
/** Gets a use of this variable. */
|
||||
Expr getAUse() { result = super.getARead() }
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
/**
|
||||
* Provides classes and predicates for the 'js/useless-expression' query.
|
||||
*/
|
||||
overlay[local]
|
||||
module;
|
||||
|
||||
import javascript
|
||||
import DOMProperties
|
||||
@@ -60,6 +62,7 @@ predicate isDeclaration(Expr e) {
|
||||
/**
|
||||
* Holds if there exists a getter for a property called `name` anywhere in the program.
|
||||
*/
|
||||
overlay[global]
|
||||
predicate isGetterProperty(string name) {
|
||||
// there is a call of the form `Object.defineProperty(..., name, descriptor)` ...
|
||||
exists(CallToObjectDefineProperty defProp | name = defProp.getPropertyName() |
|
||||
@@ -85,6 +88,7 @@ predicate isGetterProperty(string name) {
|
||||
/**
|
||||
* A property access that may invoke a getter.
|
||||
*/
|
||||
overlay[global]
|
||||
class GetterPropertyAccess extends PropAccess {
|
||||
override predicate isImpure() { isGetterProperty(this.getPropertyName()) }
|
||||
}
|
||||
@@ -123,6 +127,7 @@ predicate isReceiverSuppressingCall(CallExpr c, Expr dummy, PropAccess callee) {
|
||||
* even if they do, the call itself is useless and should be flagged by this
|
||||
* query.
|
||||
*/
|
||||
overlay[global]
|
||||
predicate noSideEffects(Expr e) {
|
||||
e.isPure()
|
||||
or
|
||||
@@ -148,6 +153,7 @@ predicate isCompoundExpression(Expr e) {
|
||||
/**
|
||||
* Holds if the expression `e` should be reported as having no effect.
|
||||
*/
|
||||
overlay[global]
|
||||
predicate hasNoEffect(Expr e) {
|
||||
noSideEffects(e) and
|
||||
inVoidContext(e) and
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
/**
|
||||
* Provides a predicate for identifying unused index variables in loops.
|
||||
*/
|
||||
overlay[local]
|
||||
module;
|
||||
|
||||
import javascript
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
* Provides classes for working with
|
||||
* [Asynchronous Module Definitions](https://github.com/amdjs/amdjs-api/wiki/AMD).
|
||||
*/
|
||||
overlay[local]
|
||||
module;
|
||||
|
||||
import javascript
|
||||
private import semmle.javascript.internal.CachedStages
|
||||
@@ -62,9 +64,11 @@ class AmdModuleDefinition extends CallExpr instanceof AmdModuleDefinition::Range
|
||||
}
|
||||
|
||||
/** DEPRECATED. Use `getDependencyExpr` instead. */
|
||||
overlay[global]
|
||||
deprecated PathExpr getDependency(int i) { result = this.getDependencyExpr(i) }
|
||||
|
||||
/** DEPRECATED. Use `getADependencyExpr` instead. */
|
||||
overlay[global]
|
||||
deprecated PathExpr getADependency() { result = this.getADependencyExpr() }
|
||||
|
||||
/** Gets the `i`th dependency of this module definition. */
|
||||
@@ -194,16 +198,19 @@ class AmdModuleDefinition extends CallExpr instanceof AmdModuleDefinition::Range
|
||||
* Gets an abstract value representing one or more values that may flow
|
||||
* into this module's `module.exports` property.
|
||||
*/
|
||||
overlay[global]
|
||||
DefiniteAbstractValue getAModuleExportsValue() {
|
||||
result = [this.getAnImplicitExportsValue(), this.getAnExplicitExportsValue()]
|
||||
}
|
||||
|
||||
overlay[global]
|
||||
pragma[noinline, nomagic]
|
||||
private AbstractValue getAnImplicitExportsValue() {
|
||||
// implicit exports: anything that is returned from the factory function
|
||||
result = this.getModuleExpr().analyze().getAValue()
|
||||
}
|
||||
|
||||
overlay[global]
|
||||
pragma[noinline]
|
||||
private AbstractValue getAnExplicitExportsValue() {
|
||||
// explicit exports: anything assigned to `module.exports`
|
||||
@@ -227,6 +234,7 @@ class AmdModuleDefinition extends CallExpr instanceof AmdModuleDefinition::Range
|
||||
private predicate isPseudoDependency(string s) { s = ["exports", "require", "module"] }
|
||||
|
||||
/** An AMD dependency, considered as a path expression. */
|
||||
overlay[global]
|
||||
private class AmdDependencyPath extends PathExprCandidate {
|
||||
AmdDependencyPath() {
|
||||
exists(AmdModuleDefinition amd |
|
||||
@@ -239,6 +247,7 @@ private class AmdDependencyPath extends PathExprCandidate {
|
||||
}
|
||||
|
||||
/** A constant path element appearing in an AMD dependency expression. */
|
||||
overlay[global]
|
||||
deprecated private class ConstantAmdDependencyPathElement extends PathExpr, ConstantString {
|
||||
ConstantAmdDependencyPathElement() { this = any(AmdDependencyPath amd).getAPart() }
|
||||
|
||||
@@ -281,6 +290,7 @@ private class AmdDependencyImport extends Import {
|
||||
* Specifically, we look for files whose absolute path ends with the imported path, possibly
|
||||
* adding well-known JavaScript file extensions like `.js`.
|
||||
*/
|
||||
overlay[global]
|
||||
private File guessTarget() {
|
||||
exists(FilePath imported, string abspath, string dirname, string basename |
|
||||
this.targetCandidate(result, abspath, imported, dirname, basename)
|
||||
@@ -303,6 +313,7 @@ private class AmdDependencyImport extends Import {
|
||||
* Additionally, `abspath` is bound to the absolute path of `f`, `imported` to the imported path, and
|
||||
* `dirname` and `basename` to the dirname and basename (respectively) of `imported`.
|
||||
*/
|
||||
overlay[global]
|
||||
private predicate targetCandidate(
|
||||
File f, string abspath, FilePath imported, string dirname, string basename
|
||||
) {
|
||||
@@ -316,10 +327,12 @@ private class AmdDependencyImport extends Import {
|
||||
/**
|
||||
* Gets the module whose absolute path matches this import, if there is only a single such module.
|
||||
*/
|
||||
overlay[global]
|
||||
private Module resolveByAbsolutePath() {
|
||||
result.getFile() = unique(File file | file = this.guessTarget())
|
||||
}
|
||||
|
||||
overlay[global]
|
||||
override Module getImportedModule() {
|
||||
result = super.getImportedModule()
|
||||
or
|
||||
@@ -348,14 +361,12 @@ private class AmdDependencyImport extends Import {
|
||||
*/
|
||||
class AmdModule extends Module {
|
||||
cached
|
||||
AmdModule() {
|
||||
Stages::DataFlowStage::ref() and
|
||||
exists(unique(AmdModuleDefinition def | amdModuleTopLevel(def, this)))
|
||||
}
|
||||
AmdModule() { exists(unique(AmdModuleDefinition def | amdModuleTopLevel(def, this))) }
|
||||
|
||||
/** Gets the definition of this module. */
|
||||
AmdModuleDefinition getDefine() { amdModuleTopLevel(result, this) }
|
||||
|
||||
overlay[global]
|
||||
override DataFlow::Node getAnExportedValue(string name) {
|
||||
exists(DataFlow::PropWrite pwn | result = pwn.getRhs() |
|
||||
pwn.getBase().analyze().getAValue() = this.getDefine().getAModuleExportsValue() and
|
||||
@@ -363,6 +374,7 @@ class AmdModule extends Module {
|
||||
)
|
||||
}
|
||||
|
||||
overlay[global]
|
||||
override DataFlow::Node getABulkExportedNode() {
|
||||
// Assigned to `module.exports` via the factory's `module` parameter
|
||||
exists(AbstractModuleObject m, DataFlow::PropWrite write |
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
/**
|
||||
* Provides classes for working with the AST-based representation of JavaScript programs.
|
||||
*/
|
||||
overlay[local]
|
||||
module;
|
||||
|
||||
import javascript
|
||||
private import internal.StmtContainers
|
||||
@@ -172,6 +174,7 @@ class AstNode extends @ast_node, NodeInStmtContainer {
|
||||
* The TypeScript compiler emits no code for ambient declarations, but they
|
||||
* can affect name resolution and type checking at compile-time.
|
||||
*/
|
||||
overlay[caller?]
|
||||
pragma[inline]
|
||||
predicate isAmbient() {
|
||||
this.isAmbientInternal()
|
||||
@@ -470,9 +473,12 @@ module AST {
|
||||
*/
|
||||
class ValueNode extends AstNode, @dataflownode {
|
||||
/** Gets type inference results for this element. */
|
||||
overlay[global]
|
||||
DataFlow::AnalyzedNode analyze() { result = DataFlow::valueNode(this).analyze() }
|
||||
|
||||
/** Gets the data flow node associated with this program element. */
|
||||
overlay[caller]
|
||||
pragma[inline]
|
||||
DataFlow::ValueNode flow() { result = DataFlow::valueNode(this) }
|
||||
|
||||
/**
|
||||
@@ -481,6 +487,7 @@ module AST {
|
||||
* This can be used to map an expression to the class it refers to, or
|
||||
* associate it with a named value coming from an dependency.
|
||||
*/
|
||||
overlay[global]
|
||||
ExprNameBindingNode getNameBinding() { result = this }
|
||||
|
||||
/**
|
||||
@@ -490,6 +497,7 @@ module AST {
|
||||
* (according to the type system), or to associate it with a named type coming
|
||||
* from a dependency.
|
||||
*/
|
||||
overlay[global]
|
||||
TypeNameBindingNode getTypeBinding() { TypeResolution::valueHasType(this, result) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,6 +272,8 @@
|
||||
* Note that the `import` statement as a whole is part of the CFG of the body, while its single
|
||||
* import specifier `x as y` forms part of the preamble.
|
||||
*/
|
||||
overlay[local]
|
||||
module;
|
||||
|
||||
import javascript
|
||||
private import internal.StmtContainers
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
* Class declarations and class expressions are modeled by (QL) classes `ClassDeclaration`
|
||||
* and `ClassExpression`, respectively, which are both subclasses of `ClassDefinition`.
|
||||
*/
|
||||
overlay[local]
|
||||
module;
|
||||
|
||||
import javascript
|
||||
|
||||
@@ -119,6 +121,7 @@ class ClassOrInterface extends @class_or_interface, TypeParameterized {
|
||||
*
|
||||
* Anonymous classes and interfaces do not have a canonical name.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated TypeName getTypeName() { result.getADefinition() = this }
|
||||
|
||||
/**
|
||||
@@ -253,6 +256,7 @@ class ClassDefinition extends @class_definition, ClassOrInterface, AST::ValueNod
|
||||
/**
|
||||
* Gets the definition of the super class of this class, if it can be determined.
|
||||
*/
|
||||
overlay[global]
|
||||
ClassDefinition getSuperClassDefinition() {
|
||||
result = this.getSuperClass().analyze().getAValue().(AbstractClass).getClass()
|
||||
}
|
||||
@@ -580,6 +584,7 @@ class MemberDeclaration extends @property, Documentable {
|
||||
int getMemberIndex() { properties(this, _, result, _, _) }
|
||||
|
||||
/** Holds if the name of this member is computed by an impure expression. */
|
||||
overlay[global]
|
||||
predicate hasImpureNameExpr() { this.isComputed() and this.getNameExpr().isImpure() }
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
/**
|
||||
* Provides classes for working with the Closure-Library module system.
|
||||
*/
|
||||
overlay[local]
|
||||
module;
|
||||
|
||||
import javascript
|
||||
|
||||
@@ -40,6 +42,7 @@ module Closure {
|
||||
/**
|
||||
* A reference to a Closure namespace.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated class ClosureNamespaceRef extends DataFlow::Node instanceof ClosureNamespaceRef::Range {
|
||||
/**
|
||||
* Gets the namespace being referenced.
|
||||
@@ -47,6 +50,7 @@ module Closure {
|
||||
string getClosureNamespace() { result = super.getClosureNamespace() }
|
||||
}
|
||||
|
||||
overlay[global]
|
||||
deprecated module ClosureNamespaceRef {
|
||||
/**
|
||||
* A reference to a Closure namespace.
|
||||
@@ -64,9 +68,11 @@ module Closure {
|
||||
/**
|
||||
* A data flow node that returns the value of a closure namespace.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated class ClosureNamespaceAccess extends ClosureNamespaceRef instanceof ClosureNamespaceAccess::Range
|
||||
{ }
|
||||
|
||||
overlay[global]
|
||||
deprecated module ClosureNamespaceAccess {
|
||||
/**
|
||||
* A data flow node that returns the value of a closure namespace.
|
||||
@@ -79,6 +85,7 @@ module Closure {
|
||||
/**
|
||||
* A call to a method on the `goog.` namespace, as a closure reference.
|
||||
*/
|
||||
overlay[global]
|
||||
abstract deprecated private class DefaultNamespaceRef extends DataFlow::MethodCallNode,
|
||||
ClosureNamespaceRef::Range
|
||||
{
|
||||
@@ -91,6 +98,7 @@ module Closure {
|
||||
* Holds if `node` is the data flow node corresponding to the expression in
|
||||
* a top-level expression statement.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated private predicate isTopLevelExpr(DataFlow::Node node) {
|
||||
any(TopLevel tl).getAChildStmt().(ExprStmt).getExpr().flow() = node
|
||||
}
|
||||
@@ -98,6 +106,7 @@ module Closure {
|
||||
/**
|
||||
* A top-level call to `goog.provide`.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated private class DefaultClosureProvideCall extends DefaultNamespaceRef {
|
||||
DefaultClosureProvideCall() {
|
||||
this.getMethodName() = "provide" and
|
||||
@@ -108,12 +117,14 @@ module Closure {
|
||||
/**
|
||||
* A top-level call to `goog.provide`.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated class ClosureProvideCall extends ClosureNamespaceRef, DataFlow::MethodCallNode instanceof DefaultClosureProvideCall
|
||||
{ }
|
||||
|
||||
/**
|
||||
* A call to `goog.require`.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated private class DefaultClosureRequireCall extends DefaultNamespaceRef,
|
||||
ClosureNamespaceAccess::Range
|
||||
{
|
||||
@@ -123,12 +134,14 @@ module Closure {
|
||||
/**
|
||||
* A call to `goog.require`.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated class ClosureRequireCall extends ClosureNamespaceAccess, DataFlow::MethodCallNode instanceof DefaultClosureRequireCall
|
||||
{ }
|
||||
|
||||
/**
|
||||
* A top-level call to `goog.module` or `goog.declareModuleId`.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated private class DefaultClosureModuleDeclaration extends DefaultNamespaceRef {
|
||||
DefaultClosureModuleDeclaration() {
|
||||
(this.getMethodName() = "module" or this.getMethodName() = "declareModuleId") and
|
||||
@@ -139,6 +152,7 @@ module Closure {
|
||||
/**
|
||||
* A top-level call to `goog.module` or `goog.declareModuleId`.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated class ClosureModuleDeclaration extends ClosureNamespaceRef, DataFlow::MethodCallNode instanceof DefaultClosureModuleDeclaration
|
||||
{ }
|
||||
|
||||
@@ -156,6 +170,7 @@ module Closure {
|
||||
/**
|
||||
* Gets the call to `goog.module` or `goog.declareModuleId` in this module.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated ClosureModuleDeclaration getModuleDeclaration() { result.getTopLevel() = this }
|
||||
|
||||
/**
|
||||
@@ -181,6 +196,7 @@ module Closure {
|
||||
result = this.getScope().getVariable("exports")
|
||||
}
|
||||
|
||||
overlay[global]
|
||||
override DataFlow::Node getAnExportedValue(string name) {
|
||||
exists(DataFlow::PropWrite write, Expr base |
|
||||
result = write.getRhs() and
|
||||
@@ -193,6 +209,7 @@ module Closure {
|
||||
)
|
||||
}
|
||||
|
||||
overlay[global]
|
||||
override DataFlow::Node getABulkExportedNode() {
|
||||
result = this.getExportsVariable().getAnAssignedExpr().flow()
|
||||
}
|
||||
@@ -232,6 +249,7 @@ module Closure {
|
||||
/**
|
||||
* Holds if `name` is a closure namespace, including proper namespace prefixes.
|
||||
*/
|
||||
overlay[global]
|
||||
pragma[noinline]
|
||||
predicate isClosureNamespace(string name) {
|
||||
exists(string namespace |
|
||||
@@ -253,6 +271,7 @@ module Closure {
|
||||
* Holds if a prefix of `name` is a closure namespace.
|
||||
*/
|
||||
bindingset[name]
|
||||
overlay[global]
|
||||
private predicate hasClosureNamespacePrefix(string name) {
|
||||
isClosureNamespace(name.substring(0, name.indexOf(".")))
|
||||
or
|
||||
@@ -262,6 +281,7 @@ module Closure {
|
||||
/**
|
||||
* Gets the closure namespace path addressed by the given data flow node, if any.
|
||||
*/
|
||||
overlay[global]
|
||||
string getClosureNamespaceFromSourceNode(DataFlow::SourceNode node) {
|
||||
node = AccessPath::getAReferenceOrAssignmentTo(result) and
|
||||
hasClosureNamespacePrefix(result)
|
||||
@@ -270,6 +290,7 @@ module Closure {
|
||||
/**
|
||||
* Gets the closure namespace path written to by the given property write, if any.
|
||||
*/
|
||||
overlay[global]
|
||||
string getWrittenClosureNamespace(DataFlow::PropWrite node) {
|
||||
node.getRhs() = AccessPath::getAnAssignmentTo(result) and
|
||||
hasClosureNamespacePrefix(result)
|
||||
@@ -278,6 +299,7 @@ module Closure {
|
||||
/**
|
||||
* Gets a data flow node that refers to the given value exported from a Closure module.
|
||||
*/
|
||||
overlay[global]
|
||||
DataFlow::SourceNode moduleImport(string moduleName) {
|
||||
getClosureNamespaceFromSourceNode(result) = moduleName
|
||||
}
|
||||
@@ -285,6 +307,7 @@ module Closure {
|
||||
/**
|
||||
* A call to `goog.bind`, as a partial function invocation.
|
||||
*/
|
||||
overlay[global]
|
||||
private class BindCall extends DataFlow::PartialInvokeNode::Range, DataFlow::CallNode {
|
||||
BindCall() { this = moduleImport("goog.bind").getACall() }
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
/** Provides classes for working with JavaScript comments. */
|
||||
overlay[local]
|
||||
module;
|
||||
|
||||
import javascript
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
/**
|
||||
* Provides classes for working with expressions that evaluate to constant values.
|
||||
*/
|
||||
overlay[local]
|
||||
module;
|
||||
|
||||
import javascript
|
||||
private import semmle.javascript.internal.CachedStages
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
/** Provides classes and predicates for working with variable definitions and uses. */
|
||||
overlay[local]
|
||||
module;
|
||||
|
||||
import javascript
|
||||
|
||||
@@ -231,6 +233,7 @@ class VarUse extends ControlFlowNode, @varref instanceof RValue {
|
||||
*
|
||||
* For global variables, each definition is considered to reach each use.
|
||||
*/
|
||||
overlay[global]
|
||||
VarDef getADef() {
|
||||
result = this.getSsaVariable().getDefinition().getAContributingVarDef() or
|
||||
result.getAVariable() = this.getVariable().(GlobalVariable)
|
||||
@@ -241,5 +244,6 @@ class VarUse extends ControlFlowNode, @varref instanceof RValue {
|
||||
*
|
||||
* This predicate is only defined for variables that can be SSA-converted.
|
||||
*/
|
||||
overlay[global]
|
||||
SsaVariable getSsaVariable() { result.getAUse() = this }
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
/**
|
||||
* Provides classes for working with E4X.
|
||||
*/
|
||||
overlay[local]
|
||||
module;
|
||||
|
||||
import javascript
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
/** Provides classes for working with ECMAScript 2015 modules. */
|
||||
overlay[local]
|
||||
module;
|
||||
|
||||
import javascript
|
||||
private import semmle.javascript.internal.CachedStages
|
||||
@@ -29,11 +31,13 @@ class ES2015Module extends Module {
|
||||
/** Gets an export declaration in this module. */
|
||||
ExportDeclaration getAnExport() { result.getTopLevel() = this }
|
||||
|
||||
overlay[global]
|
||||
override DataFlow::Node getAnExportedValue(string name) {
|
||||
exists(ExportDeclaration ed | ed = this.getAnExport() and result = ed.getSourceNode(name))
|
||||
}
|
||||
|
||||
/** Holds if this module exports variable `v` under the name `name`. */
|
||||
overlay[global]
|
||||
predicate exportsAs(LexicalName v, string name) { this.getAnExport().exportsAs(v, name) }
|
||||
|
||||
override predicate isStrict() {
|
||||
@@ -50,6 +54,7 @@ class ES2015Module extends Module {
|
||||
* When a module has both named and `default` exports, the non-standard interpretation can lead to
|
||||
* ambiguities, so we only allow the standard interpretation in that case.
|
||||
*/
|
||||
overlay[global]
|
||||
predicate hasBothNamedAndDefaultExports() {
|
||||
hasNamedExports(this) and
|
||||
hasDefaultExport(this)
|
||||
@@ -59,6 +64,7 @@ class ES2015Module extends Module {
|
||||
/**
|
||||
* Holds if `mod` contains one or more named export declarations other than `default`.
|
||||
*/
|
||||
overlay[global]
|
||||
private predicate hasNamedExports(ES2015Module mod) {
|
||||
mod.getAnExport().(ExportNamedDeclaration).getASpecifier().getExportedName() != "default"
|
||||
or
|
||||
@@ -71,6 +77,7 @@ private predicate hasNamedExports(ES2015Module mod) {
|
||||
/**
|
||||
* Holds if this module contains a default export.
|
||||
*/
|
||||
overlay[global]
|
||||
private predicate hasDefaultExport(ES2015Module mod) {
|
||||
// export default foo;
|
||||
mod.getAnExport() instanceof ExportDefaultDeclaration
|
||||
@@ -172,6 +179,7 @@ class ImportDeclaration extends Stmt, Import, @import_declaration {
|
||||
}
|
||||
|
||||
/** A literal path expression appearing in an `import` declaration. */
|
||||
overlay[global]
|
||||
deprecated private class LiteralImportPath extends PathExpr, ConstantString {
|
||||
LiteralImportPath() { exists(ImportDeclaration req | this = req.getChildExpr(-1)) }
|
||||
|
||||
@@ -198,6 +206,7 @@ deprecated private class LiteralImportPath extends PathExpr, ConstantString {
|
||||
*/
|
||||
class ImportSpecifier extends Expr, @import_specifier {
|
||||
/** Gets the import declaration in which this specifier appears. */
|
||||
overlay[global]
|
||||
ImportDeclaration getImportDeclaration() { result.getASpecifier() = this }
|
||||
|
||||
/** Gets the imported symbol; undefined for default and namespace import specifiers. */
|
||||
@@ -297,6 +306,7 @@ class BulkImportDeclaration extends ImportDeclaration {
|
||||
* import console, { log } from 'console';
|
||||
* ```
|
||||
*/
|
||||
overlay[global]
|
||||
class SelectiveImportDeclaration extends ImportDeclaration {
|
||||
SelectiveImportDeclaration() { not this instanceof BulkImportDeclaration }
|
||||
|
||||
@@ -330,9 +340,11 @@ class SelectiveImportDeclaration extends ImportDeclaration {
|
||||
*/
|
||||
abstract class ExportDeclaration extends Stmt, @export_declaration {
|
||||
/** Gets the module to which this export declaration belongs. */
|
||||
overlay[global]
|
||||
ES2015Module getEnclosingModule() { this = result.getAnExport() }
|
||||
|
||||
/** Holds if this export declaration exports variable `v` under the name `name`. */
|
||||
overlay[global]
|
||||
abstract predicate exportsAs(LexicalName v, string name);
|
||||
|
||||
/**
|
||||
@@ -356,6 +368,7 @@ abstract class ExportDeclaration extends Stmt, @export_declaration {
|
||||
* exports under the same name. In particular, its source node belongs
|
||||
* to module `a` or possibly to some other module from which `a` re-exports.
|
||||
*/
|
||||
overlay[global]
|
||||
abstract DataFlow::Node getSourceNode(string name);
|
||||
|
||||
/** Holds if is declared with the `type` keyword, so only types are exported. */
|
||||
@@ -407,11 +420,13 @@ class BulkReExportDeclaration extends ReExportDeclaration, @export_all_declarati
|
||||
/** Gets the name of the module from which this declaration re-exports. */
|
||||
override ConstantString getImportedPath() { result = this.getChildExpr(0) }
|
||||
|
||||
overlay[global]
|
||||
override predicate exportsAs(LexicalName v, string name) {
|
||||
this.getReExportedES2015Module().exportsAs(v, name) and
|
||||
not isShadowedFromBulkExport(this, name)
|
||||
}
|
||||
|
||||
overlay[global]
|
||||
override DataFlow::Node getSourceNode(string name) {
|
||||
result = this.getReExportedES2015Module().getAnExport().getSourceNode(name)
|
||||
}
|
||||
@@ -430,6 +445,7 @@ class BulkReExportDeclaration extends ReExportDeclaration, @export_all_declarati
|
||||
* At runtime, the interface `X` will have been removed, so `X` is actually re-exported anyway,
|
||||
* but we ignore this subtlety.
|
||||
*/
|
||||
overlay[global]
|
||||
private predicate isShadowedFromBulkExport(BulkReExportDeclaration reExport, string name) {
|
||||
exists(ExportNamedDeclaration other | other.getTopLevel() = reExport.getEnclosingModule() |
|
||||
other.getAnExportedDecl().getName() = name
|
||||
@@ -452,6 +468,7 @@ class ExportDefaultDeclaration extends ExportDeclaration, @export_default_declar
|
||||
/** Gets the operand statement or expression that is exported by this declaration. */
|
||||
ExprOrStmt getOperand() { result = this.getChild(0) }
|
||||
|
||||
overlay[global]
|
||||
override predicate exportsAs(LexicalName v, string name) {
|
||||
name = "default" and v = this.getADecl().getVariable()
|
||||
}
|
||||
@@ -464,6 +481,7 @@ class ExportDefaultDeclaration extends ExportDeclaration, @export_default_declar
|
||||
)
|
||||
}
|
||||
|
||||
overlay[global]
|
||||
override DataFlow::Node getSourceNode(string name) {
|
||||
name = "default" and result = DataFlow::valueNode(this.getOperand())
|
||||
}
|
||||
@@ -506,6 +524,7 @@ class ExportNamedDeclaration extends ExportDeclaration, @export_named_declaratio
|
||||
/** Gets the variable declaration, if any, exported by this named export. */
|
||||
VarDecl getADecl() { result = this.getAnExportedDecl() }
|
||||
|
||||
overlay[global]
|
||||
override predicate exportsAs(LexicalName v, string name) {
|
||||
exists(LexicalDecl vd | vd = this.getAnExportedDecl() |
|
||||
name = vd.getName() and v = vd.getALexicalName()
|
||||
@@ -518,6 +537,7 @@ class ExportNamedDeclaration extends ExportDeclaration, @export_named_declaratio
|
||||
)
|
||||
}
|
||||
|
||||
overlay[global]
|
||||
override DataFlow::Node getSourceNode(string name) {
|
||||
exists(VarDef d | d.getTarget() = this.getADecl() |
|
||||
name = d.getTarget().(VarDecl).getName() and
|
||||
@@ -555,6 +575,7 @@ class ExportNamedDeclaration extends ExportDeclaration, @export_named_declaratio
|
||||
|
||||
private import semmle.javascript.dataflow.internal.PreCallGraphStep
|
||||
|
||||
overlay[global]
|
||||
private class ExportNamespaceStep extends PreCallGraphStep {
|
||||
override predicate storeStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) {
|
||||
exists(ExportNamedDeclaration exprt, ExportNamespaceSpecifier spec |
|
||||
@@ -572,6 +593,7 @@ private class ExportNamespaceStep extends PreCallGraphStep {
|
||||
private class TypeOnlyExportDeclaration extends ExportNamedDeclaration {
|
||||
TypeOnlyExportDeclaration() { this.isTypeOnly() }
|
||||
|
||||
overlay[global]
|
||||
override predicate exportsAs(LexicalName v, string name) {
|
||||
super.exportsAs(v, name) and
|
||||
not v instanceof Variable
|
||||
@@ -745,9 +767,11 @@ abstract class ReExportDeclaration extends ExportDeclaration {
|
||||
abstract ConstantString getImportedPath();
|
||||
|
||||
/** Gets the module from which this declaration re-exports, if it is an ES2015 module. */
|
||||
overlay[global]
|
||||
ES2015Module getReExportedES2015Module() { result = this.getReExportedModule() }
|
||||
|
||||
/** Gets the module from which this declaration re-exports. */
|
||||
overlay[global]
|
||||
cached
|
||||
Module getReExportedModule() {
|
||||
Stages::Imports::ref() and
|
||||
@@ -756,6 +780,7 @@ abstract class ReExportDeclaration extends ExportDeclaration {
|
||||
}
|
||||
|
||||
/** A literal path expression appearing in a re-export declaration. */
|
||||
overlay[global]
|
||||
deprecated private class LiteralReExportPath extends PathExpr, ConstantString {
|
||||
LiteralReExportPath() { exists(ReExportDeclaration bred | this = bred.getImportedPath()) }
|
||||
|
||||
@@ -795,11 +820,13 @@ class SelectiveReExportDeclaration extends ReExportDeclaration, ExportNamedDecla
|
||||
class OriginalExportDeclaration extends ExportDeclaration {
|
||||
OriginalExportDeclaration() { not this instanceof ReExportDeclaration }
|
||||
|
||||
overlay[global]
|
||||
override predicate exportsAs(LexicalName v, string name) {
|
||||
this.(ExportDefaultDeclaration).exportsAs(v, name) or
|
||||
this.(ExportNamedDeclaration).exportsAs(v, name)
|
||||
}
|
||||
|
||||
overlay[global]
|
||||
override DataFlow::Node getSourceNode(string name) {
|
||||
result = this.(ExportDefaultDeclaration).getSourceNode(name) or
|
||||
result = this.(ExportNamedDeclaration).getSourceNode(name)
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
/** Provides classes for working with syntax errors. */
|
||||
overlay[local]
|
||||
module;
|
||||
|
||||
import javascript
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
/**
|
||||
* Provides classes for working with expressions.
|
||||
*/
|
||||
overlay[local]
|
||||
module;
|
||||
|
||||
import javascript
|
||||
private import semmle.javascript.internal.CachedStages
|
||||
@@ -115,12 +117,14 @@ class Expr extends @expr, ExprOrStmt, ExprOrType, AST::ValueNode {
|
||||
string getStringValue() { Stages::Ast::ref() and result = getStringValue(this) }
|
||||
|
||||
/** Holds if this expression is impure, that is, its evaluation could have side effects. */
|
||||
overlay[global]
|
||||
predicate isImpure() { any() }
|
||||
|
||||
/**
|
||||
* Holds if this expression is pure, that is, its evaluation is guaranteed
|
||||
* to be side-effect free.
|
||||
*/
|
||||
overlay[global]
|
||||
predicate isPure() { not this.isImpure() }
|
||||
|
||||
/**
|
||||
@@ -153,21 +157,25 @@ class Expr extends @expr, ExprOrStmt, ExprOrType, AST::ValueNode {
|
||||
* Holds if this expression accesses the global variable `g`, either directly
|
||||
* or through the `window` object.
|
||||
*/
|
||||
overlay[global]
|
||||
predicate accessesGlobal(string g) { this.flow().accessesGlobal(g) }
|
||||
|
||||
/**
|
||||
* Holds if this expression may evaluate to `s`.
|
||||
*/
|
||||
overlay[global]
|
||||
predicate mayHaveStringValue(string s) { this.flow().mayHaveStringValue(s) }
|
||||
|
||||
/**
|
||||
* Holds if this expression may evaluate to `b`.
|
||||
*/
|
||||
overlay[global]
|
||||
predicate mayHaveBooleanValue(boolean b) { this.flow().mayHaveBooleanValue(b) }
|
||||
|
||||
/**
|
||||
* Holds if this expression may refer to the initial value of parameter `p`.
|
||||
*/
|
||||
overlay[global]
|
||||
predicate mayReferToParameter(Parameter p) { DataFlow::parameterNode(p).flowsToExpr(this) }
|
||||
|
||||
/**
|
||||
@@ -178,6 +186,7 @@ class Expr extends @expr, ExprOrStmt, ExprOrType, AST::ValueNode {
|
||||
* Has no result if the expression is in a JavaScript file or in a TypeScript
|
||||
* file that was extracted without type information.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated Type getType() { ast_node_type(this, result) }
|
||||
|
||||
/**
|
||||
@@ -240,21 +249,16 @@ class Expr extends @expr, ExprOrStmt, ExprOrType, AST::ValueNode {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
private Stmt getRawEnclosingStmt(Expr e) {
|
||||
// For performance reasons, we need the enclosing statement without overrides
|
||||
enclosing_stmt(e, result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the data-flow node where exceptions thrown by this expression will
|
||||
* propagate if this expression causes an exception to be thrown.
|
||||
*/
|
||||
overlay[caller]
|
||||
pragma[inline]
|
||||
DataFlow::Node getExceptionTarget() {
|
||||
result = getCatchParameterFromStmt(this.getRawEnclosingStmt(this))
|
||||
result = getCatchParameterFromStmt(getRawEnclosingStmt(this))
|
||||
or
|
||||
not exists(getCatchParameterFromStmt(this.getRawEnclosingStmt(this))) and
|
||||
not exists(getCatchParameterFromStmt(getRawEnclosingStmt(this))) and
|
||||
result =
|
||||
any(DataFlow::FunctionNode f | f.getFunction() = this.getContainer()).getExceptionalReturn()
|
||||
}
|
||||
@@ -267,6 +271,13 @@ private DataFlow::Node getCatchParameterFromStmt(Stmt stmt) {
|
||||
DataFlow::parameterNode(stmt.getEnclosingTryCatchStmt().getACatchClause().getAParameter())
|
||||
}
|
||||
|
||||
overlay[caller]
|
||||
pragma[inline]
|
||||
private Stmt getRawEnclosingStmt(Expr e) {
|
||||
// For performance reasons, we need the enclosing statement without overrides
|
||||
enclosing_stmt(e, result)
|
||||
}
|
||||
|
||||
/**
|
||||
* An identifier.
|
||||
*
|
||||
@@ -301,6 +312,7 @@ class Identifier extends @identifier, ExprOrType {
|
||||
* ```
|
||||
*/
|
||||
class Label extends @label, Identifier, Expr {
|
||||
overlay[global]
|
||||
override predicate isImpure() { none() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "Label" }
|
||||
@@ -330,6 +342,7 @@ class Literal extends @literal, Expr {
|
||||
*/
|
||||
string getRawValue() { literals(_, result, this) }
|
||||
|
||||
overlay[global]
|
||||
override predicate isImpure() { none() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "Literal" }
|
||||
@@ -352,6 +365,7 @@ class ParExpr extends @par_expr, Expr {
|
||||
|
||||
override int getIntValue() { result = this.getExpression().getIntValue() }
|
||||
|
||||
overlay[global]
|
||||
override predicate isImpure() { this.getExpression().isImpure() }
|
||||
|
||||
override Expr getUnderlyingValue() { result = this.getExpression().getUnderlyingValue() }
|
||||
@@ -500,6 +514,7 @@ class RegExpLiteral extends @regexp_literal, Literal, RegExpParent {
|
||||
* ```
|
||||
*/
|
||||
class ThisExpr extends @this_expr, Expr {
|
||||
overlay[global]
|
||||
override predicate isImpure() { none() }
|
||||
|
||||
/**
|
||||
@@ -555,6 +570,7 @@ class ArrayExpr extends @array_expr, Expr {
|
||||
/** Holds if this array literal has an omitted element. */
|
||||
predicate hasOmittedElement() { this.elementIsOmitted(_) }
|
||||
|
||||
overlay[global]
|
||||
override predicate isImpure() { this.getAnElement().isImpure() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ArrayExpr" }
|
||||
@@ -597,6 +613,7 @@ class ObjectExpr extends @obj_expr, Expr {
|
||||
*/
|
||||
predicate hasTrailingComma() { this.getLastToken().getPreviousToken().getValue() = "," }
|
||||
|
||||
overlay[global]
|
||||
override predicate isImpure() { this.getAProperty().isImpure() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ObjectExpr" }
|
||||
@@ -664,6 +681,7 @@ class Property extends @property, Documentable {
|
||||
* Holds if this property is impure, that is, the evaluation of its name or
|
||||
* its initializer expression could have side effects.
|
||||
*/
|
||||
overlay[global]
|
||||
predicate isImpure() {
|
||||
this.isComputed() and this.getNameExpr().isImpure()
|
||||
or
|
||||
@@ -826,6 +844,7 @@ class FunctionExpr extends @function_expr, Expr, Function {
|
||||
Stages::Ast::ref() and result = Expr.super.getContainer()
|
||||
}
|
||||
|
||||
overlay[global]
|
||||
override predicate isImpure() { none() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "FunctionExpr" }
|
||||
@@ -846,6 +865,7 @@ class ArrowFunctionExpr extends @arrow_function_expr, Expr, Function {
|
||||
|
||||
override StmtContainer getEnclosingContainer() { result = Expr.super.getContainer() }
|
||||
|
||||
overlay[global]
|
||||
override predicate isImpure() { none() }
|
||||
|
||||
override Function getThisBinder() {
|
||||
@@ -877,6 +897,7 @@ class SeqExpr extends @seq_expr, Expr {
|
||||
/** Gets the last expression in this sequence. */
|
||||
Expr getLastOperand() { result = this.getOperand(this.getNumOperands() - 1) }
|
||||
|
||||
overlay[global]
|
||||
override predicate isImpure() { this.getAnOperand().isImpure() }
|
||||
|
||||
override Expr getUnderlyingValue() { result = this.getLastOperand().getUnderlyingValue() }
|
||||
@@ -906,6 +927,7 @@ class ConditionalExpr extends @conditional_expr, Expr {
|
||||
/** Gets either the 'then' or the 'else' expression of this conditional. */
|
||||
Expr getABranch() { result = this.getConsequent() or result = this.getAlternate() }
|
||||
|
||||
overlay[global]
|
||||
override predicate isImpure() {
|
||||
this.getCondition().isImpure() or
|
||||
this.getABranch().isImpure()
|
||||
@@ -985,6 +1007,7 @@ class InvokeExpr extends @invokeexpr, Expr {
|
||||
*
|
||||
* This predicate is an approximation, computed using only local data flow.
|
||||
*/
|
||||
overlay[global]
|
||||
predicate hasOptionArgument(int i, string name, Expr value) {
|
||||
value = this.flow().(DataFlow::InvokeNode).getOptionArgument(i, name).asExpr()
|
||||
}
|
||||
@@ -997,6 +1020,7 @@ class InvokeExpr extends @invokeexpr, Expr {
|
||||
*
|
||||
* This predicate is only populated for files extracted with full TypeScript extraction.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated CallSignatureType getResolvedSignature() { invoke_expr_signature(this, result) }
|
||||
|
||||
/**
|
||||
@@ -1014,6 +1038,7 @@ class InvokeExpr extends @invokeexpr, Expr {
|
||||
*
|
||||
* This predicate is only populated for files extracted with full TypeScript extraction.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated CanonicalFunctionName getResolvedCalleeName() { ast_node_symbol(this, result) }
|
||||
|
||||
/**
|
||||
@@ -1022,6 +1047,7 @@ class InvokeExpr extends @invokeexpr, Expr {
|
||||
* Note that the resolved function may be overridden in a subclass and thus is not
|
||||
* necessarily the actual target of this invocation at runtime.
|
||||
*/
|
||||
overlay[global]
|
||||
Function getResolvedCallee() { TypeResolution::callTarget(this, result) }
|
||||
}
|
||||
|
||||
@@ -1156,6 +1182,7 @@ class DotExpr extends @dot_expr, PropAccess {
|
||||
/** Gets the identifier specifying the name of the accessed property. */
|
||||
Identifier getProperty() { result = this.getChildExpr(1) }
|
||||
|
||||
overlay[global]
|
||||
override predicate isImpure() { this.getBase().isImpure() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "DotExpr" }
|
||||
@@ -1176,6 +1203,7 @@ class IndexExpr extends @index_expr, PropAccess {
|
||||
|
||||
override string getPropertyName() { result = this.getIndex().(Literal).getValue() }
|
||||
|
||||
overlay[global]
|
||||
override predicate isImpure() {
|
||||
this.getBase().isImpure() or
|
||||
this.getIndex().isImpure()
|
||||
@@ -1201,6 +1229,7 @@ class UnaryExpr extends @unaryexpr, Expr {
|
||||
/** Gets the operator of this expression. */
|
||||
string getOperator() { none() }
|
||||
|
||||
overlay[global]
|
||||
override predicate isImpure() { this.getOperand().isImpure() }
|
||||
|
||||
override ControlFlowNode getFirstControlFlowNode() {
|
||||
@@ -1302,6 +1331,7 @@ class VoidExpr extends @void_expr, UnaryExpr {
|
||||
class DeleteExpr extends @delete_expr, UnaryExpr {
|
||||
override string getOperator() { result = "delete" }
|
||||
|
||||
overlay[global]
|
||||
override predicate isImpure() { any() }
|
||||
}
|
||||
|
||||
@@ -1352,6 +1382,7 @@ class BinaryExpr extends @binaryexpr, Expr {
|
||||
/** Gets the operator of this expression. */
|
||||
string getOperator() { none() }
|
||||
|
||||
overlay[global]
|
||||
override predicate isImpure() { this.getAnOperand().isImpure() }
|
||||
|
||||
override ControlFlowNode getFirstControlFlowNode() {
|
||||
@@ -1617,13 +1648,19 @@ private string getConstantString(Expr e) {
|
||||
result = e.(TemplateElement).getValue()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate hasConstantStringValue(Expr e) {
|
||||
exists(getConstantString(e))
|
||||
or
|
||||
hasAllConstantLeafs(e.getUnderlyingValue())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `add` is a string-concatenation where all the transitive leafs have a constant string value.
|
||||
*/
|
||||
private predicate hasAllConstantLeafs(AddExpr add) {
|
||||
forex(Expr leaf | leaf = getAnAddOperand*(add) and not exists(getAnAddOperand(leaf)) |
|
||||
exists(getConstantString(leaf))
|
||||
)
|
||||
hasConstantStringValue(add.getLeftOperand()) and
|
||||
hasConstantStringValue(add.getRightOperand())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2233,6 +2270,7 @@ class YieldExpr extends @yield_expr, Expr {
|
||||
/** Holds if this is a `yield*` expression. */
|
||||
predicate isDelegating() { is_delegating(this) }
|
||||
|
||||
overlay[global]
|
||||
override predicate isImpure() { any() }
|
||||
|
||||
override ControlFlowNode getFirstControlFlowNode() {
|
||||
@@ -2289,6 +2327,7 @@ class ComprehensionExpr extends @comprehension_expr, Expr {
|
||||
/** Gets the body expression of this comprehension. */
|
||||
Expr getBody() { result = this.getChildExpr(0) }
|
||||
|
||||
overlay[global]
|
||||
override predicate isImpure() {
|
||||
this.getABlock().isImpure() or
|
||||
this.getAFilter().isImpure() or
|
||||
@@ -2349,6 +2388,7 @@ class ComprehensionBlock extends @comprehension_block, Expr {
|
||||
/** Gets the domain over which this comprehension block iterates. */
|
||||
Expr getDomain() { result = this.getChildExpr(1) }
|
||||
|
||||
overlay[global]
|
||||
override predicate isImpure() {
|
||||
this.getIterator().isImpure() or
|
||||
this.getDomain().isImpure()
|
||||
@@ -2675,6 +2715,7 @@ class AwaitExpr extends @await_expr, Expr {
|
||||
/** Gets the operand of this `await` expression. */
|
||||
Expr getOperand() { result = this.getChildExpr(0) }
|
||||
|
||||
overlay[global]
|
||||
override predicate isImpure() { any() }
|
||||
|
||||
override ControlFlowNode getFirstControlFlowNode() {
|
||||
@@ -2698,6 +2739,7 @@ class AwaitExpr extends @await_expr, Expr {
|
||||
* ```
|
||||
*/
|
||||
class FunctionSentExpr extends @function_sent_expr, Expr {
|
||||
overlay[global]
|
||||
override predicate isImpure() { none() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "FunctionSentExpr" }
|
||||
@@ -2857,6 +2899,7 @@ class DynamicImportExpr extends @dynamic_import, Expr, Import {
|
||||
}
|
||||
|
||||
/** A literal path expression appearing in a dynamic import. */
|
||||
overlay[global]
|
||||
deprecated private class LiteralDynamicImportPath extends PathExpr, ConstantString {
|
||||
LiteralDynamicImportPath() {
|
||||
exists(DynamicImportExpr di | this.getParentExpr*() = di.getSource())
|
||||
@@ -2919,6 +2962,7 @@ class OptionalChainRoot extends ChainElem {
|
||||
* ```
|
||||
*/
|
||||
class ImportMetaExpr extends @import_meta_expr, Expr {
|
||||
overlay[global]
|
||||
override predicate isImpure() { none() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ImportMetaExpr" }
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
/**
|
||||
* Provides classes for reasoning about `extend`-like functions.
|
||||
*/
|
||||
overlay[local]
|
||||
module;
|
||||
|
||||
import javascript
|
||||
|
||||
@@ -169,6 +171,7 @@ private class FunctionalExtendCallShallow extends ExtendCall {
|
||||
*
|
||||
* Since all object properties are preserved, we model this as a value-preserving step.
|
||||
*/
|
||||
overlay[global]
|
||||
private class ExtendCallStep extends PreCallGraphStep {
|
||||
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
exists(ExtendCall extend |
|
||||
@@ -184,6 +187,7 @@ private import semmle.javascript.dataflow.internal.PreCallGraphStep
|
||||
/**
|
||||
* A step through a cloning library, such as `clone` or `fclone`.
|
||||
*/
|
||||
overlay[global]
|
||||
private class CloneStep extends PreCallGraphStep {
|
||||
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
exists(DataFlow::CallNode call |
|
||||
|
||||
@@ -36,6 +36,8 @@
|
||||
* Array.prototype.length;
|
||||
* </pre>
|
||||
*/
|
||||
overlay[local]
|
||||
module;
|
||||
|
||||
import javascript
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
/** Provides classes for working with files and folders. */
|
||||
overlay[local]
|
||||
module;
|
||||
|
||||
import javascript
|
||||
private import NodeModuleResolutionImpl
|
||||
@@ -33,12 +35,14 @@ module Folder = Impl::Folder;
|
||||
/** A folder. */
|
||||
class Folder extends Container, Impl::Folder {
|
||||
/** Gets the file or subfolder in this folder that has the given `name`, if any. */
|
||||
overlay[global]
|
||||
Container getChildContainer(string name) {
|
||||
result = this.getAChildContainer() and
|
||||
result.getBaseName() = name
|
||||
}
|
||||
|
||||
/** Gets the file in this folder that has the given `stem` and `extension`, if any. */
|
||||
overlay[global]
|
||||
File getFile(string stem, string extension) {
|
||||
result = this.getAChildContainer() and
|
||||
result.getStem() = stem and
|
||||
@@ -46,6 +50,7 @@ class Folder extends Container, Impl::Folder {
|
||||
}
|
||||
|
||||
/** Like `getFile` except `d.ts` is treated as a single extension. */
|
||||
overlay[global]
|
||||
private File getFileLongExtension(string stem, string extension) {
|
||||
not (stem.matches("%.d") and extension = "ts") and
|
||||
result = this.getFile(stem, extension)
|
||||
@@ -65,6 +70,7 @@ class Folder extends Container, Impl::Folder {
|
||||
*
|
||||
* HTML files will not be found by this method.
|
||||
*/
|
||||
overlay[global]
|
||||
File getJavaScriptFile(string stem) {
|
||||
result =
|
||||
min(int p, string ext |
|
||||
@@ -78,6 +84,7 @@ class Folder extends Container, Impl::Folder {
|
||||
* Gets an implementation file and/or a typings file from this folder that has the given `stem`.
|
||||
* This could be a single `.ts` file or a pair of `.js` and `.d.ts` files.
|
||||
*/
|
||||
overlay[global]
|
||||
File getJavaScriptFileOrTypings(string stem) {
|
||||
exists(File jsFile | jsFile = this.getJavaScriptFile(stem) |
|
||||
result = jsFile
|
||||
@@ -88,6 +95,7 @@ class Folder extends Container, Impl::Folder {
|
||||
}
|
||||
|
||||
/** Gets a subfolder contained in this folder. */
|
||||
overlay[global]
|
||||
Folder getASubFolder() { result = this.getAChildContainer() }
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
/** Provides classes for working with functions. */
|
||||
overlay[local]
|
||||
module;
|
||||
|
||||
import javascript
|
||||
|
||||
@@ -434,11 +436,13 @@ class Function extends @function, Parameterized, TypeParameterized, StmtContaine
|
||||
*
|
||||
* This predicate is only populated for files extracted with full TypeScript extraction.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated CanonicalFunctionName getCanonicalName() { ast_node_symbol(this, result) }
|
||||
|
||||
/**
|
||||
* Gets the call signature of this function, as determined by the TypeScript compiler, if any.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated CallSignatureType getCallSignature() { declared_function_signature(this, result) }
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
/**
|
||||
* Provides predicates for associating qualified names with data flow nodes.
|
||||
*/
|
||||
overlay[local]
|
||||
module;
|
||||
|
||||
import javascript
|
||||
private import semmle.javascript.dataflow.InferredTypes
|
||||
@@ -204,6 +206,7 @@ module AccessPath {
|
||||
* Holds if the global `accessPath` is only assigned to from one file, not counting
|
||||
* self-assignments.
|
||||
*/
|
||||
overlay[global]
|
||||
predicate isAssignedInUniqueFile(string accessPath) {
|
||||
strictcount(File f | isAssignedInFile(accessPath, f)) = 1
|
||||
}
|
||||
@@ -354,6 +357,7 @@ module AccessPath {
|
||||
* Gets a variable that is relevant for the computations in the `GetLaterAccess` module.
|
||||
* This predicate restricts as much as it can, but without depending on `getAVariableRef`.
|
||||
*/
|
||||
overlay[caller]
|
||||
pragma[inline]
|
||||
private SsaVariable getARelevantVariableSimple() {
|
||||
// The variable might be used where `getLaterBaseAccess()` is called.
|
||||
@@ -405,6 +409,7 @@ module AccessPath {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
overlay[caller]
|
||||
pragma[inline]
|
||||
DataFlow::Node getAReferenceTo(Root root, string path) {
|
||||
path = fromReference(result, root) and
|
||||
@@ -428,6 +433,7 @@ module AccessPath {
|
||||
* })(NS = NS || {});
|
||||
* ```
|
||||
*/
|
||||
overlay[caller]
|
||||
pragma[inline]
|
||||
DataFlow::Node getAReferenceTo(string path) {
|
||||
path = fromReference(result, DataFlow::globalAccessPathRootPseudoNode())
|
||||
@@ -449,6 +455,7 @@ module AccessPath {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
overlay[caller]
|
||||
pragma[inline]
|
||||
DataFlow::Node getAnAssignmentTo(Root root, string path) {
|
||||
path = fromRhs(result, root) and
|
||||
@@ -470,6 +477,7 @@ module AccessPath {
|
||||
* })(foo = foo || {});
|
||||
* ```
|
||||
*/
|
||||
overlay[caller]
|
||||
pragma[inline]
|
||||
DataFlow::Node getAnAssignmentTo(string path) {
|
||||
path = fromRhs(result, DataFlow::globalAccessPathRootPseudoNode())
|
||||
@@ -480,6 +488,7 @@ module AccessPath {
|
||||
*
|
||||
* See `getAReferenceTo` and `getAnAssignmentTo` for more details.
|
||||
*/
|
||||
overlay[caller]
|
||||
pragma[inline]
|
||||
DataFlow::Node getAReferenceOrAssignmentTo(string path) {
|
||||
result = getAReferenceTo(path)
|
||||
@@ -492,6 +501,7 @@ module AccessPath {
|
||||
*
|
||||
* See `getAReferenceTo` and `getAnAssignmentTo` for more details.
|
||||
*/
|
||||
overlay[caller]
|
||||
pragma[inline]
|
||||
DataFlow::Node getAReferenceOrAssignmentTo(Root root, string path) {
|
||||
result = getAReferenceTo(root, path)
|
||||
@@ -502,6 +512,7 @@ module AccessPath {
|
||||
/**
|
||||
* Holds if there is a step from `pred` to `succ` through an assignment to an access path.
|
||||
*/
|
||||
overlay[caller?]
|
||||
pragma[inline]
|
||||
predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
exists(string name, Root root |
|
||||
@@ -519,6 +530,7 @@ module AccessPath {
|
||||
/**
|
||||
* Gets a `SourceNode` that refers to the same value or access path as the given node.
|
||||
*/
|
||||
overlay[caller]
|
||||
pragma[inline]
|
||||
DataFlow::SourceNode getAnAliasedSourceNode(DataFlow::Node node) {
|
||||
exists(DataFlow::SourceNode root, string accessPath |
|
||||
@@ -657,7 +669,7 @@ module AccessPath {
|
||||
*/
|
||||
cached
|
||||
predicate hasDominatingWrite(DataFlow::PropRead read) {
|
||||
Stages::TypeTracking::ref() and
|
||||
Stages::DataFlowStage::ref() and
|
||||
// within the same basic block.
|
||||
exists(ReachableBasicBlock bb, Root root, string path, int ranking |
|
||||
read.asExpr() = rankedAccessPath(bb, root, path, ranking, AccessPathRead()) and
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
/** Provides classes for working with HTML documents. */
|
||||
overlay[local]
|
||||
module;
|
||||
|
||||
import javascript
|
||||
|
||||
@@ -283,6 +285,7 @@ module HTML {
|
||||
/**
|
||||
* A path string arising from the `src` attribute of a `script` tag.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated private class ScriptSrcPath extends PathString {
|
||||
ScriptSrcPath() { scriptSrc(this, _) }
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
/** Provides classes for working with JSDoc comments. */
|
||||
overlay[local]
|
||||
module;
|
||||
|
||||
import javascript
|
||||
private import semmle.javascript.internal.CachedStages
|
||||
@@ -627,6 +629,7 @@ module JSDoc {
|
||||
/**
|
||||
* A statement container which may declare JSDoc name aliases.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated class Environment extends StmtContainer {
|
||||
/**
|
||||
* Gets the fully qualified name aliased by the given unqualified name
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
/**
|
||||
* Provides classes for working with JSON data.
|
||||
*/
|
||||
overlay[local]
|
||||
module;
|
||||
|
||||
import javascript
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
/**
|
||||
* Provides classes for working with JSX code.
|
||||
*/
|
||||
overlay[local]
|
||||
module;
|
||||
|
||||
import javascript
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
* This information is only available for snapshots that have been extracted with
|
||||
* the `--extract-program-text` flag.
|
||||
*/
|
||||
overlay[local]
|
||||
module;
|
||||
|
||||
import javascript
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
/** Provides classes for working with locations and program elements that have locations. */
|
||||
overlay[local]
|
||||
module;
|
||||
|
||||
import javascript
|
||||
|
||||
@@ -30,6 +32,7 @@ final class Location extends @location_default {
|
||||
int getNumLines() { result = this.getEndLine() - this.getStartLine() + 1 }
|
||||
|
||||
/** Holds if this location starts before location `that`. */
|
||||
overlay[caller]
|
||||
pragma[inline]
|
||||
predicate startsBefore(Location that) {
|
||||
exists(string f, int sl1, int sc1, int sl2, int sc2 |
|
||||
@@ -43,6 +46,7 @@ final class Location extends @location_default {
|
||||
}
|
||||
|
||||
/** Holds if this location ends after location `that`. */
|
||||
overlay[caller]
|
||||
pragma[inline]
|
||||
predicate endsAfter(Location that) {
|
||||
exists(string f, int el1, int ec1, int el2, int ec2 |
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
* ECMAScript 2015-style modules, and the older CommonJS and AMD-style
|
||||
* modules.
|
||||
*/
|
||||
overlay[local]
|
||||
module;
|
||||
|
||||
import javascript
|
||||
private import semmle.javascript.internal.CachedStages
|
||||
@@ -23,9 +25,11 @@ abstract class Module extends TopLevel {
|
||||
Import getAnImport() { result.getTopLevel() = this }
|
||||
|
||||
/** Gets a module from which this module imports. */
|
||||
overlay[global]
|
||||
Module getAnImportedModule() { result = this.getAnImport().getImportedModule() }
|
||||
|
||||
/** Gets a symbol exported by this module. */
|
||||
overlay[global]
|
||||
string getAnExportedSymbol() { exists(this.getAnExportedValue(result)) }
|
||||
|
||||
/**
|
||||
@@ -39,12 +43,14 @@ abstract class Module extends TopLevel {
|
||||
* Symbols defined in another module that are re-exported by
|
||||
* this module are only sometimes considered.
|
||||
*/
|
||||
overlay[global]
|
||||
cached
|
||||
abstract DataFlow::Node getAnExportedValue(string name);
|
||||
|
||||
/**
|
||||
* Gets a value that is exported as the whole exports object of this module.
|
||||
*/
|
||||
overlay[global]
|
||||
cached
|
||||
DataFlow::Node getABulkExportedNode() { none() } // overridden in subclasses
|
||||
|
||||
@@ -55,6 +61,7 @@ abstract class Module extends TopLevel {
|
||||
* This can be used to determine which value a default-import will likely refer to,
|
||||
* as the interaction between different module types is not standardized.
|
||||
*/
|
||||
overlay[global]
|
||||
DataFlow::Node getDefaultOrBulkExport() {
|
||||
result = [this.getAnExportedValue("default"), this.getABulkExportedNode()]
|
||||
}
|
||||
@@ -69,6 +76,7 @@ abstract class Module extends TopLevel {
|
||||
* This predicate is not part of the public API, it is only exposed to allow
|
||||
* overriding by subclasses.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated predicate searchRoot(PathExpr path, Folder searchRoot, int priority) {
|
||||
path.getEnclosingModule() = this and
|
||||
priority = 0 and
|
||||
@@ -90,6 +98,7 @@ abstract class Module extends TopLevel {
|
||||
* resolves to a folder containing a main module (such as `index.js`), then
|
||||
* that file is the result.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated File resolve(PathExpr path) {
|
||||
path.getEnclosingModule() = this and
|
||||
(
|
||||
@@ -124,6 +133,7 @@ abstract class Import extends AstNode {
|
||||
abstract Module getEnclosingModule();
|
||||
|
||||
/** DEPRECATED. Use `getImportedPathExpr` instead. */
|
||||
overlay[global]
|
||||
deprecated PathExpr getImportedPath() { result = this.getImportedPathExpr() }
|
||||
|
||||
/** Gets the (unresolved) path that this import refers to. */
|
||||
@@ -138,6 +148,7 @@ abstract class Import extends AstNode {
|
||||
* Any externs module whose name exactly matches the imported
|
||||
* path is assumed to be a possible target of the import.
|
||||
*/
|
||||
overlay[global]
|
||||
Module resolveExternsImport() {
|
||||
result.isExterns() and result.getName() = this.getImportedPathString()
|
||||
}
|
||||
@@ -145,16 +156,19 @@ abstract class Import extends AstNode {
|
||||
/**
|
||||
* Gets the module the path of this import resolves to.
|
||||
*/
|
||||
overlay[global]
|
||||
Module resolveImportedPath() { result.getFile() = this.getImportedFile() }
|
||||
|
||||
/**
|
||||
* Gets the module the path of this import resolves to.
|
||||
*/
|
||||
overlay[global]
|
||||
File getImportedFile() { result = ImportPathResolver::resolveExpr(this.getImportedPathExpr()) }
|
||||
|
||||
/**
|
||||
* DEPRECATED. Use `getImportedModule()` instead.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated Module resolveFromTypeScriptSymbol() {
|
||||
exists(CanonicalName symbol |
|
||||
ast_node_symbol(this, symbol) and
|
||||
@@ -170,6 +184,7 @@ abstract class Import extends AstNode {
|
||||
* behavior of Node.js imports, which prefer core modules such as `fs` over any
|
||||
* source module of the same name.
|
||||
*/
|
||||
overlay[global]
|
||||
cached
|
||||
Module getImportedModule() {
|
||||
Stages::Imports::ref() and
|
||||
@@ -210,6 +225,7 @@ abstract class Import extends AstNode {
|
||||
* in cases where it would cause ambiguity between named exports and properties
|
||||
* of a default export.
|
||||
*/
|
||||
overlay[global]
|
||||
final DataFlow::Node getImportedModuleNodeStrict() {
|
||||
result = this.getImportedModuleNode() and
|
||||
not (
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
/**
|
||||
* Provides classes for working with NPM module definitions and dependencies.
|
||||
*/
|
||||
overlay[local?]
|
||||
module;
|
||||
|
||||
import javascript
|
||||
private import NodeModuleResolutionImpl
|
||||
|
||||
@@ -17,6 +17,7 @@ private import semmle.javascript.dataflow.internal.DataFlowNode
|
||||
* process.stdout.write(fs.readFileSync(process.argv[i], 'utf8'));
|
||||
* ```
|
||||
*/
|
||||
overlay[local]
|
||||
class NodeModule extends Module {
|
||||
NodeModule() {
|
||||
is_module(this) and
|
||||
@@ -36,11 +37,13 @@ class NodeModule extends Module {
|
||||
* Gets an abstract value representing one or more values that may flow
|
||||
* into this module's `module.exports` property.
|
||||
*/
|
||||
overlay[global]
|
||||
pragma[noinline]
|
||||
DefiniteAbstractValue getAModuleExportsValue() {
|
||||
result = this.getAModuleExportsProperty().getAValue()
|
||||
}
|
||||
|
||||
overlay[global]
|
||||
pragma[noinline]
|
||||
private AbstractProperty getAModuleExportsProperty() {
|
||||
result.getBase().(AbstractModuleObject).getModule() = this and
|
||||
@@ -52,12 +55,14 @@ class NodeModule extends Module {
|
||||
* For performance this predicate only computes relevant expressions (in `getAModuleExportsCandidate`).
|
||||
* So if using this predicate - consider expanding the list of relevant expressions.
|
||||
*/
|
||||
overlay[global]
|
||||
DataFlow::AnalyzedNode getAModuleExportsNode() {
|
||||
result = getAModuleExportsCandidate() and
|
||||
result.getAValue() = this.getAModuleExportsValue()
|
||||
}
|
||||
|
||||
/** Gets a symbol exported by this module. */
|
||||
overlay[global]
|
||||
override string getAnExportedSymbol() {
|
||||
result = super.getAnExportedSymbol()
|
||||
or
|
||||
@@ -70,6 +75,7 @@ class NodeModule extends Module {
|
||||
)
|
||||
}
|
||||
|
||||
overlay[global]
|
||||
override DataFlow::Node getAnExportedValue(string name) {
|
||||
// a property write whose base is `exports` or `module.exports`
|
||||
exists(DataFlow::PropWrite pwn | result = pwn.getRhs() |
|
||||
@@ -114,6 +120,7 @@ class NodeModule extends Module {
|
||||
)
|
||||
}
|
||||
|
||||
overlay[global]
|
||||
override DataFlow::Node getABulkExportedNode() {
|
||||
Stages::Imports::ref() and
|
||||
exists(DataFlow::PropWrite write |
|
||||
@@ -124,6 +131,7 @@ class NodeModule extends Module {
|
||||
}
|
||||
|
||||
/** Gets a symbol that the module object inherits from its prototypes. */
|
||||
overlay[global]
|
||||
private string getAnImplicitlyExportedSymbol() {
|
||||
exists(ExternalConstructor ec | ec = this.getPrototypeOfExportedExpr() |
|
||||
result = ec.getAMember().getName()
|
||||
@@ -136,6 +144,7 @@ class NodeModule extends Module {
|
||||
}
|
||||
|
||||
/** Gets an externs declaration of the prototype object of a value exported by this module. */
|
||||
overlay[global]
|
||||
private ExternalConstructor getPrototypeOfExportedExpr() {
|
||||
exists(AbstractValue exported | exported = this.getAModuleExportsValue() |
|
||||
result instanceof ObjectExternal
|
||||
@@ -146,6 +155,7 @@ class NodeModule extends Module {
|
||||
)
|
||||
}
|
||||
|
||||
overlay[global]
|
||||
deprecated override predicate searchRoot(PathExpr path, Folder searchRoot, int priority) {
|
||||
path.getEnclosingModule() = this and
|
||||
exists(string pathval | pathval = path.getValue() |
|
||||
@@ -224,6 +234,7 @@ predicate findNodeModulesFolder(Folder f, Folder nodeModules, int distance) {
|
||||
/**
|
||||
* A Node.js `require` variable.
|
||||
*/
|
||||
overlay[local]
|
||||
private class RequireVariable extends Variable {
|
||||
RequireVariable() {
|
||||
this = any(ModuleScope m).getVariable("require")
|
||||
@@ -236,6 +247,7 @@ private class RequireVariable extends Variable {
|
||||
}
|
||||
}
|
||||
|
||||
overlay[local]
|
||||
private predicate isModuleModule(EarlyStageNode nd) {
|
||||
exists(ImportDeclaration imp | imp.getRawImportPath() = "module" |
|
||||
nd = TDestructuredModuleImportNode(imp)
|
||||
@@ -249,6 +261,7 @@ private predicate isModuleModule(EarlyStageNode nd) {
|
||||
)
|
||||
}
|
||||
|
||||
overlay[local]
|
||||
private predicate isCreateRequire(EarlyStageNode nd) {
|
||||
exists(PropAccess prop |
|
||||
isModuleModule(TValueNode(prop.getBase())) and
|
||||
@@ -278,6 +291,7 @@ private predicate isCreateRequire(EarlyStageNode nd) {
|
||||
/**
|
||||
* Holds if `nd` may refer to `require`, either directly or modulo local data flow.
|
||||
*/
|
||||
overlay[local]
|
||||
cached
|
||||
private predicate isRequire(EarlyStageNode nd) {
|
||||
exists(VarAccess access |
|
||||
@@ -320,6 +334,7 @@ private predicate isRequire(EarlyStageNode nd) {
|
||||
* require('fs')
|
||||
* ```
|
||||
*/
|
||||
overlay[local]
|
||||
class Require extends CallExpr, Import {
|
||||
Require() { isRequire(TValueNode(this.getCallee())) }
|
||||
|
||||
|
||||
@@ -186,11 +186,13 @@ module Promises {
|
||||
/**
|
||||
* Gets the pseudo-field used to describe resolved values in a promise.
|
||||
*/
|
||||
overlay[local]
|
||||
string valueProp() { result = "$PromiseResolveField$" }
|
||||
|
||||
/**
|
||||
* Gets the pseudo-field used to describe rejected values in a promise.
|
||||
*/
|
||||
overlay[local]
|
||||
string errorProp() { result = "$PromiseRejectField$" }
|
||||
|
||||
/** A property set containing the pseudo-properites of a promise object. */
|
||||
@@ -236,6 +238,7 @@ module PromiseTypeTracking {
|
||||
*
|
||||
* These type-tracking steps are already included in the default type-tracking steps (through `PreCallGraphStep`).
|
||||
*/
|
||||
overlay[caller?]
|
||||
pragma[inline]
|
||||
DataFlow::Node promiseStep(DataFlow::Node pred, StepSummary summary) {
|
||||
exists(string field | field = Promises::valueProp() |
|
||||
@@ -254,6 +257,7 @@ module PromiseTypeTracking {
|
||||
* Gets the result from a single step through a promise, from `pred` with tracker `t2` to `result` with tracker `t`.
|
||||
* This can be loading a resolved value from a promise, storing a value in a promise, or copying a resolved value from one promise to another.
|
||||
*/
|
||||
overlay[caller?]
|
||||
pragma[inline]
|
||||
DataFlow::SourceNode promiseStep(
|
||||
DataFlow::SourceNode pred, DataFlow::TypeTracker t, DataFlow::TypeTracker t2
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
* Regular expression literals are represented as an abstract syntax tree of regular expression
|
||||
* terms.
|
||||
*/
|
||||
overlay[local]
|
||||
module;
|
||||
|
||||
import javascript
|
||||
private import semmle.javascript.dataflow.InferredTypes
|
||||
@@ -150,6 +152,7 @@ class RegExpTerm extends Locatable, @regexpterm {
|
||||
* /[a-z]+/g; // YES - Regexp literals are always used as regexp
|
||||
* ```
|
||||
*/
|
||||
overlay[global]
|
||||
predicate isUsedAsRegExp() {
|
||||
exists(RegExpParent parent | parent = this.getRootTerm().getParent() |
|
||||
parent instanceof RegExpLiteral
|
||||
@@ -964,6 +967,7 @@ class RegExpParseError extends Error, @regexp_parse_error {
|
||||
/**
|
||||
* Holds if `func` is a method defined on `String.prototype` with name `name`.
|
||||
*/
|
||||
overlay[global]
|
||||
private predicate isNativeStringMethod(Function func, string name) {
|
||||
exists(ExternalInstanceMemberDecl decl |
|
||||
decl.hasQualifiedName("String", name) and
|
||||
@@ -975,6 +979,7 @@ private predicate isNativeStringMethod(Function func, string name) {
|
||||
* Holds if `name` is the name of a property on a Match object returned by `String.prototype.match`,
|
||||
* not including array indices.
|
||||
*/
|
||||
overlay[global]
|
||||
private predicate isMatchObjectProperty(string name) {
|
||||
any(ExternalInstanceMemberDecl decl).hasQualifiedName("Array", name)
|
||||
or
|
||||
@@ -982,6 +987,7 @@ private predicate isMatchObjectProperty(string name) {
|
||||
}
|
||||
|
||||
/** Holds if `call` is a call to `match` whose result is used in a way that is incompatible with Match objects. */
|
||||
overlay[global]
|
||||
private predicate isUsedAsNonMatchObject(DataFlow::MethodCallNode call) {
|
||||
call.getMethodName() = ["match", "matchAll"] and
|
||||
call.getNumArgument() = 1 and
|
||||
@@ -1006,10 +1012,11 @@ private predicate isUsedAsNonMatchObject(DataFlow::MethodCallNode call) {
|
||||
/**
|
||||
* Holds if `value` is used in a way that suggests it returns a number.
|
||||
*/
|
||||
overlay[global]
|
||||
pragma[inline]
|
||||
private predicate isUsedAsNumber(DataFlow::LocalSourceNode value) {
|
||||
any(Comparison compare)
|
||||
.hasOperands(value.getALocalUse().asExpr(), any(Expr e | e.analyze().getAType() = TTNumber()))
|
||||
.hasOperands(value.getALocalUse().asExpr(), any(Expr e | canBeNumber(e.analyze())))
|
||||
or
|
||||
value.flowsToExpr(any(ArithmeticExpr e).getAnOperand())
|
||||
or
|
||||
@@ -1024,20 +1031,31 @@ private predicate isUsedAsNumber(DataFlow::LocalSourceNode value) {
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[node]
|
||||
overlay[global]
|
||||
pragma[inline_late]
|
||||
private predicate canBeString(DataFlow::AnalyzedNode node) { node.getAType() = TTString() }
|
||||
|
||||
bindingset[node]
|
||||
overlay[global]
|
||||
pragma[inline_late]
|
||||
private predicate canBeNumber(DataFlow::AnalyzedNode node) { node.getAType() = TTNumber() }
|
||||
|
||||
/**
|
||||
* Holds if `source` may be interpreted as a regular expression.
|
||||
*/
|
||||
overlay[global]
|
||||
cached
|
||||
predicate isInterpretedAsRegExp(DataFlow::Node source) {
|
||||
Stages::Taint::ref() and
|
||||
source.analyze().getAType() = TTString() and
|
||||
canBeString(source) and
|
||||
(
|
||||
// The first argument to an invocation of `RegExp` (with or without `new`).
|
||||
source = DataFlow::globalVarRef("RegExp").getAnInvocation().getArgument(0)
|
||||
or
|
||||
// The argument of a call that coerces the argument to a regular expression.
|
||||
exists(DataFlow::MethodCallNode mce, string methodName |
|
||||
mce.getReceiver().analyze().getAType() = TTString() and
|
||||
canBeString(mce.getReceiver()) and
|
||||
mce.getMethodName() = methodName and
|
||||
not exists(Function func | func = mce.getACallee() |
|
||||
not isNativeStringMethod(func, methodName)
|
||||
@@ -1073,6 +1091,7 @@ predicate isInterpretedAsRegExp(DataFlow::Node source) {
|
||||
* Gets a node whose value may flow (inter-procedurally) to `re`, where it is interpreted
|
||||
* as a part of a regular expression.
|
||||
*/
|
||||
overlay[global]
|
||||
private DataFlow::Node regExpSource(DataFlow::Node re, DataFlow::TypeBackTracker t) {
|
||||
t.start() and
|
||||
re = result and
|
||||
@@ -1090,6 +1109,7 @@ private DataFlow::Node regExpSource(DataFlow::Node re, DataFlow::TypeBackTracker
|
||||
* Gets a node whose value may flow (inter-procedurally) to `re`, where it is interpreted
|
||||
* as a part of a regular expression.
|
||||
*/
|
||||
overlay[global]
|
||||
private DataFlow::Node regExpSource(DataFlow::Node re) {
|
||||
result = regExpSource(re, DataFlow::TypeBackTracker::end())
|
||||
}
|
||||
@@ -1098,6 +1118,7 @@ private DataFlow::Node regExpSource(DataFlow::Node re) {
|
||||
* A node whose value may flow to a position where it is interpreted
|
||||
* as a part of a regular expression.
|
||||
*/
|
||||
overlay[global]
|
||||
abstract class RegExpPatternSource extends DataFlow::Node {
|
||||
/**
|
||||
* Gets a node where the pattern of this node is parsed as a part of
|
||||
@@ -1126,6 +1147,7 @@ abstract class RegExpPatternSource extends DataFlow::Node {
|
||||
/**
|
||||
* A regular expression literal, viewed as the pattern source for itself.
|
||||
*/
|
||||
overlay[global]
|
||||
private class RegExpLiteralPatternSource extends RegExpPatternSource, DataFlow::ValueNode {
|
||||
override RegExpLiteral astNode;
|
||||
|
||||
@@ -1145,6 +1167,7 @@ private class RegExpLiteralPatternSource extends RegExpPatternSource, DataFlow::
|
||||
* A node whose string value may flow to a position where it is interpreted
|
||||
* as a part of a regular expression.
|
||||
*/
|
||||
overlay[global]
|
||||
private class StringRegExpPatternSource extends RegExpPatternSource {
|
||||
DataFlow::Node parse;
|
||||
|
||||
@@ -1169,6 +1192,7 @@ private class StringRegExpPatternSource extends RegExpPatternSource {
|
||||
* A node whose string value may flow to a position where it is interpreted
|
||||
* as a part of a regular expression.
|
||||
*/
|
||||
overlay[global]
|
||||
private class StringConcatRegExpPatternSource extends RegExpPatternSource {
|
||||
DataFlow::Node parse;
|
||||
|
||||
@@ -1331,6 +1355,7 @@ module RegExp {
|
||||
/**
|
||||
* Gets the AST of a regular expression object that can flow to `node`.
|
||||
*/
|
||||
overlay[global]
|
||||
RegExpTerm getRegExpObjectFromNode(DataFlow::Node node) {
|
||||
exists(DataFlow::RegExpCreationNode regexp |
|
||||
regexp.getAReference().flowsTo(node) and
|
||||
@@ -1342,6 +1367,7 @@ module RegExp {
|
||||
* Gets the AST of a regular expression that can flow to `node`,
|
||||
* including `RegExp` objects as well as strings interpreted as regular expressions.
|
||||
*/
|
||||
overlay[global]
|
||||
RegExpTerm getRegExpFromNode(DataFlow::Node node) {
|
||||
result = getRegExpObjectFromNode(node)
|
||||
or
|
||||
|
||||
@@ -73,6 +73,8 @@
|
||||
* expression in `k` induces a re-capture of `x` to reflect the fact that `x`
|
||||
* is incremented between the two `console.log` calls.
|
||||
*/
|
||||
overlay[local]
|
||||
module;
|
||||
|
||||
import javascript
|
||||
private import semmle.javascript.dataflow.Refinements
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
/** Provides classes for working with statements. */
|
||||
overlay[local]
|
||||
module;
|
||||
|
||||
import javascript
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
/** Provides classes for working with ECMAScript 2015-style template expressions. */
|
||||
overlay[local]
|
||||
module;
|
||||
|
||||
import javascript
|
||||
|
||||
@@ -58,6 +60,7 @@ class TemplateLiteral extends Expr, @template_literal {
|
||||
*/
|
||||
int getNumElement() { result = count(this.getAnElement()) }
|
||||
|
||||
overlay[global]
|
||||
override predicate isImpure() { this.getAnElement().isImpure() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "TemplateLiteral" }
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
/**
|
||||
* Provides classes for working with the token-based representation of JavaScript programs.
|
||||
*/
|
||||
overlay[local]
|
||||
module;
|
||||
|
||||
import javascript
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
/**
|
||||
* Provides classes for reasoning about type annotations independently of dialect.
|
||||
*/
|
||||
overlay[local]
|
||||
module;
|
||||
|
||||
import javascript
|
||||
private import internal.StmtContainers
|
||||
@@ -18,6 +20,7 @@ class TypeAnnotation extends @type_annotation, NodeInStmtContainer {
|
||||
* This can be used to map a type name to the class/interface it refers to, or
|
||||
* associate it with a named type coming from an dependency.
|
||||
*/
|
||||
overlay[global]
|
||||
TypeNameBindingNode getTypeBinding() { result = this }
|
||||
|
||||
/** Holds if this is the `any` type. */
|
||||
@@ -90,6 +93,7 @@ class TypeAnnotation extends @type_annotation, NodeInStmtContainer {
|
||||
*
|
||||
* Holds if this is a reference to the type with qualified name `globalName` relative to the global scope.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated predicate hasQualifiedName(string globalName) {
|
||||
UnderlyingTypes::nodeHasUnderlyingType(this, globalName)
|
||||
}
|
||||
@@ -99,6 +103,7 @@ class TypeAnnotation extends @type_annotation, NodeInStmtContainer {
|
||||
*
|
||||
* Holds if this is a reference to the type exported from `moduleName` under the name `exportedName`.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated predicate hasQualifiedName(string moduleName, string exportedName) {
|
||||
UnderlyingTypes::nodeHasUnderlyingType(this, moduleName, exportedName)
|
||||
}
|
||||
@@ -107,6 +112,7 @@ class TypeAnnotation extends @type_annotation, NodeInStmtContainer {
|
||||
* Holds if this is a reference to the type with qualified name `globalName` relative to the global scope,
|
||||
* or is declared as a subtype thereof, or is a union or intersection containing such a type.
|
||||
*/
|
||||
overlay[global]
|
||||
final predicate hasUnderlyingType(string globalName) {
|
||||
UnderlyingTypes::nodeHasUnderlyingType(this, globalName)
|
||||
}
|
||||
@@ -115,6 +121,7 @@ class TypeAnnotation extends @type_annotation, NodeInStmtContainer {
|
||||
* Holds if this is a reference to the type exported from `moduleName` under the name `exportedName`,
|
||||
* or is declared as a subtype thereof, or is a union or intersection containing such a type.
|
||||
*/
|
||||
overlay[global]
|
||||
final predicate hasUnderlyingType(string moduleName, string exportedName) {
|
||||
UnderlyingTypes::nodeHasUnderlyingType(this, moduleName, exportedName)
|
||||
}
|
||||
@@ -135,6 +142,7 @@ class TypeAnnotation extends @type_annotation, NodeInStmtContainer {
|
||||
*
|
||||
* Note that this has no result for JSDoc type annotations.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated Type getType() { none() }
|
||||
|
||||
/**
|
||||
@@ -142,5 +150,6 @@ class TypeAnnotation extends @type_annotation, NodeInStmtContainer {
|
||||
*
|
||||
* This unfolds nullability modifiers and generic type applications.
|
||||
*/
|
||||
overlay[global]
|
||||
final DataFlow::ClassNode getClass() { UnderlyingTypes::nodeHasUnderlyingClassType(this, result) }
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
overlay[local]
|
||||
module;
|
||||
|
||||
import javascript
|
||||
|
||||
/**
|
||||
@@ -31,6 +34,7 @@ class NamespaceDefinition extends Stmt, @namespace_definition, AST::ValueNode {
|
||||
/**
|
||||
* Gets the canonical name of the namespace being defined.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated Namespace getNamespace() { result.getADefinition() = this }
|
||||
}
|
||||
|
||||
@@ -111,11 +115,13 @@ class TypeDefinition extends AstNode, @type_definition {
|
||||
/**
|
||||
* Gets the canonical name of the type being defined.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated TypeName getTypeName() { result.getADefinition() = this }
|
||||
|
||||
/**
|
||||
* Gets the type defined by this declaration.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated Type getType() { ast_node_type(this.getIdentifier(), result) }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "TypeDefinition" }
|
||||
@@ -221,6 +227,7 @@ class ExternalModuleReference extends Expr, Import, @external_module_reference {
|
||||
}
|
||||
|
||||
/** A literal path expression appearing in an external module reference. */
|
||||
overlay[global]
|
||||
deprecated private class LiteralExternalModulePath extends PathExpr, ConstantString {
|
||||
LiteralExternalModulePath() {
|
||||
exists(ExternalModuleReference emr | this.getParentExpr*() = emr.getExpression())
|
||||
@@ -268,6 +275,7 @@ class TypeAliasDeclaration extends @type_alias_declaration, TypeParameterized, S
|
||||
/**
|
||||
* Gets the canonical name of the type being defined.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated TypeName getTypeName() { result.getADefinition() = this }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "TypeAliasDeclaration" }
|
||||
@@ -548,6 +556,7 @@ class LocalNamespaceName extends @local_namespace_name, LexicalName {
|
||||
/**
|
||||
* Gets the canonical name of the namespace referenced by this name.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated Namespace getNamespace() { result = this.getADeclaration().getNamespace() }
|
||||
|
||||
override DeclarationSpace getDeclarationSpace() { result = "namespace" }
|
||||
@@ -568,6 +577,7 @@ class TypeExpr extends ExprOrType, @typeexpr, TypeAnnotation {
|
||||
* Has no result if this occurs in a TypeScript file that was extracted
|
||||
* without type information.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated override Type getType() { ast_node_type(this, result) }
|
||||
|
||||
override Stmt getEnclosingStmt() { result = ExprOrType.super.getEnclosingStmt() }
|
||||
@@ -692,6 +702,7 @@ class TypeAccess extends @typeaccess, TypeExpr, TypeRef {
|
||||
/**
|
||||
* Gets the canonical name of the type being accessed.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated TypeName getTypeName() { ast_node_symbol(this, result) }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "TypeAccess" }
|
||||
@@ -1379,6 +1390,7 @@ class LocalNamespaceDecl extends VarDecl, NamespaceRef {
|
||||
/**
|
||||
* Gets the canonical name of the namespace being defined or aliased by this name.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated Namespace getNamespace() { ast_node_symbol(this, result) }
|
||||
}
|
||||
|
||||
@@ -1397,6 +1409,7 @@ class NamespaceAccess extends TypeExpr, NamespaceRef, @namespace_access {
|
||||
/**
|
||||
* Gets the canonical name of the namespace being accessed.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated Namespace getNamespace() { ast_node_symbol(this, result) }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "NamespaceAccess" }
|
||||
@@ -1506,6 +1519,7 @@ class EnumDeclaration extends NamespaceDefinition, @enum_declaration, AST::Value
|
||||
/**
|
||||
* Gets the canonical name of the type being defined.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated TypeName getTypeName() { ast_node_symbol(this, result) }
|
||||
|
||||
/**
|
||||
@@ -1594,6 +1608,7 @@ class EnumMember extends AstNode, @enum_member {
|
||||
/**
|
||||
* Gets the canonical name of the type defined by this enum member.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated TypeName getTypeName() { ast_node_symbol(this, result) }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "EnumMember" }
|
||||
@@ -1762,6 +1777,7 @@ class TypeRootFolder extends Folder {
|
||||
/**
|
||||
* Gets the priority with which this type root folder should be used from within the given search root.
|
||||
*/
|
||||
overlay[global]
|
||||
int getSearchPriority(Folder searchRoot) {
|
||||
findNodeModulesFolder(searchRoot, this.getNodeModulesFolder(), result)
|
||||
}
|
||||
@@ -1780,6 +1796,7 @@ class TypeRootFolder extends Folder {
|
||||
* For instance, there may be many AST nodes representing different uses of the
|
||||
* `number` keyword, but there only exists one `number` type.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated class Type extends @type {
|
||||
/**
|
||||
* Gets a string representation of this type.
|
||||
@@ -1984,6 +2001,7 @@ deprecated class Type extends @type {
|
||||
*
|
||||
* A union type or intersection type, such as `string | number` or `T & U`.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated class UnionOrIntersectionType extends Type, @union_or_intersection_type {
|
||||
/**
|
||||
* Gets the `i`th member of this union or intersection, starting at 0.
|
||||
@@ -2012,6 +2030,7 @@ deprecated class UnionOrIntersectionType extends Type, @union_or_intersection_ty
|
||||
* Note that the `boolean` type is represented as the union `true | false`,
|
||||
* but is still displayed as `boolean` in string representations.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated class UnionType extends UnionOrIntersectionType, @union_type { }
|
||||
|
||||
/**
|
||||
@@ -2022,6 +2041,7 @@ deprecated class UnionType extends UnionOrIntersectionType, @union_type { }
|
||||
*
|
||||
* An intersection type, such as `T & {x: number}`.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated class IntersectionType extends UnionOrIntersectionType, @intersection_type { }
|
||||
|
||||
/**
|
||||
@@ -2040,6 +2060,7 @@ deprecated class IntersectionType extends UnionOrIntersectionType, @intersection
|
||||
* Foreign array-like objects such as `HTMLCollection` are not normal JavaScript arrays,
|
||||
* and their corresponding types are not considered array types either.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated class ArrayType extends Type {
|
||||
ArrayType() {
|
||||
this instanceof @tuple_type or
|
||||
@@ -2061,6 +2082,7 @@ deprecated class ArrayType extends Type {
|
||||
*
|
||||
* An array type such as `Array<string>`, or equivalently, `string[]`.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated class PlainArrayType extends ArrayType, TypeReference {
|
||||
PlainArrayType() { this.hasQualifiedName("Array") }
|
||||
|
||||
@@ -2075,6 +2097,7 @@ deprecated class PlainArrayType extends ArrayType, TypeReference {
|
||||
*
|
||||
* A read-only array type such as `ReadonlyArray<string>`.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated class ReadonlyArrayType extends ArrayType, TypeReference {
|
||||
ReadonlyArrayType() { this.hasQualifiedName("ReadonlyArray") }
|
||||
}
|
||||
@@ -2087,6 +2110,7 @@ deprecated class ReadonlyArrayType extends ArrayType, TypeReference {
|
||||
*
|
||||
* A tuple type, such as `[number, string]`.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated class TupleType extends ArrayType, @tuple_type {
|
||||
/**
|
||||
* Gets the `i`th member of this tuple type, starting at 0.
|
||||
@@ -2148,6 +2172,7 @@ deprecated class TupleType extends ArrayType, @tuple_type {
|
||||
*
|
||||
* The predefined `any` type.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated class AnyType extends Type, @any_type { }
|
||||
|
||||
/**
|
||||
@@ -2158,6 +2183,7 @@ deprecated class AnyType extends Type, @any_type { }
|
||||
*
|
||||
* The predefined `unknown` type.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated class UnknownType extends Type, @unknown_type { }
|
||||
|
||||
/**
|
||||
@@ -2168,6 +2194,7 @@ deprecated class UnknownType extends Type, @unknown_type { }
|
||||
*
|
||||
* The predefined `string` type.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated class StringType extends Type, @string_type { }
|
||||
|
||||
/**
|
||||
@@ -2178,6 +2205,7 @@ deprecated class StringType extends Type, @string_type { }
|
||||
*
|
||||
* The predefined `number` type.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated class NumberType extends Type, @number_type { }
|
||||
|
||||
/**
|
||||
@@ -2188,6 +2216,7 @@ deprecated class NumberType extends Type, @number_type { }
|
||||
*
|
||||
* The predefined `bigint` type.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated class BigIntType extends Type, @bigint_type { }
|
||||
|
||||
/**
|
||||
@@ -2198,6 +2227,7 @@ deprecated class BigIntType extends Type, @bigint_type { }
|
||||
*
|
||||
* A boolean, number, or string literal type.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated class LiteralType extends Type, @literal_type {
|
||||
/**
|
||||
* Gets the string value of this literal.
|
||||
@@ -2213,6 +2243,7 @@ deprecated class LiteralType extends Type, @literal_type {
|
||||
*
|
||||
* The boolean literal type `true` or `false`.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated class BooleanLiteralType extends LiteralType, @boolean_literal_type {
|
||||
/**
|
||||
* Gets the boolean value represented by this type.
|
||||
@@ -2227,6 +2258,7 @@ deprecated class BooleanLiteralType extends LiteralType, @boolean_literal_type {
|
||||
/**
|
||||
* A number literal as a static type.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated class NumberLiteralType extends LiteralType, @number_literal_type {
|
||||
override string getStringValue() { type_literal_value(this, result) }
|
||||
|
||||
@@ -2249,6 +2281,7 @@ deprecated class NumberLiteralType extends LiteralType, @number_literal_type {
|
||||
*
|
||||
* A string literal as a static type.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated class StringLiteralType extends LiteralType, @string_literal_type {
|
||||
override string getStringValue() { type_literal_value(this, result) }
|
||||
}
|
||||
@@ -2261,6 +2294,7 @@ deprecated class StringLiteralType extends LiteralType, @string_literal_type {
|
||||
*
|
||||
* A bigint literal as a static type.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated class BigIntLiteralType extends LiteralType {
|
||||
override string getStringValue() { type_literal_value(this, result) }
|
||||
|
||||
@@ -2283,6 +2317,7 @@ deprecated class BigIntLiteralType extends LiteralType {
|
||||
*
|
||||
* The `boolean` type, internally represented as the union type `true | false`.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated class BooleanType extends UnionType {
|
||||
BooleanType() {
|
||||
this.getAnElementType() instanceof @true_type and
|
||||
@@ -2299,6 +2334,7 @@ deprecated class BooleanType extends UnionType {
|
||||
*
|
||||
* The `string` type or a string literal type.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated class StringLikeType extends Type {
|
||||
StringLikeType() {
|
||||
this instanceof StringType or
|
||||
@@ -2314,6 +2350,7 @@ deprecated class StringLikeType extends Type {
|
||||
*
|
||||
* The `number` type or a number literal type.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated class NumberLikeType extends Type {
|
||||
NumberLikeType() {
|
||||
this instanceof NumberType or
|
||||
@@ -2329,6 +2366,7 @@ deprecated class NumberLikeType extends Type {
|
||||
*
|
||||
* The `boolean`, `true,` or `false` type.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated class BooleanLikeType extends Type {
|
||||
BooleanLikeType() {
|
||||
this instanceof BooleanType or
|
||||
@@ -2344,6 +2382,7 @@ deprecated class BooleanLikeType extends Type {
|
||||
*
|
||||
* The `void` type.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated class VoidType extends Type, @void_type { }
|
||||
|
||||
/**
|
||||
@@ -2354,6 +2393,7 @@ deprecated class VoidType extends Type, @void_type { }
|
||||
*
|
||||
* The `undefined` type.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated class UndefinedType extends Type, @undefined_type { }
|
||||
|
||||
/**
|
||||
@@ -2364,6 +2404,7 @@ deprecated class UndefinedType extends Type, @undefined_type { }
|
||||
*
|
||||
* The `null` type.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated class NullType extends Type, @null_type { }
|
||||
|
||||
/**
|
||||
@@ -2374,6 +2415,7 @@ deprecated class NullType extends Type, @null_type { }
|
||||
*
|
||||
* The `never` type.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated class NeverType extends Type, @never_type { }
|
||||
|
||||
/**
|
||||
@@ -2384,6 +2426,7 @@ deprecated class NeverType extends Type, @never_type { }
|
||||
*
|
||||
* The `symbol` type or a specific `unique symbol` type.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated class SymbolType extends Type, @symbol_type { }
|
||||
|
||||
/**
|
||||
@@ -2394,6 +2437,7 @@ deprecated class SymbolType extends Type, @symbol_type { }
|
||||
*
|
||||
* The `symbol` type.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated class PlainSymbolType extends SymbolType, @plain_symbol_type { }
|
||||
|
||||
/**
|
||||
@@ -2404,6 +2448,7 @@ deprecated class PlainSymbolType extends SymbolType, @plain_symbol_type { }
|
||||
*
|
||||
* A `unique symbol` type.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated class UniqueSymbolType extends SymbolType, @unique_symbol_type {
|
||||
/**
|
||||
* Gets the canonical name of the variable exposing the symbol.
|
||||
@@ -2438,6 +2483,7 @@ deprecated class UniqueSymbolType extends SymbolType, @unique_symbol_type {
|
||||
*
|
||||
* The `object` type.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated class ObjectKeywordType extends Type, @objectkeyword_type { }
|
||||
|
||||
/**
|
||||
@@ -2448,6 +2494,7 @@ deprecated class ObjectKeywordType extends Type, @objectkeyword_type { }
|
||||
*
|
||||
* A type that refers to a class, interface, enum, or enum member.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated class TypeReference extends Type, @type_reference {
|
||||
/**
|
||||
* Gets the canonical name of the type being referenced.
|
||||
@@ -2506,6 +2553,7 @@ deprecated class TypeReference extends Type, @type_reference {
|
||||
*
|
||||
* A type that refers to a class, possibly with type arguments.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated class ClassType extends TypeReference {
|
||||
ClassDefinition declaration;
|
||||
|
||||
@@ -2525,6 +2573,7 @@ deprecated class ClassType extends TypeReference {
|
||||
*
|
||||
* A type that refers to an interface, possibly with type arguents.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated class InterfaceType extends TypeReference {
|
||||
InterfaceDeclaration declaration;
|
||||
|
||||
@@ -2544,6 +2593,7 @@ deprecated class InterfaceType extends TypeReference {
|
||||
*
|
||||
* A type that refers to an enum.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated class EnumType extends TypeReference {
|
||||
EnumDeclaration declaration;
|
||||
|
||||
@@ -2563,6 +2613,7 @@ deprecated class EnumType extends TypeReference {
|
||||
*
|
||||
* A type that refers to the value of an enum member.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated class EnumLiteralType extends TypeReference {
|
||||
EnumMember declaration;
|
||||
|
||||
@@ -2582,6 +2633,7 @@ deprecated class EnumLiteralType extends TypeReference {
|
||||
*
|
||||
* A type that refers to a type alias.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated class TypeAliasReference extends TypeReference {
|
||||
TypeAliasReference() { type_alias(this, _) }
|
||||
|
||||
@@ -2601,6 +2653,7 @@ deprecated class TypeAliasReference extends TypeReference {
|
||||
*
|
||||
* An anonymous interface type, such as `{ x: number }`.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated class AnonymousInterfaceType extends Type, @object_type { }
|
||||
|
||||
/**
|
||||
@@ -2611,6 +2664,7 @@ deprecated class AnonymousInterfaceType extends Type, @object_type { }
|
||||
*
|
||||
* A type that refers to a type variable.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated class TypeVariableType extends Type, @typevariable_type {
|
||||
/**
|
||||
* Gets a syntactic declaration of this type variable.
|
||||
@@ -2656,6 +2710,7 @@ deprecated class TypeVariableType extends Type, @typevariable_type {
|
||||
*
|
||||
* A type that refers to a type variable declared on a class, interface or function.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated class CanonicalTypeVariableType extends TypeVariableType, @canonical_type_variable_type {
|
||||
override TypeName getHostType() { result = this.getCanonicalName().getParent() }
|
||||
|
||||
@@ -2681,6 +2736,7 @@ deprecated class CanonicalTypeVariableType extends TypeVariableType, @canonical_
|
||||
* - `<T>(x: T) => T`
|
||||
* - `<S, T>(x: S, y: T) => T`.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated class LexicalTypeVariableType extends TypeVariableType, @lexical_type_variable_type {
|
||||
override string getName() {
|
||||
types(this, _, result) // The toString value contains the name.
|
||||
@@ -2703,6 +2759,7 @@ deprecated class LexicalTypeVariableType extends TypeVariableType, @lexical_type
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated class ThisType extends Type, @this_type {
|
||||
/**
|
||||
* Gets the type containing the `this` type.
|
||||
@@ -2721,6 +2778,7 @@ deprecated class ThisType extends Type, @this_type {
|
||||
* The type of a named value, `typeof X`, typically denoting the type of
|
||||
* a class constructor, namespace object, enum object, or module object.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated class TypeofType extends Type, @typeof_type {
|
||||
/**
|
||||
* Gets the canonical name of the named value.
|
||||
@@ -2801,6 +2859,7 @@ module SignatureKind {
|
||||
*
|
||||
* A function or constructor signature in a TypeScript type.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated class CallSignatureType extends @signature_type {
|
||||
/**
|
||||
* Gets a value indicating if this is a function or constructor signature.
|
||||
@@ -2955,6 +3014,7 @@ deprecated class CallSignatureType extends @signature_type {
|
||||
*
|
||||
* A function call signature in a type, that is, a signature without the `new` keyword.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated class FunctionCallSignatureType extends CallSignatureType, @function_signature_type { }
|
||||
|
||||
/**
|
||||
@@ -2965,6 +3025,7 @@ deprecated class FunctionCallSignatureType extends CallSignatureType, @function_
|
||||
*
|
||||
* A constructor call signature in a type, that is, a signature with the `new` keyword.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated class ConstructorCallSignatureType extends CallSignatureType, @constructor_signature_type
|
||||
{ }
|
||||
|
||||
@@ -2976,6 +3037,7 @@ deprecated class ConstructorCallSignatureType extends CallSignatureType, @constr
|
||||
* - It has one type parameter, say, `T`
|
||||
* - It has a `then` method whose first argument is a callback that takes a `T` as argument.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated private class PromiseTypeName extends TypeName {
|
||||
PromiseTypeName() {
|
||||
// The name must suggest it is a promise.
|
||||
@@ -3005,6 +3067,7 @@ deprecated private class PromiseTypeName extends TypeName {
|
||||
* This includes types whose name and `then` method signature suggest it is a promise,
|
||||
* such as `PromiseLike<T>` and `Thenable<T>`.
|
||||
*/
|
||||
overlay[global]
|
||||
deprecated class PromiseType extends TypeReference {
|
||||
PromiseType() {
|
||||
this.getNumTypeArgument() = 1 and
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
/** Provides classes for modeling program variables. */
|
||||
overlay[local]
|
||||
module;
|
||||
|
||||
import javascript
|
||||
|
||||
@@ -62,6 +64,7 @@ class LocalScope extends Scope {
|
||||
*/
|
||||
class ModuleScope extends Scope, @module_scope {
|
||||
/** Gets the module that induces this scope. */
|
||||
overlay[global]
|
||||
Module getModule() { result = this.getScopeElement() }
|
||||
|
||||
override string toString() { result = "module scope" }
|
||||
@@ -256,6 +259,7 @@ class VarRef extends @varref, Identifier, BindingPattern, LexicalRef {
|
||||
|
||||
override VarRef getABindingVarRef() { result = this }
|
||||
|
||||
overlay[global]
|
||||
override predicate isImpure() { none() }
|
||||
|
||||
override Expr getUnderlyingReference() { result = this }
|
||||
@@ -543,6 +547,7 @@ class ArrayPattern extends DestructuringPattern, @array_pattern {
|
||||
/** Holds if this array pattern has an omitted element. */
|
||||
predicate hasOmittedElement() { this.elementIsOmitted(_) }
|
||||
|
||||
overlay[global]
|
||||
override predicate isImpure() { this.getAnElement().isImpure() }
|
||||
|
||||
override VarRef getABindingVarRef() {
|
||||
@@ -583,6 +588,7 @@ class ObjectPattern extends DestructuringPattern, @object_pattern {
|
||||
/** Gets the rest property pattern of this object pattern, if any. */
|
||||
override Expr getRest() { result = this.getChildExpr(-1) }
|
||||
|
||||
overlay[global]
|
||||
override predicate isImpure() { this.getAPropertyPattern().isImpure() }
|
||||
|
||||
override VarRef getABindingVarRef() {
|
||||
@@ -640,6 +646,7 @@ class PropertyPattern extends @property, AstNode {
|
||||
ObjectPattern getObjectPattern() { properties(this, result, _, _, _) }
|
||||
|
||||
/** Holds if this pattern is impure, that is, if its evaluation could have side effects. */
|
||||
overlay[global]
|
||||
predicate isImpure() {
|
||||
this.isComputed() and this.getNameExpr().isImpure()
|
||||
or
|
||||
@@ -844,6 +851,7 @@ class SimpleParameter extends Parameter, VarDecl {
|
||||
* Gets a use of this parameter that refers to its initial value as
|
||||
* passed in from the caller.
|
||||
*/
|
||||
overlay[global]
|
||||
VarUse getAnInitialUse() {
|
||||
exists(SsaDefinition ssa |
|
||||
ssa.getAContributingVarDef() = this and
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
/**
|
||||
* Provides classes and predicates for working with XML files and their content.
|
||||
*/
|
||||
overlay[local]
|
||||
module;
|
||||
|
||||
import semmle.files.FileSystem
|
||||
private import codeql.xml.Xml
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
* YAML documents are represented as abstract syntax trees whose nodes
|
||||
* are either YAML values or alias nodes referring to another YAML value.
|
||||
*/
|
||||
overlay[local]
|
||||
module;
|
||||
|
||||
import javascript
|
||||
private import codeql.yaml.Yaml as LibYaml
|
||||
|
||||
@@ -37,6 +37,8 @@
|
||||
* they represent; additionally, indefinite abstract values record
|
||||
* the source of imprecision that caused them to arise.
|
||||
*/
|
||||
overlay[local]
|
||||
module;
|
||||
|
||||
private import javascript
|
||||
private import semmle.javascript.dataflow.internal.AbstractValuesImpl
|
||||
@@ -57,6 +59,7 @@ class AbstractValue extends TAbstractValue {
|
||||
* Gets the Boolean value some concrete value represented by this
|
||||
* abstract value coerces to.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
abstract boolean getBooleanValue();
|
||||
|
||||
/**
|
||||
@@ -97,6 +100,7 @@ class AbstractValue extends TAbstractValue {
|
||||
* In all cases, purely local flow tracking is used to find prototype objects, so
|
||||
* this predicate cannot be relied on to compute all possible prototype objects.
|
||||
*/
|
||||
overlay[global]
|
||||
DefiniteAbstractValue getAPrototype() {
|
||||
exists(AbstractProtoProperty proto |
|
||||
proto.getBase() = this and
|
||||
|
||||
@@ -33,21 +33,25 @@ private import semmle.javascript.internal.CachedStages
|
||||
* Note: For performance reasons, all subclasses of this class should be part
|
||||
* of the standard library. Use `isAdditionalFlowStep` for query-specific flow steps.
|
||||
*/
|
||||
overlay[local]
|
||||
class AdditionalFlowStep extends Unit {
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a value-preserving data flow edge.f
|
||||
*/
|
||||
overlay[global]
|
||||
predicate step(DataFlow::Node pred, DataFlow::Node succ) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a value-preserving data flow edge that
|
||||
* crosses calling contexts.
|
||||
*/
|
||||
overlay[global]
|
||||
predicate jumpStep(DataFlow::Node pred, DataFlow::Node succ) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `pred` should be stored in the given `content` of the object `succ`.
|
||||
*/
|
||||
overlay[global]
|
||||
predicate storeStep(DataFlow::Node pred, DataFlow::ContentSet contents, DataFlow::Node succ) {
|
||||
none()
|
||||
}
|
||||
@@ -55,6 +59,7 @@ class AdditionalFlowStep extends Unit {
|
||||
/**
|
||||
* Holds if the given `content` of the object in `pred` should be read into `succ`.
|
||||
*/
|
||||
overlay[global]
|
||||
predicate readStep(DataFlow::Node pred, DataFlow::ContentSet contents, DataFlow::Node succ) {
|
||||
none()
|
||||
}
|
||||
|
||||
@@ -625,15 +625,19 @@ abstract deprecated class LabeledBarrierGuardNode extends BarrierGuardNode {
|
||||
*
|
||||
* For use with load/store steps in `DataFlow::SharedFlowStep` and TypeTracking.
|
||||
*/
|
||||
overlay[local]
|
||||
module PseudoProperties {
|
||||
/** Holds if `s` is a pseudo-property. */
|
||||
bindingset[s]
|
||||
overlay[caller]
|
||||
predicate isPseudoProperty(string s) { s.matches("$%$") }
|
||||
|
||||
bindingset[s]
|
||||
overlay[caller]
|
||||
private string pseudoProperty(string s) { result = "$" + s + "$" }
|
||||
|
||||
bindingset[s, v]
|
||||
overlay[caller]
|
||||
private string pseudoProperty(string s, string v) { result = "$" + s + "|" + v + "$" }
|
||||
|
||||
/**
|
||||
@@ -680,6 +684,7 @@ module PseudoProperties {
|
||||
* Gets a pseudo-property for the location of a map value where the key is `key`.
|
||||
* The string value of the `key` is encoded in the result, and there is only a result if the string value of `key` is known.
|
||||
*/
|
||||
overlay[caller]
|
||||
pragma[inline]
|
||||
string mapValueKnownKey(DataFlow::Node key) {
|
||||
result = mapValueKey(any(string s | key.mayHaveStringValue(s)))
|
||||
@@ -689,17 +694,20 @@ module PseudoProperties {
|
||||
* Gets a pseudo-property for the location of a map value where the key is `key`.
|
||||
*/
|
||||
bindingset[key]
|
||||
overlay[caller]
|
||||
string mapValueKey(string key) { result = pseudoProperty("mapValue", key) }
|
||||
|
||||
/**
|
||||
* Holds if `prop` equals `mapValueKey(key)` for some value of `key`.
|
||||
*/
|
||||
bindingset[prop]
|
||||
overlay[caller]
|
||||
predicate isMapValueKey(string prop) { prop.matches("$mapValue|%$") }
|
||||
|
||||
/**
|
||||
* Gets a pseudo-property for the location of a map value where the key is `key`.
|
||||
*/
|
||||
overlay[caller]
|
||||
pragma[inline]
|
||||
string mapValue(DataFlow::Node key) {
|
||||
result = mapValueKnownKey(key)
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
* For performance reasons, all subclasses of `CustomAbstractValueDefinition`
|
||||
* should be part of the standard library.
|
||||
*/
|
||||
overlay[local]
|
||||
module;
|
||||
|
||||
private import javascript
|
||||
private import internal.AbstractValuesImpl
|
||||
@@ -32,6 +34,7 @@ class CustomAbstractValueFromDefinition extends AbstractValue, TCustomAbstractVa
|
||||
|
||||
override predicate isIndefinite(DataFlow::Incompleteness cause) { def.isIndefinite(cause) }
|
||||
|
||||
overlay[global]
|
||||
override DefiniteAbstractValue getAPrototype() { result = def.getAPrototype() }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
@@ -98,6 +101,7 @@ abstract class CustomAbstractValueDefinition extends Locatable {
|
||||
* Gets an abstract value that represents a prototype object of the
|
||||
* induced abstract value.
|
||||
*/
|
||||
overlay[global]
|
||||
AbstractValue getAPrototype() {
|
||||
exists(AbstractProtoProperty proto |
|
||||
proto.getBase() = this.getAbstractValue() and
|
||||
@@ -119,6 +123,7 @@ abstract class CustomAbstractValueDefinition extends Locatable {
|
||||
/**
|
||||
* Flow analysis for custom abstract values.
|
||||
*/
|
||||
overlay[global]
|
||||
class CustomAbstractValueFromDefinitionNode extends DataFlow::AnalyzedNode, DataFlow::ValueNode {
|
||||
CustomAbstractValueFromDefinition val;
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
* Flow through global variables, object properties or function calls is not
|
||||
* modeled (except for immediately invoked functions as explained above).
|
||||
*/
|
||||
overlay[local]
|
||||
module;
|
||||
|
||||
import javascript
|
||||
private import internal.CallGraphs
|
||||
@@ -64,9 +66,11 @@ module DataFlow {
|
||||
* `p.getALocalSource()` does _not_ return the corresponding argument, and
|
||||
* `p.isIncomplete("call")` holds.
|
||||
*/
|
||||
overlay[global]
|
||||
predicate isIncomplete(Incompleteness cause) { isIncomplete(this, cause) }
|
||||
|
||||
/** Gets type inference results for this data flow node. */
|
||||
overlay[global]
|
||||
AnalyzedNode analyze() { result = this }
|
||||
|
||||
/** Gets the expression corresponding to this data flow node, if any. */
|
||||
@@ -124,11 +128,13 @@ module DataFlow {
|
||||
int getIntValue() { result = this.asExpr().getIntValue() }
|
||||
|
||||
/** Gets a function value that may reach this node. */
|
||||
overlay[global]
|
||||
final FunctionNode getAFunctionValue() {
|
||||
CallGraph::getAFunctionReference(result, 0).flowsTo(this)
|
||||
}
|
||||
|
||||
/** Gets a function value that may reach this node with the given `imprecision` level. */
|
||||
overlay[global]
|
||||
final FunctionNode getAFunctionValue(int imprecision) {
|
||||
CallGraph::getAFunctionReference(result, imprecision).flowsTo(this)
|
||||
}
|
||||
@@ -137,6 +143,7 @@ module DataFlow {
|
||||
* Gets a function value that may reach this node,
|
||||
* possibly derived from a partial function invocation.
|
||||
*/
|
||||
overlay[global]
|
||||
final FunctionNode getABoundFunctionValue(int boundArgs) {
|
||||
result = this.getAFunctionValue() and boundArgs = 0
|
||||
or
|
||||
@@ -192,6 +199,7 @@ module DataFlow {
|
||||
FlowSteps::identityFunctionStep(result, this)
|
||||
}
|
||||
|
||||
overlay[global]
|
||||
private NameResolution::Node getNameResolutionNode() {
|
||||
this = valueNode(result)
|
||||
or
|
||||
@@ -205,6 +213,7 @@ module DataFlow {
|
||||
* Holds if this node is annotated with the given named type,
|
||||
* or is declared as a subtype thereof, or is a union or intersection containing such a type.
|
||||
*/
|
||||
overlay[global]
|
||||
cached
|
||||
predicate hasUnderlyingType(string globalName) {
|
||||
Stages::TypeTracking::ref() and
|
||||
@@ -218,6 +227,7 @@ module DataFlow {
|
||||
* Holds if this node is annotated with the given named type,
|
||||
* or is declared as a subtype thereof, or is a union or intersection containing such a type.
|
||||
*/
|
||||
overlay[global]
|
||||
cached
|
||||
predicate hasUnderlyingType(string moduleName, string typeName) {
|
||||
Stages::TypeTracking::ref() and
|
||||
@@ -466,6 +476,7 @@ module DataFlow {
|
||||
/**
|
||||
* Gets an accessor (`get` or `set` method) that may be invoked by this property reference.
|
||||
*/
|
||||
overlay[global]
|
||||
final DataFlow::FunctionNode getAnAccessorCallee() {
|
||||
result = CallGraph::getAnAccessorCallee(this)
|
||||
}
|
||||
@@ -1419,11 +1430,13 @@ module DataFlow {
|
||||
* This predicate is only defined for expressions, properties, and for statements that declare
|
||||
* a function, a class, or a TypeScript namespace or enum.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
ValueNode valueNode(AstNode nd) { result.getAstNode() = nd }
|
||||
|
||||
/**
|
||||
* Gets the data flow node corresponding to `e`.
|
||||
*/
|
||||
overlay[caller?]
|
||||
pragma[inline]
|
||||
ExprNode exprNode(Expr e) { result = valueNode(e) }
|
||||
|
||||
@@ -1762,6 +1775,7 @@ module DataFlow {
|
||||
)
|
||||
}
|
||||
|
||||
overlay[global]
|
||||
private class ReflectiveParamsStep extends LegacyPreCallGraphStep {
|
||||
override predicate loadStep(DataFlow::Node obj, DataFlow::Node element, string prop) {
|
||||
exists(DataFlow::ReflectiveParametersNode params, DataFlow::FunctionNode f, int i |
|
||||
@@ -1774,6 +1788,7 @@ module DataFlow {
|
||||
}
|
||||
|
||||
/** A taint step from the reflective parameters node to any parameter. */
|
||||
overlay[global]
|
||||
private class ReflectiveParamsTaintStep extends TaintTracking::LegacyTaintStep {
|
||||
override predicate step(DataFlow::Node obj, DataFlow::Node element) {
|
||||
exists(DataFlow::ReflectiveParametersNode params, DataFlow::FunctionNode f |
|
||||
@@ -1787,6 +1802,7 @@ module DataFlow {
|
||||
/**
|
||||
* Holds if there is a step from `pred` to `succ` through a field accessed through `this` in a class.
|
||||
*/
|
||||
overlay[global]
|
||||
predicate localFieldStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
exists(ClassNode cls, string prop |
|
||||
pred = AccessPath::getAnAssignmentTo(cls.getADirectSuperClass*().getAReceiverNode(), prop) or
|
||||
@@ -1819,6 +1835,7 @@ module DataFlow {
|
||||
* `p.getALocalSource()` does _not_ return the corresponding argument, and
|
||||
* `p.isIncomplete("call")` holds.
|
||||
*/
|
||||
overlay[global]
|
||||
predicate isIncomplete(Node nd, Incompleteness cause) {
|
||||
exists(SsaVariable ssa | nd = TSsaDefNode(ssa.getDefinition()) |
|
||||
defIsIncomplete(ssa.(SsaExplicitDefinition).getDef(), cause)
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
/** Provides classes and predicates for defining flow summaries. */
|
||||
overlay[local]
|
||||
module;
|
||||
|
||||
private import javascript
|
||||
private import semmle.javascript.dataflow.internal.sharedlib.FlowSummaryImpl as Impl
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
overlay[local]
|
||||
module;
|
||||
|
||||
/**
|
||||
* Types inferred by the flow analysis, represented as type tags.
|
||||
*
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
* as nodes corresponding to function definitions or nodes corresponding to
|
||||
* parameters.
|
||||
*/
|
||||
overlay[local]
|
||||
module;
|
||||
|
||||
private import javascript
|
||||
private import semmle.javascript.dependencies.Dependencies
|
||||
@@ -158,6 +160,7 @@ class InvokeNode extends DataFlow::SourceNode instanceof DataFlow::Impl::InvokeN
|
||||
* addEventHandler("click", foo.bind(this, "value of x"))
|
||||
* ```
|
||||
*/
|
||||
overlay[global]
|
||||
ParameterNode getABoundCallbackParameter(int callback, int param) {
|
||||
exists(int boundArgs |
|
||||
result =
|
||||
@@ -178,6 +181,7 @@ class InvokeNode extends DataFlow::SourceNode instanceof DataFlow::Impl::InvokeN
|
||||
private ObjectLiteralNode getOptionsArgument(int i) { result.flowsTo(this.getArgument(i)) }
|
||||
|
||||
/** Gets an abstract value representing possible callees of this call site. */
|
||||
overlay[global]
|
||||
final AbstractValue getACalleeValue() {
|
||||
exists(DataFlow::Node callee, DataFlow::AnalyzedNode analyzed |
|
||||
pragma[only_bind_into](callee) = this.getCalleeNode() and
|
||||
@@ -192,6 +196,7 @@ class InvokeNode extends DataFlow::SourceNode instanceof DataFlow::Impl::InvokeN
|
||||
* To alter the call graph as seen by the interprocedural data flow libraries, override
|
||||
* the `getACallee(int imprecision)` predicate instead.
|
||||
*/
|
||||
overlay[global]
|
||||
final Function getACallee() { result = this.getACallee(0) }
|
||||
|
||||
/**
|
||||
@@ -206,6 +211,7 @@ class InvokeNode extends DataFlow::SourceNode instanceof DataFlow::Impl::InvokeN
|
||||
* This predicate can be overridden to alter the call graph used by the interprocedural
|
||||
* data flow libraries.
|
||||
*/
|
||||
overlay[global]
|
||||
Function getACallee(int imprecision) {
|
||||
result = CallGraph::getACallee(this, imprecision).getFunction()
|
||||
}
|
||||
@@ -214,6 +220,7 @@ class InvokeNode extends DataFlow::SourceNode instanceof DataFlow::Impl::InvokeN
|
||||
* Holds if the approximation of possible callees for this call site is
|
||||
* affected by the given analysis incompleteness `cause`.
|
||||
*/
|
||||
overlay[global]
|
||||
predicate isIndefinite(DataFlow::Incompleteness cause) {
|
||||
this.getACalleeValue().isIndefinite(cause)
|
||||
}
|
||||
@@ -229,6 +236,7 @@ class InvokeNode extends DataFlow::SourceNode instanceof DataFlow::Impl::InvokeN
|
||||
* independent contexts, so tracking flow through it leads to
|
||||
* imprecision.
|
||||
*/
|
||||
overlay[global]
|
||||
predicate isImprecise() {
|
||||
this.isIndefinite("global") and
|
||||
exists(DefiniteAbstractValue v | v = this.getACalleeValue() | not v instanceof AbstractCallable)
|
||||
@@ -238,6 +246,7 @@ class InvokeNode extends DataFlow::SourceNode instanceof DataFlow::Impl::InvokeN
|
||||
* Holds if our approximation of possible callees for this call site is
|
||||
* likely to be incomplete.
|
||||
*/
|
||||
overlay[global]
|
||||
predicate isIncomplete() {
|
||||
// the flow analysis identifies a source of incompleteness other than
|
||||
// global flow (which usually leads to imprecision rather than incompleteness)
|
||||
@@ -248,6 +257,7 @@ class InvokeNode extends DataFlow::SourceNode instanceof DataFlow::Impl::InvokeN
|
||||
* Holds if our approximation of possible callees for this call site is
|
||||
* likely to be imprecise or incomplete.
|
||||
*/
|
||||
overlay[global]
|
||||
predicate isUncertain() { this.isImprecise() or this.isIncomplete() }
|
||||
|
||||
/**
|
||||
@@ -763,7 +773,7 @@ module ModuleImportNode {
|
||||
cached
|
||||
ModuleImportNode moduleImport(string path) {
|
||||
// NB. internal modules may be imported with a "node:" prefix
|
||||
Stages::Imports::ref() and result.getPath() = ["node:" + path, path]
|
||||
result.getPath() = ["node:" + path, path]
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -771,6 +781,7 @@ ModuleImportNode moduleImport(string path) {
|
||||
* `require("lodash")` in a context where a package.json file includes
|
||||
* `"lodash"` as a dependency.
|
||||
*/
|
||||
overlay[global]
|
||||
ModuleImportNode dependencyModuleImport(Dependency dep) {
|
||||
result = dep.getAUse("import").(Import).getImportedModuleNode()
|
||||
}
|
||||
@@ -861,6 +872,7 @@ module MemberKind {
|
||||
*
|
||||
* Additional patterns can be recognized as class nodes, by extending `DataFlow::ClassNode::Range`.
|
||||
*/
|
||||
overlay[global]
|
||||
class ClassNode extends DataFlow::ValueNode, DataFlow::SourceNode {
|
||||
override AST::ValueNode astNode;
|
||||
AbstractCallable function;
|
||||
@@ -1329,6 +1341,7 @@ class ClassNode extends DataFlow::ValueNode, DataFlow::SourceNode {
|
||||
/**
|
||||
* Helper predicate to get a prototype reference in a file.
|
||||
*/
|
||||
overlay[global]
|
||||
private DataFlow::PropRef getAPrototypeReferenceInFile(string name, File f) {
|
||||
result.getBase() = AccessPath::getAReferenceOrAssignmentTo(name) and
|
||||
result.getPropertyName() = "prototype" and
|
||||
@@ -1338,6 +1351,7 @@ private DataFlow::PropRef getAPrototypeReferenceInFile(string name, File f) {
|
||||
/**
|
||||
* Helper predicate to get an instantiation in a file.
|
||||
*/
|
||||
overlay[global]
|
||||
private DataFlow::NewNode getAnInstantiationInFile(string name, File f) {
|
||||
result = AccessPath::getAReferenceTo(name).(DataFlow::LocalSourceNode).getAnInstantiation() and
|
||||
result.getFile() = f
|
||||
@@ -1346,6 +1360,7 @@ private DataFlow::NewNode getAnInstantiationInFile(string name, File f) {
|
||||
/**
|
||||
* Gets a reference to the function `func`, where there exists a read/write of the "prototype" property on that reference.
|
||||
*/
|
||||
overlay[global]
|
||||
pragma[noinline]
|
||||
private DataFlow::SourceNode getAFunctionValueWithPrototype(AbstractValue func) {
|
||||
exists(result.getAPropertyReference("prototype")) and
|
||||
@@ -1353,6 +1368,7 @@ private DataFlow::SourceNode getAFunctionValueWithPrototype(AbstractValue func)
|
||||
func instanceof AbstractCallable // the join-order goes bad if `func` has type `AbstractFunction`.
|
||||
}
|
||||
|
||||
overlay[global]
|
||||
module ClassNode {
|
||||
/**
|
||||
* A dataflow node that should be considered a class node.
|
||||
@@ -1435,6 +1451,7 @@ module ClassNode {
|
||||
* _.partial(fn, x, y, z)
|
||||
* ```
|
||||
*/
|
||||
overlay[global]
|
||||
class PartialInvokeNode extends DataFlow::Node instanceof PartialInvokeNode::Range {
|
||||
/** Gets a node holding a callback invoked by this partial invocation node. */
|
||||
DataFlow::Node getACallbackNode() {
|
||||
@@ -1470,6 +1487,7 @@ class PartialInvokeNode extends DataFlow::Node instanceof PartialInvokeNode::Ran
|
||||
}
|
||||
}
|
||||
|
||||
overlay[global]
|
||||
module PartialInvokeNode {
|
||||
/**
|
||||
* A data flow node that performs a partial function application.
|
||||
@@ -1717,6 +1735,7 @@ class RegExpCreationNode extends DataFlow::SourceNode {
|
||||
predicate maybeGlobal() { RegExp::maybeGlobal(this.tryGetFlags()) }
|
||||
|
||||
/** Gets a data flow node referring to this regular expression. */
|
||||
overlay[global]
|
||||
private DataFlow::SourceNode getAReference(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
result = this
|
||||
@@ -1725,6 +1744,7 @@ class RegExpCreationNode extends DataFlow::SourceNode {
|
||||
}
|
||||
|
||||
/** Gets a data flow node referring to this regular expression. */
|
||||
overlay[global]
|
||||
cached
|
||||
DataFlow::SourceNode getAReference() {
|
||||
Stages::FlowSteps::ref() and
|
||||
@@ -1736,6 +1756,7 @@ class RegExpCreationNode extends DataFlow::SourceNode {
|
||||
* A guard node for a variable in a negative condition, such as `x` in `if(!x)`.
|
||||
* Can be added to a `isBarrier` in a data-flow configuration to block flow through such checks.
|
||||
*/
|
||||
overlay[global]
|
||||
class VarAccessBarrier extends DataFlow::Node {
|
||||
VarAccessBarrier() {
|
||||
exists(ConditionGuardNode guard, SsaRefinementNode refinement |
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
* so the refinement can evaluate to both `true` and `false` for the same
|
||||
* candidate value.
|
||||
*/
|
||||
overlay[local]
|
||||
module;
|
||||
|
||||
import javascript
|
||||
private import AbstractValues
|
||||
@@ -45,6 +47,7 @@ abstract class RefinementCandidate extends Expr {
|
||||
/**
|
||||
* Gets a refinement value inferred for this expression in context `ctxt`.
|
||||
*/
|
||||
overlay[global]
|
||||
pragma[nomagic]
|
||||
abstract RefinementValue eval(RefinementContext ctxt);
|
||||
}
|
||||
@@ -64,6 +67,7 @@ class Refinement extends Expr instanceof RefinementCandidate {
|
||||
/**
|
||||
* Gets a refinement value inferred for this expression in context `ctxt`.
|
||||
*/
|
||||
overlay[global]
|
||||
RefinementValue eval(RefinementContext ctxt) { result = super.eval(ctxt) }
|
||||
}
|
||||
|
||||
@@ -71,6 +75,7 @@ class Refinement extends Expr instanceof RefinementCandidate {
|
||||
abstract private class LiteralRefinement extends RefinementCandidate, Literal {
|
||||
override SsaSourceVariable getARefinedVar() { none() }
|
||||
|
||||
overlay[global]
|
||||
override RefinementValue eval(RefinementContext ctxt) {
|
||||
ctxt.appliesTo(this) and result = this.eval()
|
||||
}
|
||||
@@ -78,16 +83,19 @@ abstract private class LiteralRefinement extends RefinementCandidate, Literal {
|
||||
/**
|
||||
* Gets the refinement value that represents this literal.
|
||||
*/
|
||||
overlay[global]
|
||||
RefinementValue eval() { result = TAny() }
|
||||
}
|
||||
|
||||
/** A `null` literal, viewed as a refinement expression. */
|
||||
private class NullLiteralRefinement extends LiteralRefinement, NullLiteral {
|
||||
overlay[global]
|
||||
override RefinementValue eval() { result = TValueWithType(TTNull()) }
|
||||
}
|
||||
|
||||
/** A Boolean literal, viewed as a refinement expression. */
|
||||
private class BoolRefinement extends LiteralRefinement, BooleanLiteral {
|
||||
overlay[global]
|
||||
override RefinementValue eval() {
|
||||
exists(boolean b | b.toString() = this.getValue() | result = TBoolConstant(b))
|
||||
}
|
||||
@@ -95,11 +103,13 @@ private class BoolRefinement extends LiteralRefinement, BooleanLiteral {
|
||||
|
||||
/** A constant string, viewed as a refinement expression. */
|
||||
private class StringRefinement extends LiteralRefinement, ConstantString {
|
||||
overlay[global]
|
||||
override RefinementValue eval() { result = TStringConstant(this.getStringValue()) }
|
||||
}
|
||||
|
||||
/** A numeric literal, viewed as a refinement expression. */
|
||||
abstract private class NumberRefinement extends LiteralRefinement, NumberLiteral {
|
||||
overlay[global]
|
||||
override RefinementValue eval() { result = TValueWithType(TTNumber()) }
|
||||
}
|
||||
|
||||
@@ -112,6 +122,7 @@ abstract private class NumberRefinement extends LiteralRefinement, NumberLiteral
|
||||
private class IntRefinement extends NumberRefinement, NumberLiteral {
|
||||
IntRefinement() { this.getValue().toInt() = 0 }
|
||||
|
||||
overlay[global]
|
||||
override RefinementValue eval() { result = TIntConstant(this.getValue().toInt()) }
|
||||
}
|
||||
|
||||
@@ -123,6 +134,7 @@ private class UndefinedInRefinement extends RefinementCandidate,
|
||||
{
|
||||
override SsaSourceVariable getARefinedVar() { none() }
|
||||
|
||||
overlay[global]
|
||||
override RefinementValue eval(RefinementContext ctxt) {
|
||||
ctxt.appliesTo(this) and
|
||||
result = TValueWithType(TTUndefined())
|
||||
@@ -135,6 +147,7 @@ private class VariableRefinement extends RefinementCandidate, VarUse {
|
||||
|
||||
override SsaSourceVariable getARefinedVar() { result = this.getVariable() }
|
||||
|
||||
overlay[global]
|
||||
override RefinementValue eval(RefinementContext ctxt) {
|
||||
ctxt.appliesTo(this) and
|
||||
result = ctxt.(VarRefinementContext).getAValue()
|
||||
@@ -149,6 +162,7 @@ private class ParRefinement extends RefinementCandidate, ParExpr {
|
||||
result = this.getExpression().(RefinementCandidate).getARefinedVar()
|
||||
}
|
||||
|
||||
overlay[global]
|
||||
override RefinementValue eval(RefinementContext ctxt) {
|
||||
result = this.getExpression().(RefinementCandidate).eval(ctxt)
|
||||
}
|
||||
@@ -162,6 +176,7 @@ private class TypeofRefinement extends RefinementCandidate, TypeofExpr {
|
||||
result = this.getOperand().(RefinementCandidate).getARefinedVar()
|
||||
}
|
||||
|
||||
overlay[global]
|
||||
override RefinementValue eval(RefinementContext ctxt) {
|
||||
exists(RefinementValue opVal |
|
||||
opVal = this.getOperand().(RefinementCandidate).eval(ctxt) and
|
||||
@@ -182,6 +197,7 @@ private class EqRefinement extends RefinementCandidate, EqualityTest {
|
||||
result = this.getRightOperand().(RefinementCandidate).getARefinedVar()
|
||||
}
|
||||
|
||||
overlay[global]
|
||||
override RefinementValue eval(RefinementContext ctxt) {
|
||||
exists(RefinementCandidate l, RefinementValue lv, RefinementCandidate r, RefinementValue rv |
|
||||
l = this.getLeftOperand() and
|
||||
@@ -220,6 +236,7 @@ private class IndexRefinement extends RefinementCandidate, IndexExpr {
|
||||
result = this.getIndex().(RefinementCandidate).getARefinedVar()
|
||||
}
|
||||
|
||||
overlay[global]
|
||||
override RefinementValue eval(RefinementContext ctxt) {
|
||||
exists(
|
||||
RefinementCandidate base, RefinementValue baseVal, RefinementCandidate index,
|
||||
@@ -242,6 +259,7 @@ private class IndexRefinement extends RefinementCandidate, IndexExpr {
|
||||
* if any.
|
||||
*/
|
||||
bindingset[s, i]
|
||||
overlay[global]
|
||||
private RefinementValue evalIndex(StringConstant s, IntConstant i) {
|
||||
result = TStringConstant(s.getValue().charAt(i.getValue()))
|
||||
}
|
||||
@@ -249,6 +267,7 @@ private RefinementValue evalIndex(StringConstant s, IntConstant i) {
|
||||
/**
|
||||
* A context in which a refinement expression is analyzed.
|
||||
*/
|
||||
overlay[global]
|
||||
newtype TRefinementContext =
|
||||
/**
|
||||
* A refinement context associated with refinement `ref`, specifying that variable `var`
|
||||
@@ -266,6 +285,7 @@ newtype TRefinementContext =
|
||||
/**
|
||||
* A context in which a refinement expression is analyzed.
|
||||
*/
|
||||
overlay[global]
|
||||
class RefinementContext extends TRefinementContext {
|
||||
/**
|
||||
* Holds if refinement expression `cand` might be analyzed in this context.
|
||||
@@ -280,6 +300,7 @@ class RefinementContext extends TRefinementContext {
|
||||
* A refinement context specifying that some variable is assumed to have one particular
|
||||
* abstract value.
|
||||
*/
|
||||
overlay[global]
|
||||
class VarRefinementContext extends RefinementContext, TVarRefinementContext {
|
||||
override predicate appliesTo(RefinementCandidate cand) {
|
||||
exists(AnalyzedRefinement ref, SsaSourceVariable var |
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
* Note that unlike `TypeTracking.qll`, this library only performs
|
||||
* local tracking within a function.
|
||||
*/
|
||||
overlay[local]
|
||||
module;
|
||||
|
||||
private import javascript
|
||||
private import semmle.javascript.dataflow.TypeTracking
|
||||
@@ -192,6 +194,7 @@ class SourceNode extends DataFlow::Node instanceof SourceNode::Range {
|
||||
*
|
||||
* See `TypeTracker` for more details about how to use this.
|
||||
*/
|
||||
overlay[global]
|
||||
pragma[inline]
|
||||
DataFlow::SourceNode track(TypeTracker t2, TypeTracker t) { t = t2.step(this, result) }
|
||||
|
||||
@@ -200,6 +203,7 @@ class SourceNode extends DataFlow::Node instanceof SourceNode::Range {
|
||||
*
|
||||
* See `TypeBackTracker` for more details about how to use this.
|
||||
*/
|
||||
overlay[global]
|
||||
pragma[inline]
|
||||
DataFlow::SourceNode backtrack(TypeBackTracker t2, TypeBackTracker t) {
|
||||
t2 = t.step(result, this)
|
||||
|
||||
@@ -92,10 +92,15 @@ class AnalyzedNode extends DataFlow::Node {
|
||||
PrimitiveType getAPrimitiveType() { result = this.getAValue().toPrimitive().getType() }
|
||||
|
||||
/** Gets a Boolean value that this node evaluates to. */
|
||||
bindingset[this]
|
||||
overlay[caller?]
|
||||
pragma[inline_late]
|
||||
boolean getABooleanValue() { result = this.getAValue().getBooleanValue() }
|
||||
|
||||
/** Gets the unique Boolean value that this node evaluates to, if any. */
|
||||
boolean getTheBooleanValue() { forex(boolean bv | bv = this.getABooleanValue() | result = bv) }
|
||||
overlay[caller?]
|
||||
pragma[inline]
|
||||
boolean getTheBooleanValue() { result = unique( | | this.getABooleanValue()) }
|
||||
|
||||
/** Gets the unique type inferred for this node, if any. */
|
||||
InferredType getTheType() { result = unique(InferredType t | t = this.getAType()) }
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
*
|
||||
* Provides a representation for abstract values.
|
||||
*/
|
||||
overlay[local]
|
||||
module;
|
||||
|
||||
private import javascript
|
||||
import semmle.javascript.dataflow.AbstractValues
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
* to the same value have the same access paths, so access paths are neither sound nor
|
||||
* complete as an approximation of expression semantics.
|
||||
*/
|
||||
overlay[local]
|
||||
module;
|
||||
|
||||
import javascript
|
||||
private import semmle.javascript.internal.CachedStages
|
||||
|
||||
@@ -5,6 +5,7 @@ private import semmle.javascript.dataflow.internal.DataFlowPrivate
|
||||
/**
|
||||
* Gets a data-flow node synthesized using `AdditionalFlowInternal#needsSynthesizedNode`.
|
||||
*/
|
||||
overlay[local]
|
||||
DataFlow::Node getSynthesizedNode(AstNode node, string tag) {
|
||||
result = TGenericSynthesizedNode(node, tag, _)
|
||||
}
|
||||
@@ -12,6 +13,7 @@ DataFlow::Node getSynthesizedNode(AstNode node, string tag) {
|
||||
/**
|
||||
* An extension to `AdditionalFlowStep` with additional internal-only predicates.
|
||||
*/
|
||||
overlay[local]
|
||||
class AdditionalFlowInternal extends DataFlow::AdditionalFlowStep {
|
||||
/**
|
||||
* Holds if a data-flow node should be synthesized for the pair `(node, tag)`.
|
||||
@@ -25,10 +27,12 @@ class AdditionalFlowInternal extends DataFlow::AdditionalFlowStep {
|
||||
/**
|
||||
* Holds if `node` should only permit flow of values stored in `contents`.
|
||||
*/
|
||||
overlay[global]
|
||||
predicate expectsContent(DataFlow::Node node, DataFlow::ContentSet contents) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `node` should not permit flow of values stored in `contents`.
|
||||
*/
|
||||
overlay[global]
|
||||
predicate clearsContent(DataFlow::Node node, DataFlow::ContentSet contents) { none() }
|
||||
}
|
||||
|
||||
@@ -358,25 +358,18 @@ module MakeStateBarrierGuard<
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a logical `and` expression, or parenthesized expression, that contains `guard`.
|
||||
* Gets any of the ancestors of `guard` that preserves the value of `possibleOutcome`. Includes the guard itself.
|
||||
*/
|
||||
private Expr getALogicalAndParent(BarrierGuard guard) {
|
||||
barrierGuardIsRelevant(guard) and result = guard.asExpr()
|
||||
private Expr getALogicalOperatorParent(BarrierGuard guard, boolean possibleOutcome) {
|
||||
barrierGuardIsRelevant(guard) and result = guard.asExpr() and possibleOutcome = [true, false]
|
||||
or
|
||||
result.(LogAndExpr).getAnOperand() = getALogicalAndParent(guard)
|
||||
result.(LogOrExpr).getAnOperand() = getALogicalOperatorParent(guard, possibleOutcome) and
|
||||
possibleOutcome = false
|
||||
or
|
||||
result.getUnderlyingValue() = getALogicalAndParent(guard)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a logical `or` expression, or parenthesized expression, that contains `guard`.
|
||||
*/
|
||||
private Expr getALogicalOrParent(BarrierGuard guard) {
|
||||
barrierGuardIsRelevant(guard) and result = guard.asExpr()
|
||||
result.(LogAndExpr).getAnOperand() = getALogicalOperatorParent(guard, possibleOutcome) and
|
||||
possibleOutcome = true
|
||||
or
|
||||
result.(LogOrExpr).getAnOperand() = getALogicalOrParent(guard)
|
||||
or
|
||||
result.getUnderlyingValue() = getALogicalOrParent(guard)
|
||||
result.getUnderlyingValue() = getALogicalOperatorParent(guard, possibleOutcome)
|
||||
}
|
||||
|
||||
final private class FinalFunction = Function;
|
||||
@@ -394,15 +387,7 @@ module MakeStateBarrierGuard<
|
||||
exists(BarrierGuard guard |
|
||||
barrierGuardIsRelevant(guard) and
|
||||
exists(Expr e |
|
||||
exists(Expr returnExpr |
|
||||
returnExpr = guard.asExpr()
|
||||
or
|
||||
// ad hoc support for conjunctions:
|
||||
getALogicalAndParent(guard) = returnExpr and guardOutcome = true
|
||||
or
|
||||
// ad hoc support for disjunctions:
|
||||
getALogicalOrParent(guard) = returnExpr and guardOutcome = false
|
||||
|
|
||||
exists(Expr returnExpr | returnExpr = getALogicalOperatorParent(guard, guardOutcome) |
|
||||
exists(SsaExplicitDefinition ssa |
|
||||
ssa.getDef().getSource() = returnExpr and
|
||||
ssa.getVariable().getAUse() = this.getAReturnedExpr()
|
||||
|
||||
@@ -97,9 +97,14 @@ module CallGraph {
|
||||
not exists(read.getPropertyName()) and
|
||||
result = read and
|
||||
// there exists only local reads of the object, nothing else.
|
||||
forex(DataFlow::Node ref | ref = obj.getALocalUse() and exists(ref.asExpr()) |
|
||||
ref = [obj, any(DataFlow::PropRead r).getBase()]
|
||||
)
|
||||
objectOnlyUsedForPropRead(obj)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate objectOnlyUsedForPropRead(DataFlow::ObjectLiteralNode obj) {
|
||||
forex(DataFlow::Node ref | ref = obj.getALocalUse() and exists(ref.asExpr()) |
|
||||
ref = [obj, any(DataFlow::PropRead r).getBase()]
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
overlay[local]
|
||||
module;
|
||||
|
||||
private import javascript
|
||||
private import semmle.javascript.frameworks.data.internal.ApiGraphModels as ApiGraphModels
|
||||
private import semmle.javascript.dataflow.internal.FlowSummaryPrivate as FlowSummaryPrivate
|
||||
@@ -194,6 +197,7 @@ module Public {
|
||||
*/
|
||||
class ContentSet extends TContentSet {
|
||||
/** Gets a content that may be stored into when storing into this set. */
|
||||
overlay[caller?]
|
||||
pragma[inline]
|
||||
Content getAStoreContent() {
|
||||
result = this.asSingleton()
|
||||
@@ -333,12 +337,14 @@ module Public {
|
||||
/**
|
||||
* A content set containing only the given content.
|
||||
*/
|
||||
overlay[caller]
|
||||
pragma[inline]
|
||||
ContentSet singleton(Content content) { result.asSingleton() = content }
|
||||
|
||||
/**
|
||||
* A content set corresponding to the given property name.
|
||||
*/
|
||||
overlay[caller]
|
||||
pragma[inline]
|
||||
ContentSet property(PropertyName name) { result.asSingleton().asPropertyName() = name }
|
||||
|
||||
@@ -399,6 +405,7 @@ module Public {
|
||||
* If `bound` is too large, it is truncated to the greatest lower bound we can represent.
|
||||
*/
|
||||
bindingset[bound]
|
||||
overlay[caller]
|
||||
ContentSet arrayElementLowerBoundFromInt(int bound) {
|
||||
result = arrayElementLowerBound(bound.minimum(getMaxPreciseArrayIndex() + 1))
|
||||
}
|
||||
@@ -409,6 +416,7 @@ module Public {
|
||||
* If `n` is too large, it is truncated to the greatest lower bound we can represent.
|
||||
*/
|
||||
bindingset[n]
|
||||
overlay[caller]
|
||||
ContentSet arrayElementFromInt(int n) {
|
||||
result = arrayElementKnown(n)
|
||||
or
|
||||
@@ -448,6 +456,7 @@ module Public {
|
||||
* If `key` is not one of the keys we track precisely, this is mapped to the unknown key instead.
|
||||
*/
|
||||
bindingset[key]
|
||||
overlay[caller]
|
||||
ContentSet mapValueFromKey(string key) {
|
||||
result = mapValueWithKnownKey(key)
|
||||
or
|
||||
@@ -510,6 +519,7 @@ module Public {
|
||||
* are mapped to their corresponding content sets (which are no longer seen as property names).
|
||||
*/
|
||||
bindingset[propertyName]
|
||||
overlay[caller]
|
||||
ContentSet fromLegacyProperty(string propertyName) {
|
||||
result = fromLegacyPseudoProperty(propertyName)
|
||||
or
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
*
|
||||
* Contains the raw data type underlying `DataFlow::Node`.
|
||||
*/
|
||||
overlay[local]
|
||||
module;
|
||||
|
||||
private import javascript
|
||||
private import codeql.util.Boolean
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user