Azure Pipelines advanced patterns: API gate, UI publishing, central template#4
Azure Pipelines advanced patterns: API gate, UI publishing, central template#4appsechq-brian wants to merge 14 commits into
Conversation
…ning - Add vulnerable_packages/ directory for SCA testing - Add n8n-workflow example with vulnerable n8n v1.100.0 - Add Dockerfile.n8n-vulnerable with CVE-2026-21858 - Update README with new vulnerable_packages section
- gha-excessive-permissions: detects permissions: write-all and broad write scope grants at workflow and job level - gha-dangerous-pr-target-checkout: detects pull_request_target trigger combined with checkout of incoming PR code (pwn request pattern)
Scans vulnerable_apps/ for SAST findings on push, PR, and manual trigger. Blocks build on any finding (default CLI exit code 1).
Scans only the diff between PR base and head commits, not the full codebase. Falls back to HEAD~1 on manual trigger.
Full scan runs on push to main only. PRs use the delta scan workflow.
…centralized template Three new patterns complementing the existing CLI gate in azure-pipelines.yml: - scripts/cycode-gate.sh — queries the Cycode RIG Graph API for Open violations in a named repo; fails the build if any match. Filters by severity, category, and risk score via env vars. Emits ##vso[task.logissue] for Azure Pipelines. - scripts/cycode-json-to-junit.py + scripts/cycode-summary.py — converters that take `cycode -o json scan ...` output and produce JUnit XML (for PublishTestResults@2 → Tests tab) and a Markdown report (for ##vso[task.uploadsummary] → custom tab on build summary). - templates/cycode-scan.yml — centralized template consumed via `extends`. App pipelines pass parameters (scanPath, scanTypeFlags, severityThreshold, repoName, gateMode) and inherit the scan + publish + gate logic. Cross-repo usage documented in the header. Example pipelines: - azure-pipelines-api-gate.yml — API gate standalone - azure-pipelines-publish-results.yml — Tests tab + summary + artifact - azure-pipelines-template-consumer.yml — minimal consumer of the template
| # Database credentials | ||
| DB_HOST = "prod-db.internal.example.com" | ||
| DB_USER = "app_service" | ||
| DB_PASSWORD = "Pr0d_S3cure!P@ssw0rd_2025_xK9m" |
There was a problem hiding this comment.
❗Cycode: Secret of type: 'Generic Password' was found.
Severity: Medium
Confidence Score: 99%
SHA: cf03e5240e
Description
A generic secret or password is an authentication token used to access a computer or application and is assigned to a password variable.
Cycode Remediation Guideline
❗ How to revoke
- Change the password or secret in the system or application where it is used.
- Update any services, applications, or scripts that use the old password or secret with the new one.
- Invalidate any sessions or tokens that were authenticated using the old password or secret.
Tell us how you wish to proceed using one of the following commands:
| Tag | Short Description |
|---|---|
| #cycode_secret_false_positive <reason> | Applies to this secret value for all repos in your organization |
| #cycode_secret_ignore_here <reason> | Applies to this request only |
| #cycode_secret_ignore_everywhere <reason> | Applies to this secret value for all repos in your organization |
| #cycode_secret_revoked | Applies to this secret value for all repos in your organization |
| import os | ||
|
|
||
| # Slack integration | ||
| SLACK_BOT_TOKEN = "xoxb-7391528460193-5827461039285-kR4mXpLn7QdWtYvBs9jH3gFe" |
There was a problem hiding this comment.
❗Cycode: Secret of type: 'Slack Token' was found.
Severity: Medium
SHA: 4ffb66c634
Description
In the scope of the Slack API, a token is an identifier that is used to authenticate Slack app app when making API requests
Cycode Remediation Guideline
❗ How to revoke
- Navigate to the Slack API dashboard at https://api.slack.com/.
- Log in with your Slack account credentials.
- Go to the "Your Apps" section and select the app associated with the token.
- Click on the "OAuth & Permissions" tab.
- Scroll down to the "OAuth Tokens for Your Workspace" section.
- Locate the token you need to revoke and click the "Revoke" button next to it.
- Generate a new token if necessary and update your application with the new token.
Tell us how you wish to proceed using one of the following commands:
| Tag | Short Description |
|---|---|
| #cycode_secret_false_positive <reason> | Applies to this secret value for all repos in your organization |
| #cycode_secret_ignore_here <reason> | Applies to this request only |
| #cycode_secret_ignore_everywhere <reason> | Applies to this secret value for all repos in your organization |
| #cycode_secret_revoked | Applies to this secret value for all repos in your organization |
| # Vulnerable n8n Container | ||
| # This Dockerfile uses a vulnerable version of n8n affected by CVE-2026-21858 | ||
|
|
||
| FROM n8nio/n8n:1.100.0 |
There was a problem hiding this comment.
❗Cycode: Infrastructure configuration issue: 'Specific user should be defined'.
Severity: High
Description
The image will run as root unless a lesser privileged user is defined
Cycode Remediation Guideline
Ensure that at least one USER instruction is defined before or in any none 'FROM scratch' build stage)
Tell us how you wish to proceed using one of the following commands:
| Tag | Short Description |
|---|---|
| #cycode_ignore_violation_for_resource_everywhere <reason> | Applies to this resource for this violation for all repos in your organization |
| #cycode_ai_remediation | Request remediation guidance using Cycode AI |
| #cycode_ignore_violation_for_resource_here <reason> | Applies to this resource for this violation in this request only |
| #cycode_ignore_resource_everywhere <reason> | Applies to this resource for all repos in your organization |
| #cycode_iac_false_positive <reason> | Applies to this resource for this violation for all repos in your organization |
|
|
||
|
|
||
| def main(src: str) -> int: | ||
| with open(src) as f: |
There was a problem hiding this comment.
❗Cycode: SAST violation: 'Unsanitized user input in file path'.
Severity: High
Description
Unsanitized user input in file path resolution can lead to security vulnerabilities. This issue arises when an application directly uses input from the user to determine file paths or names without proper validation or sanitization. Attackers can exploit this to access unauthorized files or directories, leading to data breaches or other security compromises.
Cycode Remediation Guideline
✅ Do
- Do use a safelist to define accessible paths or directories. Only allow user input to influence file paths within these predefined, safe boundaries.
- Do sanitize user input used in file path resolution. For example, use absolute paths and check against the expected base directory
BASE_DIRECTORY = '/path/to/safe/directory' my_path = os.path.abspath(os.path.join(BASE_DIRECTORY, user_input)) if my_path.startswith(BASE_DIRECTORY): open(my_path)
❌ Don't
- Do not directly use user input in file paths without sanitization. Failure to sanitize could allow attackers to manipulate file paths and to access or manipulate unauthorized files.
🎥 Learning materials (by Secure Code Warrior)
Tell us how you wish to proceed using one of the following commands:
| Tag | Short Description |
|---|---|
| #cycode_sast_ignore_here <reason> | Ignore this violation — applies to this violation only |
| #cycode_ai_remediation | Request remediation guidance using Cycode AI |
| #cycode_sast_false_positive <reason> | Mark as false positive — applies to this violation only |
|
|
||
| out.append("</testsuite>") | ||
|
|
||
| with open(dst, "w") as f: |
There was a problem hiding this comment.
❗Cycode: SAST violation: 'Unsanitized user input in file path'.
Severity: High
Description
Unsanitized user input in file path resolution can lead to security vulnerabilities. This issue arises when an application directly uses input from the user to determine file paths or names without proper validation or sanitization. Attackers can exploit this to access unauthorized files or directories, leading to data breaches or other security compromises.
Cycode Remediation Guideline
✅ Do
- Do use a safelist to define accessible paths or directories. Only allow user input to influence file paths within these predefined, safe boundaries.
- Do sanitize user input used in file path resolution. For example, use absolute paths and check against the expected base directory
BASE_DIRECTORY = '/path/to/safe/directory' my_path = os.path.abspath(os.path.join(BASE_DIRECTORY, user_input)) if my_path.startswith(BASE_DIRECTORY): open(my_path)
❌ Don't
- Do not directly use user input in file paths without sanitization. Failure to sanitize could allow attackers to manipulate file paths and to access or manipulate unauthorized files.
🎥 Learning materials (by Secure Code Warrior)
Tell us how you wish to proceed using one of the following commands:
| Tag | Short Description |
|---|---|
| #cycode_sast_ignore_here <reason> | Ignore this violation — applies to this violation only |
| #cycode_ai_remediation | Request remediation guidance using Cycode AI |
| #cycode_sast_false_positive <reason> | Mark as false positive — applies to this violation only |
|
|
||
|
|
||
| def main(src: str, dst: str) -> int: | ||
| with open(src) as f: |
There was a problem hiding this comment.
❗Cycode: SAST violation: 'Unsanitized user input in file path'.
Severity: High
Description
Unsanitized user input in file path resolution can lead to security vulnerabilities. This issue arises when an application directly uses input from the user to determine file paths or names without proper validation or sanitization. Attackers can exploit this to access unauthorized files or directories, leading to data breaches or other security compromises.
Cycode Remediation Guideline
✅ Do
- Do use a safelist to define accessible paths or directories. Only allow user input to influence file paths within these predefined, safe boundaries.
- Do sanitize user input used in file path resolution. For example, use absolute paths and check against the expected base directory
BASE_DIRECTORY = '/path/to/safe/directory' my_path = os.path.abspath(os.path.join(BASE_DIRECTORY, user_input)) if my_path.startswith(BASE_DIRECTORY): open(my_path)
❌ Don't
- Do not directly use user input in file paths without sanitization. Failure to sanitize could allow attackers to manipulate file paths and to access or manipulate unauthorized files.
🎥 Learning materials (by Secure Code Warrior)
Tell us how you wish to proceed using one of the following commands:
| Tag | Short Description |
|---|---|
| #cycode_sast_ignore_here <reason> | Ignore this violation — applies to this violation only |
| #cycode_ai_remediation | Request remediation guidance using Cycode AI |
| #cycode_sast_false_positive <reason> | Mark as false positive — applies to this violation only |
|
Superseded by #5 — clean branch based on origin/main with only the Azure Pipelines advanced patterns commits. |
Summary
Staging branch for three Azure Pipelines integration patterns that extend the existing CLI gate in
azure-pipelines.yml. Not meant to merge to main here — this is a reference copy for customer visibility. The customer-facing doc is inse-copilot/artifacts/azure-pipelines-cycode-advanced-patterns.md.Patterns added
scripts/cycode-gate.shqueries the Cycode RIG Graph API for Open violations in a named repo and exits non-zero on match. Tunable viaSEVERITY_MIN,CATEGORY,RISK_SCORE_MIN.scripts/cycode-json-to-junit.py+scripts/cycode-summary.pytransformcycode -o json scan ...output forPublishTestResults@2(Tests tab) and##vso[task.uploadsummary](custom tab). Raw JSON also published viaPublishBuildArtifacts@1.templates/cycode-scan.ymlowns the scan + publish + gate logic. Consumersextendsit and pass parameters. Cross-repo usage documented in the template header.Example pipelines
azure-pipelines-api-gate.ymlazure-pipelines-publish-results.ymlazure-pipelines-template-consumer.ymltemplates/cycode-scan.ymlReal deployment target
Customer will configure and test in a private Azure DevOps environment with a self-hosted runner.
pool: name: Defaultmatches the existing pipeline; swap toubuntu-latestfor Microsoft-hosted agents.Secrets needed
Azure DevOps:
CYCODE_CLIENT_ID,CYCODE_CLIENT_SECRETas secret pipeline variables (ideally via Variable Group backed by Key Vault).Test plan
scripts/cycode-gate.shlocally with real env vars againstlevine-se-playgroundto confirm the RIG filter ondetection_details.repository_nameresolvesazure-pipelines-api-gate.ymlpasses / fails based on tenant stateazure-pipelines-publish-results.ymlpopulates Tests tab, custom summary tab, and artifactazure-pipelines-template-consumer.ymlextends the template and runs end-to-endresources.repositories+extendspattern