Skip to main content

devsecops

DevSecOps foundations

Agile vs Waterfall

Waterfall was a paradigm in the past where devs would spend months trying to figure out the architecture of the app, then implement that architecture. After a year is over they deploy the production-ready app.

The main problem with Waterfall was that clients don't know what they want so the finished app would often end up being very bad.

Agile fixes this by making sure the product updates in 2-3 week sprints and accounting for client feedback, and multiple sprints make a release.

However, even with Agile, we are still missing essential components that DevOps fixes:

  • waterfall: development, testing, and operations are all separate teams, so progress is slow and frustrating.
  • agile: development and testing are intertwined, but operations are left out, so deployment is a headache.
  • devops: brings development, testing, and operations to work together with ease, simplifying the development and deployment process

NOTE

DevOps is a culture shift that integrates Agile to make both development and release cycle very fast

Why devsecops

The old way of security testing was manual and took a long time

The DevSecOps approach aims to improve security testing speed via two approaches:

  1. increase speed by automation: include automation scripts that run code analysis checks
  2. enable developers to participate in security: developers should be responsible for the security of the code they write. Enable them with security tools for this purpose.

NOTE

DevSecOps is the merging of the security team into the DevOps process, to further the goal of giving dev teams more ownership over security by integrating automated security checks into every step of the process.

Here is how DevSecOps differs from traditional security:

  • Traditional security is often slow and manual, with security teams running scans and reviewing code over weeks, which doesn't fit the fast pace of DevOps.
    • Traditional application security is slow and manual, with security teams working separately from developers, causing delays and bottlenecks.
  • DevSecOps integrates security directly into the development pipeline, automating tests and giving developers ownership of security tasks.
    • DevSecOps integrates security directly into the development pipeline, embedding automated security checks early to provide real-time feedback to developers.

This means developers get real-time feedback within their workflow and can fix issues quickly, while security teams shift to an auditing role.

How DevSecOps helps developers

The focus is on empowering developers with tools and education to handle security themselves, making security a continuous, fast, and collaborative process.

We can help devs by integrating into their workflow and building automations they would actually use:

  • security notifications: automate delivering important security notifications not in cloudwatch logs but in a Slack or Teams channel everyone uses.
  • automate what matters: don't automate pushing a button. Automate painful workflows for devs and make those easy.

If you automate a mess, you get an automated mess.

So when you automate security, do it right. Make sure automation helps devs, not frustrates them.

Shift security to the left

DevSecOps is all about shifting security to the left, meaning having security checking earlier in the agile sprint pipeline.

We accomplish this by moving automated security checks like SAST and DAST into the development lifecycle.

CI/CD in DevSecOps

Continuous integration

Continuous integration is when a bunch of devs commit to source control, and then a build server like TeamCity builds an artifact from source control, then runs tests and code quality checks on it before deploying it.

Here's the basic flow:

  1. Developers push their code to centralized source control
  2. Build server like TeamCity detects changes, builds an artifact, runs tests, and deploys to different environments

Continuous delivery

Software is built in short cycles via agile, and then continuously deployed sequentially to testing, preprod, and prod environments, where source code only passes from one stage to the nest if it passes all tests for that stage.

Continuous delivery ensures that only tested and approved software is delivered to users.

Continuous Improvement and feedback

DevOps is meant to be a continuous loop that builds upon feedback to improve.

  • continuous feedback: each DevOps task should have a feedback loop that notifies developers as to what went wrong or right.
  • continuous improvement: improving based on feedback

DevOps tools and methodologies

Use APIs

APIs are crucial in DevSecOps security automation because they allow security tools to be controlled remotely and integrated seamlessly into the development pipeline.

Instead of manual scans, APIs enable automated security testing to run frequently and efficiently without slowing down development.

  • They also help connect security processes with systems like Jira for automatic updates, reducing manual work.
  • This automation makes it easier for developers to incorporate security into their workflow, supporting faster and more continuous security checks aligned with DevSecOps principles.

Metrics

Metrics are used to help define the success of a program, and turn goals into measurable outcomes.

There are 3 types of metrics:

  • operational metrics: metrics for the ops team, ensuring pipeline and infrastructure health
    • MTTR (mean time to recover)
    • MTTD (mean time to delivery)
    • Deployment frequency
    • flow time: detection to resolution
  • vulnerabilities: number of critical vulnerabilities, and vulnerabilities by type.
  • code metrics: focus on security and quality assurance of code.
    • application coverage: measures test coverage percentage
    • vulnerability remediation time: how quickly issues get fixed

NOTE

Track metrics that encourage good behaviors and practice, like limiting vulnerabilities.

Intro to security testing

There are two types of pipelines that a product goes through:

  • build pipeline: packages certain source code for deployment
  • release pipeline: pipeline that handles the process of releasing code to a target environment, prod, staging, or preprod.

NOTE

The main reason why you would want to separate these two pipelines is so you can run them a different number of times or where you only want to build once but then you want to release to multiple different environments from one single build.

Here are the two main types of testing:

  • white-box testing: you have full knowledge of the codebase, so you test internal logic.

  • black-box testing: the code is a black box to you - like testing an external API - so you test functional logic

You can further subdivide those blanket testing categories into two more categories of testing:

  • static security testing: analyzes source code for security vulnerabilities.
    • con: It is language specific and can lead to several false positives.
    • pro: very quick
  • dynamic security testing: tests an app while it is currently running to test application flow and discovers vulnerabilities by interacting with the website./
    • pro: catches elusive bugs and vulnerabilities
    • con: a black-box approach that takes a lot of time to complete

Here are the static security testing techniques:

  • Software composition analysis (SCA): analyzes open source packages to see if they are vulnerability-free and up to date
  • Static code analysis (SCA): analyzes IaC in an automated manner to check for known vulnerabilities when creating the infra by analyzing code patterns.
    • Pro: quick, automated, no need for devs to know intimate security details.
    • Con: not 100% effective, may lead to false positives or may not find everything.
  • Static application security testing (SAST): a white-box testing method with normal application code testing via unit and integration tests

Here are the dynamic security testing techniques:

  • Dynamic application security testing (DAST): black-box testing method that examines an application while it's running to find vulnerabilities, testing the app as it runs.
  • runtime testing: testing suspicious API calls, networking, commands run on the server, audit trails.

Static testing

SAST

SAST is static application security testing, where it reviews the source code of software to identify potential vulnerabilities.

pros

  • CI/CD friendly: runs very quickly and can catch many common vulnerabilities

cons

  • many false positives
  • doesn't catch runtime errors: can't catch runtime security vulnerabilities like authorization misconfiguration

Static code analysis (SCA)

Static code analysis is a white-box testing procedure that works by first defining checks or policies based on what your organization wants and then, based on those policies, scanning for common security vulnerabilities, deployment best practices, and coding best practices.

Here is an example of using SNYK as an SCA tool:

docker run --rm -it --env SNYK_TOKEN -v $(pwd):/app snyk/snyk:node

Software Composition Analysis (SCA)

SCA analyzes the third-party package dependency tree for any vulnerabilities.

Continuous secret scanning

Secret scanning is a white-box testing approach that statically searches for exposed secrets in your codebase via Regex or Entropy-based searching (entropy-based is better).

Secret scanning is crucial to prevent accidental exposure of sensitive credentials like AWS keys, passwords, and API tokens, especially in infrastructure as code files.

Here are the best practices:

  • add pre-commit hooks to block exposed secrets: implement pre-commit hooks that block commits if secrets are detected, ensuring secrets are caught early in the development process.
  • use automated secret-scanning tools: Tools like Aikido and TruffleHog can automate secret scanning by integrating with code repositories and CI/CD pipelines, enabling continuous protection as part of your DevSecOps workflow.

Continuous dependency scanning

