Merge branch 'main' into experimental-strong-params

This commit is contained in:
thiggy1342
2022-07-19 10:29:36 -04:00
committed by GitHub
85 changed files with 1982 additions and 52 deletions

View File

@@ -0,0 +1,44 @@
{
SymmetricKey aesKey = new SymmetricKey(kid: "symencryptionkey");
// BAD: Using the outdated client side encryption version V1_0
BlobEncryptionPolicy uploadPolicy = new BlobEncryptionPolicy(key: aesKey, keyResolver: null);
BlobRequestOptions uploadOptions = new BlobRequestOptions() { EncryptionPolicy = uploadPolicy };
MemoryStream stream = new MemoryStream(buffer);
blob.UploadFromStream(stream, length: size, accessCondition: null, options: uploadOptions);
}
var client = new BlobClient(myConnectionString, new SpecializedBlobClientOptions()
{
// BAD: Using an outdated SDK that does not support client side encryption version V2_0
ClientSideEncryption = new ClientSideEncryptionOptions()
{
KeyEncryptionKey = myKey,
KeyResolver = myKeyResolver,
KeyWrapAlgorihm = myKeyWrapAlgorithm
}
});
var client = new BlobClient(myConnectionString, new SpecializedBlobClientOptions()
{
// BAD: Using the outdated client side encryption version V1_0
ClientSideEncryption = new ClientSideEncryptionOptions(ClientSideEncryptionVersion.V1_0)
{
KeyEncryptionKey = myKey,
KeyResolver = myKeyResolver,
KeyWrapAlgorihm = myKeyWrapAlgorithm
}
});
var client = new BlobClient(myConnectionString, new SpecializedBlobClientOptions()
{
// GOOD: Using client side encryption version V2_0
ClientSideEncryption = new ClientSideEncryptionOptions(ClientSideEncryptionVersion.V2_0)
{
KeyEncryptionKey = myKey,
KeyResolver = myKeyResolver,
KeyWrapAlgorihm = myKeyWrapAlgorithm
}
});

View File

@@ -0,0 +1,29 @@
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
<qhelp>
<overview>
<p>Azure Storage .NET, Java, and Python SDKs support encryption on the client with a customer-managed key that is maintained in Azure Key Vault or another key store.</p>
<p>Current release versions of the Azure Storage SDKs use cipher block chaining (CBC mode) for client-side encryption (referred to as <code>v1</code>).</p>
</overview>
<recommendation>
<p>Consider switching to <code>v2</code> client-side encryption.</p>
</recommendation>
<example>
<sample src="UnsafeUsageOfClientSideEncryptionVersion.cs" />
</example>
<references>
<li>
<a href="http://aka.ms/azstorageclientencryptionblog">Azure Storage Client Encryption Blog.</a>
</li>
<li>
<a href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-30187">CVE-2022-30187</a>
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,81 @@
/**
* @name Unsafe usage of v1 version of Azure Storage client-side encryption (CVE-2022-30187).
* @description Unsafe usage of v1 version of Azure Storage client-side encryption, please refer to http://aka.ms/azstorageclientencryptionblog
* @kind problem
* @tags security
* cryptography
* external/cwe/cwe-327
* @id cs/azure-storage/unsafe-usage-of-client-side-encryption-version
* @problem.severity error
* @precision high
*/
import csharp
/**
* Holds if `oc` is creating an object of type `c` = `Azure.Storage.ClientSideEncryptionOptions`
* and `e` is the `version` argument to the constructor
*/
predicate isCreatingAzureClientSideEncryptionObject(ObjectCreation oc, Class c, Expr e) {
exists(Parameter p | p.hasName("version") |
c.hasQualifiedName("Azure.Storage.ClientSideEncryptionOptions") and
oc.getTarget() = c.getAConstructor() and
e = oc.getArgumentForParameter(p)
)
}
/**
* Holds if `oc` is an object creation of the outdated type `c` = `Microsoft.Azure.Storage.Blob.BlobEncryptionPolicy`
*/
predicate isCreatingOutdatedAzureClientSideEncryptionObject(ObjectCreation oc, Class c) {
c.hasQualifiedName("Microsoft.Azure.Storage.Blob.BlobEncryptionPolicy") and
oc.getTarget() = c.getAConstructor()
}
/**
* Holds if the Azure.Storage assembly for `c` is a version known to support
* version 2+ for client-side encryption
*/
predicate doesAzureStorageAssemblySupportSafeClientSideEncryption(Assembly asm) {
exists(int versionCompare |
versionCompare = asm.getVersion().compareTo("12.12.0.0") and
versionCompare >= 0
) and
asm.getName() = "Azure.Storage.Common"
}
/**
* Holds if the Azure.Storage assembly for `c` is a version known to support
* version 2+ for client-side encryption and if the argument for the constructor `version`
* is set to a secure value.
*/
predicate isObjectCreationArgumentSafeAndUsingSafeVersionOfAssembly(Expr versionExpr, Assembly asm) {
// Check if the Azure.Storage assembly version has the fix
doesAzureStorageAssemblySupportSafeClientSideEncryption(asm) and
// and that the version argument for the constructor is guaranteed to be Version2
isExprAnAccessToSafeClientSideEncryptionVersionValue(versionExpr)
}
/**
* Holds if the expression `e` is an access to a safe version of the enum `ClientSideEncryptionVersion`
* or an equivalent numeric value
*/
predicate isExprAnAccessToSafeClientSideEncryptionVersionValue(Expr e) {
exists(EnumConstant ec |
ec.hasQualifiedName("Azure.Storage.ClientSideEncryptionVersion.V2_0") and
ec.getAnAccess() = e
)
}
from Expr e, Class c, Assembly asm
where
asm = c.getLocation() and
(
exists(Expr e2 |
isCreatingAzureClientSideEncryptionObject(e, c, e2) and
not isObjectCreationArgumentSafeAndUsingSafeVersionOfAssembly(e2, asm)
)
or
isCreatingOutdatedAzureClientSideEncryptionObject(e, c)
)
select e, "Unsafe usage of v1 version of Azure Storage client-side encryption."

View File

@@ -0,0 +1,46 @@
// BAD: Using an outdated SDK that does not support client side encryption version V2_0
new EncryptedBlobClientBuilder()
.blobClient(blobClient)
.key(resolver.buildAsyncKeyEncryptionKey(keyid).block(), keyWrapAlgorithm)
.buildEncryptedBlobClient()
.uploadWithResponse(new BlobParallelUploadOptions(data)
.setMetadata(metadata)
.setHeaders(headers)
.setTags(tags)
.setTier(tier)
.setRequestConditions(requestConditions)
.setComputeMd5(computeMd5)
.setParallelTransferOptions(parallelTransferOptions),
timeout, context);
// BAD: Using the deprecatedd client side encryption version V1_0
new EncryptedBlobClientBuilder(EncryptionVersion.V1)
.blobClient(blobClient)
.key(resolver.buildAsyncKeyEncryptionKey(keyid).block(), keyWrapAlgorithm)
.buildEncryptedBlobClient()
.uploadWithResponse(new BlobParallelUploadOptions(data)
.setMetadata(metadata)
.setHeaders(headers)
.setTags(tags)
.setTier(tier)
.setRequestConditions(requestConditions)
.setComputeMd5(computeMd5)
.setParallelTransferOptions(parallelTransferOptions),
timeout, context);
// GOOD: Using client side encryption version V2_0
new EncryptedBlobClientBuilder(EncryptionVersion.V2)
.blobClient(blobClient)
.key(resolver.buildAsyncKeyEncryptionKey(keyid).block(), keyWrapAlgorithm)
.buildEncryptedBlobClient()
.uploadWithResponse(new BlobParallelUploadOptions(data)
.setMetadata(metadata)
.setHeaders(headers)
.setTags(tags)
.setTier(tier)
.setRequestConditions(requestConditions)
.setComputeMd5(computeMd5)
.setParallelTransferOptions(parallelTransferOptions),
timeout, context);

View File

@@ -0,0 +1,29 @@
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
<qhelp>
<overview>
<p>Azure Storage .NET, Java, and Python SDKs support encryption on the client with a customer-managed key that is maintained in Azure Key Vault or another key store.</p>
<p>The Azure Storage SDK version 12.18.0 or later supports version <code>V2</code> for client-side encryption. All previous versions of Azure Storage SDK only support client-side encryption <code>V1</code> which is unsafe.</p>
</overview>
<recommendation>
<p>Consider switching to <code>V2</code> client-side encryption.</p>
</recommendation>
<example>
<sample src="UnsafeUsageOfClientSideEncryptionVersion.java" />
</example>
<references>
<li>
<a href="http://aka.ms/azstorageclientencryptionblog">Azure Storage Client Encryption Blog.</a>
</li>
<li>
<a href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-30187">CVE-2022-30187</a>
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,92 @@
/**
* @name Unsafe usage of v1 version of Azure Storage client-side encryption (CVE-2022-30187).
* @description Unsafe usage of v1 version of Azure Storage client-side encryption, please refer to http://aka.ms/azstorageclientencryptionblog
* @kind problem
* @tags security
* cryptography
* external/cwe/cwe-327
* @id java/azure-storage/unsafe-client-side-encryption-in-use
* @problem.severity error
* @precision high
*/
import java
import semmle.code.java.dataflow.DataFlow
/**
* Holds if `call` is an object creation for a class `EncryptedBlobClientBuilder`
* that takes no arguments, which means that it is using V1 encryption.
*/
predicate isCreatingOutdatedAzureClientSideEncryptionObject(Call call, Class c) {
exists(string package, string type, Constructor constructor |
c.hasQualifiedName(package, type) and
c.getAConstructor() = constructor and
call.getCallee() = constructor and
(
type = "EncryptedBlobClientBuilder" and
package = "com.azure.storage.blob.specialized.cryptography" and
constructor.hasNoParameters()
or
type = "BlobEncryptionPolicy" and package = "com.microsoft.azure.storage.blob"
)
)
}
/**
* Holds if `call` is an object creation for a class `EncryptedBlobClientBuilder`
* that takes `versionArg` as the argument specifying the encryption version.
*/
predicate isCreatingAzureClientSideEncryptionObjectNewVersion(Call call, Class c, Expr versionArg) {
exists(string package, string type, Constructor constructor |
c.hasQualifiedName(package, type) and
c.getAConstructor() = constructor and
call.getCallee() = constructor and
type = "EncryptedBlobClientBuilder" and
package = "com.azure.storage.blob.specialized.cryptography" and
versionArg = call.getArgument(0)
)
}
/**
* A dataflow config that tracks `EncryptedBlobClientBuilder.version` argument initialization.
*/
private class EncryptedBlobClientBuilderSafeEncryptionVersionConfig extends DataFlow::Configuration {
EncryptedBlobClientBuilderSafeEncryptionVersionConfig() {
this = "EncryptedBlobClientBuilderSafeEncryptionVersionConfig"
}
override predicate isSource(DataFlow::Node source) {
exists(FieldRead fr, Field f | fr = source.asExpr() |
f.getAnAccess() = fr and
f.hasQualifiedName("com.azure.storage.blob.specialized.cryptography", "EncryptionVersion",
"V2")
)
}
override predicate isSink(DataFlow::Node sink) {
isCreatingAzureClientSideEncryptionObjectNewVersion(_, _, sink.asExpr())
}
}
/**
* Holds if `call` is an object creation for a class `EncryptedBlobClientBuilder`
* that takes `versionArg` as the argument specifying the encryption version, and that version is safe.
*/
predicate isCreatingSafeAzureClientSideEncryptionObject(Call call, Class c, Expr versionArg) {
isCreatingAzureClientSideEncryptionObjectNewVersion(call, c, versionArg) and
exists(EncryptedBlobClientBuilderSafeEncryptionVersionConfig config, DataFlow::Node sink |
sink.asExpr() = versionArg
|
config.hasFlow(_, sink)
)
}
from Expr e, Class c
where
exists(Expr argVersion |
isCreatingAzureClientSideEncryptionObjectNewVersion(e, c, argVersion) and
not isCreatingSafeAzureClientSideEncryptionObject(e, c, argVersion)
)
or
isCreatingOutdatedAzureClientSideEncryptionObject(e, c)
select e, "Unsafe usage of v1 version of Azure Storage client-side encryption."

View File

