Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
2501666
Add n8n CVE-2026-21858 vulnerable examples for SCA and container scan…
levine-cycode Jan 27, 2026
247033b
Merge branch 'levinebw:main' into main
levine-cycode Mar 17, 2026
8b6e561
Add API configuration for service integrations
appsechq-brian Feb 18, 2026
68b78f7
Add custom Semgrep rules for GitHub Actions security
appsechq-brian Apr 7, 2026
2ac1f0d
Add Cycode CLI SAST scan workflow
appsechq-brian Apr 13, 2026
78692bb
Pin GitHub Actions to commit SHAs for supply chain security
appsechq-brian Apr 13, 2026
7e09b25
Add Cycode SAST delta scan workflow for PRs
appsechq-brian Apr 13, 2026
3cfda9c
Skip delta scan on fork PRs to protect secrets
appsechq-brian Apr 13, 2026
62e1741
Remove PR trigger from full scan workflow
appsechq-brian Apr 13, 2026
36212ef
Update pinned action SHAs to v6 (Node.js 24 compatible)
appsechq-brian Apr 13, 2026
fd18dad
Add Azure Pipelines advanced patterns: API gate, results publishing, …
appsechq-brian Apr 22, 2026
cffbf60
Fix cycode-gate.sh jq paths and use bare repo name
appsechq-brian Apr 22, 2026
ebfb9a2
Merge pull request #5 from AppSecHQ/feature/azure-cycode-advanced-pat…
levine-cycode Apr 22, 2026
0133d0a
Fix JUnit XML attribute escaping (use quoteattr)
appsechq-brian Apr 22, 2026
9cb537a
Use ADO Library variable group for credentials; harden jq install
appsechq-brian Apr 22, 2026
e1e977a
Merge pull request #7 from AppSecHQ/fix/junit-xml-attribute-escaping
levine-cycode Apr 23, 2026
492d2e6
Merge branch 'main' into fix/variable-group-and-jq-install
levine-cycode Apr 23, 2026
043ce7d
Merge pull request #6 from AppSecHQ/fix/variable-group-and-jq-install
levine-cycode Apr 23, 2026
d34014c
Rich reporting: HTML + CSV artifacts with customer-requested columns
appsechq-brian Apr 23, 2026
606e9fe
Merge pull request #8 from AppSecHQ/feature/rich-reporting-html-csv
levine-cycode Apr 23, 2026
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
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
55 changes: 55 additions & 0 deletions azure-pipelines-api-gate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# 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):
# - Library variable group 'cycode-credentials' with CYCODE_CLIENT_ID and
# CYCODE_CLIENT_SECRET (both marked 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:
# Pulls CYCODE_CLIENT_ID and CYCODE_CLIENT_SECRET from ADO Library.
- group: cycode-credentials
# Must match the repo name shown in Cycode's Violations UI.
# Bare repo name as stored in Cycode's RIG — NOT "owner/repo".
- name: REPO_NAME
value: "vectorvictor"

steps:
- checkout: self

- script: |
set -e
if command -v jq >/dev/null 2>&1; then
jq --version
exit 0
fi
if command -v apt-get >/dev/null 2>&1; then
sudo apt-get update -qq && sudo apt-get install -y -qq jq
elif command -v brew >/dev/null 2>&1; then
brew install jq
else
echo "##vso[task.logissue type=error]jq is not installed and no supported package manager (apt-get, brew) was found. Preinstall jq on the agent."
exit 1
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
97 changes: 97 additions & 0 deletions azure-pipelines-publish-results.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# 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:
# Pulls CYCODE_CLIENT_ID and CYCODE_CLIENT_SECRET from ADO Library.
# Create via Pipelines → Library → + Variable group → 'cycode-credentials'.
- group: cycode-credentials
- name: SCAN_PATH
value: "./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 + Surface 3: rich report artifact ----
# Generates Markdown (for the summary tab), HTML (rich interactive report),
# and CSV (Excel-friendly export) from a single cycode-results.json. The
# HTML + CSV + raw JSON are bundled into one 'cycode-report' artifact.
- script: |
set -e
mkdir -p cycode-report
cp cycode-results.json cycode-report/cycode-results.json
python3 scripts/cycode-summary.py cycode-results.json \
--md cycode-report/cycode-summary.md \
--html cycode-report/cycode-report.html \
--csv cycode-report/cycode-report.csv
ls -la cycode-report/
echo "##vso[task.uploadsummary]$(System.DefaultWorkingDirectory)/cycode-report/cycode-summary.md"
displayName: 'Build reports (Markdown summary, HTML, CSV) + publish summary tab'
condition: succeededOrFailed()
env:
# Optional: override console base URL if your tenant uses a custom domain.
CYCODE_CONSOLE_URL: "https://app.cycode.com"

- task: PublishBuildArtifacts@1
displayName: "Publish Cycode report bundle (JSON + HTML + CSV)"
condition: succeededOrFailed()
inputs:
pathToPublish: "cycode-report"
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)
38 changes: 38 additions & 0 deletions azure-pipelines-template-consumer.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# 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

variables:
# Pulls CYCODE_CLIENT_ID and CYCODE_CLIENT_SECRET from ADO Library.
# Create via Pipelines → Library → + Variable group → 'cycode-credentials'.
- group: cycode-credentials

extends:
template: templates/cycode-scan.yml
parameters:
scanPath: "./vulnerable_apps/"
scanTypeFlags: "-t sast"
severityThreshold: "high"
repoName: "vectorvictor" # bare repo name (not "owner/repo")
gateMode: "both" # defense in depth: CLI + API
publishResults: true
poolName: "Default"
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"

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