Azure DevOps Code Scan

The QualysCodeScan@1 task performs Software Composition Analysis (SCA) on your source code to identify vulnerable dependencies. It supports SBOM generation, secrets detection, and integrates with Azure DevOps Advanced Security.

Task Name: QualysCodeScan@1

Task Inputs

Service Connection

Input Required Default Description
qualysConnection Yes - Qualys API service connection name

Scan Target

Input Required Default Description
scanPath No $(Build.SourcesDirectory) Path to the directory to scan
excludeDirs No - Comma-separated list of directories to exclude
includeDev No false Include development dependencies

SBOM Options

Input Required Default Description
generateSbom No false Generate a Software Bill of Materials
sbomFormat No spdx SBOM format: spdx or cyclonedx
sbomOutput No sbom.json Output filename for the SBOM

Scan Options

Input Required Default Description
scanSecrets No false Enable secrets detection in source code
offlineMode No false Scan without uploading to Qualys platform

Threshold Configuration

Input Required Default Description
maxCritical No -1 Maximum critical vulnerabilities allowed (-1 = unlimited)
maxHigh No -1 Maximum high vulnerabilities allowed (-1 = unlimited)
maxMedium No -1 Maximum medium vulnerabilities allowed (-1 = unlimited)
maxLow No -1 Maximum low vulnerabilities allowed (-1 = unlimited)

Policy Configuration

Input Required Default Description
usePolicyEvaluation No false Enable Qualys cloud policy evaluation
failOnAudit No false Fail pipeline when policy result is AUDIT

Output Options

Input Required Default Description
publishResults No false Publish SARIF to Azure DevOps Advanced Security

Work Item Creation

Input Required Default Description
createWorkItems No false Create Azure Boards work items for vulnerabilities
workItemSeverities No 4,5 Severity levels to create work items for
workItemType No Bug Work item type to create
workItemAreaPath No - Area path for created work items

Supported Package Managers

Language Package Manager Manifest Files
JavaScript/Node.js npm, yarn, pnpm package.json, package-lock.json, yarn.lock, pnpm-lock.yaml
Python pip, poetry, pipenv requirements.txt, Pipfile.lock, poetry.lock, setup.py
Java Maven, Gradle pom.xml, build.gradle, build.gradle.kts
Go Go Modules go.mod, go.sum
.NET NuGet *.csproj, packages.config, packages.lock.json
Ruby Bundler Gemfile, Gemfile.lock
PHP Composer composer.json, composer.lock
Rust Cargo Cargo.toml, Cargo.lock

Complete Example

trigger:
  - main

pool:
  vmImage: 'ubuntu-latest'

steps:
  - task: QualysCodeScan@1
    displayName: 'Qualys Code Scan'
    inputs:
      qualysConnection: 'QualysConnection'
      scanPath: '$(Build.SourcesDirectory)'
      excludeDirs: 'node_modules,vendor,test,dist'
      includeDev: false
      scanSecrets: true
      generateSbom: true
      sbomFormat: 'spdx'
      maxCritical: 0
      maxHigh: 10
      usePolicyEvaluation: false
      publishResults: true
      createWorkItems: true
      workItemSeverities: '4,5'
    env:
      SYSTEM_ACCESSTOKEN: $(System.AccessToken)

  - script: |
      echo "Vulnerabilities: $(qualysCodeScan.vulnerabilityCount)"
      echo "Packages scanned: $(qualysCodeScan.packagesCount)"
      echo "Scan passed: $(qualysCodeScan.scanPassed)"
    displayName: 'Display Scan Results'

  - task: PublishBuildArtifacts@1
    inputs:
      pathToPublish: '$(qualysCodeScan.sbomPath)'
      artifactName: 'sbom'
    displayName: 'Publish SBOM'

Monorepo Scanning

Scan specific subdirectories in a monorepo:

trigger:
  - main

pool:
  vmImage: 'ubuntu-latest'

strategy:
  matrix:
    Frontend:
      projectPath: 'packages/frontend'
    Backend:
      projectPath: 'packages/backend'
    Shared:
      projectPath: 'packages/shared'

steps:
  - task: QualysCodeScan@1
    displayName: 'Scan $(projectPath)'
    inputs:
      qualysConnection: 'QualysConnection'
      scanPath: '$(Build.SourcesDirectory)/$(projectPath)'
      generateSbom: true
      sbomOutput: 'sbom-$(Build.BuildId).json'
      publishResults: true

SBOM Generation Only

Generate an SBOM without vulnerability scanning:

steps:
  - task: QualysCodeScan@1
    displayName: 'Generate SBOM'
    inputs:
      qualysConnection: 'QualysConnection'
      scanPath: '$(Build.SourcesDirectory)'
      generateSbom: true
      sbomFormat: 'cyclonedx'
      sbomOutput: '$(Build.ArtifactStagingDirectory)/sbom.json'
      offlineMode: true

  - task: PublishBuildArtifacts@1
    inputs:
      pathToPublish: '$(Build.ArtifactStagingDirectory)/sbom.json'
      artifactName: 'sbom'

Output Variables

Variable Description
$(taskName.vulnerabilityCount) Total number of vulnerabilities found
$(taskName.criticalCount) Number of critical vulnerabilities
$(taskName.highCount) Number of high vulnerabilities
$(taskName.mediumCount) Number of medium vulnerabilities
$(taskName.lowCount) Number of low vulnerabilities
$(taskName.secretsCount) Number of secrets detected
$(taskName.packagesCount) Total number of packages/dependencies found
$(taskName.policyResult) Policy result: ALLOW, DENY, AUDIT, or NONE
$(taskName.scanPassed) true/false based on thresholds or policy
$(taskName.sarifPath) Path to SARIF report file
$(taskName.jsonPath) Path to JSON report file
$(taskName.sbomPath) Path to generated SBOM file
$(taskName.workItemsCreated) Number of work items created

Using Output Variables

steps:
  - task: QualysCodeScan@1
    name: codeScan
    displayName: 'Scan Code'
    inputs:
      qualysConnection: 'QualysConnection'
      scanPath: '$(Build.SourcesDirectory)'
      generateSbom: true

  - script: |
      echo "Total vulnerabilities: $(codeScan.vulnerabilityCount)"
      echo "Critical: $(codeScan.criticalCount)"
      echo "Packages scanned: $(codeScan.packagesCount)"
      echo "Policy result: $(codeScan.policyResult)"
      echo "Scan passed: $(codeScan.scanPassed)"
    displayName: 'Show Results'

  - task: PublishBuildArtifacts@1
    inputs:
      pathToPublish: '$(codeScan.sbomPath)'
      artifactName: 'sbom'
    displayName: 'Publish SBOM'

  - task: PublishBuildArtifacts@1
    inputs:
      pathToPublish: '$(codeScan.sarifPath)'
      artifactName: 'security-reports'
    displayName: 'Publish SARIF Report'

Multi-Stage Pipeline

trigger:
  - main

stages:
  - stage: Build
    jobs:
      - job: BuildApp
        pool:
          vmImage: 'ubuntu-latest'
        steps:
          - task: NodeTool@0
            inputs:
              versionSpec: '18.x'
          - script: npm ci
          - script: npm run build

  - stage: Security
    dependsOn: Build
    jobs:
      - job: CodeScan
        pool:
          vmImage: 'ubuntu-latest'
        steps:
          - task: QualysCodeScan@1
            inputs:
              qualysConnection: 'QualysConnection'
              scanPath: '$(Build.SourcesDirectory)'
              excludeDirs: 'node_modules,dist'
              generateSbom: true
              maxCritical: 0
              publishResults: true

  - stage: Deploy
    dependsOn: Security
    condition: succeeded()
    jobs:
      - deployment: DeployToProduction
        environment: 'production'

Next Steps