Continuous dependency scanning uses an automated dependency scanner tool that scans your codebase's third party packages for vulnerabilities or outdated dependencies.

NOTE

Automated dependency scanning integrated into your CI/CD pipeline helps quickly identify vulnerable components by comparing dependencies against known vulnerability databases (CVEs).

Continuous container scanning

Continuous container scanning scans these three main security focus areas for containerized applications:

  • image vulnerabilities: Identifying known vulnerabilities in the container's base image and its installed libraries and dependencies.
  • Policy Enforcement: Ensuring containers are built and configured following security best practices, such as those outlined in CIS Benchmarks.
  • Runtime Protection: Monitoring running containers for suspicious activity and preventing container breakouts.

Continuous IaC scanning

Continuous IaC scanning scans your IaC code with static analysis tools like Checkov that can be fit into CI/CD pipelines to catch misconfigurations with your infra design.

NOTE

A single misconfiguration in IaC can propagate across all deployments, so integrating security checks early in the development process is crucial to catch issues when they are cheapest to fix.

Security scanning tools like Akido Security and open-source Checkov can analyze IaC for vulnerabilities, providing instant feedback through IDE integration and bug tracking systems.

Dynamic testing

DAST

DAST stands for Dynamic Application Security Testing, it is a form of black-box testing, and it tests a running application via its UI or API for common vulnerabilities such as SQL injection and buffer overflows.

NOTE

DAST simulates how a real attacker interacts with apps, testing vulnerabilities with simulated SQL injections and XSS attacks.

Here are the three core components DAST handles:

  • Crawling and Spidering: The scanner navigates the live application to discover all entry points, URLs, forms, inputs, and API endpoints.
  • Active Attack Simulation: It sends automated malicious inputs—such as SQL injections, Cross-Site Scripting (XSS) payloads, and path traversals—into those entry points.
  • Response Analysis: The tool monitors the application’s responses, looking for error messages, unexpected behaviors, or exposed data that indicate a vulnerability.

NOTE

It can be run in CI/CD, but it's more accurate when using in manual testing.

This is an example of using OWASP ZAP DAST tool, which tests a URL for networking and OWASP vulnerabilities:

docker run -t owasp/zap2docker-stable zap-baseline.py -t http://10.0.2.15:3000

Dynamic code analysis

For DevSecOps, dynamic scans should run asynchronously in CI/CD pipelines to avoid blocking builds, and tools should be fast, accurate, support automation (API/CLI), and integrate with bug trackers

IAST

IAST stands for interactive app security testing, and is a combination of DAST with SAST, where it performs testing by integrating into the runtime.

Interactive Application Security Testing (IAST) works by instrumenting the application during runtime, allowing real-time monitoring of data flows and behavior to detect vulnerabilities accurately.

NOTE

IAST offers continuous security testing with fewer false positives compared to static or dynamic scanning, making it well-suited for integration into DevSecOps pipelines.

Continous application runtime monitoring

Continuous monitoring is essential because new vulnerabilities and threats emerge daily, and traditional periodic scans can't catch everything, especially zero-day vulnerabilities.

Runtime monitoring detects suspicious activities in real time, such as unusual processes, unexpected connections, and unauthorized changes, enabling rapid incident response.

AWS GuardDuty is an example of a runtime monitoring solution that collects logs from various sources (like EC2 and Aurora) to detect suspicious API calls, malware, and misconfigurations, providing comprehensive visibility across your cloud environment.

Complete pipeline

SAST tools

SNYK

snyk is a static vulnerability analysis tool that also offers a CLI that lets you find out any vulnerabilities of code files.

Checkov

Checkov is a popular static code analysis tool used to scan cloud infrastructure configurations across major cloud providers, used for scanning vulnerabilities in IaC cocdebases.

  1. It works by applying pre-built policies that enforce security and compliance best practices, and you can also add custom policies if needed.
  2. In the pipeline you're watching, Checkov is installed and run to scan the entire code directory, generating a report of any security vulnerabilities found.

