Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions .github/workflows/cycode-release-gate.yml
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"
40 changes: 40 additions & 0 deletions .github/workflows/cycode-sast-pr-scan.yml
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
28 changes: 28 additions & 0 deletions .github/workflows/cycode-sast-scan.yml
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/
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ Examples of insecure Docker configurations and container practices:
- **Dockerfile.secrets-exposed** - Hardcoded secrets and credentials
- **Dockerfile.rootful-privileged** - Privileged containers running as root
- **Dockerfile.multistage-bad** - Insecure multi-stage builds
- **Dockerfile.n8n-vulnerable** - n8n with CVE-2026-21858 (CVSS 10.0)
- **docker-compose.vulnerable.yml** - Insecure Docker Compose configuration

### 🏗️ Vulnerable Terraform (`vulnerable_terraform/`)
Expand All @@ -60,6 +61,12 @@ Infrastructure-as-Code examples with security misconfigurations:
- **aws_iam_vulnerable.tf** - Overly permissive IAM policies and roles
- **aws_misc_vulnerable.tf** - Additional AWS security issues

### 📦 Vulnerable Packages (`vulnerable_packages/`)

Examples of applications using vulnerable open-source dependencies for SCA testing:

- **n8n-workflow/** - Workflow automation with n8n v1.100.0 (CVE-2026-21858, CVSS 10.0)

### 🌐 Vulnerable Web Applications (`vulnerable_apps/`)

Python web application examples demonstrating **OWASP Top 10 (2021)** vulnerabilities:
Expand All @@ -76,6 +83,30 @@ Python web application examples demonstrating **OWASP Top 10 (2021)** vulnerabil
- Hardcoded Secrets


## 🔧 Cycode CI/CD Integration Examples

Working examples of Cycode as a CI/CD gate in Azure Pipelines and GitHub Actions.

### GitHub Actions (`.github/workflows/`)
- `cycode-sast-scan.yml` — full SAST scan on push to main
- `cycode-sast-pr-scan.yml` — delta scan on pull requests
- `cycode-release-gate.yml` — release-stage gate (SAST + SCA + Secrets)

### Azure Pipelines

| File | Pattern |
|------|---------|
| `azure-pipelines.yml` | **CLI gate** — full scan on push, delta on PR (original pattern) |
| `azure-pipelines-api-gate.yml` | **API gate** — query Cycode RIG for Open violations in the repo |
| `azure-pipelines-publish-results.yml` | **Surface results in Azure UI** — Tests tab + custom summary tab + downloadable artifact |
| `azure-pipelines-template-consumer.yml` | **Centralized template** — consumer that `extends: templates/cycode-scan.yml` |
| `templates/cycode-scan.yml` | Master template (scan + publish + gate) consumed by many pipelines |
| `scripts/cycode-gate.sh` | Standalone API-gate script (works from any shell) |
| `scripts/cycode-json-to-junit.py` | Converts `cycode -o json` output to JUnit XML |
| `scripts/cycode-summary.py` | Generates a short Markdown summary for the build summary tab |

Secrets needed in Azure DevOps (Pipelines → Library or pipeline variables with lock icon): `CYCODE_CLIENT_ID`, `CYCODE_CLIENT_SECRET`.

## 🚀 Getting Started

Each directory contains its own README with specific vulnerability descriptions.
Expand Down
40 changes: 40 additions & 0 deletions azure-pipelines-api-gate.yml
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
81 changes: 81 additions & 0 deletions azure-pipelines-publish-results.yml
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)
33 changes: 33 additions & 0 deletions azure-pipelines-template-consumer.yml
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"
49 changes: 49 additions & 0 deletions azure-pipelines.yml
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)
11 changes: 11 additions & 0 deletions config/api_config.py
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"

Check failure on line 6 in config/api_config.py

View check run for this annotation

Cycode Security / Cycode: Secrets

config/api_config.py#L6

Slack Token found
Copy link
Copy Markdown

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


  • 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

⚠️ When commenting on Github, you may need to refresh the page to see the latest updates.


# Database credentials
DB_HOST = "prod-db.internal.example.com"
DB_USER = "app_service"
DB_PASSWORD = "Pr0d_S3cure!P@ssw0rd_2025_xK9m"

Check failure on line 11 in config/api_config.py

View check run for this annotation

Cycode Security / Cycode: Secrets

config/api_config.py#L11

Generic Password found
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

⚠️ When commenting on Github, you may need to refresh the page to see the latest updates.

Loading
Loading