From 57eaed4dcc4645cdd4d82960448e444d28f0d03d Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Wed, 22 Apr 2026 13:37:35 +0100 Subject: [PATCH 1/2] Refactor: remove fields from `EncryptionOperation` Co-authored-by: Copilot --- go/ql/lib/semmle/go/Concepts.qll | 16 +++++----- .../semmle/go/frameworks/CryptoLibraries.qll | 30 +++++++++++++------ 2 files changed, 30 insertions(+), 16 deletions(-) diff --git a/go/ql/lib/semmle/go/Concepts.qll b/go/ql/lib/semmle/go/Concepts.qll index ffa48ea9654d..c33fb0ae6bb0 100644 --- a/go/ql/lib/semmle/go/Concepts.qll +++ b/go/ql/lib/semmle/go/Concepts.qll @@ -574,19 +574,17 @@ module Cryptography { * is one) have been initialized separately. */ abstract class EncryptionOperation extends CryptographicOperation::Range { - DataFlow::Node encryptionFlowTarget; - DataFlow::Node inputNode; + /** Gets the target node for the encryption flow. */ + abstract DataFlow::Node getEncryptionFlowTarget(); override DataFlow::Node getInitialization() { - EncryptionFlow::flow(result, encryptionFlowTarget) + EncryptionFlow::flow(result, this.getEncryptionFlowTarget()) } override EncryptionAlgorithm getAlgorithm() { result = this.getInitialization().(EncryptionAlgorithmInit).getAlgorithm() } - override DataFlow::Node getAnInput() { result = inputNode } - override BlockMode getBlockMode() { result = this.getInitialization().(BlockModeInit).getMode() } @@ -601,8 +599,12 @@ module Cryptography { int inputArg; EncryptionMethodCall() { - encryptionFlowTarget = super.getReceiver() and - inputNode = super.getArgument(inputArg) + exists(super.getReceiver()) and + exists(super.getArgument(inputArg)) } + + override DataFlow::Node getEncryptionFlowTarget() { result = super.getReceiver() } + + override DataFlow::Node getAnInput() { result = super.getArgument(inputArg) } } } diff --git a/go/ql/lib/semmle/go/frameworks/CryptoLibraries.qll b/go/ql/lib/semmle/go/frameworks/CryptoLibraries.qll index 7f0e52b449a2..f8714e8602ad 100644 --- a/go/ql/lib/semmle/go/frameworks/CryptoLibraries.qll +++ b/go/ql/lib/semmle/go/frameworks/CryptoLibraries.qll @@ -381,19 +381,26 @@ private module Crypto { } private class StreamReader extends EncryptionOperation { + DataFlow::Node encryptionFlowTarget; + DataFlow::Node inputNode; + 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) + exists(DataFlow::Write wS, DataFlow::Node baseS, Field fS | + fS.hasQualifiedName("crypto/cipher", "StreamReader", "S") and + wS.writesField(baseS, fS, encryptionFlowTarget) and + DataFlow::localFlow(baseS, 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) + exists(DataFlow::Write wR, DataFlow::Node baseR, Field fR | + fR.hasQualifiedName("crypto/cipher", "StreamReader", "R") and + wR.writesField(baseR, fR, inputNode) and + DataFlow::localFlow(baseR, this) ) } + + override DataFlow::Node getEncryptionFlowTarget() { result = encryptionFlowTarget } + + override DataFlow::Node getAnInput() { result = inputNode } } /** @@ -402,9 +409,10 @@ private module Crypto { * so it only works within one function. */ private class StreamWriter extends EncryptionOperation { + DataFlow::Node encryptionFlowTarget; + 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") @@ -413,6 +421,10 @@ private module Crypto { TaintTracking::localTaint(base, this.(DataFlow::PostUpdateNode).getPreUpdateNode()) ) } + + override DataFlow::Node getEncryptionFlowTarget() { result = encryptionFlowTarget } + + override DataFlow::Node getAnInput() { result = this } } } } From e37e103889efb9e3d6dfd658f1bac6519964b15e Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Tue, 21 Apr 2026 11:22:46 +0100 Subject: [PATCH 2/2] Make `getACallee` overlay[global] Co-authored-by: Copilot --- go/ql/lib/semmle/go/Decls.qll | 3 ++- go/ql/lib/semmle/go/Scopes.qll | 3 ++- go/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll | 4 +++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/go/ql/lib/semmle/go/Decls.qll b/go/ql/lib/semmle/go/Decls.qll index 7588ab913bed..f42058cd3e88 100644 --- a/go/ql/lib/semmle/go/Decls.qll +++ b/go/ql/lib/semmle/go/Decls.qll @@ -1,7 +1,7 @@ /** * Provides classes for working with declarations. */ -overlay[local] +overlay[local?] module; import go @@ -137,6 +137,7 @@ class FuncDef extends @funcdef, StmtParent, ExprParent { /** * Gets a call to this function. */ + overlay[global] DataFlow::CallNode getACall() { result.getACallee() = this } /** Holds if this function is variadic. */ diff --git a/go/ql/lib/semmle/go/Scopes.qll b/go/ql/lib/semmle/go/Scopes.qll index 4e9a13c8ea1f..9f18290fb011 100644 --- a/go/ql/lib/semmle/go/Scopes.qll +++ b/go/ql/lib/semmle/go/Scopes.qll @@ -1,7 +1,7 @@ /** * Provides classes for working with scopes and declared objects. */ -overlay[local] +overlay[local?] module; import go @@ -418,6 +418,7 @@ class Function extends ValueEntity, @functionobject { * This includes calls that target this function indirectly, by calling an * interface method that this function implements. */ + overlay[global] pragma[nomagic] DataFlow::CallNode getACall() { this = result.getACalleeIncludingExternals().asFunction() } diff --git a/go/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll b/go/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll index 8fca4bec8c63..603da6364df7 100644 --- a/go/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll +++ b/go/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll @@ -1,4 +1,4 @@ -overlay[local] +overlay[local?] module; private import go @@ -488,6 +488,7 @@ module Public { * For virtual calls, we look up possible targets in all types that implement the receiver * interface type. */ + overlay[global] Callable getACalleeIncludingExternals() { result = this.getACalleeWithoutVirtualDispatch() or @@ -504,6 +505,7 @@ module Public { * As `getACalleeIncludingExternals`, except excluding external functions (those for which * we lack a definition, such as standard library functions). */ + overlay[global] pragma[nomagic] FuncDef getACallee() { result = this.getACalleeIncludingExternals().getFuncDef() }