- Software Composition Analysis (SCA): At this stage, we will ensure that the libraries we use in our project are secure and licence compliant (e.g., identifying vulnerable library versions or non-commercial libraries used in commercial applications).
- Static Application Security Test (SAST): During this stage, we will ensure that the code at rest in your repository does not contain any security vulnerabilities (e.g., authentication secrets in code or vulnerabilities introduced by poor code standards).
- Dynamic Application Security Test (DAST): In this stage, we will ensure that the code running on a server, while “alive,” does not have any security vulnerabilities.
SCA
For this example, we will use Mend Bolt to perform the analysis of the libraries and deliver a report in the build pipeline to the developers’ team.
The following code will allow you to add a task to perform the SCA analysis:
- stage: SAST
displayName: SAST stage
jobs:
- job: SCA
steps:
- task: WhiteSource@21
inputs:
cwd: '$(System.DefaultWorkingDirectory)'
Once the pipeline is finalised, you will have a report similar to the following:

SAST
The SAST stage will leverage SonarQube that has a community version that can be used in your environment. We will not cover how to setup your server in this post, and I will assume you already have it up and linked with you Microsoft Azure DevOps instance. The following code will add another task to your build pipeline and submit your code to the analysis process of SonarQube.
- stage: SAST
displayName: SAST stage
- job: SAST
steps:
- task: SonarQubePrepare@5
inputs:
SonarQube: 'Server connection'
scannerMode: 'CLI'
configMode: 'manual'
cliProjectKey: 'Project Key'
cliSources: '.'
- task: SonarQubeAnalyze@5
- task: SonarQubePublish@5
inputs:
pollingTimeoutSec: '300'
Once the pipeline is finalised, you will have a report similar to the following:

DAST
The final stage of the pipeline DevSecOps approach will be adding the OWASP ZAP container to perform dynamic tests once your application is deployed and is running. The following code will add the DAST task to your release pipeline:
- Install Docker (Docker CLI installer)
- OWASP ZAP Image (Bash Task)
# Download OWASP ZAP v2.12.0
echo 'Get OWASP ZAP v2.12.0'
docker pull owasp/zap2docker-stable:2.12.0
- OWASP ZAP Full Scan (Bash Task)
# Full Scan
echo 'Full Scan https://YOUR_APPLICATION.com.au/'
chmod -R 777 ./
docker run --rm -v $(System.DefaultWorkingDirectory):/zap/wrk/:rw -t owasp/zap2docker-stable:2.12.0 zap-full-scan.py -t https://YOUR_APPLICATION.com.au/ -g gen.conf -r report-full.html -x report-full.xml >> report-full.txt
true
- Translate XML to NUnit (PowerShell)
$XSLT = '
a
.
, , ,
.
, , ,
'
# Create XSLT file
$Folder = Get-Location
$fileName = "OWASP-XML-2-Unit.xslt"
$XsltFilePath = Join-Path -Path $Folder -ChildPath $fileName
New-Item -ItemType File -Name $fileName -Force
$XSLT | Add-Content -Path $XsltFilePath
- File Names (PowerShell)
# filenames
$Folder = Get-Location
$fileName = "OWASP-XML-2-Unit.xslt"
$XsltFilePath = Join-Path -Path $Folder -ChildPath $fileName
$ScanReport = "report-full.xml"
$TestReport = "report-full-Tests.xml"
$XmlInputPath = Join-Path -Path $Folder -ChildPath $ScanReport
$XmlOutputPath = Join-Path -Path $Folder -ChildPath $TestReport
Write-Host $XsltFilePath
Write-Host $XmlInputPath
Write-Host $XmlOutputPath
# transform
$XslTransform = New-Object System.Xml.Xsl.XslCompiledTransform
$XslTransform.Load($XsltFilePath)
$XslTransform.Transform($XmlInputPath, $XmlOutputPath)
# Tests Folder
$TestFolderName = "tests"
Write-Host "create $TestFolderName"
if ((Test-Path -Path $TestFolderName) -eq $false) {
$testsFolder = New-Item -Name $TestFolderName -ItemType Directory
}
Copy-Item -Path $TestReport -Destination $TestFolderName
- Add tests results to NUnit (PowerShell)
# Add Passed tests from log to Nunit results
function Add-PassedTests {
[CmdletBinding()]
param (
[string]$LogFilename,
[string]$TestFilename,
[string]$OutputFile
)
# Test Case
$testCase = '
'
$TestsPassed = @()
foreach ($line in Get-Content $LogFilename) {
if (Select-String -InputObject $line -Pattern "^PASS:") {
$count++
$Name = $line.Replace("PASS: ", "")
$Name = $Name.Replace('"', "")
$IdCount = "1-" + $count
$testCase2 = $testCase.Replace("{line}", $Name)
$testCase2 = $testCase2.Replace("{id}", $IdCount)
$TestsPassed += $testCase2
}
}
New-Item -Path $OutputFile -ItemType File -Force
$FirstElement = $true
foreach ($line in Get-Content $TestFilename) {
if ($line -match "
- Publish DAST Results (Publish Test Results)
Test result format:
NUnit
Test results files:
tests/*full*Tests-*.xml
Once the pipeline is finalised, you will have a report similar to the following:
