DEV Community

Hejun Wong
Hejun Wong

Posted on

Implement a DevSecOps Pipeline with GitHub Actions

Introduction - Birth of DevSecOps

The story of DevSecOps follows the the story of Software Development closely. We saw how the industry moved from Waterfall to Agile and everything changed after Agile. With much shorter development cycles, there was also a need for faster deployments to production.

It was no longer feasible for Security teams to get the Dev / Ops teams to wait till Vulnerability Assessment and Penetration Testing (VAPT) was complete before changes could be pushed to production. If not, we nullify the advantage the team had with speed and agility.

DevOps is a set of practices intended to reduce the time between committing a change to a system and the change being placed into production, while ensuring high quality - Bass, Weber and Zhu

By definition, DevOps already includes Security as part of Operations but the Security industry wanted more focus and emphasis on Security hence the term DevSecOps or Secure DevOps came about.

Security Terms

Before diving into the implementation phase, let's familiarise ourselves with these 3 security terms.

SCA - stands for Software Composition Analysis. It is a technique used to find security vulnerabilities in third-party components that we use in our projects / products. They can be libraries, packages that we install.

We will be using Snyk (pronounced as "Sneak") for our SCA tool. Snyk is a developer-first SCA solution, helping developers find, prioritize and fix security vulnerabilities and license issues in open source dependencies

  1. Create a free SNYK account at SNYK
  2. Navigate to Account Settings
  3. Generate Auth Token
  4. This key would be your SNYK_TOKEN. Store it within your GitHub Actions Secrets.

SAST - stands for Static Application Security Testing. It is a technique used to analyse source codes, binary and byte codes for security vulnerabilities without running the code. Since the codes are not running but examined in static state, it is called static analysis. SAST, SCA and Linting are typical examples of static analysis

For SAST, we will be using Sonar. SonarCloud is a cloud based static analysis tool for your CI/CD pipeline. It supports dozens of popular languages, development frameworks and IaC platforms.

  1. Create a free Sonar account at SonarCloud
  2. Create a new Organisation and Project
  3. Navigate to My Account, Security Tab
  4. Generate a new Token
  5. This token would be your SONAR_TOKEN. Store it within your GitHub Actions Secrets.

DAST - stands for Dynamic Application Security Testing. This is a technique used to analyse the running application for security vulnerabilities. Since the application is running and being examined dynamically, it is called dynamic analysis.

For DAST, we will be using OWASP ZAP. ZAP is the world’s most widely used web app scanner. It is a free, open-source penetration testing tool and at its core, ZAP is known as a “man-in-the-middle proxy”. You would find 3 Github actions belonging to OWASP ZAP within the GitHub Marketplace.

GitHub Actions

GitHub Actions is a CI/CD platform that allows us to automate our build, test and deployment pipeline

You can find it within your code repository on GitHub. It is the Actions Tab.

And when you click onto any of these workflow runs, you would be able to see the jobs that ran under that workflow

GitHub Actions

Sample GitHub Actions Workflow

Above is a sample workflow.

Workflows are automated processes that you can configure to run one or more jobs. Workflows are defined by YAML files checked into your repository. These yaml files are stored in the .github/workflows directory. You can have multiple workflows, each of them doing a different set of tasks

The workflow can be triggered by an event (e.g. a pull request / when a developer pushes a change into the code repository)

When that happens, one or more jobs will start running.

Jobs are steps are executed in order and are dependent on each other. You can share data from one step to another as they are ran on the same runner.

Runners are servers that run your workflows when triggered and Github provides Linux, Windows and MacOS virtual machines for you to run your workflows.

Our Workflow

Our DevSecOps pipeline will consist of 3 jobs.

Build - requests for the latest ubuntu server, installs the latest github actions (v4), installs nodeJS version 20, installs our project's dependencies npm run install, runs our unit test npm run test and performs SAST using Sonar.

SCA - requests for the latest ubuntu server and for this job to start running, it needs the build job to be complete. It will install the latest github actions (v4) and runs Snyk against our code repository.

DAST - requests for latest ubuntu server, waits for SCA job to be complete, checks out the latest github actiosn (v4) and runs OWASP ZAP against a sample website (example.com).

The entire CICD pipeline can be implemented in just 50 lines of codes below.

name: Build code, run unit test, run SAST, SCA, DAST security scans for NodeJs App
on: push

jobs:
  Build:
    runs-on: ubuntu-latest
    name: Unit Test and SAST
    steps:
    - uses: actions/checkout@v4
    - uses: actions/setup-node@v4
      with:
        node-version: '20.x'
        cache: npm
    - name: Install dependencies
      run: npm install
    - name: Test and coverage
      run: npm run test
    - name: SonarCloud Scan
      uses: sonarsource/sonarcloud-github-action@master
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
      with:
        args: >
          -Dsonar.organization=[YOUR_SONAR_ORGANISATION]
          -Dsonar.projectKey=[YOUR_SONAR_PROJECT]
  SCA:
    runs-on: ubuntu-latest
    needs: Build
    name: SCA - SNYK
    steps:
      - uses: actions/checkout@v4
      - name: Run Snyk to check for vulnerabilities
        uses: snyk/actions/node@master
        continue-on-error: true
        env:
          SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
  DAST:
    runs-on: ubuntu-latest
    needs: SCA
    name: DAST - ZAP
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          ref: main
      - name: ZAP Scan
        uses: zaproxy/action-baseline@v0.11.0
        with:
          target: 'http://example.com/'
Enter fullscreen mode Exit fullscreen mode

Troubleshooting

You would notice that your unit test coverage report isn't being uploaded into SonarCloud. To fix this, create a sonar-project.properties file in the root of your repository. The file will inform Sonar where to retrieve your code coverage reports.

sonar.organization=[INPUT_YOUR_ORGANISATION]
sonar.projectKey=[INPUT_YOUR_PROJECT_KEY]

# relative paths to source directories. More details and properties are described
# in https://sonarcloud.io/documentation/project-administration/narrowing-the-focus/
sonar.sources=.
sonar.exclusions=**/tests/*.js
sonar.language=js

sonar.javascript.lcov.reportPaths=./coverage/lcov.info
sonar.testExecutionReportPaths=./test-report.xml

sonar.sourceEncoding=UTF-8
Enter fullscreen mode Exit fullscreen mode

Resources

For a working example, you can refer to my repository

Cheers!

Top comments (0)