diff --git a/ql/src/codeql_ruby/ast/Constant.qll b/ql/src/codeql_ruby/ast/Constant.qll index 3ae3c1207b2..d8a5b9030a1 100644 --- a/ql/src/codeql_ruby/ast/Constant.qll +++ b/ql/src/codeql_ruby/ast/Constant.qll @@ -1,5 +1,6 @@ private import codeql_ruby.AST private import internal.AST +private import internal.Variable private import internal.TreeSitter /** An access to a constant. */ @@ -40,6 +41,27 @@ class ConstantAccess extends Expr, TConstantAccess { override AstNode getAChild(string pred) { pred = "getScopeExpr" and result = this.getScopeExpr() } } +private class TokenConstantAccess extends ConstantAccess, TTokenConstantAccess { + private Generated::Constant g; + + TokenConstantAccess() { this = TTokenConstantAccess(g) } + + final override string getName() { result = g.getValue() } +} + +private class ScopeResolutionConstantAccess extends ConstantAccess, TScopeResolutionConstantAccess { + private Generated::ScopeResolution g; + private Generated::Constant constant; + + ScopeResolutionConstantAccess() { this = TScopeResolutionConstantAccess(g, constant) } + + final override string getName() { result = constant.getValue() } + + final override Expr getScopeExpr() { toGenerated(result) = g.getScope() } + + final override predicate hasGlobalScope() { not exists(g.getScope()) } +} + /** * A use (read) of a constant. * @@ -56,36 +78,17 @@ class ConstantAccess extends Expr, TConstantAccess { * end * ``` */ -class ConstantReadAccess extends ConstantAccess, TConstantReadAccess { - override Expr getScopeExpr() { none() } - - override predicate hasGlobalScope() { none() } +class ConstantReadAccess extends ConstantAccess { + ConstantReadAccess() { + not this instanceof ConstantWriteAccess + or + // `X` in `X ||= 10` is considered both a read and a write + this = any(AssignOperation a).getLeftOperand() + } final override string getAPrimaryQlClass() { result = "ConstantReadAccess" } } -private class TokenConstantReadAccess extends ConstantReadAccess, TTokenConstantReadAccess { - private Generated::Constant g; - - TokenConstantReadAccess() { this = TTokenConstantReadAccess(g) } - - final override string getName() { result = g.getValue() } -} - -private class ScopeResolutionConstantReadAccess extends ConstantReadAccess, - TScopeResolutionConstantReadAccess { - private Generated::ScopeResolution g; - private Generated::Constant constant; - - ScopeResolutionConstantReadAccess() { this = TScopeResolutionConstantReadAccess(g, constant) } - - final override string getName() { result = constant.getValue() } - - final override Expr getScopeExpr() { toGenerated(result) = g.getScope() } - - final override predicate hasGlobalScope() { not exists(g.getScope()) } -} - /** * A definition of a constant. * @@ -102,32 +105,14 @@ private class ScopeResolutionConstantReadAccess extends ConstantReadAccess, * module M::Baz; end # defines constant Baz as a module in module M * ``` */ -class ConstantWriteAccess extends ConstantAccess, TConstantWriteAccess { +class ConstantWriteAccess extends ConstantAccess { + ConstantWriteAccess() { + explicitAssignmentNode(toGenerated(this), _) or this instanceof TNamespace + } + override string getAPrimaryQlClass() { result = "ConstantWriteAccess" } } -private class TokenConstantWriteAccess extends ConstantWriteAccess, TTokenConstantWriteAccess { - private Generated::Constant g; - - TokenConstantWriteAccess() { this = TTokenConstantWriteAccess(g) } - - final override string getName() { result = g.getValue() } -} - -private class ScopeResolutionConstantWriteAccess extends ConstantWriteAccess, - TScopeResolutionConstantWriteAccess { - private Generated::ScopeResolution g; - private Generated::Constant constant; - - ScopeResolutionConstantWriteAccess() { this = TScopeResolutionConstantWriteAccess(g, constant) } - - final override string getName() { result = constant.getValue() } - - final override Expr getScopeExpr() { toGenerated(result) = g.getScope() } - - final override predicate hasGlobalScope() { not exists(g.getScope()) } -} - /** * A definition of a constant via assignment. For example, the left-hand * operand in the following example: @@ -136,6 +121,6 @@ private class ScopeResolutionConstantWriteAccess extends ConstantWriteAccess, * MAX_SIZE = 100 * ``` */ -class ConstantAssignment extends ConstantWriteAccess, LhsExpr, TConstantAssignment { +class ConstantAssignment extends ConstantWriteAccess, LhsExpr { override string getAPrimaryQlClass() { result = "ConstantAssignment" } } diff --git a/ql/src/codeql_ruby/ast/Pattern.qll b/ql/src/codeql_ruby/ast/Pattern.qll index 39ddfbab383..8cf30a3ea4b 100644 --- a/ql/src/codeql_ruby/ast/Pattern.qll +++ b/ql/src/codeql_ruby/ast/Pattern.qll @@ -16,7 +16,9 @@ class Pattern extends AstNode { Variable getAVariable() { none() } } -private class LhsExpr_ = TVariableAccess or TConstantAssignment or TMethodCall or TSimpleParameter; +private class LhsExpr_ = + TVariableAccess or TTokenConstantAccess or TScopeResolutionConstantAccess or TMethodCall or + TSimpleParameter; /** * A "left-hand-side" expression. An `LhsExpr` can occur on the left-hand side of diff --git a/ql/src/codeql_ruby/ast/internal/AST.qll b/ql/src/codeql_ruby/ast/internal/AST.qll index 23cabfa08ab..6cf9696c510 100644 --- a/ql/src/codeql_ruby/ast/internal/AST.qll +++ b/ql/src/codeql_ruby/ast/internal/AST.qll @@ -162,15 +162,16 @@ private module Cached { TRescueModifierExpr(Generated::RescueModifier g) or TRetryStmt(Generated::Retry g) or TReturnStmt(Generated::Return g) or - TScopeResolutionConstantReadAccess(Generated::ScopeResolution g, Generated::Constant constant) { - // A tree-sitter `scope_resolution` node with a `constant` name field is a - // read of that constant in any context where an identifier would be a - // vcall. + TScopeResolutionConstantAccess(Generated::ScopeResolution g, Generated::Constant constant) { constant = g.getName() and - vcall(g) - } or - TScopeResolutionConstantWriteAccess(Generated::ScopeResolution g, Generated::Constant constant) { - explicitAssignmentNode(g, _) and constant = g.getName() + ( + // A tree-sitter `scope_resolution` node with a `constant` name field is a + // read of that constant in any context where an identifier would be a + // vcall. + vcall(g) + or + explicitAssignmentNode(g, _) + ) } or TScopeResolutionMethodCall(Generated::ScopeResolution g, Generated::Identifier i) { i = g.getName() and @@ -196,12 +197,13 @@ private module Cached { TSymbolArrayLiteral(Generated::SymbolArray g) or TTernaryIfExpr(Generated::Conditional g) or TThen(Generated::Then g) or - TTokenConstantReadAccess(Generated::Constant g) { + TTokenConstantAccess(Generated::Constant g) { // A tree-sitter `constant` token is a read of that constant in any context // where an identifier would be a vcall. vcall(g) + or + explicitAssignmentNode(g, _) } or - TTokenConstantWriteAccess(Generated::Constant g) { explicitAssignmentNode(g, _) } or TTokenMethodName(MethodName::Token g) { MethodName::range(g) } or TTokenSuperCall(Generated::Super g) { vcall(g) } or TToplevel(Generated::Program g) { g.getLocation().getFile().getExtension() != "erb" } or @@ -323,8 +325,7 @@ private module Cached { n = TRescueModifierExpr(result) or n = TRetryStmt(result) or n = TReturnStmt(result) or - n = TScopeResolutionConstantReadAccess(result, _) or - n = TScopeResolutionConstantWriteAccess(result, _) or + n = TScopeResolutionConstantAccess(result, _) or n = TScopeResolutionMethodCall(result, _) or n = TSelf(result) or n = TSimpleParameter(result) or @@ -344,8 +345,7 @@ private module Cached { n = TSymbolArrayLiteral(result) or n = TTernaryIfExpr(result) or n = TThen(result) or - n = TTokenConstantReadAccess(result) or - n = TTokenConstantWriteAccess(result) or + n = TTokenConstantAccess(result) or n = TTokenMethodName(result) or n = TTokenSuperCall(result) or n = TToplevel(result) or @@ -377,13 +377,7 @@ class TMethodCall = class TSuperCall = TTokenSuperCall or TRegularSuperCall; -class TConstantAccess = TConstantReadAccess or TConstantWriteAccess; - -class TConstantReadAccess = TTokenConstantReadAccess or TScopeResolutionConstantReadAccess; - -class TConstantWriteAccess = TConstantAssignment or TNamespace; - -class TConstantAssignment = TTokenConstantWriteAccess or TScopeResolutionConstantWriteAccess; +class TConstantAccess = TTokenConstantAccess or TScopeResolutionConstantAccess or TNamespace; class TControlExpr = TConditionalExpr or TCaseExpr or TLoop;