This helps catch issues early in the build process, ensuring your infrastructure code follows industry standards and improving overall security and compliance in your deployments.

Basics

Here's how to use it generally:

  1. Install checkov
pip3 install checkov
  1. Scan a directory
checkov --directory src/ --soft-fail -o junitxml --output-file-path checkovreport.xml

Here are the flags on the checkov command you can set:

  • --directory / -d: accepts a folder path to scan all code in
  • --soft-fail: exits with a 0 exit code no matter what
  • -o <output-style>: outputs the security testing analysis results in a certain output style, of which you have these possible values:
    • junitxml: outputs results in XML
  • --output-file-path <filepath>: the filepath to publish test results to.

NOTE

Checkov always returns a zero exit code by default.

Skipping checkov checks

You can use comments with Checkov in order to skip checking certain problematic lines of code that you know are not vulnerabilities but Checkov flags them as false positives.

DAST tools

OWASP Zap

OWASP ZAP (Zed Attack Proxy) is one of the most widely used open-source DAST tools. Ot os a PEN-testing tool that performs MITM attacks against your app to test it dynamically.

It can be operated via a Desktop GUI, a command-line interface, or an automated Docker container.

Docker networking issue

When running a DAST tool like OWASP ZAP inside a Docker container, it is isolated in its own virtual network. If you tell ZAP to scan http://localhost:3000, it will try to attack port 3000 inside itself and fail.

To bypass this and let the container reach services running on your host machine, you must use special host addresses:

  • Linux/Windows/macOS: Use http://docker.internal to route traffic out of the container network back to your host machine's ports.

Basic use cases

  • Baseline Scan: Runs the spider for a few minutes to map the site and reports passive vulnerabilities without attacking.
docker run -t owasp/zap2docker-stable zap-baseline.py -t http://10.0.2.15:3000

  • Full Scan: Performs a deep spidering process followed by an aggressive active scan against every uncovered parameter.
docker run -t owasp/zap2docker-stable zap-full-scan.py -t http://10.0.2.15:3000

  • API Scan: Tailored for REST, GraphQL, or OpenAPI/Swagger structures.
docker run -t owasp/zap2docker-stable zap-api-scan.py -t http://10.0.2.15:3000 -f openapi

Application security

Application security means building software that is secure from the start by protecting it from vulnerabilities that attackers might exploit. It involves adding layers of protection to prevent unauthorized access, data breaches, and malicious attacks. This applies to all modern applications, including web apps, mobile apps, APIs, and microservices.

It's important because it helps safeguard sensitive data, ensures the stability of systems, prevents costly disruptions, maintains customer trust, and helps meet regulatory requirements.

Application security principles

Secure by design

Secure by Design in software applications means building security into the product from the very beginning rather than adding it later. It shifts the responsibility of security from users to developers and organizations. The approach is based on three core principles:

  • Take ownership of customer security outcomes: Security features like multi-factor authentication and strong credentials are enabled automatically without user configuration.
  • Embrace radical transparency and accountability: Organizations openly disclose vulnerabilities and share their security processes to build trust and improve continuously.
  • Lead from the top: Senior leadership prioritizes security as a core business goal, ensuring proper resources and accountability.

Secure coding practices

  • Memory safety is fundamental: Using languages like Rust, Go, Python, or Java can prevent common vulnerabilities like buffer overflows, or use compiler tools for C/C++.
  • Input validation is your first defense: Always validate data types, length, character sets, and business logic to prevent injection attacks.
  • Output encoding protects users: Encode outputs to prevent cross-site scripting (XSS) by ensuring user input is treated as plain text, not executable code.
  • Defensive programming builds resilience: Implement fail-safe defaults, least privilege access, and error handling that avoids revealing sensitive system details.
    • fail-safe defaults: deny by default if detection fails, prioritize security over convenience.

Secure by default

Secure by default in application configuration means that security features and settings are automatically enabled and correctly configured out of the box, without requiring manual setup by users or administrators.

Here are the common security settings to enable in a secure by default approach:

  • HTTPS: force HTTPS and automatic redirect from HTTP to HTTPS
  • session timeout: timeout sessions for all users to avoid hijacking somebody else's session
  • secure cookies: make sure cookies are HTTP-only, secure, and lax.
  • secure DB credentials: generate unique DB credentials on install
  • disable debug modes and unused services in production: prevent security misconfiguration by ensuring there is a smaller attack surface, and no verbose logs that reveal too much info.

Supply chain attacks and SBOM

A supply chain attack happens when attackers compromise trusted software components or updates that your application depends on.

Instead of attacking your code directly, they infiltrate the software build or distribution process—like what happened in the SolarWinds breach—injecting malicious code into legitimate updates of third-party packages. This lets attackers access thousands of organizations while staying hidden for months.

NOTE

This attack is devastating because each third-party GitHub repo we use or each third-party NPM package we install becomes a possible attack vector.

NOTE

These attacks are dangerous because you’re not just trusting your own code but also all third-party libraries and tools you use.

SBOM

To defend against this, the course emphasizes using a Software Bill of Materials (SBOM), which is like an ingredient list detailing every component and its origin in your software.

A Software Bill of Materials (SBOM) is like an ingredient list for your software. It details all the components used in an application, including direct and transitive dependencies, along with their origins (pedigree) and authenticity (provenance). This comprehensive inventory helps you understand exactly what makes up your software.

An SBOM consists of three critical elements:

  1. inventory: lists direct and transitive dependencies
    • direct dependencies: dependencies you explicitly install with npm commands
    • transitive dependencies: the peer dependencies of direct dependencies, so you indirectly depend on these dependencies.
  2. pedigree: shows the complete origin and version control history of every code component.
  3. provenance: verifies the authenticity and integrity of components in code. It ensures that the code you downloaded is exactly what the author intended to publish.

SBOMs are important because they give you full visibility into your software supply chain, allowing you to quickly identify and respond to vulnerabilities.

For example, in supply chain attacks like the SolarWinds breach, attackers compromised trusted software updates to infiltrate thousands of organizations. With an SBOM, you can track and verify every component, reducing the risk of such attacks and enabling faster threat detection and response.

Dependency management

Along with SBOM, this is how you you perform dependency management:

  • Systematic Evaluation: Use OWASP and industry criteria to assess open source components before adding them, focusing on maintainer credibility, release frequency, and vulnerability response.
  • Dependency Pinning: Control exactly which versions of dependencies run in production through version pinning, lock files, or hash pinning, depending on your risk tolerance.
  • Balanced Update Policies: Prioritize updates based on severity and stability, with immediate updates for critical vulnerabilities and scheduled updates for less severe issues.

Third-party vendor security frameworks

Third-party vendors pose significant security risks as attackers often exploit trusted partners to breach organizations, making systematic vendor risk management essential.

Three main frameworks help manage vendor security effectively:

  1. NIST Cybersecurity Framework 2.0 (with six core functions and supply chain risk focus)
  2. ISO 27001 (with comprehensive ISMS controls and certification requirements
  3. SOC 2 Type 2 (an evidence-based approach assessing operational effectiveness over time).

Continuous supply chain monitoring

Continuous supply chain monitoring in software security means continuously tracking and analyzing all software components and dependencies in real time to detect vulnerabilities and threats as soon as they appear.

Instead of periodic scans, it provides ongoing visibility into changes in your software bill of materials (SBOM) and integrates automated threat intelligence to prioritize risks based on severity and usage.

This proactive approach enables faster response to security issues, reducing the time from discovery to remediation from weeks to hours, and helps maintain a secure software supply chain.

Continuous supply chain monitoring is only possible with these core components:

  • Realtime SBOM management: treats SBOM as a living inventory and constantly updates with each new deployment.
  • automated threat intelligence integration: goes beyond detection of known vulnerabilities and also covers emerging threats.

Supply chain Compliance

The EU Cyber Resilience Act mandates maintaining a comprehensive, machine-readable Software Bill of Materials (SBOM), secure-by-default product configurations, formal vulnerability disclosure programs, and ongoing security updates throughout the product lifecycle.

Crowdstrike case study: incident response

Organizations that recovered faster from the CrowdStrike incident shared several key capabilities:

  • They had mature supply chain monitoring systems that detected widespread issues quickly and activated incident response protocols.

  • They maintained direct, pre-established communication channels with vendors, enabling fast technical guidance and coordinated response.

  • They had dedicated teams trained for manual system recovery with clear roles specific to supply chain incidents.

  • They implemented alternative processes to keep critical operations running during recovery.

  • They used systematic recovery approaches prioritizing revenue-generating systems and had pre-positioned teams trained in complex recovery procedures.

In contrast, organizations lacking these capabilities treated the problem as isolated hardware failures, lacked vendor communication, and had poor recovery prioritization, resulting in prolonged downtime.

NOTE

This shows that preparation, monitoring, communication, and structured recovery planning are crucial for rapid incident recovery.

Secure logging and monitoring

Logging and monitoring are essential to create a complete audit trail to catch and flag suspicious activity. It has 4 pillars:

  1. authentication events: log every single authentication event, like logins, passwords changes, sessions ending, etc.
  2. authorization violations: log when users attempt to access resources they are unauthorized to access.
  3. data access events: log when sensitive data is accessed and by who
  4. system and configuration changes: log whenever security configuration or permissions change.

There are 6 essential attributes of a security log entry:

  1. who: unique identification of the prinicipal performing the action
  2. what: action attempted or performed
  3. when: timestamp with timezone
  4. where: system, application, or resource
  5. why: context for triggering the event
  6. outcome: success, failure, or partial completion

There are three ways to classify the severity of what you should log and the actions you should take based on those logs:

  • high-severity (alert): for anything like multiple failed authentication attempts, suspicious data access patterns, and access to unauthorized resources, you should immediately alert point of contacts about the attempt.
  • medium severity (monitor): unusual login times or locations, failed authorization attempts, or configuration changes
  • low-severity: successful logins, usual attempts, maintenance

Artifact security

SLSA framework

The SLSA framework, which stands for Supply Chain Levels for Software Artifacts, is a security framework designed to improve software supply chain integrity.

It provides four graduated levels of security assurance in increasing competence, from no guarantees (Level 0) to maximum tamper protection with strict controls (Level 4).

  1. level 0 - no controls, test builds only: no supply chain information or SBOM generated
  2. level 1 - build automation and provenance: Basic supply chain visibility and provenance generated from metadata about the software build
  3. level 2 - version control and tamper protection
  4. level 3 - enforce hardened source and build platforms: prevents threats like cross-build contamination by using dedicated build platforms that handle stuff like build agents and runners.
  5. level 4 - requires two-person code reviews and hermetic builds:

Provenance is verifiable information about software artifacts describing where and when it was created, how it was produced, and who created it.

SLSA focuses on creating verifiable, tamper-evident records called build provenance, which document where, when, and how software was built—including details like the build platform, source repository, build recipe, dependencies, and cryptographic signatures. This helps ensure software authenticity and prevents supply chain attacks.

So SLSA creates build provenance through these 5 components:

  1. builder identity: which platform built the software
  2. source repository: the source code
  3. build recipe: how the software was built, and the build pipeline info
  4. materials: what dependencies were needed to build the app
  5. crypto signatures: proof of authenticity

Tools like the SLSA Verifier automate checking these records to confirm that software artifacts are trustworthy before deployment.

Digital signatures and artifact integrity

In order to ensure the integrity of artifacts, we need to use digital signatures to create tamper-evident artifacts where it makes unauthorized modifications of the artifact immediately detectable.

  • what they are: Digital signatures protect software artifacts by providing cryptographic proof that the software has not been altered since it was signed.
  • how they work: They mathematically bind the signature to both the content and the signer's identity, so even a single bit change invalidates the signature, making tampering immediately detectable.
  • the resultThis ensures that software artifacts come from a trusted source and have not been modified during distribution, establishing a secure and trusted software supply chain.
  • tools: Tools like Sigstore and Cosign automate this process, enabling verification that software is authentic and untampered before deployment.

DevSecOps pipeline creation

Gitlab

  1. Create a YAML like so
# This file is a template, and might need editing before it works on your project.
# To contribute improvements to CI/CD templates, please follow the Development guide at:
# https://docs.gitlab.com/ee/development/cicd/templates.html
# This specific template is located at:
# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Getting-Started.gitlab-ci.yml

# This is a sample GitLab CI/CD configuration file that should run without any modifications.
# It demonstrates a basic 3 stage CI/CD pipeline. Instead of real tests or scripts,
# it uses echo commands to simulate the pipeline execution.
#
# A pipeline is composed of independent jobs that run scripts, grouped into stages.
# Stages run in sequential order, but jobs within stages run in parallel.
#
# For more information, see: https://docs.gitlab.com/ee/ci/yaml/index.html#stages

image: docker:19.03.12

services:
- docker:19.03.12-dind

before_script:
- docker info
- apk --update add npm # Install npm so we can use npm install

stages: # List of stages for jobs, and their order of execution
- build
- test
- security
- deploy

build-job: # This job runs in the build stage, which runs first.
stage: build
script:
- echo "Compiling the code..."
- echo "Compile complete."
- npm install # This job will run npm install in our example but could be anything
artifacts:
paths:
- node_modules # Save the node_modules folder so we can use it in the security-test-sca job

unit-test-job: # This job runs in the test stage.
stage: test # It only starts when the job in the build stage completes successfully.
script:
- echo "Running unit tests... This will take about 60 seconds."
#- sleep 60
- echo "Code coverage is 90%"

lint-test-job: # This job also runs in the test stage.
stage: test # It can run at the same time as unit-test-job (in parallel).
script:
- echo "Linting code... This will take about 10 seconds."
#- sleep 10
- echo "No lint issues found." #test comment for commit

security-test-dast: # This job runs in the security stage.
stage: security
script:
- echo "Security DAST testing..."
- docker run -d -p 3000:3000 --name juice-shop bkimminich/juice-shop # Run juice-shop so we have something to test
- containerip=$(docker inspect -f "{{ .NetworkSettings.Networks.bridge.IPAddress }}" juice-shop) # Get container IP so we can test it
- docker run -t --name dast owasp/zap2docker-stable zap-baseline.py -t http://$containerip:3000 || failure=true #run DAST testing against juice shop container
- if [[ "$(docker logs dast >& container-logs ; cat container-logs | grep 'WARN-NEW. [1-9]\d*' | wc -l)" -gt 0 ]]; then echo 'Failing job due to identified failures'; exit 1; else echo "no issues found"; exit 0; fi # If issues are found, fail job, if no issues are found pass job

security-test-sast:
stage: security
script:
- echo "Security SAST testing..."
- ls -l
- docker run --name sast -v /var/run/docker.sock:/var/run/docker.sock -v $(pwd):/src horuszup/horusec-cli:v2.7 horusec start -p /src -P $(pwd) || failure=true
- if [[ "$(docker logs sast >& container-logs ; cat container-logs | grep 'Vulnerability MEDIUM is. [1-9]\d*' | wc -l)" -gt 0 ]]; then echo 'Failing job due to identified failures'; exit 1; else echo "no issues found"; exit 0; fi # If issues are found, fail job, if no issues are found pass job

security-test-sca:
stage: security
script:
- echo "Security SCA testing..."
- docker run --name sca --env SNYK_TOKEN -v $(pwd):/app snyk/snyk:node || failure=true
- if [[ "$(docker logs sca >& container-logs ; cat container-logs | grep 'found [1-9]\d* issues' | wc -l)" -gt 0 ]]; then echo 'Failing job due to identified failures'; exit 1; else echo "no issues found"; exit 0; fi # If issues are found, fail job, if no issues are found pass job

deploy-job: # This job runs in the deploy stage.
stage: deploy # It only runs when *both* jobs in the test stage complete successfully.
script:
- echo "Deploying application..."
- echo "Application successfully deployed."
  1. Add an environment variable secrets to the CI/CD pipeline

Let's examine the YAML piece by piece:

  1. Add docker, make it available in the runner
image: docker:19.03.12

services:
- docker:19.03.12-dind
  1. The before_script directive runs before any job, so use it to view package version info and install packages
before_script:
- docker info
- apk --update add npm
  1. Create a list of stages in sequential order, such that jobs belonging to the same stage can execute in parallel.
    • Each job in a stage has the environment of the stage before.
    • If you want to make files available for any next stages to access, then a job should register those files as artifacts
stages:          # List of stages for jobs, and their order of execution
- build
- test
- security
- deploy
  1. In the build job, make the node_modules folder an artifact so later stages can access dependencies.
build-job:       # This job runs in the build stage, which runs first.
stage: build
script:
- echo "Compiling the code..."
- echo "Compile complete."
- npm install # This job will run npm install in our example but could be anything
artifacts:
paths:
- node_modules # Save the node_modules folder so we can use it in the security-test-sca job
  1. Run basic tests in parallel
unit-test-job:   # This job runs in the test stage.
stage: test # It only starts when the job in the build stage completes successfully.
script:
- echo "Running unit tests... This will take about 60 seconds."
#- sleep 60
- echo "Code coverage is 90%"

lint-test-job: # This job also runs in the test stage.
stage: test # It can run at the same time as unit-test-job (in parallel).
script:
- echo "Linting code... This will take about 10 seconds."
#- sleep 10
- echo "No lint issues found." #test comment for commit

  1. Run security tests, fail based on result of docker logs, which you can do via bash:
    • exit 0: success
    • exit 1: failure
security-test-dast:   # This job runs in the security stage.
stage: security
script:
- echo "Security DAST testing..."
- docker run -d -p 3000:3000 --name juice-shop bkimminich/juice-shop # Run juice-shop so we have something to test
- containerip=$(docker inspect -f "{{ .NetworkSettings.Networks.bridge.IPAddress }}" juice-shop) # Get container IP so we can test it
- docker run -t --name dast owasp/zap2docker-stable zap-baseline.py -t http://$containerip:3000 || failure=true #run DAST testing against juice shop container
- if [[ "$(docker logs dast >& container-logs ; cat container-logs | grep 'WARN-NEW. [1-9]\d*' | wc -l)" -gt 0 ]]; then echo 'Failing job due to identified failures'; exit 1; else echo "no issues found"; exit 0; fi # If issues are found, fail job, if no issues are found pass job

security-test-sast:
stage: security
script:
- echo "Security SAST testing..."
- ls -l
- docker run --name sast -v /var/run/docker.sock:/var/run/docker.sock -v $(pwd):/src horuszup/horusec-cli:v2.7 horusec start -p /src -P $(pwd) || failure=true
- if [[ "$(docker logs sast >& container-logs ; cat container-logs | grep 'Vulnerability MEDIUM is. [1-9]\d*' | wc -l)" -gt 0 ]]; then echo 'Failing job due to identified failures'; exit 1; else echo "no issues found"; exit 0; fi # If issues are found, fail job, if no issues are found pass job

security-test-sca:
stage: security
script:
- echo "Security SCA testing..."
- docker run --name sca --env SNYK_TOKEN -v $(pwd):/app snyk/snyk:node || failure=true
- if [[ "$(docker logs sca >& container-logs ; cat container-logs | grep 'found [1-9]\d* issues' | wc -l)" -gt 0 ]]; then echo 'Failing job due to identified failures'; exit 1; else echo "no issues found"; exit 0; fi # If issues are found, fail job, if no issues are found pass job