Merge branch 'main' into redsun82/update-fmt

This commit is contained in:
Paolo Tranquilli
2025-11-20 17:06:24 +01:00
210 changed files with 2808 additions and 953 deletions

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -6,6 +6,7 @@ extractor: go
library: true
upgrades: upgrades
dependencies:
codeql/concepts: ${workspace}
codeql/dataflow: ${workspace}
codeql/mad: ${workspace}
codeql/threat-models: ${workspace}

View File

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

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

View File

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

View File

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

View 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

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

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

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,4 @@
query: Security/CWE-327/BrokenCryptoAlgorithm.ql
postprocess:
- utils/test/PrettyPrintModels.ql
- utils/test/InlineExpectationsTestQuery.ql

View File

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

View File

@@ -0,0 +1,2 @@
testFailures
invalidModelRow

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

View File

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

View File

@@ -1 +0,0 @@
experimental/CWE-327/WeakCryptoAlgorithm.ql

View File

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

View File

@@ -0,0 +1,4 @@
query: Security/CWE-327/WeakSensitiveDataHashing.ql
postprocess:
- utils/test/PrettyPrintModels.ql
- utils/test/InlineExpectationsTestQuery.ql

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

View File

@@ -0,0 +1,5 @@
module test
go 1.24.0
require golang.org/x/crypto v0.43.0

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

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

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

View File

@@ -0,0 +1,4 @@
# golang.org/x/crypto v0.43.0
## explicit
golang.org/x/crypto/md4
golang.org/x/crypto/ripemd160

View File

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

View File

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

View File

@@ -1,6 +1,8 @@
/**
* Provides a predicate for identifying unused index variables in loops.
*/
overlay[local]
module;
import javascript

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,6 @@
/** Provides classes for working with JavaScript comments. */
overlay[local]
module;
import javascript

View File

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

View File

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

View File

@@ -1,6 +1,8 @@
/**
* Provides classes for working with E4X.
*/
overlay[local]
module;
import javascript

View File

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

View File

@@ -1,4 +1,6 @@
/** Provides classes for working with syntax errors. */
overlay[local]
module;
import javascript

View File

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

View File

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

View File

@@ -36,6 +36,8 @@
* Array.prototype.length;
* </pre>
*/
overlay[local]
module;
import javascript

View File

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

View File

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

View File

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

View File

@@ -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, _) }

View File

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

View File

@@ -1,6 +1,8 @@
/**
* Provides classes for working with JSON data.
*/
overlay[local]
module;
import javascript

View File

@@ -1,6 +1,8 @@
/**
* Provides classes for working with JSX code.
*/
overlay[local]
module;
import javascript

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,8 @@
/**
* Provides classes for working with NPM module definitions and dependencies.
*/
overlay[local?]
module;
import javascript
private import NodeModuleResolutionImpl

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,6 @@
/** Provides classes for working with statements. */
overlay[local]
module;
import javascript

View File

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

View File

@@ -1,6 +1,8 @@
/**
* Provides classes for working with the token-based representation of JavaScript programs.
*/
overlay[local]
module;
import javascript

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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` &rarr; `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` &rarr; `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()
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,3 +1,6 @@
overlay[local]
module;
/**
* Types inferred by the flow analysis, represented as type tags.
*

View File

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

View File

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

View File

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

View File

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

View File

@@ -3,6 +3,8 @@
*
* Provides a representation for abstract values.
*/
overlay[local]
module;
private import javascript
import semmle.javascript.dataflow.AbstractValues

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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