In the previous lab, you packaged your application and are now almost ready for deployment. But first, we need to ensure we don't introduce any security risks to our production infrastructure with our changes.
After all, NASA wouldn't launch a rocket without ensuring its safety, right?
Security is an integral part of software development. Too much is at stake; it cannot merely be an afterthought in what you do. Instead, it needs to be tightly integrated into your software development lifecycle!
We must spot and fix vulnerabilities as soon as possible, and for this, automation plays a significant role. Hence, enter the security stage: GitHub Actions!
In this lab, you will leverage GitHub Actions to enhance security through automation by creating two new workflows:
-
Supply-chain security: You will use the dependency review action to ensure you're not introducing vulnerable dependencies in your pull requests. This is crucial since, on average, 80% of the code in your project comes from third-party libraries. We need to ensure they are secure before using them!
-
Code security: You will perform static code analysis with CodeQL to ensure you're not introducing security vulnerabilities through the code changes you make. After all, even rocket scientists make mistakes!
Note: Both of these features are part of GitHub Advanced Security (or GHAS for short), which offers additional security features beyond the actions we are using in this workshop. It's free for public repositories and can thus be used in this workshop. For a private repository you will need a GitHub Advanced Security license. For more details, see this page.
To activate both features, we first need to prepare our repository by enabling the dependency graph and adding a code scanning workflow. Typically people using the default setup to simplify code scanning setup as GitHub manages the actions workflow for you without having to commit to the repo. However, since we want to understand how it works, we will set it up with advanced mode.
Follow these steps to enable these features:
- Navigate to your repository's settings.
- Choose Advanced Security from the left menu.
- Click Enable for Dependency graph.
- Scroll down to the Code scanning section and click the Set up drop down within CodeQL Analysis. Select Advanced to create a dedicated workflow file.
- Review the workflow file and commit it to the
mainbranch.
The steps above integrated CodeQL into your workflow. CodeQL is GitHub's static application security testing (SAST) tool.
CodeQL operates by first building a database from your code and then running a set of predefined queries against this database. Each query detects a specific type of vulnerability. These queries are written in a custom language called QL and are stored in the official CodeQL repository. Thus, when new queries are developed and added to this repository, they automatically become available for you to use.
Actions speak louder than words (pun intended), so let's review the workflow that performs code scanning with CodeQL.
In your repository, navigate to Code, then navigate the folder structure and open the file .github/workflows/codeql.yml. This file was created with the advanced configuration setup above. Let's review to understand its components.
-
The
on:section defines several triggers. You're already familiar with thepushandpull_requesttriggers from earlier workflows. Thescheduletrigger, however, might be new to you:on: push: branches: [ main ] pull_request: branches: [ main ] schedule: - cron: '23 18 * * 1'
As the name suggests, this trigger will initiate the workflow on a schedule, meaning it will execute at specified times or intervals. The
cronexpression defines this schedule in a format that's easy to understand. In this configuration, it's set to run every Monday at 6:23 PM. To gain a deeper understanding of the syntax, refer to the GitHub Docs.Running a code scan once a week is advisable since new queries might have been added to the CodeQL repositories, potentially revealing vulnerabilities that were previously undetected in your code.
-
The
strategysection introduces anothermatrix, a concept you're already familiar with:strategy: fail-fast: false matrix: include: - language: actions build-mode: none - language: javascript-typescript build-mode: none
But what about
fail-fast? By default, if any job in a job matrix fails, the remaining jobs are halted immediately to save on Actions minutes. By settingfail-fasttofalse, we override this default behavior. This ensures all jobs in the job matrix complete their execution, regardless of the outcome of individual jobs.This configuration is especially useful for projects that use multiple languages such as both javascript and actions!
In addition, the
build-modenone allows CodeQL to create the CodeQL database without needing to build the project. This simplifies setup and works for all interpreted languages like Javascript and is also supported for C/C++, C#, and Java which are compiled languages. However, for other compiled languages, it is recommended to useautobuildor your own build process. -
The steps section includes the
Initialize CodeQLstep. This step downloads the CodeQL CLI and initializes the CodeQL database by populating it with the code from our repository.- name: Initialize CodeQL uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} build-mode: ${{ matrix.build-mode }}
-
The
Perform CodeQL Analysisstep runs the CodeQL queries against the database containing your code. Once completed, it uploads the results to GitHub, enabling you to examine them.- name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v3 with: category: "/language:${{matrix.language}}"
By committing the codeql.yml file, the CodeQL workflow will be automatically triggered. You can navigate to the Actions tab and search for the CodeQL Advanced workflow. It will run for a few minutes, so you can go into the workflow run logs and observe what it's doing.
At this point you can check the Security tab of your repository and look at Code scanning results. You are likely to see some actions vulnerabilities indicating we have not set permissions in our workflows.
Let's introduce a new vulnerability to our application to see how CodeQL operates and alerts us within a pull request, enabling us to address it before it gets merged into the main branch.
Conduct the following actions in a repository cloned on your local machine or from within a GitHub Codespace:
-
Open a terminal and create a new
codeql-testbranch:git fetch --all git checkout -b codeql-test
-
Navigate to the file
src/components/OctoLink.tsxand look at the functionsanitizeUrlon line 10:function sanitizeUrl(url: string) { // UNCOMMENT THE FOLLOWING LINES TO INTRODUCE A SECURITY VULNERABILITY FOR STEP 04: SECURITY // const u = decodeURI(url).trim().toLowerCase(); // if (u.startsWith("javascript:")) { // return "about:blank"; // } return url; }
-
There is some commented-out code which is, in fact, insecure. Go ahead and remove the comments (remove the
//characters at the beginning of each line):function sanitizeUrl(url: string) { // UNCOMMENT THE FOLLOWING LINES TO INTRODUCE A SECURITY VULNERABILITY FOR STEP 04: SECURITY const u = decodeURI(url).trim().toLowerCase(); if (u.startsWith("javascript:")) { return "about:blank"; } return url; }
-
Commit your changes back to the branch by typing the following commands into your terminal:
git add . git commit -m "Add security vulnerability" git push
Go into the GitHub UI and open a pull request from this push. The pull request will trigger the CodeQL workflow.
After the CodeQL workflow has finished, navigate to the pull request and inspect the results.
-
As expected, it now found the vulnerability we just introduced. Let's quickly click on Details to find out more.
-
This will bring us to the Checks tab of the pull request, informing us that we have an incomplete URL schema check vulnerability with high severity. Click on Details again to learn more.
-
This directs us to the Code scanning tab under the repository's Security tab. Here, we find all the details of the vulnerability we've discovered: its location in the code, a description of the issue, and even guidance on how to fix it (after clicking on Show more).
-
Okay, so it's time to fix this! You should have all the information you need to address the issue on your own. However, if you have GitHub Copilot enabled, look in the pull request and it will have suggested a fix for you!
Commit this suggestion to fix the vulnerability and have code scanning run again. If you don't have Copilot enabled or want to solve it yourself, you can refer to the guidance provided in the Code scanning tab or from the autofix example above.
After you've made the changes and the CodeQL workflow runs again, the vulnerability will be resolved, and all checks on the pull request should pass.
Similar to step 3.6 of lab 2, you can enforce that both workflows need to succeed before being allowed to merge a pull request. This is achieved by adding them to the required status checks of your repository ruleset for the main branch.
This ensures that no one can introduce any new vulnerabilities to your main branch.
By enabling the dependency graph, we've allowed GitHub to analyze the package.json and package-lock.json files in our repository to monitor all dependencies.
You can verify its functionality by going to Insights > Dependency graph in your repository:
We can use this data with the dependency review action, which cross-references new dependencies and dependency versions against known vulnerabilities in the GitHub Advisory Database. This allows us to prevent the introduction of vulnerable dependencies in our pull requests. Note that while this requires a GitHub Action to run today, GitHub plans to integrate this natively in the future, similar to CodeQL default setup.
-
Create a new workflow file named
.github/workflows/dependency-review.ymlwith the following content:name: Dependency Review on: pull_request permissions: contents: read pull-requests: write jobs: dependency-review: runs-on: ubuntu-latest steps: - name: Checkout Repository uses: actions/checkout@v5 - name: Dependency Review uses: actions/dependency-review-action@v4 with: comment-summary-in-pr: true
-
Commit this file to your
mainbranch.
Let's test if this workflow functions correctly. To do so, we will install a new dependency. Follow the steps below in a repository cloned on your local machine or from within a GitHub Codespace:
-
Open a terminal.
-
Create a new branch named
add-vulnerability.git checkout -b add-vulnerability
-
Install
lodashversion4.17.20, which is known to be vulnerable:npm install [email protected]
-
This will modify both the
package.jsonand thepackage-lock.jsonfiles. Commit these changes and push the branch to GitHub:git add package.json package-lock.json git commit -m "Add vulnerable dependency" git push -u origin add-vulnerability -
Open a pull request for your branch. If you're unfamiliar with how to open a pull request, refer to our documentation on creating a pull request.
-
By opening a pull request, you will trigger the
Dependency Reviewworkflow. However, it will fail due to the newly introduced vulnerability. Since we set thecomment-summary-in-proption totrue, a comment containing a summary of the found vulnerabilities will be automatically added to the pull request.
Alternatively, you can also view the summary in the workflow run's dashboard. Click on the Details link next to the failed check, and then navigate to the workflow's Summary:
Inspect the links in the summary. They will direct you to the advisory on GitHub, where you can find more details about the vulnerability and recommendations for remediation.
Note: You have the option to fix the vulnerability by upgrading to the patched version of
lodash. This step is not mandatory to proceed with the workshop, so you can keep the pull request as a reference if you prefer.
The Dependency Review workflow summary might also touch on licenses, for instance, if you're introducing a dependency with a prohibited license based on the configuration of the dependency review action. You can learn more by reading the dependency review action README.
And that's it! By working with GitHub Advanced Security and Actions, we identified vulnerabilities both in our code as well as in our dependencies, and were able to promptly remediate them, well before they could pose an actual threat.
In this lab, you learned how to:
- 👏 Activate the dependency graph and GitHub Advanced Security in your project
- 👏 Use the dependency review action to scan your dependencies for vulnerabilities
- 👏 Use the CodeQL action to scan your code for vulnerabilities
Note What you learned in this lab is just the beginning of how GitHub Advanced Security can assist you in making your code secure. To delve deeper, feel free to read through the Addendum - GitHub Advanced Security!
Next:









