Run Cerbi's local scanner in Azure DevOps to catch risky, sensitive, and costly logging patterns before production. The extension is intentionally a Marketplace/pipeline wrapper: it installs or resolves the Cerbi.Scanner .NET tool, lets teams choose the scan path and policy file, publishes JSON, SARIF, and markdown reports, and fails the build from scanner exit codes. It does not implement detection logic itself, so CI/CD findings stay aligned with local scanner behavior.
Current implementation note: this repository contains the Azure DevOps wrapper only. Scanner rules and language support live in
Cerbi.Scanner. Do not assume language support beyond what the installed scanner version documents.
Scanner contract
Cerbi findings are expected to use stable rule IDs and include automation-friendly metadata: rule ID, title, message, severity, file path, line, column when available, excerpt when available, recommendation, confidence, and detected field or pattern category when available.
| Rule | Purpose |
|---|---|
CERBI001 |
Sensitive data appears in log message or structured field |
CERBI002 |
Secret-like token, password, API key, JWT, connection string, or credential appears in logging path |
CERBI003 |
Structured log field is not allowed by policy |
CERBI004 |
High-cardinality field is likely to increase observability cost |
CERBI005 |
Debug/trace logging appears in production-sensitive path |
CERBI006 |
Exception logging may expose sensitive object state |
Policy-as-code
The scanner should discover policy files named:
cerbi-policy.ymlcerbi-policy.yamlcerbi.logging.yml
You can also pass an explicit policy path with the task policyPath input. If no policy file exists, the scanner should use safe default rules. See samples/cerbi-policy.yml for the recommended schema covering rule enablement, severity overrides, fail threshold, allowed/disallowed structured fields, sensitive data patterns, and production overrides.
Local scanner installation (.NET 10 LTS)
Cerbi Scanner is distributed as the Cerbi.Scanner .NET global tool. For current LTS agents and workstations, install the .NET 10 LTS SDK/runtime and then install the scanner locally or globally:
dotnet --version # should report 10.0.x
dotnet tool install -g Cerbi.Scanner
cerbi-scanner --help
For reproducible enterprise builds, pin scannerVersion to the first Cerbi.Scanner release that declares .NET 10 LTS support. Do not use .NET 9 for this task: it is not an LTS target and is intentionally skipped in Cerbi Scanner pipeline examples.
.NET 10 LTS upgrade notes
- This Azure DevOps repository is a wrapper around the scanner global tool; it does not contain the scanner's
.csprojfiles or detection rules. The actualnet8.0tonet10.0target-framework change belongs in theCerbi.Scannerrepository. - Wrapper behavior is unchanged: the task still installs
Cerbi.Scanner, resolvescerbi-scanner, runscerbi-scanner scan, publishes reports, and interprets scanner exit codes. - Expected breaking change: hosted and self-hosted agents must provide the .NET 10 LTS SDK/runtime before installing or running the scanner. Pipelines using only .NET 8 SDK images must add
UseDotNet@2withversion: 10.0.x, enable the taskinstallDotNetfallback, or preinstall .NET 10 in the agent image. - The task now verifies
dotnet --list-sdks/dotnet --list-runtimesbefore installingCerbi.Scanner. If .NET 10 is missing andinstallDotNet: true, it installs the .NET 10 SDK into$(Agent.ToolsDirectory)/dotnet10for the current task. - No control-plane dependency is introduced; scanning remains local to the build agent and reports are published only as pipeline artifacts/summaries.
Pipeline usage
Install .NET 10 explicitly with UseDotNet@2 when possible. The task also has an installDotNet fallback for self-contained Marketplace usage on agents that do not already have .NET 10.
Demo/report mode (--fail-on none)
- task: UseDotNet@2
displayName: Install .NET 10 SDK
inputs:
packageType: sdk
version: 10.0.x
- task: CerbiScan@1
displayName: Cerbi logging governance scan
inputs:
scanPath: $(Build.SourcesDirectory)
failOn: none
CI/CD gate mode (--fail-on error)
- task: UseDotNet@2
displayName: Install .NET 10 SDK
inputs:
packageType: sdk
version: 10.0.x
- task: CerbiScan@1
displayName: Cerbi logging governance scan
inputs:
scanPath: $(Build.SourcesDirectory)
policyPath: $(Build.SourcesDirectory)/cerbi-policy.yml
failOn: high
ci: true
outputDir: $(Build.ArtifactStagingDirectory)/cerbi
jsonOutput: $(Build.ArtifactStagingDirectory)/cerbi/cerbi-results.json
sarifOutput: $(Build.ArtifactStagingDirectory)/cerbi/cerbi-results.sarif
markdownSummary: $(Build.ArtifactStagingDirectory)/cerbi/cerbi-report.md
publishArtifacts: true
Publish a readable Markdown build summary
- task: CerbiScan@1
displayName: Cerbi markdown summary
condition: always()
inputs:
scanPath: $(Build.SourcesDirectory)
failOn: none
markdownSummary: $(Build.ArtifactStagingDirectory)/cerbi/cerbi-report.md
See samples/azure-pipelines.yml for a complete example.
Reporting and CI output
ci: true passes --ci and requests all standard report formats (--report markdown, --report json, and --report sarif). By default the wrapper writes reports under $(Build.ArtifactStagingDirectory) using enterprise-friendly filenames:
cerbi-report.mdfor Azure DevOps build summaries, artifacts, and human review.cerbi-results.jsonfor stable automation and future ingestion workflows.cerbi-results.sariffor code scanning ingestion.
Publish the report directory as a single artifact when you want all outputs available after the job:
- task: CerbiScan@1
displayName: Run Cerbi logging governance scanner
inputs:
scanPath: $(Build.SourcesDirectory)
policyPath: $(Build.SourcesDirectory)/cerbi-policy.yml
ci: true
failOn: high
outputDir: $(Build.ArtifactStagingDirectory)/cerbi
jsonOutput: $(Build.ArtifactStagingDirectory)/cerbi/cerbi-results.json
sarifOutput: $(Build.ArtifactStagingDirectory)/cerbi/cerbi-results.sarif
markdownSummary: $(Build.ArtifactStagingDirectory)/cerbi/cerbi-report.md
publishArtifacts: true
- publish: $(Build.ArtifactStagingDirectory)/cerbi
artifact: cerbi-governance-report
displayName: Publish Cerbi governance report
Output modes
- Demo/report mode uses
failOn: none, which maps tocerbi-scanner scan --fail-on none; it generates reports without blocking the pipeline. - CI/CD gate mode uses
failOn: error, which maps tocerbi-scanner scan --fail-on error; it fails the task when scanner findings meet the error threshold. - JSON output (
--format json --output) is for automation, downstream parsing, metrics, and archival. - SARIF output (
--sarif) is for code scanning and security scanning workflows that ingest standardized static-analysis results. - Markdown summary output (
--summary) is for human-readable Azure DevOps build summaries.
Inputs
| Input | Default | Description |
|---|---|---|
scanPath |
$(Build.SourcesDirectory) |
Solution, project, folder, or file path passed to cerbi-scanner scan --path. |
policyPath |
empty | Optional policy path passed to --policy. If omitted, scanner discovery/default behavior applies. |
failOn |
none |
error | blocker | warning | info | low | medium | high | critical | none. |
failOnNew |
none |
New-finding threshold passed to --fail-on-new when a baseline is used. |
ci |
true |
Pass --ci for concise console output and standard CI report behavior. |
reports |
markdown,json,sarif |
Comma-separated report formats passed as repeated --report values. |
outputDir |
$(Build.ArtifactStagingDirectory) |
Directory passed to --output-dir for scanner-managed report placement. |
baseline |
empty | Optional baseline file passed to --baseline. |
createBaseline |
empty | Optional output path passed to --create-baseline. |
verbose |
false |
Pass --verbose when detailed scanner logs are needed. |
jsonOutput |
$(Build.ArtifactStagingDirectory)/cerbi-results.json |
JSON report path passed to --format json --output. |
sarifOutput |
$(Build.ArtifactStagingDirectory)/cerbi-results.sarif |
SARIF report path passed to --sarif. |
markdownSummary |
$(Build.ArtifactStagingDirectory)/cerbi-report.md |
Markdown report path passed to --summary and published to the build summary when generated. |
includeSnippets |
false |
Include source snippets when the scanner supports them. Disabled by default for safer shared reports. |
publishArtifacts |
true |
Publish generated JSON and SARIF reports as build artifacts. |
uploadToCerbiShield |
false |
Explicitly disabled by default. This wrapper keeps scans local and does not upload findings. |
scannerVersion |
latest |
NuGet version of Cerbi.Scanner to install. Pin 1.1.0 or later in regulated pipelines. |
installScanner |
true |
Install/update the scanner before running. Disable for self-hosted agents with a preinstalled, approved scanner. |
scannerInstallDirectory |
$(Agent.ToolsDirectory)/cerbi-scanner |
Tool-path install directory used before PATH/global-tool probing. |
packageSource |
empty | Optional NuGet feed URL or local source for private feeds or mirrored packages. |
installDotNet |
true |
Install the .NET 10 SDK with the official dotnet-install script when the agent does not already provide .NET 10. |
dotNetVersion |
10.0.x |
.NET SDK version/channel used by the fallback installer. |
dotNetInstallDirectory |
$(Agent.ToolsDirectory)/dotnet10 |
SDK install directory used by the fallback installer and prepended for the current task. |
additionalArguments |
empty | Advanced extra arguments passed directly to cerbi-scanner scan. |
Legacy inputs (policyFile, failThreshold, outputFile, sarifOutputFile, markdownSummaryFile, and failOnSeverity) are still read as aliases where practical, but new pipelines should use the scanner-first inputs above.
CLI behavior invoked by the task
The wrapper verifies that the installed scanner supports scan. If cerbi-scanner scan --help is unavailable, the task fails clearly instead of silently falling back to legacy audit behavior:
Cerbi Scanner version X does not support the scan command. Upgrade to Cerbi.Scanner >= 1.1.0.
The wrapper then runs one scanner-first command:
cerbi-scanner scan --path <scanPath> --policy <policyPath> --ci --fail-on <failOn> --output-dir <outputDir> --report markdown --report json --report sarif --format json --output <jsonOutput> --sarif <sarifOutput> --summary <markdownSummary>
--policy is omitted when policyPath is empty. Baseline inputs append --baseline, --create-baseline, and --fail-on-new only when configured. The task also appends --no-snippets unless includeSnippets: true is configured.
Exit codes are interpreted as:
| Code | Meaning |
|---|---|
0 |
Scan complete, no findings meet the fail threshold |
1 |
Findings found at or above the configured threshold |
2 |
Scanner/configuration error |
Implementation plan for scanner changes
Smallest safe plan for the scanner repository:
- Keep detection in the local
Cerbi.ScannerCLI and expose one shared finding contract for console, JSON, SARIF, markdown, and Azure DevOps. - Add policy loading for the three supported names plus
--policy, with safe defaults when absent. - Normalize severities to
warning,error, andblocker; apply rule enablement and overrides before output/fail decisions. - Implement or verify
CERBI001-CERBI006in the existing supported analyzer path only. If only .NET/Roslyn is currently implemented, document that clearly and keep future language scanners behind the same contract. - Add focused fixtures and tests for each rule, policy overrides, fail threshold, JSON, SARIF, markdown, and this Azure DevOps wrapper argument/report-publishing mapping.
Baseline adoption examples
Create a starting baseline without blocking the build:
- task: CerbiScan@1
inputs:
scanPath: $(Build.SourcesDirectory)
ci: true
failOn: none
createBaseline: $(Build.ArtifactStagingDirectory)/cerbi/cerbi-baseline.json
Fail only on new high/critical findings while allowing known legacy findings to remain tracked:
- task: CerbiScan@1
inputs:
scanPath: $(Build.SourcesDirectory)
policyPath: $(Build.SourcesDirectory)/cerbi-policy.yml
ci: true
baseline: $(Build.SourcesDirectory)/.cerbi/cerbi-baseline.json
failOn: none
failOnNew: high
Enterprise agent configuration
Recommended Marketplace defaults are safe for hosted agents: install the scanner into $(Agent.ToolsDirectory)/cerbi-scanner, scan $(Build.SourcesDirectory), publish SARIF, publish a markdown summary, suppress snippets, and only fail when failOn is configured. For regulated or offline environments:
- task: CerbiScan@1
inputs:
scannerVersion: 1.1.0
installScanner: true
scannerInstallDirectory: $(Agent.ToolsDirectory)/cerbi-scanner
packageSource: https://pkgs.dev.azure.com/your-org/_packaging/security/nuget/v3/index.json
installDotNet: true
dotNetVersion: 10.0.x
dotNetInstallDirectory: $(Agent.ToolsDirectory)/dotnet10
scanPath: $(Build.SourcesDirectory)
policyPath: $(Build.SourcesDirectory)/cerbi-policy.yml
failOn: error
jsonOutput: $(Build.ArtifactStagingDirectory)/cerbi/cerbi-results.json
sarifOutput: $(Build.ArtifactStagingDirectory)/cerbi/cerbi-results.sarif
markdownSummary: $(Build.ArtifactStagingDirectory)/cerbi/cerbi-report.md
includeSnippets: false
publishArtifacts: true
uploadToCerbiShield: false
If your self-hosted image already contains an approved scanner, set installScanner: false. The task will resolve the scanner from scannerInstallDirectory, PATH, or the .NET user tools directory without contacting a package feed.
Privacy & security
- The extension delegates to a local scanner process; it does not upload source or findings to a Cerbi service.
- Keep
includeSnippets: falsefor shared reports unless code excerpts are explicitly required. - Pin
scannerVersionin enterprise pipelines for reproducible governance behavior.
Troubleshooting
Missing .NET runtime or SDK
Symptoms usually look like dotnet was not found, You must install or update .NET, or a scanner launch failure after Cerbi.Scanner installs successfully. Fix this in one of three supported ways:
- Preferred for Microsoft-hosted agents: add
UseDotNet@2beforeCerbiScan@1withversion: 10.0.x. - Marketplace fallback: leave
installDotNet: trueso the task installs the .NET 10 SDK into$(Agent.ToolsDirectory)/dotnet10when .NET 10 is missing. - Self-hosted regulated agents: preinstall a pinned .NET 10 SDK/runtime and set
installDotNet: falseonly afterdotnet --list-sdksordotnet --list-runtimesshows a10.0.*entry.
If installScanner: true is enabled, the task needs a .NET SDK, not only a runtime, because dotnet tool install is an SDK command. If installScanner: false and the scanner is preinstalled, a compatible .NET 10 runtime is sufficient to run the tool.
Exact sample YAML
Copy this baseline for the .NET 10 Cerbi.Scanner release:
trigger:
- main
pool:
vmImage: ubuntu-latest
steps:
- task: UseDotNet@2
displayName: Install .NET 10 SDK
inputs:
packageType: sdk
version: 10.0.x
- task: CerbiScan@1
displayName: Cerbi logging governance scan
inputs:
scanPath: $(Build.SourcesDirectory)
policyPath: $(Build.SourcesDirectory)/cerbi-policy.yml
failOn: error
jsonOutput: $(Build.ArtifactStagingDirectory)/cerbi/cerbi-results.json
sarifOutput: $(Build.ArtifactStagingDirectory)/cerbi/cerbi-results.sarif
markdownSummary: $(Build.ArtifactStagingDirectory)/cerbi/cerbi-report.md
scannerVersion: latest
installScanner: true
scannerInstallDirectory: $(Agent.ToolsDirectory)/cerbi-scanner
installDotNet: true
dotNetVersion: 10.0.x
dotNetInstallDirectory: $(Agent.ToolsDirectory)/dotnet10
includeSnippets: false
publishArtifacts: true
uploadToCerbiShield: false
Requirements
- Azure DevOps agent with the .NET 10 LTS SDK installed (for example
UseDotNet@2withversion: 10.0.x) or taskinstallDotNet: trueso the task can install it locally. - Network access to the configured NuGet source when
installScanner: true, unless the agent already has the tool installed andinstallScanner: falseis used. - Self-hosted enterprise agents can pin
scannerVersion, setpackageSourceto an internal feed, or disable install and pre-provision the scanner in the agent image.