@@ -74,9 +74,7 @@ private module NotExposed {
}
/** DEPRECATED: Alias for fullyQualifiedToApiGraphPath */
deprecated string fullyQualifiedToAPIGraphPath(string fullyQaulified) {
result = fullyQualifiedToApiGraphPath(fullyQaulified)
}
deprecated predicate fullyQualifiedToAPIGraphPath = fullyQualifiedToApiGraphPath/1;
bindingset[this]
abstract class FindSubclassesSpec extends string {

View File

@@ -0,0 +1,7 @@
blob_client = blob_service_client.get_blob_client(container=container_name, blob=blob_name)
blob_client.require_encryption = True
blob_client.key_encryption_key = kek
# GOOD: Must use `encryption_version` set to `2.0`
blob_client.encryption_version = '2.0' # Use Version 2.0!
with open("decryptedcontentfile.txt", "rb") as stream:
blob_client.upload_blob(stream, overwrite=OVERWRITE_EXISTING)

View File

@@ -0,0 +1,29 @@
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
<qhelp>
<overview>
<p>Azure Storage .NET, Java, and Python SDKs support encryption on the client with a customer-managed key that is maintained in Azure Key Vault or another key store.</p>
<p>Current release versions of the Azure Storage SDKs use cipher block chaining (CBC mode) for client-side encryption (referred to as <code>v1</code>).</p>
</overview>
<recommendation>
<p>Consider switching to <code>v2</code> client-side encryption.</p>
</recommendation>
<example>
<sample src="UnsafeUsageOfClientSideEncryptionVersion.py" />
</example>
<references>
<li>
<a href="http://aka.ms/azstorageclientencryptionblog">Azure Storage Client Encryption Blog.</a>
</li>
<li>
<a href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-30187">CVE-2022-30187</a>
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,90 @@
/**
* @name Unsafe usage of v1 version of Azure Storage client-side encryption.
* @description Using version v1 of Azure Storage client-side encryption is insecure, and may enable an attacker to decrypt encrypted data
* @kind problem
* @tags security
* cryptography
* external/cwe/cwe-327
* @id py/azure-storage/unsafe-client-side-encryption-in-use
* @problem.severity error
* @precision medium
*/
import python
import semmle.python.ApiGraphs
predicate isUnsafeClientSideAzureStorageEncryptionViaAttributes(Call call, AttrNode node) {
exists(
API::Node n, API::Node n2, Attribute a, AssignStmt astmt, API::Node uploadBlob,
ControlFlowNode ctrlFlowNode, string s
|
s in ["key_encryption_key", "key_resolver_function"] and
n =
API::moduleImport("azure")
.getMember("storage")
.getMember("blob")
.getMember("BlobClient")
.getReturn()
.getMember(s) and
n2 =
API::moduleImport("azure")
.getMember("storage")
.getMember("blob")
.getMember("BlobClient")
.getReturn()
.getMember("upload_blob") and
n.getAValueReachableFromSource().asExpr() = a and
astmt.getATarget() = a and
a.getAFlowNode() = node and
uploadBlob =
API::moduleImport("azure")
.getMember("storage")
.getMember("blob")
.getMember("BlobClient")
.getReturn()
.getMember("upload_blob") and
uploadBlob.getACall().asExpr() = call and
ctrlFlowNode = call.getAFlowNode() and
node.strictlyReaches(ctrlFlowNode) and
node != ctrlFlowNode and
not exists(
AssignStmt astmt2, Attribute a2, AttrNode encryptionVersionSet, StrConst uc,
API::Node encryptionVersion
|
uc = astmt2.getValue() and
uc.getText() in ["'2.0'", "2.0"] and
encryptionVersion =
API::moduleImport("azure")
.getMember("storage")
.getMember("blob")
.getMember("BlobClient")
.getReturn()
.getMember("encryption_version") and
encryptionVersion.getAValueReachableFromSource().asExpr() = a2 and
astmt2.getATarget() = a2 and
a2.getAFlowNode() = encryptionVersionSet and
encryptionVersionSet.strictlyReaches(ctrlFlowNode)
)
)
}
predicate isUnsafeClientSideAzureStorageEncryptionViaObjectCreation(Call call, ControlFlowNode node) {
exists(API::Node c, string s, Keyword k | k.getAFlowNode() = node |
c.getACall().asExpr() = call and
c = API::moduleImport("azure").getMember("storage").getMember("blob").getMember(s) and
s in ["ContainerClient", "BlobClient", "BlobServiceClient"] and
k.getArg() = "key_encryption_key" and
k = call.getANamedArg() and
not k.getValue() instanceof None and
not exists(Keyword k2 | k2 = call.getANamedArg() |
k2.getArg() = "encryption_version" and
k2.getValue().(StrConst).getText() in ["'2.0'", "2.0"]
)
)
}
from Call call, ControlFlowNode node
where
isUnsafeClientSideAzureStorageEncryptionViaAttributes(call, node) or
isUnsafeClientSideAzureStorageEncryptionViaObjectCreation(call, node)
select node, "Unsafe usage of v1 version of Azure Storage client-side encryption."

View File

@@ -19,6 +19,7 @@ fn main() -> std::io::Result<()> {
.arg("--include-extension=.erb")
.arg("--include-extension=.gemspec")
.arg("--include=**/Gemfile")
.arg("--exclude=**/.git")
.arg("--size-limit=5m")
.arg("--language=ruby")
.arg("--working-dir=.")

View File

@@ -36,7 +36,7 @@ private import ExprNodes
* constant value in some cases.
*/
private module Propagation {
private ExprCfgNode getSource(VariableReadAccessCfgNode read) {
ExprCfgNode getSource(VariableReadAccessCfgNode read) {
exists(Ssa::WriteDefinition def |
def.assigns(result) and
read = def.getARead()
@@ -509,3 +509,53 @@ private module Cached {
}
import Cached
/**
* Holds if the control flow node `e` refers to an array constructed from the
* array literal `arr`.
* Example:
* ```rb
* [1, 2, 3]
* C = [1, 2, 3]; C
* x = [1, 2, 3]; x
* ```
*/
predicate isArrayConstant(ExprCfgNode e, ArrayLiteralCfgNode arr) {
// [...]
e = arr
or
// e = [...]; e
isArrayConstant(getSource(e), arr)
or
isArrayExpr(e.getExpr(), arr)
}
/**
* Holds if the expression `e` refers to an array constructed from the array literal `arr`.
*/
private predicate isArrayExpr(Expr e, ArrayLiteralCfgNode arr) {
// e = [...]
e = arr.getExpr()
or
// Like above, but handles the desugaring of array literals to Array.[] calls.
e.getDesugared() = arr.getExpr()
or
// A = [...]; A
// A = a; A
isArrayExpr(e.(ConstantReadAccess).getValue(), arr)
or
// Recurse via CFG nodes. Necessary for example in:
// a = [...]
// A = a
// A
//
// We map from A to a via ConstantReadAccess::getValue, yielding the Expr a.
// To get to [...] we need to go via getSource(ExprCfgNode e), so we find a
// CFG node for a and call `isArrayConstant`.
//
// The use of `forex` is intended to ensure that a is an array constant in all
// control flow paths.
// Note(hmac): I don't think this is necessary, as `getSource` will not return
// results if the source is a phi node.
forex(ExprCfgNode n | n = e.getAControlFlowNode() | isArrayConstant(n, arr))
}

View File

@@ -374,6 +374,9 @@ module ExprNodes {
MethodCallCfgNode() { super.getExpr() instanceof MethodCall }
override MethodCall getExpr() { result = super.getExpr() }
/** Gets the name of this method call. */
string getMethodName() { result = this.getExpr().getMethodName() }
}
private class CaseExprChildMapping extends ExprChildMapping, CaseExpr {

View File

@@ -3,6 +3,10 @@
private import ruby
private import codeql.ruby.DataFlow
private import codeql.ruby.CFG
private import codeql.ruby.controlflow.CfgNodes
private import codeql.ruby.dataflow.SSA
private import codeql.ruby.ast.internal.Constant
private import codeql.ruby.InclusionTests
private predicate stringConstCompare(CfgNodes::ExprCfgNode g, CfgNode e, boolean branch) {
exists(CfgNodes::ExprNodes::ComparisonOperationCfgNode c |
@@ -61,19 +65,7 @@ deprecated class StringConstCompare extends DataFlow::BarrierGuard,
// The value of the condition that results in the node being validated.
private boolean checkedBranch;
StringConstCompare() {
exists(CfgNodes::ExprNodes::StringLiteralCfgNode strLitNode |
this.getExpr() instanceof EqExpr and checkedBranch = true
or
this.getExpr() instanceof CaseEqExpr and checkedBranch = true
or
this.getExpr() instanceof NEExpr and checkedBranch = false
|
this.getLeftOperand() = strLitNode and this.getRightOperand() = checkedNode
or
this.getLeftOperand() = checkedNode and this.getRightOperand() = strLitNode
)
}
StringConstCompare() { stringConstCompare(this, checkedNode, checkedBranch) }
override predicate checks(CfgNode expr, boolean branch) {
expr = checkedNode and branch = checkedBranch
@@ -81,15 +73,19 @@ deprecated class StringConstCompare extends DataFlow::BarrierGuard,
}
private predicate stringConstArrayInclusionCall(CfgNodes::ExprCfgNode g, CfgNode e, boolean branch) {
exists(CfgNodes::ExprNodes::MethodCallCfgNode mc, ArrayLiteral aLit |
mc = g and
mc.getExpr().getMethodName() = "include?" and
[mc.getExpr().getReceiver(), mc.getExpr().getReceiver().(ConstantReadAccess).getValue()] = aLit
exists(InclusionTest t |
t.asExpr() = g and
e = t.getContainedNode().asExpr() and
branch = t.getPolarity()
|
forall(Expr elem | elem = aLit.getAnElement() | elem instanceof StringLiteral) and
mc.getArgument(0) = e
) and
branch = true
exists(ExprNodes::ArrayLiteralCfgNode arr |
isArrayConstant(t.getContainerNode().asExpr(), arr)
|
forall(ExprCfgNode elem | elem = arr.getAnArgument() |
elem instanceof ExprNodes::StringLiteralCfgNode
)
)
)
}
/**
@@ -132,16 +128,7 @@ deprecated class StringConstArrayInclusionCall extends DataFlow::BarrierGuard,
CfgNodes::ExprNodes::MethodCallCfgNode {
private CfgNode checkedNode;
StringConstArrayInclusionCall() {
exists(ArrayLiteral aLit |
this.getExpr().getMethodName() = "include?" and
[this.getExpr().getReceiver(), this.getExpr().getReceiver().(ConstantReadAccess).getValue()] =
aLit
|
forall(Expr elem | elem = aLit.getAnElement() | elem instanceof StringLiteral) and
this.getArgument(0) = checkedNode
)
}
StringConstArrayInclusionCall() { stringConstArrayInclusionCall(this, checkedNode, true) }
override predicate checks(CfgNode expr, boolean branch) { expr = checkedNode and branch = true }
}

View File

@@ -4,3 +4,4 @@
import stdlib.Open3
import stdlib.Logger
import stdlib.Pathname

View File

@@ -0,0 +1,188 @@
/** Modeling of the `Pathname` class from the Ruby standard library. */
private import codeql.ruby.AST
private import codeql.ruby.ApiGraphs
private import codeql.ruby.Concepts
private import codeql.ruby.DataFlow
private import codeql.ruby.dataflow.FlowSummary
private import codeql.ruby.dataflow.internal.DataFlowDispatch
private import codeql.ruby.frameworks.data.ModelsAsData
/**
* Modeling of the `Pathname` class from the Ruby standard library.
*
* https://docs.ruby-lang.org/en/3.1/Pathname.html
*/
module Pathname {
/**
* An instance of the `Pathname` class. For example, in
*
* ```rb
* pn = Pathname.new "foo.txt'"
* puts pn.read
* ```
*
* there are three `PathnameInstance`s - the call to `Pathname.new`, the
* assignment `pn = ...`, and the read access to `pn` on the second line.
*
* Every `PathnameInstance` is considered to be a `FileNameSource`.
*/
class PathnameInstance extends FileNameSource, DataFlow::Node {
PathnameInstance() { this = pathnameInstance() }
}
private DataFlow::Node pathnameInstance() {
// A call to `Pathname.new`.
result = API::getTopLevelMember("Pathname").getAnInstantiation()
or
// Class methods on `Pathname` that return a new `Pathname`.
result = API::getTopLevelMember("Pathname").getAMethodCall(["getwd", "pwd",])
or
// Instance methods on `Pathname` that return a new `Pathname`.
exists(DataFlow::CallNode c | result = c |
c.getReceiver() = pathnameInstance() and
c.getMethodName() =
[
"+", "/", "basename", "cleanpath", "expand_path", "join", "realpath",
"relative_path_from", "sub", "sub_ext", "to_path"
]
)
or
exists(DataFlow::Node inst |
inst = pathnameInstance() and
inst.(DataFlow::LocalSourceNode).flowsTo(result)
)
}
/** A call where the receiver is a `Pathname`. */
class PathnameCall extends DataFlow::CallNode {
PathnameCall() { this.getReceiver() instanceof PathnameInstance }
}
/**
* A call to `Pathname#open` or `Pathname#opendir`, considered as a
* `FileSystemAccess`.
*/
class PathnameOpen extends FileSystemAccess::Range, PathnameCall {
PathnameOpen() { this.getMethodName() = ["open", "opendir"] }
override DataFlow::Node getAPathArgument() { result = this.getReceiver() }
}
/** A call to `Pathname#read`, considered as a `FileSystemReadAccess`. */
class PathnameRead extends FileSystemReadAccess::Range, PathnameCall {
PathnameRead() { this.getMethodName() = "read" }
// The path is the receiver (the `Pathname` object).
override DataFlow::Node getAPathArgument() { result = this.getReceiver() }
// The read data is the return value of the call.
override DataFlow::Node getADataNode() { result = this }
}
/** A call to `Pathname#write`, considered as a `FileSystemWriteAccess`. */
class PathnameWrite extends FileSystemWriteAccess::Range, PathnameCall {
PathnameWrite() { this.getMethodName() = "write" }
// The path is the receiver (the `Pathname` object).
override DataFlow::Node getAPathArgument() { result = this.getReceiver() }
// The data to write is the 0th argument.
override DataFlow::Node getADataNode() { result = this.getArgument(0) }
}
/** A call to `Pathname#to_s`, considered as a `FileNameSource`. */
class PathnameToSFilenameSource extends FileNameSource, PathnameCall {
PathnameToSFilenameSource() { this.getMethodName() = "to_s" }
}
private class PathnamePermissionModification extends FileSystemPermissionModification::Range,
PathnameCall {
private DataFlow::Node permissionArg;
PathnamePermissionModification() {
exists(string methodName | this.getMethodName() = methodName |
methodName = ["chmod", "mkdir"] and permissionArg = this.getArgument(0)
or
methodName = "mkpath" and permissionArg = this.getKeywordArgument("mode")
or
methodName = "open" and permissionArg = this.getArgument(1)
// TODO: defaults for optional args? This may depend on the umask
)
}
override DataFlow::Node getAPermissionNode() { result = permissionArg }
}
/**
* Type summaries for the `Pathname` class, i.e. method calls that produce new
* `Pathname` instances.
*/
private class PathnameTypeSummary extends ModelInput::TypeModelCsv {
override predicate row(string row) {
// package1;type1;package2;type2;path
row =
[
// Pathname.new : Pathname
";Pathname;;;Member[Pathname].Instance",
// Pathname#+(path) : Pathname
";Pathname;;Pathname;Method[+].ReturnValue",
// Pathname#/(path) : Pathname
";Pathname;;Pathname;Method[/].ReturnValue",
// Pathname#basename(path) : Pathname
";Pathname;;Pathname;Method[basename].ReturnValue",
// Pathname#cleanpath(path) : Pathname
";Pathname;;Pathname;Method[cleanpath].ReturnValue",
// Pathname#expand_path(path) : Pathname
";Pathname;;Pathname;Method[expand_path].ReturnValue",
// Pathname#join(path) : Pathname
";Pathname;;Pathname;Method[join].ReturnValue",
// Pathname#realpath(path) : Pathname
";Pathname;;Pathname;Method[realpath].ReturnValue",
// Pathname#relative_path_from(path) : Pathname
";Pathname;;Pathname;Method[relative_path_from].ReturnValue",
// Pathname#sub(path) : Pathname
";Pathname;;Pathname;Method[sub].ReturnValue",
// Pathname#sub_ext(path) : Pathname
";Pathname;;Pathname;Method[sub_ext].ReturnValue",
// Pathname#to_path(path) : Pathname
";Pathname;;Pathname;Method[to_path].ReturnValue",
]
}
}
/** Taint flow summaries for the `Pathname` class. */
private class PathnameTaintSummary extends ModelInput::SummaryModelCsv {
override predicate row(string row) {
row =
[
// Pathname.new(path)
";;Member[Pathname].Method[new];Argument[0];ReturnValue;taint",
// Pathname#dirname
";Pathname;Method[dirname];Argument[self];ReturnValue;taint",
// Pathname#each_filename
";Pathname;Method[each_filename];Argument[self];Argument[block].Parameter[0];taint",
// Pathname#expand_path
";Pathname;Method[expand_path];Argument[self];ReturnValue;taint",
// Pathname#join
";Pathname;Method[join];Argument[self,any];ReturnValue;taint",
// Pathname#parent
";Pathname;Method[parent];Argument[self];ReturnValue;taint",
// Pathname#realpath
";Pathname;Method[realpath];Argument[self];ReturnValue;taint",
// Pathname#relative_path_from
";Pathname;Method[relative_path_from];Argument[self];ReturnValue;taint",
// Pathname#to_path
";Pathname;Method[to_path];Argument[self];ReturnValue;taint",
// Pathname#basename
";Pathname;Method[basename];Argument[self];ReturnValue;taint",
// Pathname#cleanpath
";Pathname;Method[cleanpath];Argument[self];ReturnValue;taint",
// Pathname#sub
";Pathname;Method[sub];Argument[self];ReturnValue;taint",
// Pathname#sub_ext
";Pathname;Method[sub_ext];Argument[self];ReturnValue;taint",
]
}
}
}

View File

@@ -1556,6 +1556,35 @@ constants/constants.rb:
# 73| getAnOperand/getLeftOperand: [ClassVariableAccess] @@fourty_six
# 73| getAnOperand/getRightOperand: [ConstantReadAccess] FOURTY_SIX
# 73| getScopeExpr: [ConstantReadAccess] Mod3
# 78| getStmt: [AssignExpr] ... = ...
# 78| getAnOperand/getLeftOperand: [LocalVariableAccess] a
# 78| getAnOperand/getRightOperand: [ArrayLiteral] [...]
# 78| getElement: [IntegerLiteral] 1
# 78| getElement: [IntegerLiteral] 2
# 78| getElement: [IntegerLiteral] 3
# 79| getStmt: [AssignExpr] ... = ...
# 79| getAnOperand/getLeftOperand: [ConstantAssignment] A
# 79| getAnOperand/getRightOperand: [ArrayLiteral] [...]
# 79| getElement: [IntegerLiteral] 1
# 79| getElement: [IntegerLiteral] 2
# 79| getElement: [IntegerLiteral] 3
# 80| getStmt: [AssignExpr] ... = ...
# 80| getAnOperand/getLeftOperand: [ConstantAssignment] B
# 80| getAnOperand/getRightOperand: [LocalVariableAccess] a
# 81| getStmt: [AssignExpr] ... = ...
# 81| getAnOperand/getLeftOperand: [ConstantAssignment] C
# 81| getAnOperand/getRightOperand: [ConstantReadAccess] A
# 82| getStmt: [AssignExpr] ... = ...
# 82| getAnOperand/getLeftOperand: [LocalVariableAccess] b
# 82| getAnOperand/getRightOperand: [ConstantReadAccess] B
# 84| getStmt: [IfExpr] if ...
# 84| getCondition: [MethodCall] call to condition
# 84| getReceiver: [SelfVariableAccess] self
# 84| getBranch/getThen: [StmtSequence] then ...
# 85| getStmt: [AssignExpr] ... = ...
# 85| getAnOperand/getLeftOperand: [LocalVariableAccess] c
# 85| getAnOperand/getRightOperand: [LocalVariableAccess] b
# 87| getStmt: [LocalVariableAccess] c
escape_sequences/escapes.rb:
# 1| [Toplevel] escapes.rb
# 6| getStmt: [StringLiteral] "\'"

View File

@@ -336,6 +336,18 @@ constants/constants.rb:
# 20| getComponent: [StringTextComponent] Chuck
# 20| getArgument: [StringLiteral] "Dave"
# 20| getComponent: [StringTextComponent] Dave
# 78| [ArrayLiteral] [...]
# 78| getDesugared: [MethodCall] call to []
# 78| getReceiver: [ConstantReadAccess] Array
# 78| getArgument: [IntegerLiteral] 1
# 78| getArgument: [IntegerLiteral] 2
# 78| getArgument: [IntegerLiteral] 3
# 79| [ArrayLiteral] [...]
# 79| getDesugared: [MethodCall] call to []
# 79| getReceiver: [ConstantReadAccess] Array
# 79| getArgument: [IntegerLiteral] 1
# 79| getArgument: [IntegerLiteral] 2
# 79| getArgument: [IntegerLiteral] 3
escape_sequences/escapes.rb:
# 58| [ArrayLiteral] %w(...)
# 58| getDesugared: [MethodCall] call to []

View File

@@ -1656,10 +1656,56 @@ constants/constants.rb:
# 73| 1: [ReservedWord] ::
# 73| 2: [Constant] FOURTY_SIX
# 74| 5: [ReservedWord] end
# 78| 13: [Assignment] Assignment
# 78| 0: [Identifier] a
# 78| 1: [ReservedWord] =
# 78| 2: [Array] Array
# 78| 0: [ReservedWord] [
# 78| 1: [Integer] 1
# 78| 2: [ReservedWord] ,
# 78| 3: [Integer] 2
# 78| 4: [ReservedWord] ,
# 78| 5: [Integer] 3
# 78| 6: [ReservedWord] ]
# 79| 14: [Assignment] Assignment
# 79| 0: [Constant] A
# 79| 1: [ReservedWord] =
# 79| 2: [Array] Array
# 79| 0: [ReservedWord] [
# 79| 1: [Integer] 1
# 79| 2: [ReservedWord] ,
# 79| 3: [Integer] 2
# 79| 4: [ReservedWord] ,
# 79| 5: [Integer] 3
# 79| 6: [ReservedWord] ]
# 80| 15: [Assignment] Assignment
# 80| 0: [Constant] B
# 80| 1: [ReservedWord] =
# 80| 2: [Identifier] a
# 81| 16: [Assignment] Assignment
# 81| 0: [Constant] C
# 81| 1: [ReservedWord] =
# 81| 2: [Constant] A
# 82| 17: [Assignment] Assignment
# 82| 0: [Identifier] b
# 82| 1: [ReservedWord] =
# 82| 2: [Constant] B
# 84| 18: [If] If
# 84| 0: [ReservedWord] if
# 84| 1: [Identifier] condition
# 84| 2: [Then] Then
# 85| 0: [Assignment] Assignment
# 85| 0: [Identifier] c
# 85| 1: [ReservedWord] =
# 85| 2: [Identifier] b
# 86| 3: [ReservedWord] end
# 87| 19: [Identifier] c
# 26| [Comment] # A call to Kernel::Array; despite beginning with an upper-case character,
# 27| [Comment] # we don't consider this to be a constant access.
# 55| [Comment] # refers to ::ModuleA::FOURTY_FOUR
# 57| [Comment] # refers to ::ModuleA::ModuleB::ClassB::FOURTY_FOUR
# 76| [Comment] # Array constants
# 87| [Comment] # not recognised
control/cases.rb:
# 1| [Program] Program
# 2| 0: [Assignment] Assignment

View File

@@ -109,6 +109,12 @@ exprValue
| constants/constants.rb:63:19:63:20 | 45 | 45 | int |
| constants/constants.rb:65:19:65:35 | FOURTY_FIVE | 45 | int |
| constants/constants.rb:71:18:71:19 | 46 | 46 | int |
| constants/constants.rb:78:6:78:6 | 1 | 1 | int |
| constants/constants.rb:78:9:78:9 | 2 | 2 | int |
| constants/constants.rb:78:12:78:12 | 3 | 3 | int |
| constants/constants.rb:79:6:79:6 | 1 | 1 | int |
| constants/constants.rb:79:9:79:9 | 2 | 2 | int |
| constants/constants.rb:79:12:79:12 | 3 | 3 | int |
| control/cases.rb:2:5:2:5 | 0 | 0 | int |
| control/cases.rb:3:5:3:5 | 0 | 0 | int |
| control/cases.rb:4:5:4:5 | 0 | 0 | int |
@@ -1004,6 +1010,12 @@ exprCfgNodeValue
| constants/constants.rb:63:19:63:20 | 45 | 45 | int |
| constants/constants.rb:65:19:65:35 | FOURTY_FIVE | 45 | int |
| constants/constants.rb:71:18:71:19 | 46 | 46 | int |
| constants/constants.rb:78:6:78:6 | 1 | 1 | int |
| constants/constants.rb:78:9:78:9 | 2 | 2 | int |
| constants/constants.rb:78:12:78:12 | 3 | 3 | int |
| constants/constants.rb:79:6:79:6 | 1 | 1 | int |
| constants/constants.rb:79:9:79:9 | 2 | 2 | int |
| constants/constants.rb:79:12:79:12 | 3 | 3 | int |
| control/cases.rb:2:5:2:5 | 0 | 0 | int |
| control/cases.rb:3:5:3:5 | 0 | 0 | int |
| control/cases.rb:4:5:4:5 | 0 | 0 | int |

View File

@@ -61,6 +61,13 @@ constantAccess
| constants.rb:71:5:71:14 | FOURTY_SIX | write | FOURTY_SIX | ConstantAssignment |
| constants.rb:73:18:73:21 | Mod3 | read | Mod3 | ConstantReadAccess |
| constants.rb:73:18:73:33 | FOURTY_SIX | read | FOURTY_SIX | ConstantReadAccess |
| constants.rb:78:5:78:13 | Array | read | Array | ConstantReadAccess |
| constants.rb:79:1:79:1 | A | write | A | ConstantAssignment |
| constants.rb:79:5:79:13 | Array | read | Array | ConstantReadAccess |
| constants.rb:80:1:80:1 | B | write | B | ConstantAssignment |
| constants.rb:81:1:81:1 | C | write | C | ConstantAssignment |
| constants.rb:81:5:81:5 | A | read | A | ConstantReadAccess |
| constants.rb:82:5:82:5 | B | read | B | ConstantReadAccess |
getConst
| constants.rb:1:1:15:3 | ModuleA | CONST_B | constants.rb:6:15:6:23 | "const_b" |
| constants.rb:1:1:15:3 | ModuleA | FOURTY_FOUR | constants.rb:53:17:53:29 | "fourty-four" |
@@ -71,23 +78,41 @@ getConst
| constants.rb:54:3:58:5 | ModuleA::ModuleB::ClassB | FOURTY_ONE | constants.rb:48:18:48:19 | 41 |
| constants.rb:62:3:64:5 | Mod1::Mod3 | FOURTY_FIVE | constants.rb:63:19:63:20 | 45 |
| constants.rb:70:3:72:5 | Mod1::Mod3::Mod5 | FOURTY_SIX | constants.rb:71:18:71:19 | 46 |
| file://:0:0:0:0 | Object | A | constants.rb:79:5:79:13 | [...] |
| file://:0:0:0:0 | Object | B | constants.rb:80:5:80:5 | a |
| file://:0:0:0:0 | Object | C | constants.rb:81:5:81:5 | A |
| file://:0:0:0:0 | Object | GREETING | constants.rb:17:12:17:64 | ... + ... |
lookupConst
| constants.rb:1:1:15:3 | ModuleA | CONST_B | constants.rb:6:15:6:23 | "const_b" |
| constants.rb:1:1:15:3 | ModuleA | FOURTY_FOUR | constants.rb:53:17:53:29 | "fourty-four" |
| constants.rb:2:5:4:7 | ModuleA::ClassA | A | constants.rb:79:5:79:13 | [...] |
| constants.rb:2:5:4:7 | ModuleA::ClassA | B | constants.rb:80:5:80:5 | a |
| constants.rb:2:5:4:7 | ModuleA::ClassA | C | constants.rb:81:5:81:5 | A |
| constants.rb:2:5:4:7 | ModuleA::ClassA | CONST_A | constants.rb:3:19:3:27 | "const_a" |
| constants.rb:2:5:4:7 | ModuleA::ClassA | GREETING | constants.rb:17:12:17:64 | ... + ... |
| constants.rb:8:5:14:7 | ModuleA::ModuleB | MAX_SIZE | constants.rb:39:30:39:33 | 1024 |
| constants.rb:12:9:13:11 | ModuleA::ModuleB::ClassC | A | constants.rb:79:5:79:13 | [...] |
| constants.rb:12:9:13:11 | ModuleA::ModuleB::ClassC | B | constants.rb:80:5:80:5 | a |
| constants.rb:12:9:13:11 | ModuleA::ModuleB::ClassC | C | constants.rb:81:5:81:5 | A |
| constants.rb:12:9:13:11 | ModuleA::ModuleB::ClassC | GREETING | constants.rb:17:12:17:64 | ... + ... |
| constants.rb:31:1:33:3 | ModuleA::ClassD | A | constants.rb:79:5:79:13 | [...] |
| constants.rb:31:1:33:3 | ModuleA::ClassD | B | constants.rb:80:5:80:5 | a |
| constants.rb:31:1:33:3 | ModuleA::ClassD | C | constants.rb:81:5:81:5 | A |
| constants.rb:31:1:33:3 | ModuleA::ClassD | CONST_A | constants.rb:3:19:3:27 | "const_a" |
| constants.rb:31:1:33:3 | ModuleA::ClassD | FOURTY_TWO | constants.rb:32:16:32:17 | 42 |
| constants.rb:31:1:33:3 | ModuleA::ClassD | GREETING | constants.rb:17:12:17:64 | ... + ... |
| constants.rb:35:1:37:3 | ModuleA::ModuleC | FOURTY_THREE | constants.rb:36:18:36:19 | 43 |
| constants.rb:54:3:58:5 | ModuleA::ModuleB::ClassB | A | constants.rb:79:5:79:13 | [...] |
| constants.rb:54:3:58:5 | ModuleA::ModuleB::ClassB | B | constants.rb:80:5:80:5 | a |
| constants.rb:54:3:58:5 | ModuleA::ModuleB::ClassB | C | constants.rb:81:5:81:5 | A |
| constants.rb:54:3:58:5 | ModuleA::ModuleB::ClassB | FOURTY_FOUR | constants.rb:56:19:56:20 | 44 |
| constants.rb:54:3:58:5 | ModuleA::ModuleB::ClassB | FOURTY_ONE | constants.rb:48:18:48:19 | 41 |
| constants.rb:54:3:58:5 | ModuleA::ModuleB::ClassB | GREETING | constants.rb:17:12:17:64 | ... + ... |
| constants.rb:62:3:64:5 | Mod1::Mod3 | FOURTY_FIVE | constants.rb:63:19:63:20 | 45 |
| constants.rb:70:3:72:5 | Mod1::Mod3::Mod5 | FOURTY_SIX | constants.rb:71:18:71:19 | 46 |
| file://:0:0:0:0 | Object | A | constants.rb:79:5:79:13 | [...] |
| file://:0:0:0:0 | Object | B | constants.rb:80:5:80:5 | a |
| file://:0:0:0:0 | Object | C | constants.rb:81:5:81:5 | A |
| file://:0:0:0:0 | Object | GREETING | constants.rb:17:12:17:64 | ... + ... |
constantValue
| constants.rb:17:22:17:45 | CONST_A | constants.rb:3:19:3:27 | "const_a" |
@@ -101,6 +126,8 @@ constantValue
| constants.rb:57:21:57:31 | FOURTY_FOUR | constants.rb:53:17:53:29 | "fourty-four" |
| constants.rb:57:21:57:31 | FOURTY_FOUR | constants.rb:56:19:56:20 | 44 |
| constants.rb:65:19:65:35 | FOURTY_FIVE | constants.rb:63:19:63:20 | 45 |
| constants.rb:81:5:81:5 | A | constants.rb:79:5:79:13 | [...] |
| constants.rb:82:5:82:5 | B | constants.rb:80:5:80:5 | a |
constantWriteAccessQualifiedName
| constants.rb:1:1:15:3 | ModuleA | ModuleA |
| constants.rb:2:5:4:7 | ClassA | ModuleA::ClassA |
@@ -133,3 +160,14 @@ constantWriteAccessQualifiedName
| constants.rb:70:3:72:5 | Mod5 | Mod3::Mod5 |
| constants.rb:71:5:71:14 | FOURTY_SIX | Mod1::Mod3::Mod5::FOURTY_SIX |
| constants.rb:71:5:71:14 | FOURTY_SIX | Mod3::Mod5::FOURTY_SIX |
| constants.rb:79:1:79:1 | A | A |
| constants.rb:80:1:80:1 | B | B |
| constants.rb:81:1:81:1 | C | C |
arrayConstant
| constants.rb:20:13:20:37 | call to [] | constants.rb:20:13:20:37 | call to [] |
| constants.rb:78:5:78:13 | call to [] | constants.rb:78:5:78:13 | call to [] |
| constants.rb:79:5:79:13 | call to [] | constants.rb:79:5:79:13 | call to [] |
| constants.rb:80:5:80:5 | a | constants.rb:78:5:78:13 | call to [] |
| constants.rb:81:5:81:5 | A | constants.rb:79:5:79:13 | call to [] |
| constants.rb:82:5:82:5 | B | constants.rb:78:5:78:13 | call to [] |
| constants.rb:85:7:85:7 | b | constants.rb:78:5:78:13 | call to [] |

View File

@@ -1,5 +1,6 @@
import ruby
import codeql.ruby.ast.internal.Module as M
import codeql.ruby.ast.internal.Constant
query predicate constantAccess(ConstantAccess a, string kind, string name, string cls) {
(
@@ -20,3 +21,5 @@ query predicate constantValue(ConstantReadAccess a, Expr e) { e = a.getValue() }
query predicate constantWriteAccessQualifiedName(ConstantWriteAccess w, string qualifiedName) {
w.getAQualifiedName() = qualifiedName
}
query predicate arrayConstant = isArrayConstant/2;

View File

@@ -72,3 +72,16 @@ module Mod4
end
@@fourty_six = Mod3::FOURTY_SIX
end
# Array constants
a = [1, 2, 3]
A = [1, 2, 3]
B = a
C = A
b = B
if condition
c = b
end
c # not recognised

View File

@@ -0,0 +1,22 @@
WARNING: Type BarrierGuard has been deprecated and may be removed in future (barrier-guards.ql:8,3-15)
oldStyleBarrierGuards
| barrier-guards.rb:3:4:3:15 | ... == ... | barrier-guards.rb:4:5:4:7 | foo | barrier-guards.rb:3:4:3:6 | foo | true |
| barrier-guards.rb:9:4:9:24 | call to include? | barrier-guards.rb:10:5:10:7 | foo | barrier-guards.rb:9:21:9:23 | foo | true |
| barrier-guards.rb:15:4:15:15 | ... != ... | barrier-guards.rb:18:5:18:7 | foo | barrier-guards.rb:15:4:15:6 | foo | false |
| barrier-guards.rb:21:8:21:19 | ... == ... | barrier-guards.rb:24:5:24:7 | foo | barrier-guards.rb:21:8:21:10 | foo | true |
| barrier-guards.rb:27:8:27:19 | ... != ... | barrier-guards.rb:28:5:28:7 | foo | barrier-guards.rb:27:8:27:10 | foo | false |
| barrier-guards.rb:37:4:37:20 | call to include? | barrier-guards.rb:38:5:38:7 | foo | barrier-guards.rb:37:17:37:19 | foo | true |
| barrier-guards.rb:43:4:43:15 | ... == ... | barrier-guards.rb:45:9:45:11 | foo | barrier-guards.rb:43:4:43:6 | foo | true |
| barrier-guards.rb:70:4:70:21 | call to include? | barrier-guards.rb:71:5:71:7 | foo | barrier-guards.rb:70:18:70:20 | foo | true |
| barrier-guards.rb:82:4:82:25 | ... != ... | barrier-guards.rb:83:5:83:7 | foo | barrier-guards.rb:82:15:82:17 | foo | true |
newStyleBarrierGuards
| barrier-guards.rb:4:5:4:7 | foo |
| barrier-guards.rb:10:5:10:7 | foo |
| barrier-guards.rb:18:5:18:7 | foo |
| barrier-guards.rb:24:5:24:7 | foo |
| barrier-guards.rb:28:5:28:7 | foo |
| barrier-guards.rb:38:5:38:7 | foo |
| barrier-guards.rb:45:9:45:11 | foo |
| barrier-guards.rb:71:5:71:7 | foo |
| barrier-guards.rb:83:5:83:7 | foo |
| barrier-guards.rb:91:5:91:7 | foo |

View File

@@ -0,0 +1,16 @@
import codeql.ruby.dataflow.internal.DataFlowPublic
import codeql.ruby.dataflow.BarrierGuards
import codeql.ruby.controlflow.CfgNodes
import codeql.ruby.controlflow.ControlFlowGraph
import codeql.ruby.DataFlow
query predicate oldStyleBarrierGuards(
BarrierGuard g, DataFlow::Node guardedNode, ExprCfgNode expr, boolean branch
) {
g.checks(expr, branch) and guardedNode = g.getAGuardedNode()
}
query predicate newStyleBarrierGuards(DataFlow::Node n) {
n instanceof StringConstCompareBarrier or
n instanceof StringConstArrayInclusionCallBarrier
}

View File

@@ -0,0 +1,104 @@
foo = "foo"
if foo == "foo"
foo
else
foo
end
if ["foo"].include?(foo)
foo
else
foo
end
if foo != "foo"
foo
else
foo
end
unless foo == "foo"
foo
else
foo
end
unless foo != "foo"
foo
else
foo
end
foo
FOO = ["foo"]
if FOO.include?(foo)
foo
else
foo
end
if foo == "foo"
capture {
foo # guarded
}
end
if foo == "foo"
capture {
foo = "bar"
foo # not guarded
}
end
if foo == "foo"
my_lambda = -> () {
foo # not guarded
}
foo = "bar"
my_lambda()
end
foos = nil
foos = ["foo"]
bars = NotAnArray.new
if foos.include?(foo)
foo
else
foo
end
if bars.include?(foo)
foo
else
foo
end
if foos.index(foo) != nil
foo
else
foo
end
if foos.index(foo)r == nil
foo
else
foo
end
bars = ["bar"]
if condition
bars = nil
end
if bars.include?(foo)
foo
else
foo
end

View File

@@ -0,0 +1,219 @@
failures
edges
| pathname_flow.rb:4:10:4:33 | call to new : | pathname_flow.rb:5:10:5:11 | pn |
| pathname_flow.rb:4:23:4:32 | call to source : | pathname_flow.rb:4:10:4:33 | call to new : |
| pathname_flow.rb:9:7:9:30 | call to new : | pathname_flow.rb:11:8:11:12 | ... + ... |
| pathname_flow.rb:9:20:9:29 | call to source : | pathname_flow.rb:9:7:9:30 | call to new : |
| pathname_flow.rb:10:7:10:30 | call to new : | pathname_flow.rb:11:8:11:12 | ... + ... |
| pathname_flow.rb:10:20:10:29 | call to source : | pathname_flow.rb:10:7:10:30 | call to new : |
| pathname_flow.rb:15:8:15:31 | call to new : | pathname_flow.rb:16:8:16:9 | pn : |
| pathname_flow.rb:15:21:15:30 | call to source : | pathname_flow.rb:15:8:15:31 | call to new : |
| pathname_flow.rb:16:8:16:9 | pn : | pathname_flow.rb:16:8:16:17 | call to dirname |
| pathname_flow.rb:20:7:20:30 | call to new : | pathname_flow.rb:21:3:21:3 | a : |
| pathname_flow.rb:20:20:20:29 | call to source : | pathname_flow.rb:20:7:20:30 | call to new : |
| pathname_flow.rb:21:3:21:3 | a : | pathname_flow.rb:21:23:21:23 | x : |
| pathname_flow.rb:21:23:21:23 | x : | pathname_flow.rb:22:10:22:10 | x |
| pathname_flow.rb:27:7:27:30 | call to new : | pathname_flow.rb:28:8:28:8 | a : |
| pathname_flow.rb:27:20:27:29 | call to source : | pathname_flow.rb:27:7:27:30 | call to new : |
| pathname_flow.rb:28:8:28:8 | a : | pathname_flow.rb:28:8:28:22 | call to expand_path |
| pathname_flow.rb:32:7:32:30 | call to new : | pathname_flow.rb:35:8:35:8 | a : |
| pathname_flow.rb:32:20:32:29 | call to source : | pathname_flow.rb:32:7:32:30 | call to new : |
| pathname_flow.rb:34:7:34:30 | call to new : | pathname_flow.rb:35:18:35:18 | c : |
| pathname_flow.rb:34:20:34:29 | call to source : | pathname_flow.rb:34:7:34:30 | call to new : |
| pathname_flow.rb:35:8:35:8 | a : | pathname_flow.rb:35:8:35:19 | call to join |
| pathname_flow.rb:35:18:35:18 | c : | pathname_flow.rb:35:8:35:19 | call to join |
| pathname_flow.rb:39:7:39:30 | call to new : | pathname_flow.rb:40:8:40:8 | a : |
| pathname_flow.rb:39:20:39:29 | call to source : | pathname_flow.rb:39:7:39:30 | call to new : |
| pathname_flow.rb:40:8:40:8 | a : | pathname_flow.rb:40:8:40:17 | call to parent |
| pathname_flow.rb:44:7:44:30 | call to new : | pathname_flow.rb:45:8:45:8 | a : |
| pathname_flow.rb:44:20:44:29 | call to source : | pathname_flow.rb:44:7:44:30 | call to new : |
| pathname_flow.rb:45:8:45:8 | a : | pathname_flow.rb:45:8:45:19 | call to realpath |
| pathname_flow.rb:49:7:49:30 | call to new : | pathname_flow.rb:50:8:50:8 | a : |
| pathname_flow.rb:49:20:49:29 | call to source : | pathname_flow.rb:49:7:49:30 | call to new : |
| pathname_flow.rb:50:8:50:8 | a : | pathname_flow.rb:50:8:50:39 | call to relative_path_from |
| pathname_flow.rb:54:7:54:30 | call to new : | pathname_flow.rb:55:8:55:8 | a : |
| pathname_flow.rb:54:20:54:29 | call to source : | pathname_flow.rb:54:7:54:30 | call to new : |
| pathname_flow.rb:55:8:55:8 | a : | pathname_flow.rb:55:8:55:16 | call to to_path |
| pathname_flow.rb:59:7:59:30 | call to new : | pathname_flow.rb:60:8:60:8 | a : |
| pathname_flow.rb:59:20:59:29 | call to source : | pathname_flow.rb:59:7:59:30 | call to new : |
| pathname_flow.rb:60:8:60:8 | a : | pathname_flow.rb:60:8:60:13 | call to to_s |
| pathname_flow.rb:64:7:64:30 | call to new : | pathname_flow.rb:66:8:66:8 | b |
| pathname_flow.rb:64:20:64:29 | call to source : | pathname_flow.rb:64:7:64:30 | call to new : |
| pathname_flow.rb:70:7:70:30 | call to new : | pathname_flow.rb:72:8:72:8 | b |
| pathname_flow.rb:70:20:70:29 | call to source : | pathname_flow.rb:70:7:70:30 | call to new : |
| pathname_flow.rb:76:7:76:30 | call to new : | pathname_flow.rb:77:7:77:7 | a : |
| pathname_flow.rb:76:20:76:29 | call to source : | pathname_flow.rb:76:7:76:30 | call to new : |
| pathname_flow.rb:77:7:77:7 | a : | pathname_flow.rb:77:7:77:16 | call to basename : |
| pathname_flow.rb:77:7:77:16 | call to basename : | pathname_flow.rb:78:8:78:8 | b |
| pathname_flow.rb:82:7:82:30 | call to new : | pathname_flow.rb:83:7:83:7 | a : |
| pathname_flow.rb:82:20:82:29 | call to source : | pathname_flow.rb:82:7:82:30 | call to new : |
| pathname_flow.rb:83:7:83:7 | a : | pathname_flow.rb:83:7:83:17 | call to cleanpath : |
| pathname_flow.rb:83:7:83:17 | call to cleanpath : | pathname_flow.rb:84:8:84:8 | b |
| pathname_flow.rb:88:7:88:30 | call to new : | pathname_flow.rb:89:7:89:7 | a : |
| pathname_flow.rb:88:20:88:29 | call to source : | pathname_flow.rb:88:7:88:30 | call to new : |
| pathname_flow.rb:89:7:89:7 | a : | pathname_flow.rb:89:7:89:25 | call to sub : |
| pathname_flow.rb:89:7:89:25 | call to sub : | pathname_flow.rb:90:8:90:8 | b |
| pathname_flow.rb:94:7:94:30 | call to new : | pathname_flow.rb:95:7:95:7 | a : |
| pathname_flow.rb:94:20:94:29 | call to source : | pathname_flow.rb:94:7:94:30 | call to new : |
| pathname_flow.rb:95:7:95:7 | a : | pathname_flow.rb:95:7:95:23 | call to sub_ext : |
| pathname_flow.rb:95:7:95:23 | call to sub_ext : | pathname_flow.rb:96:8:96:8 | b |
| pathname_flow.rb:101:7:101:30 | call to new : | pathname_flow.rb:104:8:104:8 | b : |
| pathname_flow.rb:101:7:101:30 | call to new : | pathname_flow.rb:107:8:107:8 | c : |
| pathname_flow.rb:101:7:101:30 | call to new : | pathname_flow.rb:109:7:109:7 | a : |
| pathname_flow.rb:101:7:101:30 | call to new : | pathname_flow.rb:112:7:112:7 | a : |
| pathname_flow.rb:101:7:101:30 | call to new : | pathname_flow.rb:115:7:115:7 | a : |
| pathname_flow.rb:101:7:101:30 | call to new : | pathname_flow.rb:118:7:118:7 | a : |
| pathname_flow.rb:101:7:101:30 | call to new : | pathname_flow.rb:121:7:121:7 | a : |
| pathname_flow.rb:101:7:101:30 | call to new : | pathname_flow.rb:124:7:124:7 | a : |
| pathname_flow.rb:101:7:101:30 | call to new : | pathname_flow.rb:127:7:127:7 | a : |
| pathname_flow.rb:101:7:101:30 | call to new : | pathname_flow.rb:130:7:130:7 | a : |
| pathname_flow.rb:101:7:101:30 | call to new : | pathname_flow.rb:133:7:133:7 | a : |
| pathname_flow.rb:101:20:101:29 | call to source : | pathname_flow.rb:101:7:101:30 | call to new : |
| pathname_flow.rb:104:8:104:8 | b : | pathname_flow.rb:104:8:104:17 | call to realpath |
| pathname_flow.rb:107:8:107:8 | c : | pathname_flow.rb:107:8:107:17 | call to realpath |
| pathname_flow.rb:109:7:109:7 | a : | pathname_flow.rb:109:7:109:16 | call to basename : |
| pathname_flow.rb:109:7:109:16 | call to basename : | pathname_flow.rb:110:8:110:8 | d : |
| pathname_flow.rb:110:8:110:8 | d : | pathname_flow.rb:110:8:110:17 | call to realpath |
| pathname_flow.rb:112:7:112:7 | a : | pathname_flow.rb:112:7:112:17 | call to cleanpath : |
| pathname_flow.rb:112:7:112:17 | call to cleanpath : | pathname_flow.rb:113:8:113:8 | e : |
| pathname_flow.rb:113:8:113:8 | e : | pathname_flow.rb:113:8:113:17 | call to realpath |
| pathname_flow.rb:115:7:115:7 | a : | pathname_flow.rb:115:7:115:19 | call to expand_path : |
| pathname_flow.rb:115:7:115:19 | call to expand_path : | pathname_flow.rb:116:8:116:8 | f : |
| pathname_flow.rb:116:8:116:8 | f : | pathname_flow.rb:116:8:116:17 | call to realpath |
| pathname_flow.rb:118:7:118:7 | a : | pathname_flow.rb:118:7:118:19 | call to join : |
| pathname_flow.rb:118:7:118:19 | call to join : | pathname_flow.rb:119:8:119:8 | g : |
| pathname_flow.rb:119:8:119:8 | g : | pathname_flow.rb:119:8:119:17 | call to realpath |
| pathname_flow.rb:121:7:121:7 | a : | pathname_flow.rb:121:7:121:16 | call to realpath : |
| pathname_flow.rb:121:7:121:16 | call to realpath : | pathname_flow.rb:122:8:122:8 | h : |
| pathname_flow.rb:122:8:122:8 | h : | pathname_flow.rb:122:8:122:17 | call to realpath |
| pathname_flow.rb:124:7:124:7 | a : | pathname_flow.rb:124:7:124:38 | call to relative_path_from : |
| pathname_flow.rb:124:7:124:38 | call to relative_path_from : | pathname_flow.rb:125:8:125:8 | i : |
| pathname_flow.rb:125:8:125:8 | i : | pathname_flow.rb:125:8:125:17 | call to realpath |
| pathname_flow.rb:127:7:127:7 | a : | pathname_flow.rb:127:7:127:25 | call to sub : |
| pathname_flow.rb:127:7:127:25 | call to sub : | pathname_flow.rb:128:8:128:8 | j : |
| pathname_flow.rb:128:8:128:8 | j : | pathname_flow.rb:128:8:128:17 | call to realpath |
| pathname_flow.rb:130:7:130:7 | a : | pathname_flow.rb:130:7:130:23 | call to sub_ext : |
| pathname_flow.rb:130:7:130:23 | call to sub_ext : | pathname_flow.rb:131:8:131:8 | k : |
| pathname_flow.rb:131:8:131:8 | k : | pathname_flow.rb:131:8:131:17 | call to realpath |
| pathname_flow.rb:133:7:133:7 | a : | pathname_flow.rb:133:7:133:15 | call to to_path : |
| pathname_flow.rb:133:7:133:15 | call to to_path : | pathname_flow.rb:134:8:134:8 | l : |
| pathname_flow.rb:134:8:134:8 | l : | pathname_flow.rb:134:8:134:17 | call to realpath |
nodes
| pathname_flow.rb:4:10:4:33 | call to new : | semmle.label | call to new : |
| pathname_flow.rb:4:23:4:32 | call to source : | semmle.label | call to source : |
| pathname_flow.rb:5:10:5:11 | pn | semmle.label | pn |
| pathname_flow.rb:9:7:9:30 | call to new : | semmle.label | call to new : |
| pathname_flow.rb:9:20:9:29 | call to source : | semmle.label | call to source : |
| pathname_flow.rb:10:7:10:30 | call to new : | semmle.label | call to new : |
| pathname_flow.rb:10:20:10:29 | call to source : | semmle.label | call to source : |
| pathname_flow.rb:11:8:11:12 | ... + ... | semmle.label | ... + ... |
| pathname_flow.rb:15:8:15:31 | call to new : | semmle.label | call to new : |
| pathname_flow.rb:15:21:15:30 | call to source : | semmle.label | call to source : |
| pathname_flow.rb:16:8:16:9 | pn : | semmle.label | pn : |
| pathname_flow.rb:16:8:16:17 | call to dirname | semmle.label | call to dirname |
| pathname_flow.rb:20:7:20:30 | call to new : | semmle.label | call to new : |
| pathname_flow.rb:20:20:20:29 | call to source : | semmle.label | call to source : |
| pathname_flow.rb:21:3:21:3 | a : | semmle.label | a : |
| pathname_flow.rb:21:23:21:23 | x : | semmle.label | x : |
| pathname_flow.rb:22:10:22:10 | x | semmle.label | x |
| pathname_flow.rb:27:7:27:30 | call to new : | semmle.label | call to new : |
| pathname_flow.rb:27:20:27:29 | call to source : | semmle.label | call to source : |
| pathname_flow.rb:28:8:28:8 | a : | semmle.label | a : |
| pathname_flow.rb:28:8:28:22 | call to expand_path | semmle.label | call to expand_path |
| pathname_flow.rb:32:7:32:30 | call to new : | semmle.label | call to new : |
| pathname_flow.rb:32:20:32:29 | call to source : | semmle.label | call to source : |
| pathname_flow.rb:34:7:34:30 | call to new : | semmle.label | call to new : |
| pathname_flow.rb:34:20:34:29 | call to source : | semmle.label | call to source : |
| pathname_flow.rb:35:8:35:8 | a : | semmle.label | a : |
| pathname_flow.rb:35:8:35:19 | call to join | semmle.label | call to join |
| pathname_flow.rb:35:18:35:18 | c : | semmle.label | c : |
| pathname_flow.rb:39:7:39:30 | call to new : | semmle.label | call to new : |
| pathname_flow.rb:39:20:39:29 | call to source : | semmle.label | call to source : |
| pathname_flow.rb:40:8:40:8 | a : | semmle.label | a : |
| pathname_flow.rb:40:8:40:17 | call to parent | semmle.label | call to parent |
| pathname_flow.rb:44:7:44:30 | call to new : | semmle.label | call to new : |
| pathname_flow.rb:44:20:44:29 | call to source : | semmle.label | call to source : |
| pathname_flow.rb:45:8:45:8 | a : | semmle.label | a : |
| pathname_flow.rb:45:8:45:19 | call to realpath | semmle.label | call to realpath |
| pathname_flow.rb:49:7:49:30 | call to new : | semmle.label | call to new : |
| pathname_flow.rb:49:20:49:29 | call to source : | semmle.label | call to source : |
| pathname_flow.rb:50:8:50:8 | a : | semmle.label | a : |
| pathname_flow.rb:50:8:50:39 | call to relative_path_from | semmle.label | call to relative_path_from |
| pathname_flow.rb:54:7:54:30 | call to new : | semmle.label | call to new : |
| pathname_flow.rb:54:20:54:29 | call to source : | semmle.label | call to source : |
| pathname_flow.rb:55:8:55:8 | a : | semmle.label | a : |
| pathname_flow.rb:55:8:55:16 | call to to_path | semmle.label | call to to_path |
| pathname_flow.rb:59:7:59:30 | call to new : | semmle.label | call to new : |
| pathname_flow.rb:59:20:59:29 | call to source : | semmle.label | call to source : |
| pathname_flow.rb:60:8:60:8 | a : | semmle.label | a : |
| pathname_flow.rb:60:8:60:13 | call to to_s | semmle.label | call to to_s |
| pathname_flow.rb:64:7:64:30 | call to new : | semmle.label | call to new : |
| pathname_flow.rb:64:20:64:29 | call to source : | semmle.label | call to source : |
| pathname_flow.rb:66:8:66:8 | b | semmle.label | b |
| pathname_flow.rb:70:7:70:30 | call to new : | semmle.label | call to new : |
| pathname_flow.rb:70:20:70:29 | call to source : | semmle.label | call to source : |
| pathname_flow.rb:72:8:72:8 | b | semmle.label | b |
| pathname_flow.rb:76:7:76:30 | call to new : | semmle.label | call to new : |
| pathname_flow.rb:76:20:76:29 | call to source : | semmle.label | call to source : |
| pathname_flow.rb:77:7:77:7 | a : | semmle.label | a : |
| pathname_flow.rb:77:7:77:16 | call to basename : | semmle.label | call to basename : |
| pathname_flow.rb:78:8:78:8 | b | semmle.label | b |
| pathname_flow.rb:82:7:82:30 | call to new : | semmle.label | call to new : |
| pathname_flow.rb:82:20:82:29 | call to source : | semmle.label | call to source : |
| pathname_flow.rb:83:7:83:7 | a : | semmle.label | a : |
| pathname_flow.rb:83:7:83:17 | call to cleanpath : | semmle.label | call to cleanpath : |
| pathname_flow.rb:84:8:84:8 | b | semmle.label | b |
| pathname_flow.rb:88:7:88:30 | call to new : | semmle.label | call to new : |
| pathname_flow.rb:88:20:88:29 | call to source : | semmle.label | call to source : |
| pathname_flow.rb:89:7:89:7 | a : | semmle.label | a : |
| pathname_flow.rb:89:7:89:25 | call to sub : | semmle.label | call to sub : |
| pathname_flow.rb:90:8:90:8 | b | semmle.label | b |
| pathname_flow.rb:94:7:94:30 | call to new : | semmle.label | call to new : |
| pathname_flow.rb:94:20:94:29 | call to source : | semmle.label | call to source : |
| pathname_flow.rb:95:7:95:7 | a : | semmle.label | a : |
| pathname_flow.rb:95:7:95:23 | call to sub_ext : | semmle.label | call to sub_ext : |
| pathname_flow.rb:96:8:96:8 | b | semmle.label | b |
| pathname_flow.rb:101:7:101:30 | call to new : | semmle.label | call to new : |
| pathname_flow.rb:101:20:101:29 | call to source : | semmle.label | call to source : |
| pathname_flow.rb:104:8:104:8 | b : | semmle.label | b : |
| pathname_flow.rb:104:8:104:17 | call to realpath | semmle.label | call to realpath |
| pathname_flow.rb:107:8:107:8 | c : | semmle.label | c : |
| pathname_flow.rb:107:8:107:17 | call to realpath | semmle.label | call to realpath |
| pathname_flow.rb:109:7:109:7 | a : | semmle.label | a : |
| pathname_flow.rb:109:7:109:16 | call to basename : | semmle.label | call to basename : |
| pathname_flow.rb:110:8:110:8 | d : | semmle.label | d : |
| pathname_flow.rb:110:8:110:17 | call to realpath | semmle.label | call to realpath |
| pathname_flow.rb:112:7:112:7 | a : | semmle.label | a : |
| pathname_flow.rb:112:7:112:17 | call to cleanpath : | semmle.label | call to cleanpath : |
| pathname_flow.rb:113:8:113:8 | e : | semmle.label | e : |
| pathname_flow.rb:113:8:113:17 | call to realpath | semmle.label | call to realpath |
| pathname_flow.rb:115:7:115:7 | a : | semmle.label | a : |
| pathname_flow.rb:115:7:115:19 | call to expand_path : | semmle.label | call to expand_path : |
| pathname_flow.rb:116:8:116:8 | f : | semmle.label | f : |
| pathname_flow.rb:116:8:116:17 | call to realpath | semmle.label | call to realpath |
| pathname_flow.rb:118:7:118:7 | a : | semmle.label | a : |
| pathname_flow.rb:118:7:118:19 | call to join : | semmle.label | call to join : |
| pathname_flow.rb:119:8:119:8 | g : | semmle.label | g : |
| pathname_flow.rb:119:8:119:17 | call to realpath | semmle.label | call to realpath |
| pathname_flow.rb:121:7:121:7 | a : | semmle.label | a : |
| pathname_flow.rb:121:7:121:16 | call to realpath : | semmle.label | call to realpath : |
| pathname_flow.rb:122:8:122:8 | h : | semmle.label | h : |
| pathname_flow.rb:122:8:122:17 | call to realpath | semmle.label | call to realpath |
| pathname_flow.rb:124:7:124:7 | a : | semmle.label | a : |
| pathname_flow.rb:124:7:124:38 | call to relative_path_from : | semmle.label | call to relative_path_from : |
| pathname_flow.rb:125:8:125:8 | i : | semmle.label | i : |
| pathname_flow.rb:125:8:125:17 | call to realpath | semmle.label | call to realpath |
| pathname_flow.rb:127:7:127:7 | a : | semmle.label | a : |
| pathname_flow.rb:127:7:127:25 | call to sub : | semmle.label | call to sub : |
| pathname_flow.rb:128:8:128:8 | j : | semmle.label | j : |
| pathname_flow.rb:128:8:128:17 | call to realpath | semmle.label | call to realpath |
| pathname_flow.rb:130:7:130:7 | a : | semmle.label | a : |
| pathname_flow.rb:130:7:130:23 | call to sub_ext : | semmle.label | call to sub_ext : |
| pathname_flow.rb:131:8:131:8 | k : | semmle.label | k : |
| pathname_flow.rb:131:8:131:17 | call to realpath | semmle.label | call to realpath |
| pathname_flow.rb:133:7:133:7 | a : | semmle.label | a : |
| pathname_flow.rb:133:7:133:15 | call to to_path : | semmle.label | call to to_path : |
| pathname_flow.rb:134:8:134:8 | l : | semmle.label | l : |
| pathname_flow.rb:134:8:134:17 | call to realpath | semmle.label | call to realpath |
subpaths
#select

View File

@@ -0,0 +1,11 @@
/**
* @kind path-problem
*/
import ruby
import TestUtilities.InlineFlowTest
import PathGraph
from DataFlow::PathNode source, DataFlow::PathNode sink, DefaultValueFlowConf conf
where conf.hasFlowPath(source, sink)
select sink, source, sink, "$@", source, source.toString()

View File

@@ -0,0 +1,135 @@
require 'pathname'
def m_new
pn = Pathname.new(source 'a')
sink pn # $ hasTaintFlow=a
end
def m_plus
a = Pathname.new(source 'a')
b = Pathname.new(source 'b')
sink(a + b) # $ hasTaintFlow=a $ hasTaintFlow=b
end
def m_dirname
pn = Pathname.new(source 'a')
sink pn.dirname # $ hasTaintFlow=a
end
def m_each_filename
a = Pathname.new(source 'a')
a.each_filename do |x|
sink x # $ hasTaintFlow=a
end
end
def m_expand_path
a = Pathname.new(source 'a')
sink a.expand_path() # $ hasTaintFlow=a
end
def m_join
a = Pathname.new(source 'a')
b = Pathname.new('foo')
c = Pathname.new(source 'c')
sink a.join(b, c) # $ hasTaintFlow=a $ hasTaintFlow=c
end
def m_parent
a = Pathname.new(source 'a')
sink a.parent() # $ hasTaintFlow=a
end
def m_realpath
a = Pathname.new(source 'a')
sink a.realpath() # $ hasTaintFlow=a
end
def m_relative_path_from
a = Pathname.new(source 'a')
sink a.relative_path_from('/foo/bar') # $ hasTaintFlow=a
end
def m_to_path
a = Pathname.new(source 'a')
sink a.to_path # $ hasTaintFlow=a
end
def m_to_s
a = Pathname.new(source 'a')
sink a.to_s # $ hasTaintFlow=a
end
def m_plus
a = Pathname.new(source 'a')
b = a + 'foo'
sink b # $ hasTaintFlow=a
end
def m_slash
a = Pathname.new(source 'a')
b = a / 'foo'
sink b # $ hasTaintFlow=a
end
def m_basename
a = Pathname.new(source 'a')
b = a.basename
sink b # $ hasTaintFlow=a
end
def m_cleanpath
a = Pathname.new(source 'a')
b = a.cleanpath
sink b # $ hasTaintFlow=a
end
def m_sub
a = Pathname.new(source 'a')
b = a.sub('foo', 'bar')
sink b # $ hasTaintFlow=a
end
def m_sub_ext
a = Pathname.new(source 'a')
b = a.sub_ext('.txt')
sink b # $ hasTaintFlow=a
end
# Test flow through intermediate pathnames
def intermediate_pathnames
a = Pathname.new(source 'a')
b = a + 'foo'
sink b.realpath # $ hasTaintFlow=a
c = a / 'foo'
sink c.realpath # $ hasTaintFlow=a
d = a.basename
sink d.realpath # $ hasTaintFlow=a
e = a.cleanpath
sink e.realpath # $ hasTaintFlow=a
f = a.expand_path
sink f.realpath # $ hasTaintFlow=a
g = a.join('foo')
sink g.realpath # $ hasTaintFlow=a
h = a.realpath
sink h.realpath # $ hasTaintFlow=a
i = a.relative_path_from('/foo/bar')
sink i.realpath # $ hasTaintFlow=a
j = a.sub('foo', 'bar')
sink j.realpath # $ hasTaintFlow=a
k = a.sub_ext('.txt')
sink k.realpath # $ hasTaintFlow=a
l = a.to_path
sink l.realpath # $ hasTaintFlow=a
end

View File

@@ -0,0 +1,134 @@
pathnameInstances
| Pathname.rb:2:1:2:33 | ... = ... |
| Pathname.rb:2:1:2:33 | ... = ... |
| Pathname.rb:2:12:2:33 | call to new |
| Pathname.rb:3:1:3:20 | ... = ... |
| Pathname.rb:3:13:3:20 | foo_path |
| Pathname.rb:4:1:4:8 | foo_path |
| Pathname.rb:6:1:6:29 | ... = ... |
| Pathname.rb:6:1:6:29 | ... = ... |
| Pathname.rb:6:12:6:29 | call to new |
| Pathname.rb:9:1:9:21 | ... = ... |
| Pathname.rb:9:1:9:21 | ... = ... |
| Pathname.rb:9:8:9:21 | call to getwd |
| Pathname.rb:10:1:10:21 | ... = ... |
| Pathname.rb:10:7:10:10 | pwd1 |
| Pathname.rb:10:7:10:21 | ... + ... |
| Pathname.rb:10:14:10:21 | foo_path |
| Pathname.rb:11:1:11:21 | ... = ... |
| Pathname.rb:11:1:11:21 | ... = ... |
| Pathname.rb:11:7:11:10 | pwd1 |
| Pathname.rb:11:7:11:21 | ... / ... |
| Pathname.rb:11:14:11:21 | bar_path |
| Pathname.rb:12:1:12:19 | ... = ... |
| Pathname.rb:12:7:12:10 | pwd1 |
| Pathname.rb:12:7:12:19 | call to basename |
| Pathname.rb:13:1:13:46 | ... = ... |
| Pathname.rb:13:7:13:36 | call to new |
| Pathname.rb:13:7:13:46 | call to cleanpath |
| Pathname.rb:14:1:14:26 | ... = ... |
| Pathname.rb:14:7:14:14 | foo_path |
| Pathname.rb:14:7:14:26 | call to expand_path |
| Pathname.rb:15:1:15:39 | ... = ... |
| Pathname.rb:15:7:15:10 | pwd1 |
| Pathname.rb:15:7:15:39 | call to join |
| Pathname.rb:16:1:16:23 | ... = ... |
| Pathname.rb:16:7:16:14 | foo_path |
| Pathname.rb:16:7:16:23 | call to realpath |
| Pathname.rb:17:1:17:59 | ... = ... |
| Pathname.rb:17:7:17:33 | call to new |
| Pathname.rb:17:7:17:59 | call to relative_path_from |
| Pathname.rb:18:1:18:33 | ... = ... |
| Pathname.rb:18:1:18:33 | ... = ... |
| Pathname.rb:18:7:18:10 | pwd1 |
| Pathname.rb:18:7:18:33 | call to sub |
| Pathname.rb:19:1:19:29 | ... = ... |
| Pathname.rb:19:7:19:14 | foo_path |
| Pathname.rb:19:7:19:29 | call to sub_ext |
| Pathname.rb:20:1:20:22 | ... = ... |
| Pathname.rb:20:7:20:14 | foo_path |
| Pathname.rb:20:7:20:22 | call to to_path |
| Pathname.rb:23:14:23:21 | foo_path |
| Pathname.rb:26:12:26:19 | foo_path |
| Pathname.rb:28:11:28:14 | pwd1 |
| Pathname.rb:32:12:32:19 | foo_path |
| Pathname.rb:35:1:35:8 | foo_path |
| Pathname.rb:38:1:38:8 | foo_path |
| Pathname.rb:39:12:39:19 | foo_path |
| Pathname.rb:41:1:41:3 | p08 |
| Pathname.rb:42:1:42:3 | p01 |
fileSystemAccesses
| Pathname.rb:26:12:26:24 | call to open | Pathname.rb:26:12:26:19 | foo_path |
| Pathname.rb:28:11:28:22 | call to opendir | Pathname.rb:28:11:28:14 | pwd1 |
| Pathname.rb:32:12:32:24 | call to read | Pathname.rb:32:12:32:19 | foo_path |
| Pathname.rb:35:1:35:23 | call to write | Pathname.rb:35:1:35:8 | foo_path |
| Pathname.rb:39:12:39:34 | call to open | Pathname.rb:39:12:39:19 | foo_path |
fileNameSources
| Pathname.rb:2:1:2:33 | ... = ... |
| Pathname.rb:2:1:2:33 | ... = ... |
| Pathname.rb:2:12:2:33 | call to new |
| Pathname.rb:3:1:3:20 | ... = ... |
| Pathname.rb:3:13:3:20 | foo_path |
| Pathname.rb:4:1:4:8 | foo_path |
| Pathname.rb:6:1:6:29 | ... = ... |
| Pathname.rb:6:1:6:29 | ... = ... |
| Pathname.rb:6:12:6:29 | call to new |
| Pathname.rb:9:1:9:21 | ... = ... |
| Pathname.rb:9:1:9:21 | ... = ... |
| Pathname.rb:9:8:9:21 | call to getwd |
| Pathname.rb:10:1:10:21 | ... = ... |
| Pathname.rb:10:7:10:10 | pwd1 |
| Pathname.rb:10:7:10:21 | ... + ... |
| Pathname.rb:10:14:10:21 | foo_path |
| Pathname.rb:11:1:11:21 | ... = ... |
| Pathname.rb:11:1:11:21 | ... = ... |
| Pathname.rb:11:7:11:10 | pwd1 |
| Pathname.rb:11:7:11:21 | ... / ... |
| Pathname.rb:11:14:11:21 | bar_path |
| Pathname.rb:12:1:12:19 | ... = ... |
| Pathname.rb:12:7:12:10 | pwd1 |
| Pathname.rb:12:7:12:19 | call to basename |
| Pathname.rb:13:1:13:46 | ... = ... |
| Pathname.rb:13:7:13:36 | call to new |
| Pathname.rb:13:7:13:46 | call to cleanpath |
| Pathname.rb:14:1:14:26 | ... = ... |
| Pathname.rb:14:7:14:14 | foo_path |
| Pathname.rb:14:7:14:26 | call to expand_path |
| Pathname.rb:15:1:15:39 | ... = ... |
| Pathname.rb:15:7:15:10 | pwd1 |
| Pathname.rb:15:7:15:39 | call to join |
| Pathname.rb:16:1:16:23 | ... = ... |
| Pathname.rb:16:7:16:14 | foo_path |
| Pathname.rb:16:7:16:23 | call to realpath |
| Pathname.rb:17:1:17:59 | ... = ... |
| Pathname.rb:17:7:17:33 | call to new |
| Pathname.rb:17:7:17:59 | call to relative_path_from |
| Pathname.rb:18:1:18:33 | ... = ... |
| Pathname.rb:18:1:18:33 | ... = ... |
| Pathname.rb:18:7:18:10 | pwd1 |
| Pathname.rb:18:7:18:33 | call to sub |
| Pathname.rb:19:1:19:29 | ... = ... |
| Pathname.rb:19:7:19:14 | foo_path |
| Pathname.rb:19:7:19:29 | call to sub_ext |
| Pathname.rb:20:1:20:22 | ... = ... |
| Pathname.rb:20:7:20:14 | foo_path |
| Pathname.rb:20:7:20:22 | call to to_path |
| Pathname.rb:23:14:23:21 | foo_path |
| Pathname.rb:23:14:23:26 | call to to_s |
| Pathname.rb:26:12:26:19 | foo_path |
| Pathname.rb:28:11:28:14 | pwd1 |
| Pathname.rb:32:12:32:19 | foo_path |
| Pathname.rb:35:1:35:8 | foo_path |
| Pathname.rb:38:1:38:8 | foo_path |
| Pathname.rb:39:12:39:19 | foo_path |
| Pathname.rb:41:1:41:3 | p08 |
| Pathname.rb:42:1:42:3 | p01 |
fileSystemReadAccesses
| Pathname.rb:32:12:32:24 | call to read | Pathname.rb:32:12:32:24 | call to read |
fileSystemWriteAccesses
| Pathname.rb:35:1:35:23 | call to write | Pathname.rb:35:16:35:23 | "output" |
fileSystemPermissionModifications
| Pathname.rb:38:1:38:19 | call to chmod | Pathname.rb:38:16:38:19 | 0644 |
| Pathname.rb:39:12:39:34 | call to open | Pathname.rb:39:31:39:34 | 0666 |
| Pathname.rb:41:1:41:14 | call to mkdir | Pathname.rb:41:11:41:14 | 0755 |
| Pathname.rb:42:1:42:22 | call to mkpath | Pathname.rb:42:18:42:21 | 0644 |

View File

@@ -0,0 +1,26 @@
private import ruby
private import codeql.ruby.Concepts
private import codeql.ruby.DataFlow
private import codeql.ruby.frameworks.stdlib.Pathname
query predicate pathnameInstances(Pathname::PathnameInstance i) { any() }
query predicate fileSystemAccesses(FileSystemAccess a, DataFlow::Node p) {
p = a.getAPathArgument()
}
query predicate fileNameSources(FileNameSource s) { any() }
query predicate fileSystemReadAccesses(FileSystemReadAccess a, DataFlow::Node d) {
d = a.getADataNode()
}
query predicate fileSystemWriteAccesses(FileSystemWriteAccess a, DataFlow::Node d) {
d = a.getADataNode()
}
query predicate fileSystemPermissionModifications(
FileSystemPermissionModification m, DataFlow::Node p
) {
p = m.getAPermissionNode()
}

View File

@@ -0,0 +1,42 @@
foo_path = Pathname.new "foo.txt"
foo_path2 = foo_path
foo_path
bar_path = Pathname.new 'bar'
# All these calls return new `Pathname` instances
pwd1 = Pathname.getwd
p00 = pwd1 + foo_path
p01 = pwd1 / bar_path
p02 = pwd1.basename
p03 = Pathname.new('bar/../baz.txt').cleanpath
p04 = foo_path.expand_path
p05 = pwd1.join 'bar', 'baz', 'qux.txt'
p06 = foo_path.realpath
p07 = Pathname.new('foo/bar.txt').relative_path_from('foo')
p08 = pwd1.sub 'wibble', 'wobble'
p09 = foo_path.sub_ext '.log'
p10 = foo_path.to_path
# `Pathname#to_s` returns a string that we consider to be a filename source.
foo_string = foo_path.to_s
# File-system accesses
foo_file = foo_path.open
foo_file.close
pwd_dir = pwd1.opendir
pwd_dir.close
# Read from a file
foo_data = foo_path.read
# Write to a file
foo_path.write 'output'
# Permission modifications
foo_path.chmod 0644
foo_file = foo_path.open 'w', 0666
foo_file.close
p08.mkdir 0755
p01.mkpath(mode: 0644)

View File

@@ -274,6 +274,16 @@ EnumCaseDecl:
IfConfigDecl:
_extends: Decl
_children:
clauses: IfConfigClause*
IfConfigClause:
_extends: Locatable
_children:
condition: Expr?
elements: AstNode*
is_active: predicate
_dir: decl
ImportDecl:
_extends: Decl
@@ -550,7 +560,6 @@ TypeExpr:
UnresolvedDeclRefExpr:
_extends: Expr
name: string?
_pragma: qltest_skip # we should really never extract these
UnresolvedDotExpr:
_extends: Expr
@@ -565,7 +574,8 @@ UnresolvedMemberExpr:
UnresolvedPatternExpr:
_extends: Expr
_pragma: qltest_skip # we should really never extract these
_children:
sub_pattern: Pattern
UnresolvedSpecializeExpr:
_extends: Expr

View File

@@ -93,6 +93,10 @@ class SwiftDispatcher {
return fetchLabelFromUnion<AstNodeTag>(node);
}
TrapLabel<IfConfigClauseTag> fetchLabel(const swift::IfConfigClause& clause) {
return fetchLabel(&clause);
}
TrapLabel<ConditionElementTag> fetchLabel(const swift::StmtConditionElement& element) {
return fetchLabel(&element);
}
@@ -140,6 +144,15 @@ class SwiftDispatcher {
attachLocation(locatable->getStartLoc(), locatable->getEndLoc(), locatableLabel);
}
void attachLocation(const swift::IfConfigClause* clause, TrapLabel<LocatableTag> locatableLabel) {
attachLocation(clause->Loc, clause->Loc, locatableLabel);
}
// Emits a Location TRAP entry and attaches it to a `Locatable` trap label for a given `SourceLoc`
void attachLocation(swift::SourceLoc loc, TrapLabel<LocatableTag> locatableLabel) {
attachLocation(loc, loc, locatableLabel);
}
// Emits a Location TRAP entry for a list of swift entities and attaches it to a `Locatable` trap
// label
template <typename Locatable>
@@ -215,7 +228,8 @@ class SwiftDispatcher {
swift::Expr,
swift::Pattern,
swift::TypeRepr,
swift::TypeBase>;
swift::TypeBase,
swift::IfConfigClause>;
void attachLocation(swift::SourceLoc start,
swift::SourceLoc end,
@@ -269,9 +283,11 @@ class SwiftDispatcher {
return realPath.str().str();
}
// TODO: The following methods are supposed to redirect TRAP emission to correpsonding visitors,
// which are to be introduced in follow-up PRs
// TODO: for const correctness these should consistently be `const` (and maybe const references
// as we don't expect `nullptr` here. However `swift::ASTVisitor` and `swift::TypeVisitor` do not
// accept const pointers
virtual void visit(swift::Decl* decl) = 0;
virtual void visit(const swift::IfConfigClause* clause) = 0;
virtual void visit(swift::Stmt* stmt) = 0;
virtual void visit(const swift::StmtCondition* cond) = 0;
virtual void visit(const swift::StmtConditionElement* cond) = 0;

View File

@@ -49,6 +49,7 @@ MAP_TAG(Argument);
#include <swift/AST/ExprNodes.def>
MAP_TAG(Decl);
MAP_TAG(IfConfigClause);
#define ABSTRACT_DECL(CLASS, PARENT) MAP_SUBTAG(CLASS##Decl, PARENT)
#define DECL(CLASS, PARENT) ABSTRACT_DECL(CLASS, PARENT)
#include <swift/AST/DeclNodes.def>

View File

@@ -358,4 +358,18 @@ void DeclVisitor::fillAbstractStorageDecl(const swift::AbstractStorageDecl& decl
fillValueDecl(decl, entry);
}
codeql::IfConfigDecl DeclVisitor::translateIfConfigDecl(const swift::IfConfigDecl& decl) {
auto entry = dispatcher_.createEntry(decl);
entry.clauses = dispatcher_.fetchRepeatedLabels(decl.getClauses());
return entry;
}
codeql::IfConfigClause DeclVisitor::translateIfConfigClause(const swift::IfConfigClause& clause) {
auto entry = dispatcher_.createEntry(clause);
entry.condition = dispatcher_.fetchOptionalLabel(clause.Cond);
entry.elements = dispatcher_.fetchRepeatedLabels(clause.Elements);
entry.is_active = clause.isActive;
return entry;
}
} // namespace codeql

View File

@@ -14,6 +14,11 @@ namespace codeql {
class DeclVisitor : public AstVisitorBase<DeclVisitor> {
public:
using AstVisitorBase<DeclVisitor>::AstVisitorBase;
using AstVisitorBase<DeclVisitor>::visit;
void visit(const swift::IfConfigClause* clause) {
dispatcher_.emit(translateIfConfigClause(*clause));
}
std::variant<codeql::ConcreteFuncDecl, codeql::ConcreteFuncDeclsTrap> translateFuncDecl(
const swift::FuncDecl& decl);
@@ -52,6 +57,8 @@ class DeclVisitor : public AstVisitorBase<DeclVisitor> {
codeql::ExtensionDecl translateExtensionDecl(const swift::ExtensionDecl& decl);
codeql::ImportDecl translateImportDecl(const swift::ImportDecl& decl);
std::optional<codeql::ModuleDecl> translateModuleDecl(const swift::ModuleDecl& decl);
codeql::IfConfigDecl translateIfConfigDecl(const swift::IfConfigDecl& decl);
codeql::IfConfigClause translateIfConfigClause(const swift::IfConfigClause& clause);
private:
std::string mangledName(const swift::ValueDecl& decl);

View File

@@ -668,4 +668,10 @@ void ExprVisitor::emitLookupExpr(const swift::LookupExpr* expr, TrapLabel<Lookup
}
}
codeql::UnresolvedPatternExpr ExprVisitor::translateUnresolvedPatternExpr(
swift::UnresolvedPatternExpr& expr) {
auto entry = dispatcher_.createEntry(expr);
entry.sub_pattern = dispatcher_.fetchLabel(expr.getSubPattern());
return entry;
}
} // namespace codeql

View File

@@ -84,6 +84,13 @@ class ExprVisitor : public AstVisitorBase<ExprVisitor> {
codeql::BridgeFromObjCExpr translateBridgeFromObjCExpr(const swift::BridgeFromObjCExpr& expr);
codeql::DotSelfExpr translateDotSelfExpr(const swift::DotSelfExpr& expr);
codeql::ErrorExpr translateErrorExpr(const swift::ErrorExpr& expr);
// The following function requires a non-const parameter because:
// * `swift::UnresolvedPatternExpr::getSubPattern` has a `const`-qualified overload returning
// `const swift::Pattern*`
// * `swift::ASTVisitor` only visits non-const pointers
// either we accept this, or we fix constness, e.g. by providing `visit` on `const` pointers
// in `VisitorBase`, or by doing a `const_cast` in `SwifDispatcher::fetchLabel`
codeql::UnresolvedPatternExpr translateUnresolvedPatternExpr(swift::UnresolvedPatternExpr& expr);
private:
void fillAbstractClosureExpr(const swift::AbstractClosureExpr& expr,

View File

@@ -21,6 +21,7 @@ class SwiftVisitor : private SwiftDispatcher {
private:
void visit(swift::Decl* decl) override { declVisitor.visit(decl); }
void visit(const swift::IfConfigClause* clause) override { declVisitor.visit(clause); }
void visit(swift::Stmt* stmt) override { stmtVisitor.visit(stmt); }
void visit(const swift::StmtCondition* cond) override {
emit(stmtVisitor.translateStmtCondition(*cond));

View File

@@ -1,4 +1,5 @@
#include "swift/extractor/visitors/TypeVisitor.h"
namespace codeql {
void TypeVisitor::visit(swift::TypeBase* type) {
TypeVisitorBase<TypeVisitor>::visit(type);
@@ -368,4 +369,10 @@ codeql::BuiltinVectorType TypeVisitor::translateBuiltinVectorType(
return createTypeEntry(type);
}
codeql::OpenedArchetypeType TypeVisitor::translateOpenedArchetypeType(
const swift::OpenedArchetypeType& type) {
auto entry = createTypeEntry(type);
fillArchetypeType(type, entry);
return entry;
}
} // namespace codeql

View File

@@ -68,6 +68,7 @@ class TypeVisitor : public TypeVisitorBase<TypeVisitor> {
codeql::BuiltinUnsafeValueBufferType translateBuiltinUnsafeValueBufferType(
const swift::BuiltinUnsafeValueBufferType& type);
codeql::BuiltinVectorType translateBuiltinVectorType(const swift::BuiltinVectorType& type);
codeql::OpenedArchetypeType translateOpenedArchetypeType(const swift::OpenedArchetypeType& type);
private:
void fillType(const swift::TypeBase& type, codeql::Type& entry);

View File

@@ -29,7 +29,7 @@ class VisitorBase {
public: \
void visit##CLASS##KIND(swift::CLASS##KIND* e) { \
using TranslateResult = std::invoke_result_t<decltype(&CrtpSubclass::translate##CLASS##KIND), \
CrtpSubclass, swift::CLASS##KIND>; \
CrtpSubclass, swift::CLASS##KIND&>; \
constexpr bool hasTranslateImplementation = !std::is_same_v<TranslateResult, void>; \
if constexpr (hasTranslateImplementation) { \
dispatcher_.emit(static_cast<CrtpSubclass*>(this)->translate##CLASS##KIND(*e)); \

View File

@@ -24,6 +24,7 @@ import codeql.swift.elements.decl.FuncDecl
import codeql.swift.elements.decl.GenericContext
import codeql.swift.elements.decl.GenericTypeDecl
import codeql.swift.elements.decl.GenericTypeParamDecl
import codeql.swift.elements.decl.IfConfigClause
import codeql.swift.elements.decl.IfConfigDecl
import codeql.swift.elements.decl.ImportDecl
import codeql.swift.elements.decl.InfixOperatorDecl

View File

@@ -0,0 +1,4 @@
// generated by codegen/codegen.py, remove this comment if you wish to edit this file
private import codeql.swift.generated.decl.IfConfigClause
class IfConfigClause extends IfConfigClauseBase { }

View File

@@ -1,4 +1,9 @@
// generated by codegen/codegen.py, remove this comment if you wish to edit this file
private import codeql.swift.generated.expr.UnresolvedDeclRefExpr
class UnresolvedDeclRefExpr extends UnresolvedDeclRefExprBase { }
class UnresolvedDeclRefExpr extends UnresolvedDeclRefExprBase {
override string toString() {
result = getName() + " (unresolved)"
or
not hasName() and result = "(unresolved)"
}
}

View File

@@ -28,6 +28,12 @@ Element getAnImmediateChild(Element e) {
or
enum_element_decl_params(e, _, x)
or
if_config_clause_conditions(e, x)
or
if_config_clause_elements(e, _, x)
or
if_config_decl_clauses(e, _, x)
or
pattern_binding_decl_inits(e, _, x)
or
pattern_binding_decl_patterns(e, _, x)
@@ -144,6 +150,8 @@ Element getAnImmediateChild(Element e) {
or
unresolved_dot_exprs(e, x, _)
or
unresolved_pattern_exprs(e, x)
or
vararg_expansion_exprs(e, x)
or
binding_patterns(e, x)

View File

@@ -0,0 +1,30 @@
// generated by codegen/codegen.py
import codeql.swift.elements.AstNode
import codeql.swift.elements.expr.Expr
import codeql.swift.elements.Locatable
class IfConfigClauseBase extends @if_config_clause, Locatable {
override string getAPrimaryQlClass() { result = "IfConfigClause" }
Expr getCondition() {
exists(Expr x |
if_config_clause_conditions(this, x) and
result = x.resolve()
)
}
predicate hasCondition() { exists(getCondition()) }
AstNode getElement(int index) {
exists(AstNode x |
if_config_clause_elements(this, index, x) and
result = x.resolve()
)
}
AstNode getAnElement() { result = getElement(_) }
int getNumberOfElements() { result = count(getAnElement()) }
predicate isActive() { if_config_clause_is_active(this) }
}

View File

@@ -1,6 +1,18 @@
// generated by codegen/codegen.py
import codeql.swift.elements.decl.Decl
import codeql.swift.elements.decl.IfConfigClause
class IfConfigDeclBase extends @if_config_decl, Decl {
override string getAPrimaryQlClass() { result = "IfConfigDecl" }
IfConfigClause getClause(int index) {
exists(IfConfigClause x |
if_config_decl_clauses(this, index, x) and
result = x.resolve()
)
}
IfConfigClause getAClause() { result = getClause(_) }
int getNumberOfClauses() { result = count(getAClause()) }
}

View File

@@ -1,6 +1,14 @@
// generated by codegen/codegen.py
import codeql.swift.elements.expr.Expr
import codeql.swift.elements.pattern.Pattern
class UnresolvedPatternExprBase extends @unresolved_pattern_expr, Expr {
override string getAPrimaryQlClass() { result = "UnresolvedPatternExpr" }
Pattern getSubPattern() {
exists(Pattern x |
unresolved_pattern_exprs(this, x) and
result = x.resolve()
)
}
}

View File

@@ -36,6 +36,7 @@ files(
@argument
| @ast_node
| @condition_element
| @if_config_clause
;
#keyset[id]
@@ -643,6 +644,35 @@ if_config_decls( //dir=decl
unique int id: @if_config_decl
);
#keyset[id, index]
if_config_decl_clauses( //dir=decl
int id: @if_config_decl ref,
int index: int ref,
int clause: @if_config_clause ref
);
if_config_clauses( //dir=decl
unique int id: @if_config_clause
);
#keyset[id]
if_config_clause_conditions( //dir=decl
int id: @if_config_clause ref,
int condition: @expr ref
);
#keyset[id, index]
if_config_clause_elements( //dir=decl
int id: @if_config_clause ref,
int index: int ref,
int element: @ast_node ref
);
#keyset[id]
if_config_clause_is_active( //dir=decl
int id: @if_config_clause ref
);
import_decls( //dir=decl
unique int id: @import_decl,
int module: @module_decl ref
@@ -1141,7 +1171,8 @@ unresolved_member_exprs( //dir=expr
);
unresolved_pattern_exprs( //dir=expr
unique int id: @unresolved_pattern_expr
unique int id: @unresolved_pattern_expr,
int sub_pattern: @pattern ref
);
unresolved_specialize_exprs( //dir=expr

View File

@@ -0,0 +1,6 @@
| if_config.swift:1:1:1:1 | IfConfigClause | isActive: | no |
| if_config.swift:4:1:4:1 | IfConfigClause | isActive: | no |
| if_config.swift:7:1:7:1 | IfConfigClause | isActive: | yes |
| if_config_active.swift:3:1:3:1 | IfConfigClause | isActive: | yes |
| if_config_active.swift:6:1:6:1 | IfConfigClause | isActive: | no |
| if_config_active.swift:9:1:9:1 | IfConfigClause | isActive: | no |

View File

@@ -0,0 +1,10 @@
// generated by codegen/codegen.py
import codeql.swift.elements
import TestUtils
from IfConfigClause x, string isActive
where
toBeTested(x) and
not x.isUnknown() and
if x.isActive() then isActive = "yes" else isActive = "no"
select x, "isActive:", isActive

View File

@@ -0,0 +1,4 @@
| if_config.swift:1:1:1:1 | IfConfigClause | if_config.swift:1:5:1:5 | FOO (unresolved) |
| if_config.swift:4:1:4:1 | IfConfigClause | if_config.swift:4:9:4:19 | call to ... |
| if_config_active.swift:3:1:3:1 | IfConfigClause | if_config_active.swift:3:5:3:5 | FOO (unresolved) |
| if_config_active.swift:6:1:6:1 | IfConfigClause | if_config_active.swift:6:9:6:17 | call to ... |

View File

@@ -0,0 +1,7 @@
// generated by codegen/codegen.py
import codeql.swift.elements
import TestUtils
from IfConfigClause x
where toBeTested(x) and not x.isUnknown()
select x, x.getCondition()

View File

@@ -0,0 +1,18 @@
| if_config.swift:1:1:1:1 | IfConfigClause | 0 | if_config.swift:2:1:2:16 | { ... } |
| if_config.swift:1:1:1:1 | IfConfigClause | 1 | if_config.swift:2:5:2:5 | foo |
| if_config.swift:1:1:1:1 | IfConfigClause | 2 | if_config.swift:3:1:3:12 | { ... } |
| if_config.swift:4:1:4:1 | IfConfigClause | 0 | if_config.swift:5:1:5:16 | { ... } |
| if_config.swift:4:1:4:1 | IfConfigClause | 1 | if_config.swift:5:5:5:5 | bar |
| if_config.swift:4:1:4:1 | IfConfigClause | 2 | if_config.swift:6:1:6:12 | { ... } |
| if_config.swift:7:1:7:1 | IfConfigClause | 0 | if_config.swift:8:1:8:16 | { ... } |
| if_config.swift:7:1:7:1 | IfConfigClause | 1 | if_config.swift:8:5:8:5 | baz |
| if_config.swift:7:1:7:1 | IfConfigClause | 2 | if_config.swift:9:1:9:12 | { ... } |
| if_config_active.swift:3:1:3:1 | IfConfigClause | 0 | if_config_active.swift:4:1:4:16 | { ... } |
| if_config_active.swift:3:1:3:1 | IfConfigClause | 1 | if_config_active.swift:4:5:4:5 | foo |
| if_config_active.swift:3:1:3:1 | IfConfigClause | 2 | if_config_active.swift:5:1:5:12 | { ... } |
| if_config_active.swift:6:1:6:1 | IfConfigClause | 0 | if_config_active.swift:7:1:7:16 | { ... } |
| if_config_active.swift:6:1:6:1 | IfConfigClause | 1 | if_config_active.swift:7:5:7:5 | bar |
| if_config_active.swift:6:1:6:1 | IfConfigClause | 2 | if_config_active.swift:8:1:8:12 | { ... } |
| if_config_active.swift:9:1:9:1 | IfConfigClause | 0 | if_config_active.swift:10:1:10:16 | { ... } |
| if_config_active.swift:9:1:9:1 | IfConfigClause | 1 | if_config_active.swift:10:5:10:5 | baz |
| if_config_active.swift:9:1:9:1 | IfConfigClause | 2 | if_config_active.swift:11:1:11:12 | { ... } |

View File

@@ -0,0 +1,7 @@
// generated by codegen/codegen.py
import codeql.swift.elements
import TestUtils
from IfConfigClause x, int index
where toBeTested(x) and not x.isUnknown()
select x, index, x.getElement(index)

View File

@@ -0,0 +1,10 @@
#if FOO
var foo: Int = 1
print("foo")
#elseif os(watchOS)
var bar: Int = 2
print("bar")
#else
var baz: Int = 3
print("baz")
#endif

View File

@@ -0,0 +1,12 @@
//codeql-extractor-options: -D FOO
#if FOO
var foo: Int = 1
print("foo")
#elseif os(macOS)
var bar: Int = 2
print("bar")
#else
var baz: Int = 3
print("baz")
#endif

View File

@@ -0,0 +1 @@
| if_config.swift:1:1:10:1 | #if ... |

View File

@@ -0,0 +1,7 @@
// generated by codegen/codegen.py
import codeql.swift.elements
import TestUtils
from IfConfigDecl x
where toBeTested(x) and not x.isUnknown()
select x

View File

@@ -0,0 +1,3 @@
| if_config.swift:1:1:10:1 | #if ... | 0 | if_config.swift:1:1:1:1 | IfConfigClause |
| if_config.swift:1:1:10:1 | #if ... | 1 | if_config.swift:4:1:4:1 | IfConfigClause |
| if_config.swift:1:1:10:1 | #if ... | 2 | if_config.swift:7:1:7:1 | IfConfigClause |

View File

@@ -0,0 +1,7 @@
// generated by codegen/codegen.py
import codeql.swift.elements
import TestUtils
from IfConfigDecl x, int index
where toBeTested(x) and not x.isUnknown()
select x, index, x.getClause(index)

View File

@@ -1,4 +0,0 @@
// generated by codegen/codegen.py
After a swift source file is added in this directory and codegen/codegen.py is run again, test queries
will appear and this file will be deleted

View File

@@ -0,0 +1,10 @@
#if FOO
var foo: Int = 1
print("foo")
#elseif BAR
var bar: Int = 2
print("bar")
#else
var baz: Int = 3
print("baz")
#endif

View File

@@ -0,0 +1,10 @@
| unresolved_decl_ref.swift:4:5:4:5 | FOO (unresolved) |
| unresolved_decl_ref.swift:4:9:4:9 | && (unresolved) |
| unresolved_decl_ref.swift:4:12:4:12 | os (unresolved) |
| unresolved_decl_ref.swift:4:15:4:15 | Windows (unresolved) |
| unresolved_decl_ref.swift:5:1:5:1 | print (unresolved) |
| unresolved_decl_ref.swift:6:9:6:9 | BAR (unresolved) |
| unresolved_decl_ref.swift:6:13:6:13 | \|\| (unresolved) |
| unresolved_decl_ref.swift:6:16:6:16 | arch (unresolved) |
| unresolved_decl_ref.swift:6:21:6:21 | i386 (unresolved) |
| unresolved_decl_ref.swift:9:1:9:1 | print (unresolved) |

View File

@@ -0,0 +1,7 @@
// generated by codegen/codegen.py
import codeql.swift.elements
import TestUtils
from UnresolvedDeclRefExpr x
where toBeTested(x) and not x.isUnknown()
select x

View File

@@ -0,0 +1,10 @@
| unresolved_decl_ref.swift:4:5:4:5 | FOO (unresolved) | FOO |
| unresolved_decl_ref.swift:4:9:4:9 | && (unresolved) | && |
| unresolved_decl_ref.swift:4:12:4:12 | os (unresolved) | os |
| unresolved_decl_ref.swift:4:15:4:15 | Windows (unresolved) | Windows |
| unresolved_decl_ref.swift:5:1:5:1 | print (unresolved) | print |
| unresolved_decl_ref.swift:6:9:6:9 | BAR (unresolved) | BAR |
| unresolved_decl_ref.swift:6:13:6:13 | \|\| (unresolved) | \|\| |
| unresolved_decl_ref.swift:6:16:6:16 | arch (unresolved) | arch |
| unresolved_decl_ref.swift:6:21:6:21 | i386 (unresolved) | i386 |
| unresolved_decl_ref.swift:9:1:9:1 | print (unresolved) | print |

View File

@@ -0,0 +1,7 @@
// generated by codegen/codegen.py
import codeql.swift.elements
import TestUtils
from UnresolvedDeclRefExpr x
where toBeTested(x) and not x.isUnknown()
select x, x.getName()

View File

@@ -0,0 +1,7 @@
// generated by codegen/codegen.py
import codeql.swift.elements
import TestUtils
from UnresolvedDeclRefExpr x
where toBeTested(x) and not x.isUnknown()
select x, x.getType()

View File

@@ -0,0 +1,10 @@
//codeql-extractor-options: -D BAR
// conditions and inactive branches in conditional compilation blocks are not resolved
#if FOO && os(Windows)
print(1)
#elseif BAR || arch(i386)
print(2)
#else
print(3)
#endif

View File

@@ -0,0 +1 @@
| unresolved_pattern_expr.swift:2:19:2:19 | UnresolvedPatternExpr | getSubPattern: | unresolved_pattern_expr.swift:2:19:2:19 | x |

View File

@@ -0,0 +1,10 @@
// generated by codegen/codegen.py
import codeql.swift.elements
import TestUtils
from UnresolvedPatternExpr x, Pattern getSubPattern
where
toBeTested(x) and
not x.isUnknown() and
getSubPattern = x.getSubPattern()
select x, "getSubPattern:", getSubPattern

View File

@@ -0,0 +1,7 @@
// generated by codegen/codegen.py
import codeql.swift.elements
import TestUtils
from UnresolvedPatternExpr x
where toBeTested(x) and not x.isUnknown()
select x, x.getType()

View File

@@ -0,0 +1,5 @@
#if FOO
if case let .some(x) = 42 {
print(x)
}
#endif

View File

@@ -1,4 +0,0 @@
// generated by codegen/codegen.py
After a swift source file is added in this directory and codegen/codegen.py is run again, test queries
will appear and this file will be deleted

View File

@@ -0,0 +1 @@
| C & P1 & P2 | getName: | C & P1 & P2 | getCanonicalType: | C & P1 & P2 | getInterfaceType: | \u03c4_0_0 |

View File

@@ -0,0 +1,13 @@
// generated by codegen/codegen.py
import codeql.swift.elements
import TestUtils
from OpenedArchetypeType x, string getName, Type getCanonicalType, Type getInterfaceType
where
toBeTested(x) and
not x.isUnknown() and
getName = x.getName() and
getCanonicalType = x.getCanonicalType() and
getInterfaceType = x.getInterfaceType()
select x, "getName:", getName, "getCanonicalType:", getCanonicalType, "getInterfaceType:",
getInterfaceType

View File

@@ -0,0 +1,2 @@
| C & P1 & P2 | 0 | opened_archetypes.swift:3:1:3:14 | P1 |
| C & P1 & P2 | 1 | opened_archetypes.swift:9:1:9:14 | P2 |

View File

@@ -0,0 +1,7 @@
// generated by codegen/codegen.py
import codeql.swift.elements
import TestUtils
from OpenedArchetypeType x, int index
where toBeTested(x) and not x.isUnknown()
select x, index, x.getProtocol(index)

View File

@@ -0,0 +1,7 @@
// generated by codegen/codegen.py
import codeql.swift.elements
import TestUtils
from OpenedArchetypeType x
where toBeTested(x) and not x.isUnknown()
select x, x.getSuperclass()

View File

@@ -0,0 +1,25 @@
// code inspired by https://github.com/apple/swift-evolution/blob/main/proposals/0352-implicit-open-existentials.md
protocol P1 {}
func isFoo<T: P1>(_: T) -> Bool {
return true
}
protocol P2 {}
class C {}
// will be ok with swift 5.7
// func test(value: any P1 & P2 & C) -> Bool { return isFoo(value) }
extension P1 {
var isFooMember: Bool {
isFoo(self)
}
}
func testMember(value: any P1 & P2 & C) -> Bool {
return value.isFooMember // here the existential type is implicitly "opened"
}