-
Notifications
You must be signed in to change notification settings - Fork 1
Azure Pipelines advanced patterns: API gate, UI publishing, central template #4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
4ada57c
a793fe1
b3fe8ca
a59ce98
00e6a55
aee8ed1
ba42ea9
7034112
2e4ffe7
ed829a8
a5d3cac
a06f773
197b104
d4b506d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| name: "Cycode Release Security Gate" | ||
|
|
||
| on: | ||
| push: | ||
| branches: | ||
| - "release/**" | ||
| workflow_dispatch: | ||
|
|
||
| jobs: | ||
| security-gate: | ||
| name: "Security Gate — SAST, SCA, Secrets" | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Checkout code | ||
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | ||
|
|
||
| - name: Set up Python | ||
| uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 | ||
| with: | ||
| python-version: "3.12" | ||
|
|
||
| - name: Install Cycode CLI | ||
| run: pip install cycode | ||
|
|
||
| - name: SAST scan | ||
| env: | ||
| CYCODE_CLIENT_ID: ${{ secrets.CYCODE_CLIENT_ID }} | ||
| CYCODE_CLIENT_SECRET: ${{ secrets.CYCODE_CLIENT_SECRET }} | ||
| run: cycode scan --severity-threshold HIGH -t sast path ./ | ||
|
|
||
| - name: SCA scan | ||
| env: | ||
| CYCODE_CLIENT_ID: ${{ secrets.CYCODE_CLIENT_ID }} | ||
| CYCODE_CLIENT_SECRET: ${{ secrets.CYCODE_CLIENT_SECRET }} | ||
| run: cycode scan --severity-threshold HIGH -t sca path ./ | ||
|
|
||
| - name: Secrets scan | ||
| env: | ||
| CYCODE_CLIENT_ID: ${{ secrets.CYCODE_CLIENT_ID }} | ||
| CYCODE_CLIENT_SECRET: ${{ secrets.CYCODE_CLIENT_SECRET }} | ||
| run: cycode scan --severity-threshold HIGH -t secret path ./ | ||
|
|
||
| build: | ||
| name: "Build & Package" | ||
| needs: security-gate | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Checkout code | ||
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | ||
|
|
||
| - name: Build | ||
| run: echo "Build, compile, package, test — your build steps here" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| name: "Cycode SAST PR Scan (Delta)" | ||
|
|
||
| on: | ||
| pull_request: | ||
| branches: [main] | ||
| workflow_dispatch: | ||
|
|
||
| jobs: | ||
| cycode-sast-delta: | ||
| name: "SAST Delta Scan — PR Changes Only" | ||
| if: github.event.pull_request.head.repo.full_name == github.repository || github.event_name == 'workflow_dispatch' | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Checkout code | ||
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | ||
| with: | ||
| fetch-depth: 0 | ||
|
|
||
| - name: Set up Python | ||
| uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 | ||
| with: | ||
| python-version: "3.12" | ||
|
|
||
| - name: Install Cycode CLI | ||
| run: pip install cycode | ||
|
|
||
| - name: Run Cycode SAST delta scan | ||
| env: | ||
| CYCODE_CLIENT_ID: ${{ secrets.CYCODE_CLIENT_ID }} | ||
| CYCODE_CLIENT_SECRET: ${{ secrets.CYCODE_CLIENT_SECRET }} | ||
| run: | | ||
| if [ "${{ github.event_name }}" = "pull_request" ]; then | ||
| BASE=${{ github.event.pull_request.base.sha }} | ||
| HEAD=${{ github.event.pull_request.head.sha }} | ||
| echo "Scanning PR delta: ${BASE}..${HEAD}" | ||
| cycode scan -t sast commit-history -r "${BASE}..${HEAD}" . | ||
| else | ||
| echo "Scanning last commit: HEAD~1" | ||
| cycode scan -t sast commit-history -r HEAD~1 . | ||
| fi |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| name: "Cycode SAST Scan" | ||
|
|
||
| on: | ||
| push: | ||
| branches: [main] | ||
| workflow_dispatch: | ||
|
|
||
| jobs: | ||
| cycode-sast: | ||
| name: "SAST Security Scan" | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Checkout code | ||
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | ||
|
|
||
| - name: Set up Python | ||
| uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 | ||
| with: | ||
| python-version: "3.12" | ||
|
|
||
| - name: Install Cycode CLI | ||
| run: pip install cycode | ||
|
|
||
| - name: Run Cycode SAST scan | ||
| env: | ||
| CYCODE_CLIENT_ID: ${{ secrets.CYCODE_CLIENT_ID }} | ||
| CYCODE_CLIENT_SECRET: ${{ secrets.CYCODE_CLIENT_SECRET }} | ||
| run: cycode scan -t sast path ./vulnerable_apps/ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| # Pattern 2 — Cycode API gate (standalone example). | ||
| # | ||
| # Queries Cycode's Risk Intelligence Graph for Open violations in this repo. | ||
| # Fails the build if any match the filters below. | ||
| # | ||
| # Prereqs (one-time in Azure DevOps): | ||
| # - Secret pipeline variables CYCODE_CLIENT_ID and CYCODE_CLIENT_SECRET | ||
| # - Agent pool 'Default' (self-hosted) or change to 'ubuntu-latest' | ||
| # | ||
| # Run manually: Pipelines → this pipeline → Run | ||
| trigger: none | ||
| pr: none | ||
|
|
||
| pool: | ||
| name: Default | ||
|
|
||
| variables: | ||
| # Must match the repo name shown in Cycode's Violations UI. | ||
| REPO_NAME: "AppSecHQ/vectorvictor" | ||
|
|
||
| steps: | ||
| - checkout: self | ||
|
|
||
| - script: | | ||
| if ! command -v jq >/dev/null 2>&1; then | ||
| sudo apt-get update -qq && sudo apt-get install -y -qq jq || true | ||
| fi | ||
| jq --version | ||
| displayName: "Ensure jq is available" | ||
|
|
||
| - script: bash scripts/cycode-gate.sh | ||
| displayName: "Cycode API gate — fail on Open violations" | ||
| env: | ||
| CYCODE_CLIENT_ID: $(CYCODE_CLIENT_ID) | ||
| CYCODE_CLIENT_SECRET: $(CYCODE_CLIENT_SECRET) | ||
| REPO_NAME: $(REPO_NAME) | ||
| # Optional tuning (uncomment to enable): | ||
| # SEVERITY_MIN: "High" # only High + Critical fail the build | ||
| # CATEGORY: "SAST" # scope to one scan type | ||
| # RISK_SCORE_MIN: "70" # ignore findings below risk score 70 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| # Pattern 3 — Publish Cycode results to the Azure Pipelines UI. | ||
| # | ||
| # Populates three surfaces from a single Cycode scan: | ||
| # - Tests tab (each finding = failed test, via JUnit) | ||
| # - Custom summary (Markdown report as a tab on the build summary page) | ||
| # - Artifact (raw cycode-results.json for download) | ||
| # | ||
| # After the results are published, a final CLI gate step enforces pass/fail. | ||
| # Swap the gate to `bash scripts/cycode-gate.sh` to gate on the platform API | ||
| # instead (see azure-pipelines-api-gate.yml). | ||
| trigger: none | ||
| pr: none | ||
|
|
||
| pool: | ||
| name: Default | ||
|
|
||
| variables: | ||
| SCAN_PATH: "./vulnerable_apps/" | ||
|
|
||
| steps: | ||
| - checkout: self | ||
| fetchDepth: 0 | ||
|
|
||
| - task: UsePythonVersion@0 | ||
| displayName: "Use Python 3.12" | ||
| inputs: | ||
| versionSpec: "3.12" | ||
|
|
||
| - script: | | ||
| set -e | ||
| python3 -m pip install --upgrade pip | ||
| pip install cycode | ||
| displayName: "Install Cycode CLI" | ||
|
|
||
| # ---- Scan with --soft-fail so we can publish results first ------------ | ||
| - script: | | ||
| set +e | ||
| cycode -o json scan --soft-fail -t sast path $(SCAN_PATH) > cycode-results.json | ||
| echo "scan exit: $?" | ||
| ls -la cycode-results.json | ||
| displayName: "Cycode SAST scan (JSON)" | ||
| env: | ||
| CYCODE_CLIENT_ID: $(CYCODE_CLIENT_ID) | ||
| CYCODE_CLIENT_SECRET: $(CYCODE_CLIENT_SECRET) | ||
|
|
||
| # ---- Surface 1: Tests tab -------------------------------------------- | ||
| - script: python3 scripts/cycode-json-to-junit.py cycode-results.json cycode-junit.xml | ||
| displayName: "Convert JSON → JUnit" | ||
| condition: succeededOrFailed() | ||
|
|
||
| - task: PublishTestResults@2 | ||
| displayName: 'Publish to "Tests" tab' | ||
| condition: succeededOrFailed() | ||
| inputs: | ||
| testResultsFormat: "JUnit" | ||
| testResultsFiles: "cycode-junit.xml" | ||
| testRunTitle: "Cycode SAST" | ||
| mergeTestResults: true | ||
| failTaskOnFailedTests: false | ||
|
|
||
| # ---- Surface 2: Build summary tab ------------------------------------ | ||
| - script: | | ||
| python3 scripts/cycode-summary.py cycode-results.json > cycode-summary.md | ||
| echo "##vso[task.uploadsummary]$(System.DefaultWorkingDirectory)/cycode-summary.md" | ||
| displayName: 'Publish "Cycode Scan Summary" tab' | ||
| condition: succeededOrFailed() | ||
|
|
||
| # ---- Surface 3: Downloadable artifact -------------------------------- | ||
| - task: PublishBuildArtifacts@1 | ||
| displayName: "Publish raw Cycode JSON" | ||
| condition: succeededOrFailed() | ||
| inputs: | ||
| pathToPublish: "cycode-results.json" | ||
| artifactName: "cycode-report" | ||
|
|
||
| # ---- Final gate ------------------------------------------------------- | ||
| - script: cycode scan -t sast path $(SCAN_PATH) | ||
| displayName: "Cycode SAST gate (fails build on findings)" | ||
| env: | ||
| CYCODE_CLIENT_ID: $(CYCODE_CLIENT_ID) | ||
| CYCODE_CLIENT_SECRET: $(CYCODE_CLIENT_SECRET) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| # Pattern 4 — Consumer pipeline that extends the centralized Cycode template. | ||
| # | ||
| # This same-repo example demonstrates the consumer shape. In production the | ||
| # template lives in a separate repo owned by the security team; consumers | ||
| # reference it via `resources.repositories` and `extends: ...@security-templates`. | ||
| # | ||
| # Cross-repo version (replace the local `extends` below): | ||
| # | ||
| # resources: | ||
| # repositories: | ||
| # - repository: security-templates | ||
| # type: git | ||
| # name: SecurityTeam/cycode-pipeline-templates | ||
| # ref: refs/tags/v1 | ||
| # extends: | ||
| # template: templates/cycode-scan.yml@security-templates | ||
| # parameters: { ... } | ||
| # | ||
| # App teams customize only the parameters — the scan logic, publishing, and | ||
| # gate behavior all live in the template and evolve centrally. | ||
| trigger: none | ||
| pr: none | ||
|
|
||
| extends: | ||
| template: templates/cycode-scan.yml | ||
| parameters: | ||
| scanPath: "./vulnerable_apps/" | ||
| scanTypeFlags: "-t sast" | ||
| severityThreshold: "high" | ||
| repoName: "AppSecHQ/vectorvictor" | ||
| gateMode: "both" # defense in depth: CLI + API | ||
| publishResults: true | ||
| poolName: "Default" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| trigger: | ||
| branches: | ||
| include: | ||
| - main | ||
|
|
||
| pr: | ||
| branches: | ||
| include: | ||
| - main | ||
|
|
||
| pool: | ||
| name: Default | ||
|
|
||
| steps: | ||
| - checkout: self | ||
| fetchDepth: 0 | ||
|
|
||
| - task: UsePythonVersion@0 | ||
| inputs: | ||
| versionSpec: "3.12" | ||
| displayName: Set up Python | ||
|
|
||
| - script: pip install cycode | ||
| displayName: Install Cycode CLI | ||
|
|
||
| # Full scan — runs on push to main | ||
| - script: cycode scan -t sast path ./vulnerable_apps/ | ||
| displayName: Run SAST full scan | ||
| condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) | ||
| env: | ||
| CYCODE_CLIENT_ID: $(CYCODE_CLIENT_ID) | ||
| CYCODE_CLIENT_SECRET: $(CYCODE_CLIENT_SECRET) | ||
|
|
||
| # Delta scan — runs on pull requests only | ||
| - bash: | | ||
| TARGET=$(echo "$(System.PullRequest.TargetBranch)" | sed 's|refs/heads/||') | ||
| git fetch origin "${TARGET}" | ||
| BASE=$(git merge-base "$(System.PullRequest.SourceCommitId)" FETCH_HEAD) | ||
| HEAD="$(System.PullRequest.SourceCommitId)" | ||
| echo "Target branch : ${TARGET}" | ||
| echo "Base SHA : ${BASE}" | ||
| echo "Head SHA : ${HEAD}" | ||
| echo "Range : ${BASE}..${HEAD}" | ||
| cycode scan -t sast commit-history -r "${BASE}..${HEAD}" . | ||
| displayName: Run SAST delta scan | ||
| condition: and(succeeded(), eq(variables['Build.Reason'], 'PullRequest')) | ||
| env: | ||
| CYCODE_CLIENT_ID: $(CYCODE_CLIENT_ID) | ||
| CYCODE_CLIENT_SECRET: $(CYCODE_CLIENT_SECRET) |
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,11 @@ | ||||||||||||
| """API configuration for external service integrations.""" | ||||||||||||
|
|
||||||||||||
| import os | ||||||||||||
|
|
||||||||||||
| # Slack integration | ||||||||||||
| SLACK_BOT_TOKEN = "xoxb-7391528460193-5827461039285-kR4mXpLn7QdWtYvBs9jH3gFe" | ||||||||||||
|
|
||||||||||||
| # 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. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ❗Cycode: Secret of type: 'Generic Password' was found. DescriptionA 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
Tell us how you wish to proceed using one of the following commands:
|
||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
❗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
Tell us how you wish to proceed using one of the following commands: