RansomLeak Secure Coding Lessons
Your scanner already tells developers what is wrong. This task tells them how to avoid it next time.
Add one step after your security scan. On every pull request build, the task reads the scanner's SARIF file and posts a single comment on the pull request. The comment links to a short secure-coding lesson for each type of issue found. Each lesson takes 5 to 10 minutes. It is free, and nobody needs to sign up.
How it works
- Your scanner writes a SARIF file. CodeQL, Semgrep, Snyk, Trivy, Gitleaks and any other SARIF tool work.
- The task reads the issue types from it: CWE identifiers, whether a CVE or GHSA advisory is involved, and the scanner name.
- It signs in to RansomLeak with the pipeline's own OIDC token. There is no API key and no service connection.
- It posts one comment on the pull request and updates the same comment on later runs. When someone finishes a lesson, the next run ticks it off.
What leaves your pipeline
Only the pull request number and, per finding, the scanner name, CWE identifiers and a yes or no for CVE or GHSA. Never source code, file paths, messages, snippets or secrets.
Setup
steps:
# ... your scanner writes results.sarif ...
- task: RansomLeakLessons@1
condition: always()
inputs:
sarif: results.sarif
The task runs only on pull request builds of Azure Repos Git repositories. Other builds skip it.
The pipeline identity needs permission to comment. Once per repository, open Project settings, then Repositories, select the repository, open Security, select the "{Project} Build Service ({Organization})" user and set "Contribute to pull requests" to Allow. If this is missing, the task prints these steps as a warning.
Use SonarQube instead of a SARIF file
If SonarQube already analyses your pull requests, the task can read the security findings from it. It works with SonarQube Cloud and with SonarQube Server Developer Edition or higher. Community Build has no pull request analysis, so use a SARIF file there.
steps:
# ... your SonarQube analysis steps in the same job ...
- task: RansomLeakLessons@1
condition: always()
inputs:
sonarHostUrl: https://sonarcloud.io
sonarOrganization: my-org
sonarProjectKey: my-org_my-repo
sonarToken: $(SONAR_READ_TOKEN)
Use one source: sarif, sonarHostUrl, semgrep, snyk, advancedSecurity, checkmarx or veracode. With more than one, or none, the task logs a warning and does nothing. The token needs Browse permission on the project, is optional for public SonarQube Cloud projects, and is only ever sent to your SonarQube host. Keep it in a secret pipeline variable. sonarIncludeHotspots: true also uses security hotspots that still need review, and sonarWaitTimeout (default 300 seconds) sets how long the task waits for the pull request analysis.
Use Semgrep instead of a SARIF file
If semgrep ci already scans your pull requests and sends results to Semgrep AppSec Platform, the task can read the open Code findings from there.
steps:
- script: semgrep ci
env:
SEMGREP_APP_TOKEN: $(SEMGREP_APP_TOKEN)
- task: RansomLeakLessons@1
condition: always()
inputs:
semgrep: true
semgrepAppToken: $(SEMGREP_WEB_API_TOKEN)
The token needs the Web API scope and is only ever sent to Semgrep. Semgrep's findings API has no pull request filter, so the task reads the open findings of the repository at the source branch semgrep ci reported (System.PullRequest.SourceBranch). Set semgrepRepo and semgrepRef if Semgrep shows other values.
Use Snyk
The simplest path is SARIF: snyk code test --sarif-file-output=snyk-code.sarif writes the findings for exactly this pull request, and the task reads them like any other SARIF file. It works on every Snyk plan.
If your Snyk plan includes API access and the pipeline records the branch in Snyk (snyk monitor or snyk code test --report with --target-reference), the task can read the open issues from the Snyk REST API instead:
steps:
- script: snyk code test --report --project-name="$(System.TeamProject)/$(Build.Repository.Name)" --target-name="$(System.TeamProject)/$(Build.Repository.Name)" --target-reference="${SYSTEM_PULLREQUEST_SOURCEBRANCH#refs/heads/}" || true
env:
SNYK_TOKEN: $(SNYK_TOKEN)
- task: RansomLeakLessons@1
condition: always()
inputs:
snyk: true
snykToken: $(SNYK_TOKEN)
snykOrg: $(SNYK_ORG_ID)
The token is only ever sent to the Snyk API host (snykApiUrl, default https://api.snyk.io; use https://api.eu.snyk.io or https://api.au.snyk.io for those regions). Snyk's API has no pull request filter, so the task reads the projects of the target System.TeamProject/Build.Repository.Name whose target reference is the source branch. Set snykTarget and snykTargetReference if Snyk shows other values.
Use Checkmarx One
Checkmarx One SARIF has no CWE ids for SAST results, so the task reads the latest completed scan of the source branch from the Checkmarx One API instead:
steps:
- script: cx scan create --project-name "$(Build.Repository.Name)" --branch "${SYSTEM_PULLREQUEST_SOURCEBRANCH#refs/heads/}" -s . --scan-types "sast,sca,iac-security"
env:
CX_APIKEY: $(CX_APIKEY)
CX_TENANT: $(CX_TENANT)
- task: RansomLeakLessons@1
condition: always()
inputs:
checkmarx: true
checkmarxApiKey: $(CX_APIKEY)
checkmarxTenant: $(CX_TENANT)
The API key is exchanged for a short-lived token at the IAM host of your region (checkmarxBaseUri, default https://ast.checkmarx.net; use https://eu.ast.checkmarx.net, https://anz.ast.checkmarx.net and so on for other regions), and neither ever leaves Checkmarx One. The task reads the SAST, SCA and KICS results of the newest completed scan of the project Build.Repository.Name on the source branch. Set checkmarxProject and checkmarxBranch if Checkmarx One shows other values.
Use Veracode
Run the task after the Veracode Pipeline Scan. It reads the scan's results.json from System.DefaultWorkingDirectory, with no call to Veracode and no credential:
steps:
- script: |
curl -sSO https://downloads.veracode.com/securityscan/pipeline-scan-LATEST.zip
unzip -o pipeline-scan-LATEST.zip pipeline-scan.jar
java -jar pipeline-scan.jar -vid "$(VERACODE_API_ID)" -vkey "$(VERACODE_API_KEY)" -f target/app.jar
- task: RansomLeakLessons@1
condition: always()
inputs:
veracode: true
The task reads each finding's CWE id and skips informational ones. With --baseline_file, set veracodeResultsFile: filtered_results.json so only new findings count. To read a sandbox or policy scan from the Veracode Findings API instead, set veracodeApp (and optionally veracodeSandbox) with veracodeApiId and veracodeApiKey from secret variables. Those requests are HMAC signed, the key is never sent, and they only go to https://api.veracode.com, https://api.veracode.eu or https://api.veracode.us.
Use GitHub Advanced Security for Azure DevOps
If GitHub Advanced Security (or Code Security and Secret Protection) is enabled on the repository, the task can read the pull request's active code scanning, dependency scanning and secret scanning alerts instead of a SARIF file. Run it after the Advanced Security tasks in the pull request build:
steps:
- task: AdvancedSecurity-Codeql-Init@1
inputs:
languages: javascript
- task: AdvancedSecurity-Dependency-Scanning@1
- task: AdvancedSecurity-Codeql-Analyze@1
- task: RansomLeakLessons@1
condition: always()
inputs:
advancedSecurity: true
The task reads alerts with the pipeline's System.AccessToken, so the {Project} Build Service ({Organization}) user needs Advanced Security: View alerts on the repository. Or pass a personal access token with the Advanced Security (Read) scope as advancedSecurityToken. Either token is only ever sent to https://advsec.dev.azure.com. Only the scanner name, CWE ids, whether an alert is a dependency advisory and whether it is a secret reach RansomLeak.
The task never fails your build. Any problem, such as a timeout or a missing permission, shows up as a warning and the build carries on.
Learn more
Setup guide: https://docs.ransomleak.com/integrations/azure-devops/