mirror of
https://github.com/github/codeql.git
synced 2026-04-26 09:15:12 +02:00
Merge pull request #3043 from shati-patel/2173-tutorials
CodeQL docs migration prep: Update QL tutorials
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
Catch the fire starter: Classes and predicates
|
||||
==============================================
|
||||
Catch the fire starter
|
||||
======================
|
||||
|
||||
Learn about QL predicates and classes to solve your second mystery as a QL detective.
|
||||
|
||||
Just as you've successfully found the thief and returned the golden crown to the castle, another terrible crime is committed. Early in the morning, a few people start a fire in a field in the north of the village and destroy all the crops!
|
||||
|
||||
@@ -103,4 +105,48 @@ You know that the fire starters live in the south *and* that they must have been
|
||||
|
||||
➤ `See the answer in the query console <https://lgtm.com/query/2551838470440192723/>`__
|
||||
|
||||
Continue to the :doc:`next page <fire-2>` to gather more clues and find out which of your suspects started the fire...
|
||||
You can now continue to gather more clues and find out which of your suspects started the fire...
|
||||
|
||||
Identify the bald bandits
|
||||
-------------------------
|
||||
|
||||
You ask the northerners if they have any more information about the fire starters. Luckily, you have a witness! The farmer living next to the field saw two people run away just after the fire started. He only saw the tops of their heads, and noticed that they were both bald.
|
||||
|
||||
This is a very helpful clue. Remember that you wrote a QL query to select all bald people:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
from Person p
|
||||
where not exists (string c | p.getHairColor() = c)
|
||||
select p
|
||||
|
||||
To avoid having to type ``not exists (string c | p.getHairColor() = c)`` every time you want to select a bald person, you can instead define another new predicate ``isBald``.
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
predicate isBald(Person p) {
|
||||
not exists (string c | p.getHairColor() = c)
|
||||
}
|
||||
|
||||
The property ``isBald(p)`` holds whenever ``p`` is bald, so you can replace the previous query with:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
from Person p
|
||||
where isBald(p)
|
||||
select p
|
||||
|
||||
The predicate ``isBald`` is defined to take a ``Person``, so it can also take a ``Southerner``, as ``Southerner`` is a subtype of ``Person``. It can't take an ``int`` for example—that would cause an error.
|
||||
|
||||
You can now write a query to select the bald southerners who are allowed into the north.
|
||||
|
||||
➤ `See the answer in the query console <https://lgtm.com/query/2572701606358725253/>`__
|
||||
|
||||
You have found the two fire starters! They are arrested and the villagers are once again impressed with your work.
|
||||
|
||||
What next?
|
||||
----------
|
||||
|
||||
- Find out who will be the new ruler of the village in the :doc:`next tutorial <crown-the-rightful-heir>`.
|
||||
- Learn more about predicates and classes in the `QL language handbook <https://help.semmle.com/QL/ql-handbook/index.html>`__.
|
||||
- Explore the libraries that help you get data about code in :doc:`Learning CodeQL <../../index>`.
|
||||
@@ -1,7 +1,10 @@
|
||||
River crossing puzzle
|
||||
#####################
|
||||
Cross the river
|
||||
===============
|
||||
|
||||
The aim of this tutorial is to write a query that finds a solution to the following classical logic puzzle:
|
||||
Use common QL features to write a query that finds a solution to the "River crossing" logic puzzle.
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
.. pull-quote::
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
Crown the rightful heir
|
||||
=======================
|
||||
|
||||
This is a QL detective puzzle that shows you how to use recursion in QL to write more complex queries.
|
||||
|
||||
King Basil's heir
|
||||
-----------------
|
||||
|
||||
Phew! No more crimes in the village—you can finally leave the village and go home.
|
||||
|
||||
But then... During your last night in the village, the old king—the great King Basil—dies in his sleep and there is chaos everywhere!
|
||||
@@ -9,9 +14,6 @@ The king never married and he had no children, so nobody knows who should inheri
|
||||
|
||||
Eventually you decide to stay in the village to resolve the argument and find the true heir to the throne.
|
||||
|
||||
King Basil's heir
|
||||
-----------------
|
||||
|
||||
You want to find out if anyone in the village is actually related to the king. This seems like a difficult task at first, but you start work confidently. You know the villagers quite well by now, and you have a list of all the parents in the village and their children.
|
||||
|
||||
To find out more about the king and his family, you get access to the castle and find some old family trees. You also include these relations in your database to see if anyone in the king's family is still alive.
|
||||
@@ -136,7 +138,7 @@ To decide who should inherit the king's fortune, the villagers carefully read th
|
||||
|
||||
*"The heir to the throne is the closest living relative of the king. Any person with a criminal record will not be considered. If there are multiple candidates, the oldest person is the heir."*
|
||||
|
||||
As your final challenge, define a predicate ``hasCriminalRecord`` so that ``hasCriminalRecord(p)`` holds if ``p`` is any of the criminals you unmasked earlier (in the :doc:`Find the thief <find-thief-1>` and :doc:`Catch the fire starter <fire-1>` tutorials).
|
||||
As your final challenge, define a predicate ``hasCriminalRecord`` so that ``hasCriminalRecord(p)`` holds if ``p`` is any of the criminals you unmasked earlier (in the :doc:`Find the thief <find-the-thief>` and :doc:`Catch the fire starter <catch-the-fire-starter>` tutorials).
|
||||
|
||||
➤ `See the answer in the query console <https://lgtm.com/query/1820692755164273290/>`__
|
||||
|
||||
@@ -160,5 +162,5 @@ What next?
|
||||
----------
|
||||
|
||||
- Learn more about recursion in the `QL language handbook <https://help.semmle.com/QL/ql-handbook/index.html>`__.
|
||||
- Put your QL skills to the test and solve the :doc:`River crossing puzzle <../ql-etudes/river-crossing>`.
|
||||
- Put your QL skills to the test and solve the :doc:`River crossing puzzle <cross-the-river>`.
|
||||
- Start using QL to analyze projects. See :doc:`Learning CodeQL <../../index>` for a summary of the available languages and resources.
|
||||
297
docs/language/learn-ql/beginner/find-the-thief.rst
Normal file
297
docs/language/learn-ql/beginner/find-the-thief.rst
Normal file
@@ -0,0 +1,297 @@
|
||||
Find the thief
|
||||
==============
|
||||
|
||||
Take on the role of a detective to find the thief in this fictional village. You will learn how to use logical connectives, quantifiers, and aggregates in QL along the way.
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
There is a small village hidden away in the mountains. The village is divided into four parts—north, south, east, and west—and in the center stands a dark and mysterious castle... Inside the castle, locked away in the highest tower, lies the king's valuable golden crown. One night, a terrible crime is committed. A thief breaks into the tower and steals the crown!
|
||||
|
||||
You know that the thief must live in the village, since nobody else knew about the crown. After some expert detective work, you obtain a list of all the people in the village and some of their personal details.
|
||||
|
||||
+------+-----+------------+--------+----------+
|
||||
| Name | Age | Hair color | Height | Location |
|
||||
+======+=====+============+========+==========+
|
||||
| ... | ... | ... | ... | ... |
|
||||
+------+-----+------------+--------+----------+
|
||||
|
||||
Sadly, you still have no idea who could have stolen the crown so you walk around the village to find clues. The villagers act very suspiciously and you are convinced they have information about the thief. They refuse to share their knowledge with you directly, but they reluctantly agree to answer questions. They are still not very talkative and **only answer questions with 'yes' or 'no'**.
|
||||
|
||||
You start asking some creative questions and making notes of the answers so you can compare them with your information later:
|
||||
|
||||
+------+--------------------------------------------------------------------+--------+
|
||||
| | Question | Answer |
|
||||
+======+====================================================================+========+
|
||||
| (1) | Is the thief taller than 150 cm? | yes |
|
||||
+------+--------------------------------------------------------------------+--------+
|
||||
| (2) | Does the thief have blond hair? | no |
|
||||
+------+--------------------------------------------------------------------+--------+
|
||||
| (3) | Is the thief bald? | no |
|
||||
+------+--------------------------------------------------------------------+--------+
|
||||
| (4) | Is the thief younger than 30? | no |
|
||||
+------+--------------------------------------------------------------------+--------+
|
||||
| (5) | Does the thief live east of the castle? | yes |
|
||||
+------+--------------------------------------------------------------------+--------+
|
||||
| (6) | Does the thief have black or brown hair? | yes |
|
||||
+------+--------------------------------------------------------------------+--------+
|
||||
| (7) | Is the thief taller than 180cm and shorter than 190cm? | no |
|
||||
+------+--------------------------------------------------------------------+--------+
|
||||
| (8) | Is the thief the tallest person in the village? | no |
|
||||
+------+--------------------------------------------------------------------+--------+
|
||||
| (9) | Is the thief shorter than the average villager? | yes |
|
||||
+------+--------------------------------------------------------------------+--------+
|
||||
| (10) | Is the thief the oldest person in the eastern part of the village? | yes |
|
||||
+------+--------------------------------------------------------------------+--------+
|
||||
|
||||
There is too much information to search through by hand, so you decide to use your newly acquired QL skills to help you with your investigation...
|
||||
|
||||
#. Open the `query console <https://lgtm.com/query>`__ to get started.
|
||||
#. Select a language and a demo project. For this tutorial, any language and project will do.
|
||||
#. Delete the default code ``import <language> select "hello world"``.
|
||||
|
||||
QL libraries
|
||||
------------
|
||||
|
||||
We've defined a number of QL `predicates <https://help.semmle.com/QL/ql-handbook/predicates.html>`__ to help you extract data from your table. A QL predicate is a mini-query that expresses a relation between various pieces of data and describes some of their properties. In this case, the predicates give you information about a person, for example their height or age.
|
||||
|
||||
+--------------------+----------------------------------------------------------------------------------------+
|
||||
| Predicate | Description |
|
||||
+====================+========================================================================================+
|
||||
| ``getAge()`` | returns the age of the person (in years) as an ``int`` |
|
||||
+--------------------+----------------------------------------------------------------------------------------+
|
||||
| ``getHairColor()`` | returns the hair color of the person as a ``string`` |
|
||||
+--------------------+----------------------------------------------------------------------------------------+
|
||||
| ``getHeight()`` | returns the height of the person (in cm) as a ``float`` |
|
||||
+--------------------+----------------------------------------------------------------------------------------+
|
||||
| ``getLocation()`` | returns the location of the person's home (north, south, east or west) as a ``string`` |
|
||||
+--------------------+----------------------------------------------------------------------------------------+
|
||||
|
||||
We've stored these predicates in the QL library ``tutorial.qll``. To access this library, type ``import tutorial`` in the query console.
|
||||
|
||||
Libraries are convenient for storing commonly used predicates. This saves you from defining a predicate every time you need it. Instead you can just ``import`` the library and use the predicate directly. Once you have imported the library, you can apply any of these predicates to an expression by appending it.
|
||||
|
||||
For example, ``t.getHeight()`` applies ``getHeight()`` to ``t`` and returns the height of ``t``.
|
||||
|
||||
Start the search
|
||||
-----------------
|
||||
|
||||
The villagers answered "yes" to the question "Is the thief taller than 150cm?" To use this information, you can write the following query to list all villagers taller than 150cm. These are all possible suspects.
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
from Person t
|
||||
where t.getHeight() > 150
|
||||
select t
|
||||
|
||||
The first line, ``from Person t``, declares that ``t`` must be a ``Person``. We say that the `type <https://help.semmle.com/QL/ql-handbook/types.html>`__ of ``t`` is ``Person``.
|
||||
|
||||
Before you use the rest of your answers in your QL search, here are some more tools and examples to help you write your own QL queries:
|
||||
|
||||
Logical connectives
|
||||
-------------------
|
||||
|
||||
Using `logical connectives <https://help.semmle.com/QL/ql-handbook/formulas.html#logical-connectives>`__, you can write more complex queries that combine different pieces of information.
|
||||
|
||||
For example, if you know that the thief is older than 30 *and* has brown hair, you can use the following ``where`` clause to link two predicates:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
where t.getAge() > 30 and t.getHairColor() = "brown"
|
||||
|
||||
.. pull-quote::
|
||||
|
||||
Note
|
||||
|
||||
The predicate ``getHairColor()`` returns a ``string``, so we need to include quotation marks around the result ``"brown"``.
|
||||
|
||||
If the thief does *not* live north of the castle, you can use:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
where not t.getLocation() = "north"
|
||||
|
||||
If the thief has brown hair *or* black hair, you can use:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
where t.getHairColor() = "brown" or t.getHairColor() = "black"
|
||||
|
||||
You can also combine these connectives into longer statements:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
where t.getAge() > 30
|
||||
and (t.getHairColor() = "brown" or t.getHairColor() = "black")
|
||||
and not t.getLocation() = "north"
|
||||
|
||||
.. pull-quote::
|
||||
|
||||
Note
|
||||
|
||||
We've placed parentheses around the ``or`` clause to make sure that the query is evaluated as intended. Without parentheses, the connective ``and`` takes precedence over ``or``.
|
||||
|
||||
Predicates don't always return exactly one value. For example, if a person ``p`` has black hair which is turning gray, ``p.getHairColor()`` will return two values: black and gray.
|
||||
|
||||
What if the thief is bald? In that case, the thief has no hair, so the ``getHairColor()`` predicate simply doesn't return any results!
|
||||
|
||||
If you know that the thief definitely isn't bald, then there must be a color that matches the thief's hair color. One way to express this in QL is to introduce a new variable ``c`` of type ``string`` and select those ``t`` where ``t.getHairColor()`` matches a value of ``c``.
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
from Person t, string c
|
||||
where t.getHairColor() = c
|
||||
select t
|
||||
|
||||
Notice that we have only temporarily introduced the variable ``c`` and we didn't need it at all in the ``select`` clause. In this case, it is better to use ``exists``:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
from Person t
|
||||
where exists(string c | t.getHairColor() = c)
|
||||
select t
|
||||
|
||||
``exists`` introduces a temporary variable ``c`` of type ``string`` and holds only if there is at least one ``string c`` that satisfies ``t.getHairColor() = c``.
|
||||
|
||||
.. pull-quote::
|
||||
|
||||
Note
|
||||
|
||||
If you are familiar with logic, you may notice that ``exists`` in QL corresponds to the existential `quantifier <https://help.semmle.com/QL/ql-handbook/formulas.html#quantified-formulas>`__ in logic. QL also has a universal quantifier ``forall(vars | formula 1 | formula 2)`` which is logically equivalent to ``not exists(vars | formula 1 | not formula 2)``.
|
||||
|
||||
The real investigation
|
||||
----------------------
|
||||
|
||||
You are now ready to track down the thief! Using the examples above, write a query to find the people who satisfy the answers to the first eight questions:
|
||||
|
||||
+---+--------------------------------------------------------+--------+
|
||||
| | Question | Answer |
|
||||
+===+========================================================+========+
|
||||
| 1 | Is the thief taller than 150 cm? | yes |
|
||||
+---+--------------------------------------------------------+--------+
|
||||
| 2 | Does the thief have blond hair? | no |
|
||||
+---+--------------------------------------------------------+--------+
|
||||
| 3 | Is the thief bald? | no |
|
||||
+---+--------------------------------------------------------+--------+
|
||||
| 4 | Is the thief younger than 30? | no |
|
||||
+---+--------------------------------------------------------+--------+
|
||||
| 5 | Does the thief live east of the castle? | yes |
|
||||
+---+--------------------------------------------------------+--------+
|
||||
| 6 | Does the thief have black or brown hair? | yes |
|
||||
+---+--------------------------------------------------------+--------+
|
||||
| 7 | Is the thief taller than 180cm and shorter than 190cm? | no |
|
||||
+---+--------------------------------------------------------+--------+
|
||||
| 8 | Is the thief the oldest person in the village? | no |
|
||||
+---+--------------------------------------------------------+--------+
|
||||
|
||||
Hints
|
||||
^^^^^
|
||||
|
||||
#. Don't forget to ``import tutorial``!
|
||||
#. Translate each question into QL separately. Look at the examples above if you get stuck.
|
||||
#. For question 3, remember that a bald person does not have a hair color.
|
||||
#. For question 8, note that if a person is *not* the oldest, then there is at least one person who is older than them.
|
||||
#. Combine the conditions using logical connectives to get a query of the form:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
import tutorial
|
||||
|
||||
from Person t
|
||||
where <condition 1> and
|
||||
not <condition 2> and
|
||||
...
|
||||
select t
|
||||
|
||||
Once you have finished, you will have a list of possible suspects. One of those people must be the thief!
|
||||
|
||||
➤ `See the answer in the query console <https://lgtm.com/query/1505743955992/>`__
|
||||
|
||||
.. pull-quote::
|
||||
|
||||
Note
|
||||
|
||||
In the answer, we used ``/*`` and ``*/`` to label the different parts of the query. Any text surrounded by ``/*`` and ``*/`` is not evaluated as part of the QL code, but is just a *comment*.
|
||||
|
||||
You are getting closer to solving the mystery! Unfortunately, you still have quite a long list of suspects... To find out which of your suspects is the thief, you must gather more information and refine your query in the next step.
|
||||
|
||||
More advanced queries
|
||||
---------------------
|
||||
|
||||
What if you want to find the oldest, youngest, tallest, or shortest person in the village? As mentioned in the previous topic, you can do this using ``exists``. However, there is also a more efficient way to do this in QL using functions like ``max`` and ``min``. These are examples of `aggregates <https://help.semmle.com/QL/ql-handbook/expressions.html#aggregations>`__.
|
||||
|
||||
In general, an aggregate is a function that performs an operation on multiple pieces of data and returns a single value as its output. Common aggregates are ``count``, ``max``, ``min``, ``avg`` (average) and ``sum``. The general way to use an aggregate is:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
<aggregate>(<variable declarations> | <logical formula> | <expression>)
|
||||
|
||||
For example, you can use the ``max`` aggregate to find the age of the oldest person in the village:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
max(int i | exists(Person p | p.getAge() = i) | i)
|
||||
|
||||
This aggregate considers all integers ``i``, limits ``i`` to values that match the ages of people in the village, and then returns the largest matching integer.
|
||||
|
||||
But how can you use this in an actual query?
|
||||
|
||||
If the thief is the oldest person in the village, then you know that the thief's age is equal to the maximum age of the villagers:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
from Person t
|
||||
where t.getAge() = max(int i | exists(Person p | p.getAge() = i) | i)
|
||||
select t
|
||||
|
||||
This general aggregate syntax is quite long and inconvenient. In most cases, you can omit certain parts of the aggregate. A particularly helpful QL feature is *ordered aggregation*. This allows you to order the expression using ``order by``.
|
||||
|
||||
For example, selecting the oldest villager becomes much simpler if you use an ordered aggregate.
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
select max(Person p | | p order by p.getAge())
|
||||
|
||||
The ordered aggregate considers every person ``p`` and selects the person with the maximum age. In this case, there are no restrictions on what people to consider, so the ``<logical formula>`` clause is empty. Note that if there are several people with the same maximum age, the query lists all of them.
|
||||
|
||||
Here are some more examples of aggregates:
|
||||
|
||||
+-------------------------------------------------------------------------+---------------------------------------------------+
|
||||
| Example | Result |
|
||||
+=========================================================================+===================================================+
|
||||
| ``min(Person p | p.getLocation() = "east" | p order by p.getHeight())`` | shortest person in the east of the village |
|
||||
+-------------------------------------------------------------------------+---------------------------------------------------+
|
||||
| ``count(Person p | p.getLocation() = "south" | p)`` | number of people in the south of the village |
|
||||
+-------------------------------------------------------------------------+---------------------------------------------------+
|
||||
| ``avg(Person p | | p.getHeight())`` | average height of the villagers |
|
||||
+-------------------------------------------------------------------------+---------------------------------------------------+
|
||||
| ``sum(Person p | p.getHairColor() = "brown" | p.getAge())`` | combined age of all the villagers with brown hair |
|
||||
+-------------------------------------------------------------------------+---------------------------------------------------+
|
||||
|
||||
Capture the culprit
|
||||
-------------------
|
||||
|
||||
You can now translate the remaining questions into QL:
|
||||
|
||||
+-----+--------------------------------------------------------------------+--------+
|
||||
| | Question | Answer |
|
||||
+=====+====================================================================+========+
|
||||
| ... | ... | ... |
|
||||
+-----+--------------------------------------------------------------------+--------+
|
||||
| 9 | Is the thief the tallest person in the village? | no |
|
||||
+-----+--------------------------------------------------------------------+--------+
|
||||
| 10 | Is the thief shorter than the average villager? | yes |
|
||||
+-----+--------------------------------------------------------------------+--------+
|
||||
| 11 | Is the thief the oldest person in the eastern part of the village? | yes |
|
||||
+-----+--------------------------------------------------------------------+--------+
|
||||
|
||||
Have you found the thief?
|
||||
|
||||
➤ `See the answer in the query console <https://lgtm.com/query/1505744186085/>`__
|
||||
|
||||
What next?
|
||||
----------
|
||||
|
||||
- Help the villagers track down another criminal in the :doc:`next tutorial <catch-the-fire-starter>`.
|
||||
- Find out more about the concepts you discovered in this tutorial in the `QL language handbook <https://help.semmle.com/QL/ql-handbook/index.html>`__.
|
||||
- Explore the libraries that help you get data about code in :doc:`Learning CodeQL <../../index>`.
|
||||
@@ -1,71 +0,0 @@
|
||||
Find the thief: Introduction
|
||||
============================
|
||||
|
||||
There is a small village hidden away in the mountains. The village is divided into four parts—north, south, east, and west—and in the center stands a dark and mysterious castle... Inside the castle, locked away in the highest tower, lies the king's valuable golden crown. One night, a terrible crime is committed. A thief breaks into the tower and steals the crown!
|
||||
|
||||
You know that the thief must live in the village, since nobody else knew about the crown. After some expert detective work, you obtain a list of all the people in the village and some of their personal details.
|
||||
|
||||
+------+-----+------------+--------+----------+
|
||||
| Name | Age | Hair color | Height | Location |
|
||||
+======+=====+============+========+==========+
|
||||
| ... | ... | ... | ... | ... |
|
||||
+------+-----+------------+--------+----------+
|
||||
|
||||
Sadly, you still have no idea who could have stolen the crown so you walk around the village to find clues. The villagers act very suspiciously and you are convinced they have information about the thief. They refuse to share their knowledge with you directly, but they reluctantly agree to answer questions. They are still not very talkative and **only answer questions with 'yes' or 'no'**.
|
||||
|
||||
You start asking some creative questions and making notes of the answers so you can compare them with your information later:
|
||||
|
||||
+------+--------------------------------------------------------------------+--------+
|
||||
| | Question | Answer |
|
||||
+======+====================================================================+========+
|
||||
| (1) | Is the thief taller than 150 cm? | yes |
|
||||
+------+--------------------------------------------------------------------+--------+
|
||||
| (2) | Does the thief have blond hair? | no |
|
||||
+------+--------------------------------------------------------------------+--------+
|
||||
| (3) | Is the thief bald? | no |
|
||||
+------+--------------------------------------------------------------------+--------+
|
||||
| (4) | Is the thief younger than 30? | no |
|
||||
+------+--------------------------------------------------------------------+--------+
|
||||
| (5) | Does the thief live east of the castle? | yes |
|
||||
+------+--------------------------------------------------------------------+--------+
|
||||
| (6) | Does the thief have black or brown hair? | yes |
|
||||
+------+--------------------------------------------------------------------+--------+
|
||||
| (7) | Is the thief taller than 180cm and shorter than 190cm? | no |
|
||||
+------+--------------------------------------------------------------------+--------+
|
||||
| (8) | Is the thief the tallest person in the village? | no |
|
||||
+------+--------------------------------------------------------------------+--------+
|
||||
| (9) | Is the thief shorter than the average villager? | yes |
|
||||
+------+--------------------------------------------------------------------+--------+
|
||||
| (10) | Is the thief the oldest person in the eastern part of the village? | yes |
|
||||
+------+--------------------------------------------------------------------+--------+
|
||||
|
||||
There is too much information to search through by hand, so you decide to use your newly acquired QL skills to help you with your investigation...
|
||||
|
||||
#. Open the `query console <https://lgtm.com/query>`__ to get started.
|
||||
#. Select a language and a demo project. For this tutorial, any language and project will do.
|
||||
#. Delete the default code ``import <language> select "hello world"``.
|
||||
|
||||
QL libraries
|
||||
------------
|
||||
|
||||
We've defined a number of QL `predicates <https://help.semmle.com/QL/ql-handbook/predicates.html>`__ to help you extract data from your table. A QL predicate is a mini-query that expresses a relation between various pieces of data and describes some of their properties. In this case, the predicates give you information about a person, for example their height or age.
|
||||
|
||||
+--------------------+----------------------------------------------------------------------------------------+
|
||||
| Predicate | Description |
|
||||
+====================+========================================================================================+
|
||||
| ``getAge()`` | returns the age of the person (in years) as an ``int`` |
|
||||
+--------------------+----------------------------------------------------------------------------------------+
|
||||
| ``getHairColor()`` | returns the hair color of the person as a ``string`` |
|
||||
+--------------------+----------------------------------------------------------------------------------------+
|
||||
| ``getHeight()`` | returns the height of the person (in cm) as a ``float`` |
|
||||
+--------------------+----------------------------------------------------------------------------------------+
|
||||
| ``getLocation()`` | returns the location of the person's home (north, south, east or west) as a ``string`` |
|
||||
+--------------------+----------------------------------------------------------------------------------------+
|
||||
|
||||
We've stored these predicates in the QL library ``tutorial.qll``. To access this library, type ``import tutorial`` in the query console.
|
||||
|
||||
Libraries are convenient for storing commonly used predicates. This saves you from defining a predicate every time you need it. Instead you can just ``import`` the library and use the predicate directly. Once you have imported the library, you can apply any of these predicates to an expression by appending it.
|
||||
|
||||
For example, ``t.getHeight()`` applies ``getHeight()`` to ``t`` and returns the height of ``t``.
|
||||
|
||||
Continue to the next page to :doc:`start the investigation <find-thief-2>`.
|
||||
@@ -1,141 +0,0 @@
|
||||
Find the thief: Start the search
|
||||
================================
|
||||
|
||||
The villagers answered "yes" to the question "Is the thief taller than 150cm?" To use this information, you can write the following query to list all villagers taller than 150cm. These are all possible suspects.
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
from Person t
|
||||
where t.getHeight() > 150
|
||||
select t
|
||||
|
||||
The first line, ``from Person t``, declares that ``t`` must be a ``Person``. We say that the `type <https://help.semmle.com/QL/ql-handbook/types.html>`__ of ``t`` is ``Person``.
|
||||
|
||||
Before you use the rest of your answers in your QL search, here are some more tools and examples to help you write your own QL queries:
|
||||
|
||||
Logical connectives
|
||||
-------------------
|
||||
|
||||
Using `logical connectives <https://help.semmle.com/QL/ql-handbook/formulas.html#logical-connectives>`__, you can write more complex queries that combine different pieces of information.
|
||||
|
||||
For example, if you know that the thief is older than 30 *and* has brown hair, you can use the following ``where`` clause to link two predicates:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
where t.getAge() > 30 and t.getHairColor() = "brown"
|
||||
|
||||
.. pull-quote::
|
||||
|
||||
Note
|
||||
|
||||
The predicate ``getHairColor()`` returns a ``string``, so we need to include quotation marks around the result ``"brown"``.
|
||||
|
||||
If the thief does *not* live north of the castle, you can use:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
where not t.getLocation() = "north"
|
||||
|
||||
If the thief has brown hair *or* black hair, you can use:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
where t.getHairColor() = "brown" or t.getHairColor() = "black"
|
||||
|
||||
You can also combine these connectives into longer statements:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
where t.getAge() > 30
|
||||
and (t.getHairColor() = "brown" or t.getHairColor() = "black")
|
||||
and not t.getLocation() = "north"
|
||||
|
||||
.. pull-quote::
|
||||
|
||||
Note
|
||||
|
||||
We've placed parentheses around the ``or`` clause to make sure that the query is evaluated as intended. Without parentheses, the connective ``and`` takes precedence over ``or``.
|
||||
|
||||
Predicates don't always return exactly one value. For example, if a person ``p`` has black hair which is turning gray, ``p.getHairColor()`` will return two values: black and gray.
|
||||
|
||||
What if the thief is bald? In that case, the thief has no hair, so the ``getHairColor()`` predicate simply doesn't return any results!
|
||||
|
||||
If you know that the thief definitely isn't bald, then there must be a color that matches the thief's hair color. One way to express this in QL is to introduce a new variable ``c`` of type ``string`` and select those ``t`` where ``t.getHairColor()`` matches a value of ``c``.
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
from Person t, string c
|
||||
where t.getHairColor() = c
|
||||
select t
|
||||
|
||||
Notice that we have only temporarily introduced the variable ``c`` and we didn't need it at all in the ``select`` clause. In this case, it is better to use ``exists``:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
from Person t
|
||||
where exists(string c | t.getHairColor() = c)
|
||||
select t
|
||||
|
||||
``exists`` introduces a temporary variable ``c`` of type ``string`` and holds only if there is at least one ``string c`` that satisfies ``t.getHairColor() = c``.
|
||||
|
||||
.. pull-quote::
|
||||
|
||||
Note
|
||||
|
||||
If you are familiar with logic, you may notice that ``exists`` in QL corresponds to the existential `quantifier <https://help.semmle.com/QL/ql-handbook/formulas.html#quantified-formulas>`__ in logic. QL also has a universal quantifier ``forall(vars | formula 1 | formula 2)`` which is logically equivalent to ``not exists(vars | formula 1 | not formula 2)``.
|
||||
|
||||
The real investigation
|
||||
----------------------
|
||||
|
||||
You are now ready to track down the thief! Using the examples above, write a query to find the people who satisfy the answers to the first eight questions:
|
||||
|
||||
+---+--------------------------------------------------------+--------+
|
||||
| | Question | Answer |
|
||||
+===+========================================================+========+
|
||||
| 1 | Is the thief taller than 150 cm? | yes |
|
||||
+---+--------------------------------------------------------+--------+
|
||||
| 2 | Does the thief have blond hair? | no |
|
||||
+---+--------------------------------------------------------+--------+
|
||||
| 3 | Is the thief bald? | no |
|
||||
+---+--------------------------------------------------------+--------+
|
||||
| 4 | Is the thief younger than 30? | no |
|
||||
+---+--------------------------------------------------------+--------+
|
||||
| 5 | Does the thief live east of the castle? | yes |
|
||||
+---+--------------------------------------------------------+--------+
|
||||
| 6 | Does the thief have black or brown hair? | yes |
|
||||
+---+--------------------------------------------------------+--------+
|
||||
| 7 | Is the thief taller than 180cm and shorter than 190cm? | no |
|
||||
+---+--------------------------------------------------------+--------+
|
||||
| 8 | Is the thief the oldest person in the village? | no |
|
||||
+---+--------------------------------------------------------+--------+
|
||||
|
||||
Hints
|
||||
^^^^^
|
||||
|
||||
#. Don't forget to ``import tutorial``!
|
||||
#. Translate each question into QL separately. Look at the examples above if you get stuck.
|
||||
#. For question 3, remember that a bald person does not have a hair color.
|
||||
#. For question 8, note that if a person is *not* the oldest, then there is at least one person who is older than them.
|
||||
#. Combine the conditions using logical connectives to get a query of the form:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
import tutorial
|
||||
|
||||
from Person t
|
||||
where <condition 1> and
|
||||
not <condition 2> and
|
||||
...
|
||||
select t
|
||||
|
||||
Once you have finished, you will have a list of possible suspects. One of those people must be the thief!
|
||||
|
||||
➤ `See the answer in the query console <https://lgtm.com/query/1505743955992/>`__
|
||||
|
||||
.. pull-quote::
|
||||
|
||||
Note
|
||||
|
||||
In the answer, we used ``/*`` and ``*/`` to label the different parts of the query. Any text surrounded by ``/*`` and ``*/`` is not evaluated as part of the QL code, but is just a *comment*.
|
||||
|
||||
You are getting closer to solving the mystery! Unfortunately, you still have quite a long list of suspects... To find out which of your suspects is the thief, you must gather more information and refine your query in the :doc:`next step <find-thief-3>`.
|
||||
@@ -1,80 +0,0 @@
|
||||
Find the thief: More advanced queries
|
||||
=====================================
|
||||
|
||||
What if you want to find the oldest, youngest, tallest, or shortest person in the village? As mentioned in the previous topic, you can do this using ``exists``. However, there is also a more efficient way to do this in QL using functions like ``max`` and ``min``. These are examples of `aggregates <https://help.semmle.com/QL/ql-handbook/expressions.html#aggregations>`__.
|
||||
|
||||
In general, an aggregate is a function that performs an operation on multiple pieces of data and returns a single value as its output. Common aggregates are ``count``, ``max``, ``min``, ``avg`` (average) and ``sum``. The general way to use an aggregate is:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
<aggregate>(<variable declarations> | <logical formula> | <expression>)
|
||||
|
||||
For example, you can use the ``max`` aggregate to find the age of the oldest person in the village:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
max(int i | exists(Person p | p.getAge() = i) | i)
|
||||
|
||||
This aggregate considers all integers ``i``, limits ``i`` to values that match the ages of people in the village, and then returns the largest matching integer.
|
||||
|
||||
But how can you use this in an actual query?
|
||||
|
||||
If the thief is the oldest person in the village, then you know that the thief's age is equal to the maximum age of the villagers:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
from Person t
|
||||
where t.getAge() = max(int i | exists(Person p | p.getAge() = i) | i)
|
||||
select t
|
||||
|
||||
This general aggregate syntax is quite long and inconvenient. In most cases, you can omit certain parts of the aggregate. A particularly helpful QL feature is *ordered aggregation*. This allows you to order the expression using ``order by``.
|
||||
|
||||
For example, selecting the oldest villager becomes much simpler if you use an ordered aggregate.
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
select max(Person p | | p order by p.getAge())
|
||||
|
||||
The ordered aggregate considers every person ``p`` and selects the person with the maximum age. In this case, there are no restrictions on what people to consider, so the ``<logical formula>`` clause is empty. Note that if there are several people with the same maximum age, the query lists all of them.
|
||||
|
||||
Here are some more examples of aggregates:
|
||||
|
||||
+-------------------------------------------------------------------------+---------------------------------------------------+
|
||||
| Example | Result |
|
||||
+=========================================================================+===================================================+
|
||||
| ``min(Person p | p.getLocation() = "east" | p order by p.getHeight())`` | shortest person in the east of the village |
|
||||
+-------------------------------------------------------------------------+---------------------------------------------------+
|
||||
| ``count(Person p | p.getLocation() = "south" | p)`` | number of people in the south of the village |
|
||||
+-------------------------------------------------------------------------+---------------------------------------------------+
|
||||
| ``avg(Person p | | p.getHeight())`` | average height of the villagers |
|
||||
+-------------------------------------------------------------------------+---------------------------------------------------+
|
||||
| ``sum(Person p | p.getHairColor() = "brown" | p.getAge())`` | combined age of all the villagers with brown hair |
|
||||
+-------------------------------------------------------------------------+---------------------------------------------------+
|
||||
|
||||
Capture the culprit
|
||||
-------------------
|
||||
|
||||
You can now translate the remaining questions into QL:
|
||||
|
||||
+-----+--------------------------------------------------------------------+--------+
|
||||
| | Question | Answer |
|
||||
+=====+====================================================================+========+
|
||||
| ... | ... | ... |
|
||||
+-----+--------------------------------------------------------------------+--------+
|
||||
| 9 | Is the thief the tallest person in the village? | no |
|
||||
+-----+--------------------------------------------------------------------+--------+
|
||||
| 10 | Is the thief shorter than the average villager? | yes |
|
||||
+-----+--------------------------------------------------------------------+--------+
|
||||
| 11 | Is the thief the oldest person in the eastern part of the village? | yes |
|
||||
+-----+--------------------------------------------------------------------+--------+
|
||||
|
||||
Have you found the thief?
|
||||
|
||||
➤ `See the answer in the query console <https://lgtm.com/query/1505744186085/>`__
|
||||
|
||||
What next?
|
||||
----------
|
||||
|
||||
- Help the villagers track down another criminal in the :doc:`next tutorial <fire-1>`.
|
||||
- Find out more about the concepts you discovered in this tutorial in the `QL language handbook <https://help.semmle.com/QL/ql-handbook/index.html>`__.
|
||||
- Explore the libraries that help you get data about code in :doc:`Learning CodeQL <../../index>`.
|
||||
@@ -1,43 +0,0 @@
|
||||
Catch the fire starter: Bald bandits
|
||||
====================================
|
||||
|
||||
You ask the northerners if they have any more information about the fire starters. Luckily, you have a witness! The farmer living next to the field saw two people run away just after the fire started. He only saw the tops of their heads, and noticed that they were both bald.
|
||||
|
||||
This is a very helpful clue. Remember that you wrote a QL query to select all bald people:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
from Person p
|
||||
where not exists (string c | p.getHairColor() = c)
|
||||
select p
|
||||
|
||||
To avoid having to type ``not exists (string c | p.getHairColor() = c)`` every time you want to select a bald person, you can instead define another new predicate ``isBald``.
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
predicate isBald(Person p) {
|
||||
not exists (string c | p.getHairColor() = c)
|
||||
}
|
||||
|
||||
The property ``isBald(p)`` holds whenever ``p`` is bald, so you can replace the previous query with:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
from Person p
|
||||
where isBald(p)
|
||||
select p
|
||||
|
||||
The predicate ``isBald`` is defined to take a ``Person``, so it can also take a ``Southerner``, as ``Southerner`` is a subtype of ``Person``. It can't take an ``int`` for example—that would cause an error.
|
||||
|
||||
You can now write a query to select the bald southerners who are allowed into the north.
|
||||
|
||||
➤ `See the answer in the query console <https://lgtm.com/query/2572701606358725253/>`__
|
||||
|
||||
You have found the two fire starters! They are arrested and the villagers are once again impressed with your work.
|
||||
|
||||
What next?
|
||||
----------
|
||||
|
||||
- Find out who will be the new ruler of the village in the :doc:`next tutorial <heir>`.
|
||||
- Learn more about predicates and classes in the `QL language handbook <https://help.semmle.com/QL/ql-handbook/index.html>`__.
|
||||
- Explore the libraries that help you get data about code in :doc:`Learning CodeQL <../../index>`.
|
||||
@@ -1,27 +1,13 @@
|
||||
QL detective tutorials
|
||||
======================
|
||||
QL tutorials
|
||||
============
|
||||
|
||||
Solve puzzles to learn the basics of QL before you analyze code with CodeQL. The tutorials teach you how to write queries and introduce you to key logic concepts along the way.
|
||||
|
||||
Before starting these tutorials, you can read the :doc:`Introduction to QL <../introduction-to-ql>` for a description of the language and some simple examples.
|
||||
|
||||
.. toctree::
|
||||
:glob:
|
||||
:hidden:
|
||||
|
||||
./*
|
||||
|
||||
Welcome to the detective tutorials! These are aimed at complete beginners who would like to learn the basics of QL,
|
||||
before analyzing code with CodeQL.
|
||||
The tutorials teach you how to write queries and introduce you to key logic concepts along the way.
|
||||
|
||||
We recommend you first read the :doc:`Introduction to QL <../introduction-to-ql>` page for a description of the language and
|
||||
some simple examples.
|
||||
|
||||
Currently the following detective tutorials are available:
|
||||
|
||||
- :doc:`Find the thief <find-thief-1>`—a three part mystery that introduces logical connectives, quantifiers, and aggregates
|
||||
- :doc:`Catch the fire starter <fire-1>`—an intriguing search that introduces predicates and classes
|
||||
- :doc:`Crown the rightful heir <heir>`—a detective puzzle that introduces recursion
|
||||
|
||||
Further resources
|
||||
-----------------
|
||||
|
||||
- For a summary of available learning resources, see :doc:`Learning CodeQL <../../index>`.
|
||||
- For an overview of the important concepts in QL, see the `QL language handbook <https://help.semmle.com/QL/ql-handbook/index.html>`__.
|
||||
find-the-thief
|
||||
catch-the-fire-starter
|
||||
crown-the-rightful-heir
|
||||
cross-the-river
|
||||
|
||||
@@ -34,7 +34,6 @@ If you are new to QL, start by looking at the following topics:
|
||||
introduction-to-ql
|
||||
about-ql
|
||||
beginner/ql-tutorials
|
||||
ql-etudes/river-crossing
|
||||
|
||||
CodeQL training and variant analysis examples
|
||||
*********************************************
|
||||
|
||||
@@ -163,6 +163,6 @@ Java
|
||||
Learning CodeQL
|
||||
---------------
|
||||
|
||||
- To find out more about how to write your own queries, try working through the :doc:`QL detective tutorials <beginner/ql-tutorials>`.
|
||||
- To find out more about how to write your own queries, try working through the :doc:`QL tutorials <beginner/ql-tutorials>`.
|
||||
- For an overview of the other available resources, see :doc:`Learning CodeQL <../index>`.
|
||||
- For a more technical description of the underlying language, see :doc:`About QL <about-ql>`.
|
||||
|
||||
@@ -240,7 +240,7 @@ and the global namespaces. (You can think of global namespaces as the enclosing
|
||||
Let's see what the module, type, and predicate namespaces look like in a concrete example:
|
||||
|
||||
For example, you could define a library module ``Villagers`` containing some of the classes and predicates that
|
||||
were defined in the `QL detective tutorials <https://help.semmle.com/QL/learn-ql/beginner/ql-tutorials.html>`_:
|
||||
were defined in the `QL tutorials <https://help.semmle.com/QL/learn-ql/beginner/ql-tutorials.html>`_:
|
||||
|
||||
**Villagers.qll**
|
||||
|
||||
|
||||
Reference in New Issue
Block a user