Azure Pipelines advanced patterns: API gate, UI publishing, central template#5
Conversation
…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
- RIG .result[] items wrap detections in a .resource object; updated the
jq filter to drill through it so severity/risk_score/policy/file_path
render correctly instead of all dashes.
- Use bare repo name ("vectorvictor") in defaults and docs — the
owner/repo form returns 0 results in RIG.
- Check .fast_query_has_more and surface "at least N (page cap hit)"
when results are paginated, so the gate message is truthful when the
tenant has more than page_size findings.
|
|
||
|
|
||
| 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 |
Summary
Reference copy of three Azure Pipelines integration patterns that extend the existing CLI gate. Customer-facing doc:
se-copilot/artifacts/azure-pipelines-cycode-advanced-patterns.md.Replaces #4 (that branch inherited unrelated local commits). This one is based cleanly on
origin/main— two commits, only the Azure work.Patterns
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. Live-tested againstlevine-se-playground.scripts/cycode-json-to-junit.py+scripts/cycode-summary.pyconvertcycode -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. Consumersextends:it 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.ymlGotchas captured
detection_details.repository_namestores the bare repo name (vectorvictor), notowner/repo. Verified 2026-04-22..result[]in RIG wraps the detection in a.resourceobject — jq paths must drill through it.at least N (page cap hit)whenfast_query_has_more=true, so the logged count is truthful when there are more than 200 findings.Secrets
Azure DevOps pipeline secrets:
CYCODE_CLIENT_ID,CYCODE_CLIENT_SECRET(ideally via Variable Group backed by Key Vault).Test plan
scripts/cycode-gate.shreturns non-zero forvectorvictoragainstlevine-se-playgroundresources.repositories+extendspattern