From 5317022d2ea7af5b0c59f058d871ecce04e2eecd Mon Sep 17 00:00:00 2001 From: Calum Grant Date: Tue, 28 Sep 2021 20:51:21 +0100 Subject: [PATCH 01/14] Basic query for Ruby --- .../basic-query-for-ruby-code.rst | 146 ++++++++++++++++++ .../codeql/reusables/ruby-further-reading.rst | 3 + 2 files changed, 149 insertions(+) create mode 100644 docs/codeql/codeql-language-guides/basic-query-for-ruby-code.rst create mode 100644 docs/codeql/reusables/ruby-further-reading.rst diff --git a/docs/codeql/codeql-language-guides/basic-query-for-ruby-code.rst b/docs/codeql/codeql-language-guides/basic-query-for-ruby-code.rst new file mode 100644 index 000000000000..a48933fc4cfa --- /dev/null +++ b/docs/codeql/codeql-language-guides/basic-query-for-ruby-code.rst @@ -0,0 +1,146 @@ +.. _basic-query-for-ruby-code: + +Basic query for Ruby code +========================= + +Learn to write and run a simple CodeQL query using CodeQL. + +About the query +--------------- + +The query we're going to run performs a basic search of the code for ``if`` expressions that are redundant, in the sense that they have an empty ``then`` branch. For example, code such as: + +.. code-block:: ruby + + if error + # Handle the error + +Running the query +----------------- + +#. In the main search box on LGTM.com, search for the project you want to query. For tips, see `Searching `__. + +#. Click the project in the search results. + +#. Click **Query this project**. + + This opens the query console. (For information about using this, see `Using the query console `__.) + + .. pull-quote:: + + Note + + Alternatively, you can go straight to the query console by clicking **Query console** (at the top of any page), selecting **Ruby** from the **Language** drop-down list, then choosing one or more projects to query from those displayed in the **Project** drop-down list. + +#. Copy the following query into the text box in the query console: + + .. code-block:: ql + + import ruby + + from IfExpr ifexpr + where + not exists(ifexpr.getThen()) + select ifexpr, "This 'if' expression is redundant." + + LGTM checks whether your query compiles and, if all is well, the **Run** button changes to green to indicate that you can go ahead and run the query. + +#. Click **Run**. + + The name of the project you are querying, and the ID of the most recently analyzed commit to the project, are listed below the query box. To the right of this is an icon that indicates the progress of the query operation: + + .. image:: ../images/query-progress.png + :align: center + + .. pull-quote:: + + Note + + Your query is always run against the most recently analyzed commit to the selected project. + + The query will take a few moments to return results. When the query completes, the results are displayed below the project name. The query results are listed in two columns, corresponding to the two expressions in the ``select`` clause of the query. The first column corresponds to the expression ``ifexpr`` and is linked to the location in the source code of the project where ``ifexpr`` occurs. The second column is the alert message. + + ➤ `Example query results `__ + + .. pull-quote:: + + Note + + An ellipsis (…) at the bottom of the table indicates that the entire list is not displayed—click it to show more results. + +#. If any matching code is found, click a link in the ``ifexpr`` column to view the ``if`` statement in the code viewer. + + The matching ``if`` expression is highlighted with a yellow background in the code viewer. If any code in the file also matches a query from the standard query library for that language, you will see a red alert message at the appropriate point within the code. + +About the query structure +~~~~~~~~~~~~~~~~~~~~~~~~~ + +After the initial ``import`` statement, this simple query comprises three parts that serve similar purposes to the FROM, WHERE, and SELECT parts of an SQL query. + ++---------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------+ +| Query part | Purpose | Details | ++===============================================================+===================================================================================================================+========================================================================================================================+ +| ``import ruby`` | Imports the standard CodeQL libraries for Ruby. | Every query begins with one or more ``import`` statements. | ++---------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------+ +| ``from IfExpr ifexpr`` | Defines the variables for the query. | We use: an ``IfExpr`` variable for ``if`` expressions. | +| | Declarations are of the form: | | +| | `` `` | | ++---------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------+ +| ``where not exists(ifexpr.getThen())`` | Defines a condition on the variables. | ``ifexpr.getThen()``: gets the ``then`` branch of the ``if`` expression. | +| | | | +| | | ``exists(...)``: requires that there is a matching element, in this case a ``then`` branch. | ++---------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------+ +| ``select ifexpr, "This 'if' expression is redundant."`` | Defines what to report for each match. | Reports the resulting ``if`` expression with a string that explains the problem. | +| | | | +| | ``select`` statements for queries that are used to find instances of poor coding practice are always in the form: | | +| | ``select , ""`` | | ++---------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------+ + +Extend the query +---------------- + +Query writing is an inherently iterative process. You write a simple query and then, when you run it, you discover examples that you had not previously considered, or opportunities for improvement. + +Remove false positive results +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Browsing the results of our basic query shows that it could be improved. Among the results you are likely to find examples of ``if`` statements with an ``else`` branch, where an empty ``then`` branch does serve a purpose. For example: + +.. code-block:: ruby + + if ... + ... + elsif option == "-verbose" + # nothing to do - handled earlier + else + error "unrecognized option" + +In this case, identifying the ``if`` statement with the empty ``then`` branch as redundant is a false positive. One solution to this is to modify the query to ignore empty ``then`` branches if the ``if`` statement has an ``else`` branch. + +To exclude ``if`` statements that have an ``else`` branch: + +#. Add the following to the where clause: + + .. code-block:: ql + + and not exists(ifstmt.getElse()) + + The ``where`` clause is now: + + .. code-block:: ql + + where + not exists(ifexpr.getThen()) and + not exists(ifexpr.getElse()) + +#. Click **Run**. + + There are now fewer results because ``if`` expressions with an ``else`` branch are no longer included. + +➤ `See this in the query console `__ + +Further reading +--------------- + +.. include:: ../reusables/ruby-further-reading.rst +.. include:: ../reusables/codeql-ref-tools-further-reading.rst diff --git a/docs/codeql/reusables/ruby-further-reading.rst b/docs/codeql/reusables/ruby-further-reading.rst new file mode 100644 index 000000000000..032eaff51124 --- /dev/null +++ b/docs/codeql/reusables/ruby-further-reading.rst @@ -0,0 +1,3 @@ +- `CodeQL queries for Ruby `__ +- `Example queries for Ruby `__ +- `CodeQL library reference for Ruby `__ From 30a00b22c92c6c7282bbb16622df82bcd0cebd67 Mon Sep 17 00:00:00 2001 From: Calum Grant Date: Tue, 5 Oct 2021 14:59:40 +0100 Subject: [PATCH 02/14] CodeQL library for Ruby --- .../codeql-library-for-ruby.rst | 622 ++++++++++++++++++ .../codeql/reusables/ruby-further-reading.rst | 4 +- 2 files changed, 624 insertions(+), 2 deletions(-) create mode 100644 docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst diff --git a/docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst b/docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst new file mode 100644 index 000000000000..146b4eb1d90b --- /dev/null +++ b/docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst @@ -0,0 +1,622 @@ +.. codeql-library-for-ruby: + +CodeQL library for Ruby +======================= + +When you're analyzing a Ruby program, you can make use of the large collection of classes in the CodeQL library for Ruby. + +Overview +-------- + +CodeQL ships with an extensive library for analyzing Ruby code. The classes in this library present +the data from a CodeQL database in an object-oriented form and provide abstractions and predicates +to help you with common analysis tasks. + +The library is implemented as a set of CodeQL modules, that is, files with the extension ``.qll``. The +module `ruby.qll `__ imports most other standard library modules, so you can include the complete +library by beginning your query with: + +.. code-block:: ql + + import ruby + +The CodeQL libraries model various aspects of Ruby code, depending on the type of query you want to write. +For example the abstract syntax tree (AST) library is used for locating program elements, to match syntactic +elements in the source code. This can be used to find values, patterns and structures. + +The control flow graph (CFG) is imported using + +.. code-block:: ql + + import codeql.ruby.CFG + +The CFG reasons about the control flow between statements and expressions, for example whether one expression can +flow to another expression, or whether an expression "dominates" another one, meaning that all paths to an +expression must flow through another expression first. + +The data flow library is imported using + +.. code-block:: ql + + import codeql.ruby.DataFlow + +Data flow tracks the flow of data through the program, including through function calls (interprocedural data flow). +Data flow is particularly useful for security queries, where untrusted data flow to vulnerable parts of the program +to exploit it. Related to data flow, is the taint-tracking library, which finds how data can *influence* other values +in a program, even when it is not copied exactly. + +The API graphs library is used to locate methods in libraries. This is particuarly useful when locating +particular functions or parameters that could be used as a source or sink of data in a security query. + +To summarise, the main Ruby modules are: + +.. list-table:: Main Ruby modules + :header-rows: 1 + + * - Import + - Description + * - ``ruby`` + - The standard Ruby library + * - ``codeql.ruby.AST`` + - The abstract syntax tree library (also imported by `ruby.qll`) + * - ``codeql.ruby.ApiGraphs`` + - The API graphs library + * - ``codeql.ruby.CFG`` + - The control flow graph library + * - ``codeql.ruby.DataFlow`` + - The data flow library + * - ``codeql.ruby.TaintTracking`` + - The taint tracking library + +The CodeQL examples are only excerpts and are not meant to represent complete queries. + +Abstract syntax +--------------- + +The abstract syntax tree (AST) represents the elements of the source code organised into a tree. The AST viewer +feature of Visual Studio Code shows the AST nodes, including the relevant CodeQL classes and predicates. + +All CodeQL AST classes inherit from the `AstNode` class, which provides the following member predicates +to all AST classes: + +.. list-table:: Main predicates in ``AstNode`` + :header-rows: 1 + + * - Predicate + - Description + * - ``getEnclosingModule()`` + - Gets the enclosing module, if any. + * - ``getEnclosingMethod()`` + - Gets the enclosing method, if any. + * - ``getLocation()`` + - Gets the location of this node. + * - ``getAChild()`` + - Gets a child node of this node. + * - ``getParent()`` + - Gets the parent of this `AstNode`, if this node is not a root node. + * - ``getDesugared`` + - Gets the desugared version of this AST node, if any. + * - ``isSynthesized()`` + - Holds if this node was synthesized to represent an implicit AST node not + present in the source code. + +Modules +~~~~~~~ + +Modules represent the main structural elements of Ruby programs, and include modules (``Module``), +namespaces (``Namespace``) and classes (``ClassDeclaration``). + +.. list-table:: Callable classes + :header-rows: 1 + + * - CodeQL class + - Description and selected predicates + * - ``Module`` + - A representation of a run-time `module` or `class` value. + + - `getADeclaration()` - Gets a declaration + - `getSuperClass()` - Gets the super class fo this module, if any. + - `getAPrependedModule()` - Gets a prepended module. + - `getAnIncludedModule()` - Gets an included module. + * - ``Namespace`` + - A class or module definition. + + - `getName()` - Gets the name of the module/class. + - `getAMethod()`, `getMethod(name)` - Gets a method in this namespace. + - `getAClass()`, `getClass(name)` - Gets a class in this namespace. + - `getAModule()`, `getModule(name)` - Gets a module in this namespace. + * - ``ClassDeclaration`` + - A class definition. + * - ``SingletonClass`` + - A definition of a singleton class on an object. + * - ``ModuleDeclaration`` + - A module definition. + * - ``Toplevel`` + - The node representing the entire Ruby source file. + +The following example lists all methods in the class `ApiController`: + +.. code-block:: ql + + import ruby + + from ClassDeclaration m + where m.getName() = "ApiController" + select m, m.getAMethod() + +Callables +~~~~~~~~~ + +`Callables` are elements that can be called, including methods and blocks. + +.. list-table:: Callable classes + :header-rows: 1 + + * - CodeQL class + - Description and main predicates + * - ``Callable`` + - A callable. + + - `getAParameter()` - gets a parameter of this callable. + - `getParameter(n)` - gets the nth parameter of this callable. + * - ``Private`` + - A call to ``private``. + * - ``Method`` + - A method. + + - `getName()` - gets the name of this method + * - ``SingletonMethod`` + - A singleton method. + * - ``Lambda`` + - A lambda (anonymous method). + * - ``Block`` + - A block. + * - ``DoBlock`` + - A block enclosed within `do` and `end`. + * - ``BraceBlock`` + - A block defined using curly braces. + +*Parameters* are the values that are passed into callables. Unlike other CodeQL language models, +parameters in Ruby are not variables themselves, but can introduce variables into the +callable. The variables of a parameter are given by the `getAVariable()` predicate. + +.. list-table:: Parameter classes + :header-rows: 1 + + * - CodeQL class + - Description and main predicates + * - ``Parameter`` + - A parameter. + + - `getCallable()` - Gets the callable that this parameter belongs to. + - `getPosition()` - Gets the zero-based position of this parameter. + - `getAVariable()`, `getVariable(name)` - Gets a variable introduced by this parameter. + * - ``PatternParameter`` + - A parameter defined using a pattern. + * - ``TuplePatternParameter`` + - A parameter defined using a tuple pattern. + * - ``NamedParameter`` + - A named parameter. + + - `getName()`, `hasName(name)` - Gets the name of this parameter. + - `getAnAccess()` - Gets an access to this parameter. + - `getDefiningAccess()` - Gets the access that defines the underlying local variable. + * - ``SimpleParameter`` + - A simple (normal) parameter. + * - ``BlockParameter`` + - A parameter that is a block. + * - ``HashSplatParameter`` + - A hash-splat (or double-splat) parameter. + * - ``KeywordParameter`` + - A keyword parameter, including a default value if the parameter is optional. + + - `getDefaultValue()` - Gets the default value, i.e. the value assigned to the parameter when one is not provided by the caller. + * - ``OptionalParameter`` + - An optional parameter. + + - `getDefaultValue()` - Gets the default value, i.e. the value assigned to the parameter when one is not provided by the caller. + * - ``SplatParameter`` + - A splat parameter. + + +Example + +.. code-block:: ql + + import ruby + + from Method m + where m.getName() = "show" + select m.getParameter(0) + +Statements +~~~~~~~~~~ + +Statements are the elements of code blocks. Statements that produce a value are called *expressions* +and have CodeQL class `Expr`. The remaining statement types (that do not produce values) are listed below. + +.. list-table:: Statement classes + :header-rows: 1 + + * - CodeQL class + - Description and main predicates + * - ``Stmt`` + - The base class for all statements. + + - `getAControlFlowNode()` - Gets a control-flow node for this statement, if any. + - `getEnclosingCallable()` - Gets the enclosing callable, if any. + * - ``EmptyStmt`` + - An empty statement. + * - ``BeginExpr`` + - A `begin` statement. + * - ``BeginBlock`` + - A `BEGIN` block. + * - ``EndBlock`` + - An `END` block. + * - ``UndefStmt`` + - An `undef` statement. + * - ``AliasStmt`` + - An `alias` statement. + * - ``ReturningStmt`` + - A statement that may return a value: `return`, `break` and `next`. + * - ``ReturnStmt`` + - A `return` statement. + * - ``BreakStmt`` + - A `break` statement. + * - ``NextStmt`` + - A `next` statement. + * - ``RedoStmt`` + - A `redo` statement. + * - ``RetryStmt`` + - A `retry` statement. + +The following example finds all literals that are returned by a `return` statement. + +.. code-block:: ql + + import ruby + + from ReturnStmt return, Literal lit + where lit.getParent() = return + select lit, "Returning a literal " + lit.getValueText() + +Expressions +~~~~~~~~~~~ + +Expressions are types of statement that evaluate to a value. The CodeQL class `Expr` is the base class of all expression types. + +.. list-table:: Expressions + :header-rows: 1 + + * - CodeQL class + - Description and main predicates + * - ``Expr`` + - An expression. + + This is the root class for all expressions. + + - `getValueText()` - Gets the textual (constant) value of this expression, if any. + * - ``Self`` + - A reference to the current object. + * - ``Pair`` + - A pair expression. + * - ``RescueClause`` + - A `rescue` clause. + * - ``RescueModifierExpr`` + - An expression with a `rescue` modifier. + * - ``StringConcatenation`` + - A concatenation of string literals. + + - `getConcatenatedValueText()` - Gets the result of concatenating all the string literals, if and only if they do not contain any interpolations. + +.. list-table:: Statement sequences + :header-rows: 1 + + * - CodeQL class + - Description + * - ``StmtSequence`` + - A sequence of expressions. + + - `getAStmt()`, `getStmt(n)` - Gets a statement in this sequence. + - `isEmpty()` - Holds if this sequence has no statements. + - `getNumberOfStatements()` - Gets the number of statements in this sequence. + * - ``BodyStmt`` + - A sequence of statements representing the body of a method, class, module, or do-block. + + - `getARescue()`, `getRescue(n)` - Gets a rescue clause in this block. + - `getElse()` - Gets the `else` clause in this block, if any. + - `getEnsure()` - Gets the `ensure` clause in this block, if any. + * - ``ParenthesizedExpr`` + - A parenthesized expression sequence, typically containing a single expression. + + +Literals are expressions that evaluate directly to the given value. The CodeQL Ruby library models all types of +Ruby literal. + +.. list-table:: Literals + :header-rows: 1 + + * - CodeQL class + - Description + * - ``Literal`` + - A literal. This is the base class for all literals. + + - `getValueText()` - Gets the source text for this literal, if this is a simple literal. + * - ``NumericLiteral`` + - A numerical literal. The literal types are ``IntegerLiteral``, ``FloatLiteral``, ``RationalLiteral``, and ``ComplexLiteral``. + * - ``NilLiteral`` + - A `nil` literal. + * - ``BooleanLiteral`` + - A Boolean value. The classes ``TrueLiteral`` and ``FalseLiteral`` match `true` and `false` respectively. + * - ``StringComponent`` + - A component of a string. Either a ``StringTextComponent``, ``StringEscapeSequenceComponent``, or ``StringInterpolationComponent``. + * - ``RegExpLiteral`` + - A regular expression literal. + * - ``SymbolLiteral`` + - A symbol literal. + * - ``SubshellLiteral`` + - A subshell literal. + * - ``CharacterLiteral`` + - A character literal. + * - ``ArrayLiteral`` + - An array literal. + * - ``HashLiteral`` + - A hash literal. + * - ``RangeLiteral`` + - A range literal. + * - ``MethodName`` + - A method name literal. + +The following example defines a string literal class containing the text "username": + +.. code-block:: ql + + class UsernameLiteral extends Literal + { + UsernameLiteral() { this.getValueText().toLowerCase().matches("%username%") } + } + + +*Operations* are types of expression that typically perform some sort of calculation. Most operations are ``MethodCalls`` because often +there is an underlying call to the operation. + +.. list-table:: Operations + :header-rows: 1 + + * - CodeQL class + - Description + * - ``Operation`` + - An operation. + * - ``UnaryOperation`` + - A unary operation. + + Types of unary operation include ``UnaryLogicalOperation``, ``NotExpr``, ``UnaryPlusExpr``, ``UnaryMinusExpr``, ``SplatExpr``, + ``HashSplatExpr``, ``UnaryBitwiseOperation``, and ``ComplementExpr``. + * - ``DefinedExpr`` + - A call to the special `defined?` operator + * - ``BinaryOperation`` + - A binary operation, that includes many other operation categories such as ``BinaryArithmeticOperation``, ``BinaryBitwiseOperation``, ``ComparisonOperation``, ``SpaceshipExpr``, and ``Assignment``. + * - ``BinaryArithmeticOperation`` + - A binary arithmetic operation. Includes: ``AddExpr``, ``SubExpr``, ``MulExpr``, ``DivExpr``, ``ModuloExpr``, and ``ExponentExpr``. + * - ``BinaryLogicalOperation`` + - A binary logical operation. Includes: ``LogicalAndExpr`` and ``LogicalOrExpr``. + * - ``BinaryBitwiseOperation`` + - A binary bitwise operation. Includes: ``LShiftExpr``, ``RShiftExpr``, ``BitwiseAndExpr``, ``BitwiseOrExpr``, and ``BitwiseXorExpr``. + * - ``ComparisonOperation`` + - A comparison operation, including the classes ``EqualityOperation``, ``EqExpr``, ``NEExpr``, ``CaseEqExpr``, ``RelationalOperation``, ``GTExpr``, ``GEExpr``, ``LTExpr``, and ``LEExpr``. + * - ``RegExpMatchExpr`` + - A regexp match expression. + * - ``NoRegExpMatchExpr`` + - A regexp-doesn't-match expression. + * - ``Assignment`` + - An assignment. Assignments are simple assignments (``AssignExpr``), or assignment operations (``AssignOperation``). + + The assignment arithmetic operations (``AssignArithmeticOperation``) are ``AssignAddExpr``, ``AssignSubExpr``, ``AssignMulExpr``, ``AssignDivExpr``, ``AssignModuloExpr``, and ``AssignExponentExpr``. + + The assignment logical operations (``AssignLogicalOperation``) are ``AssignLogicalAndExpr`` and ``AssignLogicalOrExpr``. + + The assignment bitwise operations (``AssignBitwiseOperation``) are ``AssignLShiftExpr``, ``AssignRShiftExpr``, ``AssignBitwiseAndExpr``, ``AssignBitwiseOrExpr``, and ``AssignBitwiseXorExpr``. + +The following example finds "chained assignments" (of the form ``A=B=C``): + +.. code-block:: ql + + import ruby + + from Assignment op + where op.getRightOperand() instanceof Assignment + select op, "This is a chained assignment." + +Calls pass control to another function, include explicit method calls (``MethodCall``), but also include other types of call such as `super` calls or `yield` calls. + +.. list-table:: Calls + :header-rows: 1 + + * - CodeQL class + - Description and main predicates + * - ``Call`` + - A call. + + - `getArgument(n)`, `getAnArgument()`, `getKeywordArgument(keyword)` - Gets an argument of this call. + - `getATarget()` - Gets a potential target of this call, if any. + * - ``MethodCall`` + - A method call. + + - `getReceiver()` - Gets the receiver of this call, if any. This is the object being invoked. + - `getMethodName()` - Gets the name of the method being called. + - `getBlock()` - Gets the block of this method call, if any. + * - ``SetterMethodCall`` + - A call to a setter method. + * - ``ElementReference`` + - An element reference; a call to the `[]` method. + * - ``YieldCall`` + - A call to `yield`. + * - ``SuperCall`` + - A call to `super`. + * - ``BlockArgument`` + - A block argument in a method call. + +The following example finds all method calls to a method called `delete`. + +.. code-block:: ql + + import ruby + + from MethodCall call + where call.getMethodName() = "delete" + select call, "Call to 'delete'." + +Control expressions are expressions used for control flow. They are classed as expressions because they can produce a value. + +.. list-table:: Control expressions + :header-rows: 1 + + * - CodeQL class + - Description and main predicates + * - ``ControlExpr`` + - A control expression, such as a `case`, `if`, `unless`, ternary-if (`?:`), `while`, `until` (including expression-modifier variants), and `for`. + * - ``ConditionalExpr`` + - A conditional expression. + + - `getCondition()` - Gets the condition expression. + * - ``IfExpr`` + - An `if` or `elsif` expression. + + - `getThen()` - Gets the `then` branch. + - `getElse()` - Gets the `elseif` or `else` branch. + * - ``UnlessExpr`` + - An `unless` expression. + * - ``IfModifierExpr`` + - An expression modified using `if`. + * - ``UnlessModifierExpr`` + - An expression modified using `unless`. + * - ``TernaryIfExpr`` + - A conditional expression using the ternary (`?:`) operator. + * - ``CaseExpr`` + - A `case` expression. + * - ``WhenExpr`` + - A `when` branch of a `case` expression. + * - ``Loop`` + - A loop. That is, a `for` loop, a `while` or `until` loop, or their expression-modifier variants. + * - ``ConditionalLoop`` + - A loop using a condition expression. That is, a `while` or `until` loop, or their expression-modifier variants. + + - `getCondition()` - Gets the condition expression of this loop. + * - ``WhileExpr`` + - A `while` loop. + * - ``UntilExpr`` + - An `until` loop. + * - ``WhileModifierExpr`` + - An expression looped using the `while` modifier. + * - ``UntilModifierExpr`` + - An expression looped using the `until` modifier. + * - ``ForExpr`` + - A `for` loop. + +The following example finds `if`-expressions that are missing a `then` branch. + +.. code-block:: ql + + import ruby + + from IfExpr expr + where not exists(expr.getThen()) + select expr, "This if-expression is redundant." + +Variables +~~~~~~~~~ + +*Variables* are names that hold values in a Ruby program. If you want to query *any* type +of variable, then use the ``Variable`` class, otherwise use one of the subclasses +``LocalVariable``, ``InstanceVariable``, ``ClassVariable`` or ``GlobalVariable``. + +Local variables have the scope of a single function or block, instance variables have the +scope of an object (like member variables), *class* variables have the scope of a class and are +shared between all instances of that class (like static variables), and *global* variables +have the scope of the entire program. + +.. list-table:: Variable classes + :header-rows: 1 + + * - CodeQL class + - Description and main predicates + * - ``Variable`` + - A variable declared in a scope. + + - `getName()`, `hasName(name)` - Gets the name of this variable. + - `getDeclaringScope()` - Gets the scope this variable is declared in. + - `getAnAccess()` - Gets an access to this variable. + * - ``LocalVariable`` + - A local variable. + * - ``InstanceVariable`` + - An instance variable. + * - ``ClassVariable`` + - A class variable. + * - ``GlobalVariable`` + - A global variable. + +The following example finds all class variables in the class `StaticController`: + +.. code-block:: ql + + import ruby + + from ClassDeclaration cd, ClassVariable v + where + v.getDeclaringScope() = cd and + cd.getName() = "StaticController" + select v, "This is a static variable in 'StaticController'." + +Variable accesses are the uses of a variable in the source code. Note that variables, and *uses* of variables are different concepts. +Variables are modelled using the ``Variable`` class, whereas uses of the variable are modelled using the ``VariableAccess`` class. +``Variable.getAnAccess()`` gets the accesses of a variable. + +Variable accesses come in two types: *reads* of the variable (a ``ReadAccess``), and *writes* to the variable (a ``WriteAccess``). +Accesses are a type of expression, so extend the ``Expr`` class. + +.. list-table:: Variable access classes + :header-rows: 1 + + * - CodeQL class + - Description and main predicates + * - ``VariableAccess`` + - An access to a variable. + + - `getVariable()` - Gets the variable that is accessed. + * - ``VariableReadAccess`` + - An access to a variable where the value is read. + * - ``VariableWriteAccess`` + - An access to a variable where the value is updated. + * - ``LocalVariableAccess`` + - An access to a local variable. + * - ``LocalVariableWriteAccess`` + - An access to a local variable where the value is updated. + * - ``LocalVariableReadAccess`` + - An access to a local variable where the value is read. + * - ``GlobalVariableAccess`` + - An access to a global variable where the value is updated. + * - ``InstanceVariableAccess`` + - An access to a global variable where the value is read. + * - ``InstanceVariableReadAccess`` + - An access to an instance variable. + * - ``InstanceVariableWriteAccess`` + - An access to an instance variable where the value is updated. + * - ``ClassVariableAccess`` + - An access to a class variable. + * - ``ClassVariableWriteAccess`` + - An access to a class variable where the value is updated. + * - ``ClassVariableReadAccess`` + - An access to a class variable where the value is read. + +The following example finds writes to class variables in the class `StaticController`: + +.. code-block:: ql + + import ruby + + from ClassVariableWriteAccess write, ClassDeclaration cd, ClassVariable v + where + v.getDeclaringScope() = cd and + cd.getName() = "StaticController" and + write.getVariable() = v + select write, "'StaticController' class variable is written here." \ No newline at end of file diff --git a/docs/codeql/reusables/ruby-further-reading.rst b/docs/codeql/reusables/ruby-further-reading.rst index 032eaff51124..2f12e7dfb74f 100644 --- a/docs/codeql/reusables/ruby-further-reading.rst +++ b/docs/codeql/reusables/ruby-further-reading.rst @@ -1,3 +1,3 @@ -- `CodeQL queries for Ruby `__ -- `Example queries for Ruby `__ +- `CodeQL queries for Ruby `__ +- `Example queries for Ruby `__ - `CodeQL library reference for Ruby `__ From f575139180ff586ade1352773c15ee206d65daa0 Mon Sep 17 00:00:00 2001 From: Calum Grant Date: Wed, 13 Oct 2021 09:10:46 +0100 Subject: [PATCH 03/14] Add Ruby to toctree --- docs/codeql/codeql-for-ruby.rst | 16 ++++++++++++++++ docs/codeql/codeql-language-guides/index.rst | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 docs/codeql/codeql-for-ruby.rst diff --git a/docs/codeql/codeql-for-ruby.rst b/docs/codeql/codeql-for-ruby.rst new file mode 100644 index 000000000000..eaf5e8c94e1e --- /dev/null +++ b/docs/codeql/codeql-for-ruby.rst @@ -0,0 +1,16 @@ +.. _codeql-for-ruby: + +CodeQL for Ruby +=============== + +Experiment and learn how to write effective and efficient queries for CodeQL databases generated from Ruby codebases. + +.. toctree:: + :hidden: + + basic-query-for-ruby-code + codeql-library-for-ruby + +- :doc:`Basic query for Ruby code `: Learn to write and run a simple CodeQL query using LGTM. + +- :doc:`CodeQL library for Ruby `: When you're analyzing a Ruby program, you can make use of the large collection of classes in the CodeQL library for Ruby. diff --git a/docs/codeql/codeql-language-guides/index.rst b/docs/codeql/codeql-language-guides/index.rst index 440b706a02ad..79f3f79ac54f 100644 --- a/docs/codeql/codeql-language-guides/index.rst +++ b/docs/codeql/codeql-language-guides/index.rst @@ -13,4 +13,4 @@ Experiment and learn how to write effective and efficient queries for CodeQL dat codeql-for-java codeql-for-javascript codeql-for-python - \ No newline at end of file + codeql-for-ruby From 59e4a6ff7b84d82e9224647a0736fe36957feb09 Mon Sep 17 00:00:00 2001 From: Calum Grant Date: Wed, 13 Oct 2021 09:23:04 +0100 Subject: [PATCH 04/14] Move file to correct location --- docs/codeql/{ => codeql-language-guides}/codeql-for-ruby.rst | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/codeql/{ => codeql-language-guides}/codeql-for-ruby.rst (100%) diff --git a/docs/codeql/codeql-for-ruby.rst b/docs/codeql/codeql-language-guides/codeql-for-ruby.rst similarity index 100% rename from docs/codeql/codeql-for-ruby.rst rename to docs/codeql/codeql-language-guides/codeql-for-ruby.rst From 2d61519ec6dee02e3e6e3939a6a59e1b2a8b2e83 Mon Sep 17 00:00:00 2001 From: Calum Grant <42069085+calumgrant@users.noreply.github.com> Date: Fri, 15 Oct 2021 18:01:56 +0100 Subject: [PATCH 05/14] Update docs/codeql/codeql-language-guides/basic-query-for-ruby-code.rst Co-authored-by: hubwriter --- .../codeql/codeql-language-guides/basic-query-for-ruby-code.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/codeql/codeql-language-guides/basic-query-for-ruby-code.rst b/docs/codeql/codeql-language-guides/basic-query-for-ruby-code.rst index a48933fc4cfa..d30064c1ec95 100644 --- a/docs/codeql/codeql-language-guides/basic-query-for-ruby-code.rst +++ b/docs/codeql/codeql-language-guides/basic-query-for-ruby-code.rst @@ -3,7 +3,7 @@ Basic query for Ruby code ========================= -Learn to write and run a simple CodeQL query using CodeQL. +Learn to write and run a simple CodeQL query. About the query --------------- From 579753b0fc27b623cead28e872d910cf83be411a Mon Sep 17 00:00:00 2001 From: Calum Grant <42069085+calumgrant@users.noreply.github.com> Date: Fri, 15 Oct 2021 18:02:09 +0100 Subject: [PATCH 06/14] Update docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst Co-authored-by: hubwriter --- docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst b/docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst index 146b4eb1d90b..eab0e5bae15b 100644 --- a/docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst +++ b/docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst @@ -41,7 +41,7 @@ The data flow library is imported using import codeql.ruby.DataFlow Data flow tracks the flow of data through the program, including through function calls (interprocedural data flow). -Data flow is particularly useful for security queries, where untrusted data flow to vulnerable parts of the program +Data flow is particularly useful for security queries, where untrusted data flows to vulnerable parts of the program to exploit it. Related to data flow, is the taint-tracking library, which finds how data can *influence* other values in a program, even when it is not copied exactly. From 86c5b5d94402c7dd91f9f773ee3509819969575e Mon Sep 17 00:00:00 2001 From: Calum Grant <42069085+calumgrant@users.noreply.github.com> Date: Fri, 15 Oct 2021 18:02:20 +0100 Subject: [PATCH 07/14] Update docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst Co-authored-by: hubwriter --- docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst b/docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst index eab0e5bae15b..3d462b58cc40 100644 --- a/docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst +++ b/docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst @@ -68,7 +68,7 @@ To summarise, the main Ruby modules are: * - ``codeql.ruby.TaintTracking`` - The taint tracking library -The CodeQL examples are only excerpts and are not meant to represent complete queries. +The CodeQL examples in this article are only excerpts and are not meant to represent complete queries. Abstract syntax --------------- From 48077a5757896a2ac7d61ade70d88fa4dec0714d Mon Sep 17 00:00:00 2001 From: Calum Grant <42069085+calumgrant@users.noreply.github.com> Date: Fri, 15 Oct 2021 18:02:32 +0100 Subject: [PATCH 08/14] Update docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst Co-authored-by: hubwriter --- docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst b/docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst index 3d462b58cc40..7968c9973d8e 100644 --- a/docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst +++ b/docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst @@ -74,7 +74,7 @@ Abstract syntax --------------- The abstract syntax tree (AST) represents the elements of the source code organised into a tree. The AST viewer -feature of Visual Studio Code shows the AST nodes, including the relevant CodeQL classes and predicates. +in Visual Studio Code shows the AST nodes, including the relevant CodeQL classes and predicates. All CodeQL AST classes inherit from the `AstNode` class, which provides the following member predicates to all AST classes: From bf5cc212e1084759933df76cdf9cb4087b6dd592 Mon Sep 17 00:00:00 2001 From: Calum Grant <42069085+calumgrant@users.noreply.github.com> Date: Fri, 15 Oct 2021 18:02:40 +0100 Subject: [PATCH 09/14] Update docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst Co-authored-by: hubwriter --- docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst b/docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst index 7968c9973d8e..905be333ba14 100644 --- a/docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst +++ b/docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst @@ -115,7 +115,7 @@ namespaces (``Namespace``) and classes (``ClassDeclaration``). - A representation of a run-time `module` or `class` value. - `getADeclaration()` - Gets a declaration - - `getSuperClass()` - Gets the super class fo this module, if any. + - `getSuperClass()` - Gets the super class of this module, if any. - `getAPrependedModule()` - Gets a prepended module. - `getAnIncludedModule()` - Gets an included module. * - ``Namespace`` From 5265ed6b640ffe7a61a71654a7810602bb71409d Mon Sep 17 00:00:00 2001 From: Calum Grant <42069085+calumgrant@users.noreply.github.com> Date: Fri, 15 Oct 2021 18:02:54 +0100 Subject: [PATCH 10/14] Update docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst Co-authored-by: hubwriter --- docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst b/docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst index 905be333ba14..12989d4c13f3 100644 --- a/docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst +++ b/docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst @@ -112,7 +112,7 @@ namespaces (``Namespace``) and classes (``ClassDeclaration``). * - CodeQL class - Description and selected predicates * - ``Module`` - - A representation of a run-time `module` or `class` value. + - A representation of a runtime `module` or `class` value. - `getADeclaration()` - Gets a declaration - `getSuperClass()` - Gets the super class of this module, if any. From 87adcc2e6b99e23f962bb80847a39a58c0427aeb Mon Sep 17 00:00:00 2001 From: Calum Grant <42069085+calumgrant@users.noreply.github.com> Date: Fri, 15 Oct 2021 18:03:04 +0100 Subject: [PATCH 11/14] Update docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst Co-authored-by: hubwriter --- docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst b/docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst index 12989d4c13f3..296d8e363854 100644 --- a/docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst +++ b/docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst @@ -73,7 +73,7 @@ The CodeQL examples in this article are only excerpts and are not meant to repre Abstract syntax --------------- -The abstract syntax tree (AST) represents the elements of the source code organised into a tree. The AST viewer +The abstract syntax tree (AST) represents the elements of the source code organized into a tree. The AST viewer in Visual Studio Code shows the AST nodes, including the relevant CodeQL classes and predicates. All CodeQL AST classes inherit from the `AstNode` class, which provides the following member predicates From 42c5af3cdf31fa7140fe2de76912ff15039d2ebb Mon Sep 17 00:00:00 2001 From: Calum Grant <42069085+calumgrant@users.noreply.github.com> Date: Fri, 15 Oct 2021 18:03:12 +0100 Subject: [PATCH 12/14] Update docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst Co-authored-by: hubwriter --- docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst b/docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst index 296d8e363854..c6bb83d83c25 100644 --- a/docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst +++ b/docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst @@ -48,7 +48,7 @@ in a program, even when it is not copied exactly. The API graphs library is used to locate methods in libraries. This is particuarly useful when locating particular functions or parameters that could be used as a source or sink of data in a security query. -To summarise, the main Ruby modules are: +To summarize, the main Ruby modules are: .. list-table:: Main Ruby modules :header-rows: 1 From 5861fcf4434aee9c72f7d04b1e79dfd28b94be6c Mon Sep 17 00:00:00 2001 From: Calum Grant Date: Fri, 15 Oct 2021 18:07:18 +0100 Subject: [PATCH 13/14] Address review comment --- docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst b/docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst index c6bb83d83c25..1119159314f3 100644 --- a/docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst +++ b/docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst @@ -30,7 +30,7 @@ The control flow graph (CFG) is imported using import codeql.ruby.CFG -The CFG reasons about the control flow between statements and expressions, for example whether one expression can +The CFG models the control flow between statements and expressions, for example whether one expression can flow to another expression, or whether an expression "dominates" another one, meaning that all paths to an expression must flow through another expression first. From 112d408fb94207a037507b87ef2e8d4a4313f7ae Mon Sep 17 00:00:00 2001 From: Calum Grant Date: Tue, 19 Oct 2021 16:30:54 +0100 Subject: [PATCH 14/14] Address review comments. --- .../codeql-language-guides/basic-query-for-ruby-code.rst | 6 ++---- .../codeql-language-guides/codeql-library-for-ruby.rst | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/docs/codeql/codeql-language-guides/basic-query-for-ruby-code.rst b/docs/codeql/codeql-language-guides/basic-query-for-ruby-code.rst index d30064c1ec95..7f0e6c79b124 100644 --- a/docs/codeql/codeql-language-guides/basic-query-for-ruby-code.rst +++ b/docs/codeql/codeql-language-guides/basic-query-for-ruby-code.rst @@ -108,14 +108,12 @@ Browsing the results of our basic query shows that it could be improved. Among t .. code-block:: ruby - if ... - ... - elsif option == "-verbose" + if option == "-verbose" # nothing to do - handled earlier else error "unrecognized option" -In this case, identifying the ``if`` statement with the empty ``then`` branch as redundant is a false positive. One solution to this is to modify the query to ignore empty ``then`` branches if the ``if`` statement has an ``else`` branch. +In this case, identifying the ``if`` statement with the empty ``then`` branch as redundant is a false positive. One solution to this is to modify the query to select ``if`` statements where both the ``then`` and ``else`` branches are missing. To exclude ``if`` statements that have an ``else`` branch: diff --git a/docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst b/docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst index 1119159314f3..ede3182fd56e 100644 --- a/docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst +++ b/docs/codeql/codeql-language-guides/codeql-library-for-ruby.rst @@ -73,7 +73,7 @@ The CodeQL examples in this article are only excerpts and are not meant to repre Abstract syntax --------------- -The abstract syntax tree (AST) represents the elements of the source code organized into a tree. The AST viewer +The abstract syntax tree (AST) represents the elements of the source code organized into a tree. The `AST viewer `__ in Visual Studio Code shows the AST nodes, including the relevant CodeQL classes and predicates. All CodeQL AST classes inherit from the `AstNode` class, which provides the following member predicates