├── 1 ├── challenge-1 │ └── instructions.md ├── challenge-2 │ └── instructions.md └── challenge-3 │ └── instructions.md ├── 2 ├── challenge-1 │ ├── code-injection.py │ ├── command-injection.py │ ├── instructions.md │ ├── sql-injection-flask.py │ └── sql-injection.py ├── challenge-10 │ └── instructions.md ├── challenge-11 │ └── instructions.md ├── challenge-12 │ └── intructions.md ├── challenge-2 │ └── instructions.md ├── challenge-3 │ └── instructions.md ├── challenge-4 │ ├── call.ql │ └── instructions.md ├── challenge-5 │ ├── call-eval.ql │ └── intructions.md ├── challenge-6 │ ├── function.ql │ └── instructions.md ├── challenge-7 │ ├── call-eval-predicate.ql │ └── intructions.md ├── challenge-8 │ └── intructions.md └── challenge-9 │ ├── function-command-class.ql │ ├── function-command-predicate.ql │ ├── function-command.ql │ └── intructions.md ├── 3 ├── 1 │ ├── instructions.md │ └── query.ql ├── 2 │ ├── instructions.md │ └── query.ql ├── 3 │ ├── instructions.md │ └── query.ql ├── 4 │ ├── instructions.md │ └── query.ql ├── 5 │ ├── instructions.md │ └── query.ql ├── 6 │ ├── instructions.md │ └── query.ql ├── 7 │ └── instructions.md ├── 8 │ ├── instructions.md │ └── query.ql ├── 9 │ ├── instructions.md │ └── query.ql ├── 10 │ └── instructions.md └── 11 │ └── instructions.md ├── 4 ├── README.md ├── queries │ ├── 1.ql │ ├── 2.ql │ ├── 3.ql │ ├── 4.ql │ ├── 5.ql │ ├── 6.ql │ └── 7.ql ├── vulnerable-code-snippets-db.zip └── vulnerable-code-snippets │ ├── cmdi-interface-list.py │ ├── cmdi-interface.py │ ├── cmdi-list.py │ └── cmdi.py ├── README.md └── images ├── 4-results.png ├── README.md ├── alert-view.png ├── download-from-github.png ├── specify-github-repo.png └── test-hello-world.png /1/challenge-1/instructions.md: -------------------------------------------------------------------------------- 1 | # Challenge 1 — find potential sources and sinks in a web application 2 | Choose an open source project in a language of your choice, preferably a web application. Find out what are the potential sources and sinks in a language of your choice for a vulnerability such as SQL injection. 3 | 4 | 1.1 You have already heard that a potential source could be a parameter from an HTTP GET request. What framework does the project of your choice use? If you have chosen a project that is not a web app,what could the sources be in the project? 5 | 6 | 1.2 What does an HTTP request look like in that framework? Try to find it in the project. 7 | 8 | 1.3 What are the sinks for SQL injection? 9 | 10 | 1.4 What would the sink look like in the project? Try to find it. 11 | 12 | 1.5 Do any of the sources flow into a sink? 13 | 14 | Hint: if you can’t find a project, utilize GitHub search functionality, for example, type in the GitHub search bar [“language:python stars:>100 type:repositories”](https://github.com/search?q=language%3Apython++stars%3A%3E100+type%3Arepositories&type=repositories&s=stars&o=desc). You could also choose one of the intentionally vulnerable projects from OWASP DVWA wherein we have an open source repository. 15 | -------------------------------------------------------------------------------- /1/challenge-2/instructions.md: -------------------------------------------------------------------------------- 1 | # Challenge 2 — find potential sources and sinks in a different vulnerability 2 | Find out what are the potential sources and sinks in a language of your choice for another vulnerability. For inspiration, look into the [Common Weakness Enumeration Database Top 25](https://cwe.mitre.org/top25/archive/2022/2022_cwe_top25.html) or other vulnerabilities in the CWE database. 3 | 4 | Hint: if you are having trouble identifying the sources and sinks for a given vulnerability, check out the CodeQL query with that CWE number in [CodeQL documentation](https://codeql.github.com/codeql-query-help/full-cwe/). Most queries in the documentation contain examples of vulnerable code snippets, which will make it easy to identify sources and sinks. 5 | -------------------------------------------------------------------------------- /1/challenge-3/instructions.md: -------------------------------------------------------------------------------- 1 | # Challenge 3 - analyze one of the latest injection vulnerabilities 2 | New vulnerabilities are reported every day. Think about the latest injection vulnerability in an opensource project that you have heard about. 3 | 4 | 3.1 What were the sources and the sinks for that vulnerability? 5 | 6 | 3.2 Look into the repository of the project. Can you find the source and follow the flow to the sink? 7 | 8 | Hint: find the advisory related to the vulnerability. It should contain information about in which version the vulnerability was patched. If the source code is hosted on GitHub, you can easily compare the vulnerable and the patched versions, using the [“compare” feature](https://docs.github.com/en/repositories/releasing-projects-on-github/comparing-releases). 9 | -------------------------------------------------------------------------------- /2/challenge-1/code-injection.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, request 2 | app = Flask(__name__) 3 | 4 | @app.route("/code-execution") 5 | def code_execution(): 6 | code = request.args.get("code") 7 | exec(code) # NOT OK 8 | eval(code) # NOT OK 9 | 10 | -------------------------------------------------------------------------------- /2/challenge-1/command-injection.py: -------------------------------------------------------------------------------- 1 | import re 2 | import os 3 | import subprocess 4 | 5 | from flask import Flask, request 6 | app = Flask(__name__) 7 | 8 | 9 | @app.route("/command1") 10 | def command_injection1(): 11 | files = request.args.get('files', '') 12 | # Don't let files be `; rm -rf /` 13 | os.system("ls " + files) # $result=BAD 14 | 15 | 16 | @app.route("/command2") 17 | def command_injection2(): 18 | files = request.args.get('files', '') 19 | # Don't let files be `; rm -rf /` 20 | subprocess.Popen("ls " + files, shell=True) # $result=BAD 21 | 22 | 23 | @app.route("/path-exists-not-sanitizer") 24 | def path_exists_not_sanitizer(): 25 | """os.path.exists is not a sanitizer 26 | 27 | This small example is inspired by real world code. Initially, it seems like a good 28 | sanitizer. However, if you are able to create files, you can make the 29 | `os.path.exists` check succeed, and still be able to run commands. An example is 30 | using the filename `not-there || echo pwned`. 31 | """ 32 | path = request.args.get('path', '') 33 | if os.path.exists(path): 34 | os.system("ls " + path) # $result=BAD 35 | -------------------------------------------------------------------------------- /2/challenge-1/instructions.md: -------------------------------------------------------------------------------- 1 | # Challenge 1 - add the Code Scanning GitHub action to a repository 2 | In this challenge, we will enable code scanning on a fork of this repository and observe what vulnerabilities CodeQL finds. The repository contains several intentionally vulnerable code snippets, which should be found by code scanning. Follow Instructions-option A to enable code scanning on the fork. 3 | 4 | If you prefer, you can choose another open source project for this challenge. In that case, you can either fork it (Instructions-option B) or clone and upload to a new repository on your account (Instructions—option C). 5 | 6 | You can also choose one of your own public projects instead. If you encounter problems, see documentation for [enabling code scanning](https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning-for-a-repository#configuring-code-scanning-automatically). 7 | 8 | Hint: If you can’t find a project, use the GitHub search functionality, e.g. type in the GitHub search bar “language:python stars:>100 type:repositories” 9 | 10 | ## Instructions-option A—fork this repository and enable code scanning 11 | 12 | 1. Fork this repository 13 | Note: If any of the steps below do not work or look different, check out the [documentation](https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning-for-a-repository#configuring-code-scanning-automatically). 14 | 15 | 2. Go to the 'Security' tab > click 'Set up code scanning' button. 16 | ![image](https://user-images.githubusercontent.com/102833689/236031191-09a7fc6e-cc6c-4001-853d-170d87c18a88.png) 17 | 18 | 3. You'll be moved to another page. Now in 'Code scanning' section click 'Set up' button, then 'Default'. 19 | ![image](https://user-images.githubusercontent.com/102833689/236031570-fd77279c-bb4f-422b-847c-f9d790929b1e.png) 20 | 21 | 4. A pop up should appear. Click 'Enable CodeQL' 22 | 23 | ![image](https://user-images.githubusercontent.com/102833689/236031879-815c0e57-d2d2-4d3d-bb4b-d7553c76de94.png) 24 | 25 | 5. Wait a few minutes for the scan to complete. Go to 'Security' tab and see the alerts that have been triggered. 26 | 27 | ## Instructions—option B—fork another open source repository and enable code scanning 28 | Some open source projects will have their own Actions workflows defined. If you fork a repository with existing workflows, these workflows will be disabled by default. This is a security measure to protect you from potentionally [malicious workflows](https://securitylab.github.com/research/github-actions-preventing-pwn-requests/), to prevent errors and lower Actions minutes usage. 29 | 30 | That's why before you enable code scanning, it's better if you first delete all actions workflows from your fork (generally they can be found in the `.github/workflows` folder), then go to the Actions tab and make sure that Actions are enabled. 31 | 32 | 1. Fork an open source project. When forking, select the option `Copy the main branch only`. 33 | 34 | 2. Check if the `.github/workflows` folder exists and if it does, delete it from your fork. 35 | 36 | 3. Go to the Actions tab and make sure that Actions are enabled (if they are disabled, a big pop up will show up). 37 | 38 | 4. Go to the 'Security' tab > click 'Set up code scanning' button. 39 | ![image](https://user-images.githubusercontent.com/102833689/236031191-09a7fc6e-cc6c-4001-853d-170d87c18a88.png) 40 | 41 | 5. You'll be moved to another page. Now in 'Code scanning' section click 'Set up' button, then 'Default'. 42 | ![image](https://user-images.githubusercontent.com/102833689/236031570-fd77279c-bb4f-422b-847c-f9d790929b1e.png) 43 | 44 | 6. A pop up should appear. Click 'Enable CodeQL' 45 | 46 | ![image](https://user-images.githubusercontent.com/102833689/236031879-815c0e57-d2d2-4d3d-bb4b-d7553c76de94.png) 47 | 48 | 7. Wait a few minutes for the scan to complete. Go to 'Security' tab and see the alerts that have been triggered. 49 | 50 | ## Instructions—option C—clone another open source project and upload to a new repository on your account 51 | 52 | In a similar way as in option B, we don't want unknown Actions workflows running on your account. 53 | The code for this challenge has shamelessly been copied from the [CodeQL examples](https://github.com/github/codeql/blob/main/python/ql/src/Security/CWE-089/examples/sql_injection.py). 54 | 55 | 1. Create a new repository on your account. 56 | 57 | 2. Duplicate an open source repository following the instructions [here](https://docs.github.com/en/repositories/creating-and-managing-repositories/duplicating-a-repository). Make sure to check if the `.github/workflows` folder exists and if it does, delete it from your copy of the repository. 58 | 59 | 3. Go to the Actions tab and make sure that Actions are enabled (if they are disabled, a big pop up will show up). 60 | 61 | 4. Go to the 'Security' tab > click 'Set up code scanning' button. 62 | ![image](https://user-images.githubusercontent.com/102833689/236031191-09a7fc6e-cc6c-4001-853d-170d87c18a88.png) 63 | 64 | 5. You'll be moved to another page. Now in 'Code scanning' section click 'Set up' button, then 'Default'. 65 | ![image](https://user-images.githubusercontent.com/102833689/236031570-fd77279c-bb4f-422b-847c-f9d790929b1e.png) 66 | 67 | 6. A pop up should appear. Click 'Enable CodeQL' 68 | 69 | ![image](https://user-images.githubusercontent.com/102833689/236031879-815c0e57-d2d2-4d3d-bb4b-d7553c76de94.png) 70 | 71 | 7. Wait a few minutes for the scan to complete. Go to 'Security' tab and see the alerts that have been triggered. 72 | 73 | Congrats on completing challenge 1! 🎉 74 | -------------------------------------------------------------------------------- /2/challenge-1/sql-injection-flask.py: -------------------------------------------------------------------------------- 1 | from django.db import connection, models 2 | from django.db.models.expressions import RawSQL 3 | from flask import Flask, request 4 | app = Flask(__name__) 5 | 6 | class User(models.Model): 7 | pass 8 | 9 | @app.route("/users/") 10 | def show_user(): 11 | username = request.args.get("username") 12 | with connection.cursor() as cursor: 13 | # GOOD -- Using parameters 14 | cursor.execute("SELECT * FROM users WHERE username = %s", username) 15 | User.objects.raw("SELECT * FROM users WHERE username = %s", (username,)) 16 | 17 | # BAD -- Using string formatting 18 | cursor.execute("SELECT * FROM users WHERE username = '%s'" % username) 19 | 20 | # BAD -- other ways of executing raw SQL code with string interpolation 21 | User.objects.annotate(RawSQL("insert into names_file ('name') values ('%s')" % username)) 22 | User.objects.raw("insert into names_file ('name') values ('%s')" % username) 23 | User.objects.extra("insert into names_file ('name') values ('%s')" % username) 24 | -------------------------------------------------------------------------------- /2/challenge-1/sql-injection.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url 2 | from django.db import connection 3 | 4 | 5 | def show_user(request, username): 6 | with connection.cursor() as cursor: 7 | # BAD -- Using string formatting 8 | cursor.execute("SELECT * FROM users WHERE username = %s" % username) 9 | user = cursor.fetchone() 10 | 11 | # GOOD -- Using parameters 12 | cursor.execute("SELECT * FROM users WHERE username = %s", username) 13 | user = cursor.fetchone() 14 | 15 | # BAD -- Manually quoting placeholder (%s) 16 | cursor.execute("SELECT * FROM users WHERE username = '%s'" % username) 17 | user = cursor.fetchone() 18 | 19 | # GOOD - string literal 20 | cursor.execute("SELECT * FROM users WHERE username = 'johndoe'") 21 | user = cursor.fetchone() 22 | urlpatterns = [url(r'^users/(?P[^/]+)$', show_user)] 23 | -------------------------------------------------------------------------------- /2/challenge-10/instructions.md: -------------------------------------------------------------------------------- 1 | # Challenge 10 — Query the AST 2 | 3 | Access the code in a CodeQL database of your choice using “Query the AST” option. 4 | 5 | 1. Go to the “Explorer” tab in VS Code, which should contain the source archive for the CodeQL database you have added earlier. It should be in one of the bottom folders. 6 | 2. Go into the file and code line that interests you. Right click on that line and choose “CodeQL: View AST”. This will show you the AST of the file in the “AST Viewer” in the CodeQL tab. 7 | 3. Click on any line and see the AST expand to the node that represents that line of code. 8 | -------------------------------------------------------------------------------- /2/challenge-11/instructions.md: -------------------------------------------------------------------------------- 1 | # Challenge 11 — Do the QL detective tutorials 2 | 3 | To get a better understanding of how to use the QL query language, do the QL tutorials available in the CodeQL documentation. 4 | -------------------------------------------------------------------------------- /2/challenge-12/intructions.md: -------------------------------------------------------------------------------- 1 | # Challenge 12 - Run one of the Security queries 2 | 3 | Run the SQL injection query against the database. For Python it’s located in: 4 | `python/ql/src/Security/CWE-089/SqlInjection.ql` 5 | Review the results. Try running a few other queries. 6 | -------------------------------------------------------------------------------- /2/challenge-2/instructions.md: -------------------------------------------------------------------------------- 1 | # Challenge 2 — Set up VS Code CodeQL starter workspace 2 | 3 | In this challenge you will create a codespace — your own mini container in a virtual machine with everything you need to query a codebase using CodeQL: VS Code, CodeQL extension for VSCode, CodeQL command line tool preinstalled and a pre-existing CodeQL database. The workspace will enable you to run your own CodeQL queries in the later challenges. 4 | 5 | You can choose between several options to do the exercises: 6 | * Option A: (Recommended) GitHub Codespace (Using a Browser or VS Code - CodeQL is run remotely on a Linux based GitHub Codespace in the cloud) 7 | * Option B: Local installation 8 | * CodeQL in [neovim](https://github.com/pwntester/codeql.nvim) 9 | * CodeQL in [emacs](https://github.com/anticomputer/emacs-codeql) 10 | 11 | 12 | ## Option A: GitHub Codespace 13 | 14 | Use a remote GitHub Codespace to work on the exercises. 15 | 16 | ### Prerequisites 17 | 18 | * GitHub account ([sign up](https://github.com/) for free) 19 | * Browser or [Visual Studio Code](https://code.visualstudio.com/download) (VS Code) with the [GitHub Codespaces](https://marketplace.visualstudio.com/items?itemName=GitHub.codespaces) extension installed on your local machine. 20 | 21 | Note: The first 120h hours per core of Codespace usage are free per month, we use a codespace with 4 cores for this workshop since 4 cores is the current maximum for free accounts. (If you have a Pro account, we recommend switching to an 8-core machine.) 22 | 23 | ### Step-by-Step 24 | 25 | 1. Login to your [GitHub](https://github.com/login) account 26 | 2. Go to the repo https://github.com/github/codespaces-codeql 27 | 3. Click on Use this template -> Open in a codespace 28 | 4. Continue with the instructions in [#Running the code tour](https://github.com/github/codespaces-codeql#running-the-code-tour) section. Do the tutorial in the codespace. 29 | 5. Open the Command Palette with Cmd/Ctrl+Shift+P and type 'CodeQL: Create Query'. Choose the language that you'd like to analyze — for us it will be python. Type the name of a repository with that language, so that we can download the CodeQL database for it — for us it will be "GitHubSecurityLab/codeql-zero-to-hero". 30 | 31 | => This will generate a so called CodeQL pack, which contains configuration for running Python queries. At this point, you can write as many CodeQL for Python queries as you want inside the folder `codeql-custom-queries-python` that was generated. 32 | 33 | You can also download databases from GitHub by following the steps in the #sSelect CodeQL database section. 34 | 35 | If you'd like to, you can choose another repository written in Python, but in that case the results after running the queries will be different. You can also choose another language and a repository in that language, but bear in mind that the queries for that language will be different than the ones in the blog post. 36 | 37 | ## Option B: Local installation 38 | 39 | Use a local CodeQL installation to work on the exercises. 40 | 41 | ### Prerequisites 42 | 43 | * Requires downloading up to 2 GB of data in total. 44 | * [Visual Studio Code](https://code.visualstudio.com/download) (VS Code) and `git` installed on your local machine. 45 | 46 | ### Step-by-Step 47 | 48 | 1. Install [VS Code extension for CodeQL](https://marketplace.visualstudio.com/items?itemName=GitHub.vscode-codeql) 49 | 2. In the terminal, in a directory specified by you: `$ git clone https://github.com/github/vscode-codeql-starter.git` 50 | 3. `$ cd vscode-codeql-starter` 51 | 4. `$ git submodule init` 52 | 5. `$ git submodule update --recursive` 53 | 6. In VS Code: File -> **Open Workspace from File...** `vscode-codeql-starter.code-workspace` 54 | 7. Continue with [Selecting a CodeQL Database](#select-codeql-database) 55 | 8. Then [Test your installation](#test-your-installation) 56 | 57 | ### Troubleshooting the local installation 58 | 59 | In case you see errors such as: 60 | * `Failed to run query: Could not resolve library path for [..]` 61 | * `Could not resolve module [..]` 62 | * `Could not resolve type [..]` 63 | 64 | => It is very likely that you missed cloning the git submodules (namely the ql repo). To fix this follow the [Step-by-Step](#step-by-step-1) instructions starting with step 3. 65 | 66 | ## Select CodeQL Database 67 | 68 | 1. Make sure you have the workspace `vscode-codeql-starter.code-workspace` open in VS Code. 69 | 2. Go To the CodeQL View 70 | 3. Click on choose the “Download database from GitHub” option, the one with the GitHub logo (Fun fact! Did you know that the character in the logo is called Monalisa? Now you know!). This option allows you to specify any public repo on GitHub to download as a CodeQL database - as long as it uses one of the supported languages by CodeQL. Write `GitHubSecurityLab/codeql-zero-to-hero` and press Enter. 71 | 72 | Screenshot: Select CodeQL DB from GitHub icon 73 | 74 | Screenshot: Enter a GitHub repository URL 75 | 76 | Now you can test your installation: 77 | 78 | ## Test your installation 79 | 80 | ### Prerequisites 81 | 82 | Make sure that the previously chosen CodeQL database is selected in the CodeQL view. (Click on "Select" if it's not) 83 | 84 | ### Step-by-Step 85 | 86 | 1. In VS Code: go to the workspace folder: `codeql-custom-queries-python` 87 | 2. Create a new file `test.ql` 88 | 3. add the following content: `select "Hello World!"` 89 | 4. Save file and right click in file on "CodeQL: Run Query on Selected Database" 90 | 91 | => The output should look like this: 92 | 93 | Screenshot: First CodeQL query results 94 | 95 | The rest of the challenges will assume that you have the VS Code with CodeQL extension and CodeQL starter workspace setup with a CodeQL database of your choice 96 | 97 | Congrats on completing challenge 2! 🎉 98 | -------------------------------------------------------------------------------- /2/challenge-3/instructions.md: -------------------------------------------------------------------------------- 1 | # Challenge 3 - Create CodeQL database using CodeQL CLI locally 2 | 3 | The CodeQL command line tool allows you to create databases from locally-sourced code. In this challenge, you will create a database for the vulnerable code we used in earlier exercises. 4 | 5 | ## Use the GitHub Codespace created in the previous challenge or install CodeQL CLI 6 | The GitHub Codespace that you created in the previous challenge comes with CodeQL CLI preinstalled. If you did create the codespace, you can skip this section and go to [#create-a-codeql-database](#create-a-codeql-database). 7 | 8 | If you did not create the codespace in the previous challenge, the easiest way to install the CodeQL CLI locally is as an extension to the `gh` CLI tool — GitHub's official CLI tool. 9 | 1. If you don't have `gh` installed, install it using the instructions [here](https://github.com/cli/cli#installation). 10 | 2. Install the CodeQL extension using: 11 | ```bash 12 | gh extensions install github/gh-codeql 13 | gh codeql install-stub 14 | ``` 15 | The second command will enable you to run the CLI tool just by typing `codeql` instead of `gh codeql`. 16 | If you encounter any issues, check the [docs](https://github.com/github/gh-codeql#installation). 17 | 18 | ## Create a CodeQL database 19 | For creating a CodeQL database, you can use the examples from this repository, or choose another open source repository. For creating CodeQL databases with languages other than Python, see docs. 20 | 1. Clone this repo to your local machine. 21 | ```bash 22 | git clone https://github.com/GitHubSecurityLab/codeql-zero-to-hero.git 23 | ``` 24 | 2. Move to the cloned directory. 25 | ```bash 26 | cd codeql-zero-to-hero 27 | ``` 28 | 3. Create a CodeQL database 29 | ```bash 30 | gh codeql database create example-codeql-db --language=python 31 | ``` 32 | 4. Deactivate the virtual environment 33 | ```bash 34 | deactivate 35 | ``` 36 | 5. Go to the VS Code CodeQL extension, click on the "Choose Database from folder" icon and select the "example-codeql-db" that you have created in step 5. 37 | If you haven't set up the premade codespace or installed VS Code with CodeQL extension, see the instructions in [challenge 2](https://github.com/GitHubSecurityLab/codeql-zero-to-hero/blob/main/2/challenge-2/instructions.md). 38 | 39 | And that's how you create and upload a CodeQL to continue with further analysis. 40 | Congrats on completing challenge 3! 🎉 41 | -------------------------------------------------------------------------------- /2/challenge-4/call.ql: -------------------------------------------------------------------------------- 1 | import python 2 | 3 | from Call call 4 | where call.getLocation().getFile().getRelativePath().regexpMatch("2/challenge-1/.*") 5 | select call 6 | -------------------------------------------------------------------------------- /2/challenge-4/instructions.md: -------------------------------------------------------------------------------- 1 | # Challenge 4 - Run a query to show all function calls 2 | 3 | With the setup that you created in the earlier challenges, we will run the query to show all function calls. You can either use the codespace with VS Code, or use VS Code locally. 4 | 5 | ## Make sure you have the database selected 6 | 1. In the CodeQL tab, check that you have the `GitHubSecurityLab/codeql-zero-to-hero` database downloaded and selected. There should be a checkmark next to the database name. If there isn't, hover over the database name until 'Select' button appears, then press that button. 7 | 2. If you haven't downloaded the database for this repository, go to the CodeQL tab and click on choose the “Download database from GitHub” option, the one with the GitHub logo. This option allows you to specify any public repo on GitHub to download as a CodeQL database - as long as it uses one of the supported languages by CodeQL. Write `GitHubSecurityLab/codeql-zero-to-hero` and press Enter. 8 | 9 | Screenshot: Select CodeQL DB from GitHub icon 10 | 11 | Screenshot: Enter a GitHub repository URL 12 | 3. Check that the database is selcted, like in the step 1. 13 | 14 | ## Run the query 15 | 1. Got to the 'Explorer' tab and create a new file in the `codeql-custom-queries-python` folder. Call the file `call.ql` and copy the below query into the file. 16 | ```ql 17 | import python 18 | 19 | from Call call 20 | where call.getLocation().getFile().getRelativePath().regexpMatch("2/challenge-1/.*") 21 | select call 22 | ``` 23 | 2. Hover over `Call` in the third line. This will show you the definition of the `Call` type. You can always hover over any part of the query to see if there is a definition for it. 24 | 3. Right click anywhere in the writing area of the query file and choose "CodeQL: Run Query on Selected Database" 25 | 4. After a few seconds you should see results, like so: 26 | 27 | Screenshot: Results of running the query 28 | -------------------------------------------------------------------------------- /2/challenge-5/call-eval.ql: -------------------------------------------------------------------------------- 1 | import python 2 | 3 | from Call c, Name name 4 | where name.getId() = "eval" and 5 | c.getFunc() = name and 6 | c.getLocation().getFile().getRelativePath().regexpMatch("2/challenge-1/.*") 7 | select c 8 | -------------------------------------------------------------------------------- /2/challenge-5/intructions.md: -------------------------------------------------------------------------------- 1 | # Challenge 5 — Run the query to show all function calls with name “eval” 2 | 3 | Run the query to show all function calls with name “eval”. Check out the subsection “Available types and predicates on types” and use the ideas to explore available types and predicates in the query. 4 | 5 | 6 | 1. Got to the 'Explorer' tab and create a new file in the `codeql-custom-queries-python` folder. Call the file `call-eval.ql` and copy the below query into the file. 7 | ```ql 8 | import python 9 | 10 | from Call c, Name name 11 | where name.getId() = "eval" and 12 | c.getFunc() = name and 13 | c.getLocation().getFile().getRelativePath().regexpMatch("2/challenge-1/.*") 14 | select c 15 | ``` 16 | 2. Right click anywhere in the writing area of the query file and choose "CodeQL: Run Query on Selected Database" 17 | -------------------------------------------------------------------------------- /2/challenge-6/function.ql: -------------------------------------------------------------------------------- 1 | import python 2 | 3 | from Function f 4 | where f.getLocation().getFile().getRelativePath().regexpMatch("2/challenge-1/.*") 5 | select f, "This is a function" 6 | -------------------------------------------------------------------------------- /2/challenge-6/instructions.md: -------------------------------------------------------------------------------- 1 | # Challenge 6 — Query for other simple types 2 | `Call` is one of the many types that are available in CodeQL for Python. Try to write a query for showing all function definitions. 3 | In CodeQL, we can often achieve the same results in many different ways, so don’t worry if your solution is different than the provided solution in this folder. 4 | -------------------------------------------------------------------------------- /2/challenge-7/call-eval-predicate.ql: -------------------------------------------------------------------------------- 1 | import python 2 | 3 | predicate isEvalCall(Call c, Name name) { 4 | c.getFunc() = name and 5 | name.getId() = "eval" 6 | } 7 | 8 | from Call c, Name name 9 | where isEvalCall(c, name) and 10 | c.getLocation().getFile().getRelativePath().regexpMatch("2/challenge-1/.*") 11 | select c, "call to 'eval'." 12 | -------------------------------------------------------------------------------- /2/challenge-7/intructions.md: -------------------------------------------------------------------------------- 1 | # Challenge 7 — Follow the steps to write your own external predicate 2 | Follow the instructions to write your own external predicate. 3 | 4 | A QL predicate is like a mini from-where-select query — it encapsulates a portion of a logic in a program, so it can be reused. For example the built-in predicate `getFunc()` on the `Call` type returns the name of the callable (the function name that we called). We can create our own external predicates (also known as non-member predicates) — we could, for example, create a predicate to **encapsulate** the logic from the query above. 5 | ```ql 6 | import python 7 | 8 | predicate isEvalCall(Call c, Name name) { 9 | c.getFunc() = name and 10 | name.getId() = "eval" 11 | } 12 | 13 | from Call c, Name name 14 | where isEvalCall(c, name) and 15 | c.getLocation().getFile().getRelativePath().regexpMatch("2/challenge-1/.*") 16 | select c, "call to 'eval'." 17 | ``` 18 | This query does the exact same thing as the query from challenge 5 — it searches for all functions with “eval” in their name. To create a predicate we do the following: 19 | 1. First, copy the previous query and add a predicate template above it: 20 | ```ql 21 | import python 22 | 23 | query predicate (:) { 24 | } 25 | 26 | from Call c, Name name 27 | where name.getId() = "eval" and 28 | c.getFunc() = name and 29 | c.getLocation().getFile().getRelativePath().regexpMatch("2/challenge-1/.*") 30 | select c 31 | ``` 32 | 2. Fill out the template with a name for the predicate (`isEvalCall`). Note that predicates are always written in camelCase. Next copy the variable declarations from the `from` clause (`Call c, Name name`) and paste them into the predicate variable declarations. Copy the desired functionality from the `where` clause (`c.getFunc() = name and name.getId() = "eval"`) into the body of the predicate. 33 | ```ql 34 | import python 35 | 36 | predicate isEvalCall(Call c, Name name) { 37 | c.getFunc() = name and 38 | name.getId() = "eval" 39 | } 40 | 41 | 42 | from Call c, Name name 43 | where c.getFunc().toString() = "eval" and 44 | c.getLocation().getFile().getRelativePath().regexpMatch("2/challenge-1/.*") 45 | select c, "call to 'eval'." 46 | ``` 47 | 3. Replace the copied functionality with the name of the predicate 48 | ```ql 49 | import python 50 | 51 | predicate isEvalCall(Call c, Name name) { 52 | c.getFunc() = name and 53 | name.getId() = "eval" 54 | } 55 | 56 | from Call c, Name name 57 | where isEvalCall(c, name) and 58 | c.getLocation().getFile().getRelativePath().regexpMatch("2/challenge-1/.*") 59 | select c, "call to 'eval'." 60 | ``` 61 | Predicates make your query more readable and easier to test. 62 | -------------------------------------------------------------------------------- /2/challenge-8/intructions.md: -------------------------------------------------------------------------------- 1 | # Challenge 8 - Follow the instructions to write your own class. 2 | 3 | Follow the instructions to write your own class. 4 | -------------------------------------------------------------------------------- /2/challenge-9/function-command-class.ql: -------------------------------------------------------------------------------- 1 | import python 2 | 3 | class CommandFunction extends Function { 4 | CommandFunction() { 5 | this.getName().regexpMatch(".*command.*") 6 | 7 | } 8 | } 9 | 10 | from Function f 11 | where f instanceof CommandFunction and 12 | f.getLocation().getFile().getRelativePath().regexpMatch("2/challenge-1/.*") 13 | select f 14 | -------------------------------------------------------------------------------- /2/challenge-9/function-command-predicate.ql: -------------------------------------------------------------------------------- 1 | import python 2 | 3 | query predicate isCommand(Function f) { 4 | f.getName().regexpMatch(".*command.*") 5 | } 6 | from Function f 7 | where isCommand(f) and 8 | f.getLocation().getFile().getRelativePath().regexpMatch("2/challenge-1/.*") 9 | select f 10 | -------------------------------------------------------------------------------- /2/challenge-9/function-command.ql: -------------------------------------------------------------------------------- 1 | import python 2 | 3 | from Function f 4 | where f.getName().regexpMatch(".*command.*") and 5 | f.getLocation().getFile().getRelativePath().regexpMatch("2/challenge-1/.*") 6 | select f 7 | -------------------------------------------------------------------------------- /2/challenge-9/intructions.md: -------------------------------------------------------------------------------- 1 | # Challenge 9 — Find all functions with “command” as part of its name 2 | 3 | In an earlier challenge, you wrote a query for function calls. Refine that query further to report all functions which have “command” as part of its name (hint: you can use a predicates that allows you to write regexes). 4 | 5 | 1. Try to write it first line-by-line 6 | 2. Then transform it a predicate instead 7 | 3. At last, try to make it into a new class 8 | -------------------------------------------------------------------------------- /3/1/instructions.md: -------------------------------------------------------------------------------- 1 | You will need to set up CodeQL using one of the methods presented in [challenge 2](https://github.com/GitHubSecurityLab/codeql-zero-to-hero/blob/main/2/challenge-2/instructions.md) from CodeQL zero to hero part 2 to run the queries. Remember also to download and [select a CodeQL database](https://github.com/GitHubSecurityLab/codeql-zero-to-hero/blob/main/2/challenge-2/instructions.md#select-codeql-database) - it can be the GitHubSecurityLab/codeql-zero-to-hero database, but you may also choose another project. 2 | 3 | Run the query in this challenge to find all method calls that are called ‘execute’ and come from the `django.db` library. 4 | 5 | If the path is not displaying properly, you may need to change the view to ‘alerts’. 6 | 7 | 8 | -------------------------------------------------------------------------------- /3/1/query.ql: -------------------------------------------------------------------------------- 1 | /** 2 | * @id codeql-zero-to-hero/3-1 3 | * @severity error 4 | * @kind problem 5 | */ 6 | 7 | import python 8 | import semmle.python.ApiGraphs 9 | 10 | from API::CallNode node 11 | where node = 12 | API::moduleImport("django").getMember("db").getMember("connection").getMember("cursor").getReturn().getMember("execute").getACall() 13 | and 14 | node.getLocation().getFile().getRelativePath().regexpMatch("2/challenge-1/.*") 15 | select node, "Call to django.db execute" 16 | -------------------------------------------------------------------------------- /3/10/instructions.md: -------------------------------------------------------------------------------- 1 | Run the CWE-20 Untrusted APIs query on a repo of your choice. For Python in the VS Code CodeQL Starter Workspace, it is located in `vscode-codeql-starter/ql/python/ql/src/Security/CWE-020-ExternalAPIs/UntrustedDataToExternalAPI.ql`. 2 | Try to choose a new project, download its database from GitHub (see [setup](https://github.com/GitHubSecurityLab/codeql-zero-to-hero/blob/main/2/challenge-2/instructions.md#option-b-local-installation)) and run this query on it. 3 | -------------------------------------------------------------------------------- /3/11/instructions.md: -------------------------------------------------------------------------------- 1 | Set up MRVA using instructions [here](https://codeql.github.com/docs/codeql-for-visual-studio-code/running-codeql-queries-at-scale-with-mrva/#controller-repository). Select top 10 repositories in the CodeQL extension tab. Choose one of the prewritten queries in your favorite language, right-click in the query file, and select CodeQL: Run Variant Analysis to start variant analysis. If you don’t find anything using that query, it’s likely because the project is already secured against that vulnerability. If you prefer, run one of the bigger lists with 100 or 1000 repositories. 2 | Caution: if you do find true positive vulnerabilities, make sure to verify them first and then report them using the coordinated disclosure process. See our [guide](https://github.blog/2022-02-09-coordinated-vulnerability-disclosure-cvd-open-source-projects/) for reporting vulnerabilities to open source. 3 | -------------------------------------------------------------------------------- /3/2/instructions.md: -------------------------------------------------------------------------------- 1 | Now you know how to query for calls to functions from specific libraries. 2 | 3 | If `os.system` executes input coming from a user, it could lead to a command injection. Write a query to find calls to `os.system` and run it on the database you selected in the previous challenge. 4 | 5 | See solution in this folder. 6 | -------------------------------------------------------------------------------- /3/2/query.ql: -------------------------------------------------------------------------------- 1 | /** 2 | * @id codeql-zero-to-hero/3-2 3 | * @severity error 4 | * @kind problem 5 | */ 6 | 7 | import python 8 | import semmle.python.ApiGraphs 9 | 10 | from API::CallNode node 11 | where node = API::moduleImport("os").getMember("system").getACall() 12 | and node.getLocation().getFile().getRelativePath().regexpMatch("2/challenge-1/.*") 13 | select node, "Call to os.system" 14 | -------------------------------------------------------------------------------- /3/3/instructions.md: -------------------------------------------------------------------------------- 1 | Flask is a popular Python web framework. Frameworks very often introduce potential sources for untrusted data, [Flask request](https://flask.palletsprojects.com/en/3.0.x/api/#incoming-request-data) being one of them. For example, a source of untrusted data could be: 2 | 3 | ``` 4 | username = request.args.get("username") 5 | ``` 6 | 7 | Write a query to find `request.args` 8 | 9 | See solution in this folder. 10 | -------------------------------------------------------------------------------- /3/3/query.ql: -------------------------------------------------------------------------------- 1 | /** 2 | * @id codeql-zero-to-hero/3-3 3 | * @severity error 4 | * @kind problem 5 | */ 6 | 7 | import python 8 | import semmle.python.ApiGraphs 9 | 10 | select API::moduleImport("flask").getMember("request").getMember("args").asSource(), "Flask request.args source" 11 | 12 | // Note that you can also use a wildcard to query for any method of the request object, for example: 13 | 14 | // select API::moduleImport("flask").getMember("request").getMember(_).asSource() 15 | -------------------------------------------------------------------------------- /3/4/instructions.md: -------------------------------------------------------------------------------- 1 | Run a query with `getAQlClass` predicate. 2 | 3 | See example in this folder. 4 | -------------------------------------------------------------------------------- /3/4/query.ql: -------------------------------------------------------------------------------- 1 | /** 2 | * @id codeql-zero-to-hero/3-4 3 | * @severity error 4 | * @kind problem 5 | */ 6 | import python 7 | import semmle.python.ApiGraphs 8 | 9 | from API::CallNode node 10 | where node = API::moduleImport("django").getMember("db").getMember("connection").getMember("cursor").getReturn().getMember("execute").getACall() 11 | select node, "The node has type " + node.getAQlClass() 12 | -------------------------------------------------------------------------------- /3/5/instructions.md: -------------------------------------------------------------------------------- 1 | Run the local data flow query to find execute calls that do not take a string literal. 2 | 3 | See the query in this folder. 4 | -------------------------------------------------------------------------------- /3/5/query.ql: -------------------------------------------------------------------------------- 1 | /** 2 | * @id codeql-zero-to-hero/3-5 3 | * @severity error 4 | * @kind problem 5 | */ 6 | import python 7 | import semmle.python.ApiGraphs 8 | 9 | class ExecuteCall extends DataFlow::CallCfgNode { 10 | ExecuteCall() { 11 | this = API::moduleImport("django").getMember("db").getMember("connection").getMember("cursor").getReturn().getMember("execute").getACall() 12 | } 13 | } 14 | 15 | predicate executeNotLiteral(DataFlow::CallCfgNode call) { 16 | exists(DataFlow::ExprNode expr | 17 | call instanceof ExecuteCall 18 | and DataFlow::localFlow(expr, call.getArg(0)) 19 | and expr instanceof DataFlow::LocalSourceNode 20 | and not expr.getNode().isLiteral() 21 | ) 22 | } 23 | 24 | from DataFlow::CallCfgNode call 25 | where executeNotLiteral(call) 26 | select call, "Call to django.db execute with an argument that is not a literal" 27 | -------------------------------------------------------------------------------- /3/6/instructions.md: -------------------------------------------------------------------------------- 1 | Run the taint tracking query to find flows from a Flask request to a django.db’s ‘execute’ sink. 2 | 3 | See the query in this folder. If the path is not displaying properly, you may need to change the view to ‘alerts’. 4 | 5 | 6 | -------------------------------------------------------------------------------- /3/6/query.ql: -------------------------------------------------------------------------------- 1 | /** 2 | * @kind path-problem 3 | * @problem.severity error 4 | * @id githubsecuritylab/3-6 5 | */ 6 | 7 | import python 8 | import semmle.python.dataflow.new.DataFlow 9 | import semmle.python.dataflow.new.TaintTracking 10 | import semmle.python.ApiGraphs 11 | import semmle.python.dataflow.new.RemoteFlowSources 12 | import MyFlow::PathGraph 13 | 14 | class ExecuteCall extends DataFlow::CallCfgNode { 15 | ExecuteCall() { 16 | this = API::moduleImport("django").getMember("db").getMember("connection").getMember("cursor").getReturn().getMember("execute").getACall() 17 | } 18 | } 19 | 20 | private module MyConfig implements DataFlow::ConfigSig { 21 | predicate isSource(DataFlow::Node source) { 22 | source = API::moduleImport("flask").getMember("request").asSource() 23 | } 24 | 25 | predicate isSink(DataFlow::Node sink) { 26 | exists(ExecuteCall ec | 27 | sink = ec.getArg(0) 28 | ) 29 | } 30 | } 31 | 32 | module MyFlow = TaintTracking::Global; // or DataFlow::Global<..> 33 | 34 | from MyFlow::PathNode source, MyFlow::PathNode sink 35 | where MyFlow::flowPath(source, sink) 36 | select sink.getNode(), source, sink, "Sample TaintTracking query" 37 | -------------------------------------------------------------------------------- /3/7/instructions.md: -------------------------------------------------------------------------------- 1 | You will need to use the VS Code CodeQL Starter Workspace for this challenge. See [setup](https://github.com/GitHubSecurityLab/codeql-zero-to-hero/blob/main/2/challenge-2/instructions.md#option-b-local-installation). 2 | CodeQL for Python stores all its security related queries in `python/ql/src/Security/` folder and experimental queries in `python/ql/src/experimental/Security`. The folder structure might differ a bit for other languages, for example Ruby in `ruby/ql/src/queries/security` or C# in `csharp/ql/src/Security Features`. 3 | -------------------------------------------------------------------------------- /3/8/instructions.md: -------------------------------------------------------------------------------- 1 | Find all the sources using the `RemoteFlowSource` type. 2 | Feel free to choose a different project to query on, maybe you’ll find something interesting? To download a CodeQL database for any open source project on GitHub, check [setup instructions](https://github.com/GitHubSecurityLab/codeql-zero-to-hero/blob/main/2/challenge-2/instructions.md#select-codeql-database). 3 | -------------------------------------------------------------------------------- /3/8/query.ql: -------------------------------------------------------------------------------- 1 | /** 2 | * @kind problem 3 | * @problem.severity error 4 | * @id githubsecuritylab/3-8 5 | */ 6 | import python 7 | import semmle.python.dataflow.new.RemoteFlowSources 8 | 9 | 10 | from RemoteFlowSource rfs 11 | select rfs, "A remote flow source" 12 | -------------------------------------------------------------------------------- /3/9/instructions.md: -------------------------------------------------------------------------------- 1 | Find all the SQL injection sinks. See what other sinks are available in `Concepts` and try to query for them. 2 | Feel free to choose a different project to query on. 3 | -------------------------------------------------------------------------------- /3/9/query.ql: -------------------------------------------------------------------------------- 1 | /** 2 | * @kind problem 3 | * @problem.severity error 4 | * @id githubsecuritylab/3-9 5 | */ 6 | 7 | import python 8 | import semmle.python.Concepts 9 | 10 | from SqlExecution sink 11 | select sink, "Potential SQL injection sink" 12 | -------------------------------------------------------------------------------- /4/README.md: -------------------------------------------------------------------------------- 1 | To run the queries in this folder, follow the set up instructions for [VS Code CodeQL starter workspace](https://github.com/github/vscode-codeql-starter) and install VS Code CodeQL extension. Then, in the CodeQL extension select the `vulnerable-code-snippets-db` database. 2 | 3 | For more information around CodeQL, set up and running queries, see [CodeQL zero to hero part 2](https://github.blog/developer-skills/github/codeql-zero-to-hero-part-2-getting-started-with-codeql/) and the other blog posts in the series. 4 | -------------------------------------------------------------------------------- /4/queries/1.ql: -------------------------------------------------------------------------------- 1 | /** 2 | * @id codeql-zero-to-hero/4-1 3 | * @severity error 4 | * @kind problem 5 | */ 6 | 7 | import python 8 | import semmle.python.ApiGraphs 9 | 10 | from API::CallNode node 11 | where node = 12 | API::moduleImport("gradio").getMember("Interface").getACall() 13 | 14 | select node, "Call to gr.Interface" 15 | -------------------------------------------------------------------------------- /4/queries/2.ql: -------------------------------------------------------------------------------- 1 | /** 2 | * @id codeql-zero-to-hero/4-2 3 | * @severity error 4 | * @kind problem 5 | */ 6 | 7 | import python 8 | import semmle.python.ApiGraphs 9 | 10 | from API::CallNode node 11 | where node = 12 | API::moduleImport("gradio").getMember("Interface").getACall() 13 | 14 | select node.getParameter(0, "fn").getParameter(_).asSource(), "Gradio sources" 15 | -------------------------------------------------------------------------------- /4/queries/3.ql: -------------------------------------------------------------------------------- 1 | /** 2 | * @id codeql-zero-to-hero/4-3 3 | * @severity error 4 | * @kind problem 5 | */ 6 | 7 | import python 8 | import semmle.python.ApiGraphs 9 | import semmle.python.dataflow.new.RemoteFlowSources 10 | 11 | class GradioInterface extends RemoteFlowSource::Range { 12 | GradioInterface() { 13 | exists(API::CallNode n | 14 | n = API::moduleImport("gradio").getMember("Interface").getACall() | 15 | this = n.getParameter(0, "fn").getParameter(_).asSource()) 16 | } 17 | override string getSourceType() { result = "Gradio untrusted input" } 18 | 19 | } 20 | 21 | from GradioInterface inp 22 | select inp, "Gradio sources" 23 | -------------------------------------------------------------------------------- /4/queries/4.ql: -------------------------------------------------------------------------------- 1 | /** 2 | * @id codeql-zero-to-hero/4-4 3 | * @severity error 4 | * @kind problem 5 | */ 6 | 7 | import python 8 | import semmle.python.ApiGraphs 9 | import semmle.python.dataflow.new.RemoteFlowSources 10 | 11 | class GradioInterface extends RemoteFlowSource::Range { 12 | GradioInterface() { 13 | exists(API::CallNode n | 14 | n = API::moduleImport("gradio").getMember("Interface").getACall() | 15 | this = n.getParameter(0, "fn").getParameter(_).asSource()) 16 | } 17 | override string getSourceType() { result = "Gradio untrusted input" } 18 | 19 | } 20 | 21 | 22 | from RemoteFlowSource rfs 23 | select rfs, "All python sources" 24 | -------------------------------------------------------------------------------- /4/queries/5.ql: -------------------------------------------------------------------------------- 1 | /** 2 | * @id codeql-zero-to-hero/4-5 3 | * @severity error 4 | * @kind problem 5 | */ 6 | 7 | import python 8 | import semmle.python.ApiGraphs 9 | 10 | from API::CallNode node 11 | where node = 12 | API::moduleImport("gradio").getMember("Button").getReturn() 13 | .getMember("click").getACall() 14 | 15 | select node.getParameter(0, "fn").getParameter(_), "Gradio sources" 16 | -------------------------------------------------------------------------------- /4/queries/6.ql: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @id codeql-zero-to-hero/4-6 4 | * @severity error 5 | * @kind problem 6 | */ 7 | 8 | 9 | import python 10 | import semmle.python.ApiGraphs 11 | import semmle.python.dataflow.new.RemoteFlowSources 12 | 13 | class GradioButton extends RemoteFlowSource::Range { 14 | GradioButton() { 15 | exists(API::CallNode n | 16 | n = API::moduleImport("gradio").getMember("Button").getReturn() 17 | .getMember("click").getACall() | 18 | this = n.getParameter(0, "fn").getParameter(_).asSource()) 19 | } 20 | 21 | override string getSourceType() { result = "Gradio untrusted input" } 22 | 23 | } 24 | 25 | from GradioButton inp 26 | select inp, "Gradio sources" 27 | -------------------------------------------------------------------------------- /4/queries/7.ql: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @id codeql-zero-to-hero/4-7 4 | * @severity error 5 | * @kind path-problem 6 | */ 7 | 8 | 9 | import python 10 | import semmle.python.dataflow.new.DataFlow 11 | import semmle.python.dataflow.new.TaintTracking 12 | import semmle.python.ApiGraphs 13 | import semmle.python.dataflow.new.RemoteFlowSources 14 | import MyFlow::PathGraph 15 | 16 | class GradioButton extends RemoteFlowSource::Range { 17 | GradioButton() { 18 | exists(API::CallNode n | 19 | n = API::moduleImport("gradio").getMember("Button").getReturn() 20 | .getMember("click").getACall() | 21 | this = n.getParameter(0, "fn").getParameter(_).asSource()) 22 | } 23 | 24 | override string getSourceType() { result = "Gradio untrusted input" } 25 | 26 | } 27 | 28 | class GradioInterface extends RemoteFlowSource::Range { 29 | GradioInterface() { 30 | exists(API::CallNode n | 31 | n = API::moduleImport("gradio").getMember("Interface").getACall() | 32 | this = n.getParameter(0, "fn").getParameter(_).asSource()) 33 | } 34 | override string getSourceType() { result = "Gradio untrusted input" } 35 | 36 | } 37 | 38 | 39 | 40 | class OsSystemSink extends API::CallNode { 41 | OsSystemSink() { 42 | this = API::moduleImport("os").getMember("system").getACall() 43 | } 44 | } 45 | 46 | private module MyConfig implements DataFlow::ConfigSig { 47 | predicate isSource(DataFlow::Node source) { 48 | source instanceof RemoteFlowSource 49 | } 50 | 51 | predicate isSink(DataFlow::Node sink) { 52 | exists(OsSystemSink call | 53 | sink = call.getArg(0) 54 | ) 55 | } 56 | } 57 | 58 | module MyFlow = TaintTracking::Global; 59 | 60 | from MyFlow::PathNode source, MyFlow::PathNode sink 61 | where MyFlow::flowPath(source, sink) 62 | select sink.getNode(), source, sink, "Data Flow from a Gradio source to `os.system`" 63 | -------------------------------------------------------------------------------- /4/vulnerable-code-snippets-db.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GitHubSecurityLab/codeql-zero-to-hero/89bef65d573a6f28f2ca29d165fd91208492244c/4/vulnerable-code-snippets-db.zip -------------------------------------------------------------------------------- /4/vulnerable-code-snippets/cmdi-interface-list.py: -------------------------------------------------------------------------------- 1 | import gradio as gr 2 | import os 3 | 4 | def execute_cmd(folder, logs): 5 | cmd = f"python caption.py --dir={folder} --logs={logs}" 6 | os.system(cmd) 7 | return f"Command: {cmd}" 8 | 9 | 10 | folder = gr.Textbox(placeholder="Directory to caption") 11 | logs = gr.Checkbox(label="Save verbose logs") 12 | output = gr.Textbox() 13 | 14 | demo = gr.Interface( 15 | fn=execute_cmd, 16 | inputs=[folder, logs], 17 | outputs=output) 18 | 19 | if __name__ == "__main__": 20 | demo.launch(debug=True) 21 | -------------------------------------------------------------------------------- /4/vulnerable-code-snippets/cmdi-interface.py: -------------------------------------------------------------------------------- 1 | import gradio as gr 2 | import os 3 | 4 | def execute_cmd(folder): 5 | cmd = f"python caption.py --dir={folder}" 6 | os.system(cmd) 7 | return f"Command: {cmd}" 8 | 9 | 10 | folder = gr.Textbox(placeholder="Directory to caption") 11 | output = gr.Textbox() 12 | 13 | demo = gr.Interface( 14 | execute_cmd, 15 | folder, 16 | output) 17 | 18 | if __name__ == "__main__": 19 | demo.launch(debug=True) 20 | -------------------------------------------------------------------------------- /4/vulnerable-code-snippets/cmdi-list.py: -------------------------------------------------------------------------------- 1 | import gradio as gr 2 | import os 3 | 4 | def execute_cmd(folder, logs): 5 | cmd = f"python caption.py --dir={folder} --logs={logs}" 6 | os.system(cmd) 7 | return f"Command: {cmd}" 8 | 9 | 10 | with gr.Blocks() as demo: 11 | gr.Markdown("Create caption files for images in a directory") 12 | with gr.Row(): 13 | folder = gr.Textbox(placeholder="Directory to caption") 14 | logs = gr.Checkbox(label="Save verbose logs") 15 | output = gr.Textbox() 16 | 17 | btn = gr.Button("Run") 18 | btn.click( 19 | fn=execute_cmd, 20 | inputs=[folder, logs], 21 | outputs=output) 22 | 23 | 24 | if __name__ == "__main__": 25 | demo.launch(debug=True) 26 | -------------------------------------------------------------------------------- /4/vulnerable-code-snippets/cmdi.py: -------------------------------------------------------------------------------- 1 | import gradio as gr 2 | import os 3 | 4 | def execute_cmd(folder): 5 | cmd = f"python caption.py --dir={folder}" 6 | os.system(cmd) 7 | return f"Command: {cmd}" 8 | 9 | 10 | with gr.Blocks() as demo: 11 | gr.Markdown("Create caption files for images in a directory") 12 | with gr.Row(): 13 | folder = gr.Textbox(placeholder="Directory to caption") 14 | output = gr.Textbox() 15 | 16 | btn = gr.Button("Run") 17 | btn.click( 18 | execute_cmd, 19 | folder, 20 | output) 21 | 22 | 23 | if __name__ == "__main__": 24 | demo.launch(debug=True) 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CodeQL Zero To Hero challenges 2 | 3 | This repository contains challenges for the CodeQL Zero to Hero blog post series. 4 | 5 | - [CodeQL zero to hero part 1: the fundamentals of static analysis for vulnerability research](https://github.blog/2023-03-31-codeql-zero-to-hero-part-1-the-fundamentals-of-static-analysis-for-vulnerability-research/). The challenges accompanying the blog post are in [folder 1.](https://github.com/GitHubSecurityLab/codeql-zero-to-hero/tree/main/1) 6 | - [CodeQL zero to hero part 2: getting started with CodeQL](https://github.blog/2023-06-15-codeql-zero-to-hero-part-2-getting-started-with-codeql/). The challenges accompanying the blog post are in [folder 2.](https://github.com/GitHubSecurityLab/codeql-zero-to-hero/tree/main/2) 7 | - [CodeQL zero to hero part 3: security research](https://github.blog/2024-04-29-codeql-zero-to-hero-part-3-security-research-with-codeql/). The challenges accompanying the blog post are in [folder 3](https://github.com/GitHubSecurityLab/codeql-zero-to-hero/tree/main/3). 8 | - [CodeQL zero to hero part 4: Gradio case study](https://github.blog/security/vulnerability-research/codeql-zero-to-hero-part-4-gradio-framework-case-study/). The challenges accompanying the blog post are in [folder 4](https://github.com/GitHubSecurityLab/codeql-zero-to-hero/tree/main/4). 9 | -------------------------------------------------------------------------------- /images/4-results.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GitHubSecurityLab/codeql-zero-to-hero/89bef65d573a6f28f2ca29d165fd91208492244c/images/4-results.png -------------------------------------------------------------------------------- /images/README.md: -------------------------------------------------------------------------------- 1 | Images folder. Nothing to see here. 2 | -------------------------------------------------------------------------------- /images/alert-view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GitHubSecurityLab/codeql-zero-to-hero/89bef65d573a6f28f2ca29d165fd91208492244c/images/alert-view.png -------------------------------------------------------------------------------- /images/download-from-github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GitHubSecurityLab/codeql-zero-to-hero/89bef65d573a6f28f2ca29d165fd91208492244c/images/download-from-github.png -------------------------------------------------------------------------------- /images/specify-github-repo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GitHubSecurityLab/codeql-zero-to-hero/89bef65d573a6f28f2ca29d165fd91208492244c/images/specify-github-repo.png -------------------------------------------------------------------------------- /images/test-hello-world.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GitHubSecurityLab/codeql-zero-to-hero/89bef65d573a6f28f2ca29d165fd91208492244c/images/test-hello-world.png --------------------------------------------------------------------------------