diff --git a/.github/workflows/cycode-sast-pr-scan.yml b/.github/workflows/cycode-sast-pr-scan.yml new file mode 100644 index 0000000..2255ac7 --- /dev/null +++ b/.github/workflows/cycode-sast-pr-scan.yml @@ -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 diff --git a/.github/workflows/cycode-sast-scan.yml b/.github/workflows/cycode-sast-scan.yml new file mode 100644 index 0000000..0e28b7c --- /dev/null +++ b/.github/workflows/cycode-sast-scan.yml @@ -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/ diff --git a/README.md b/README.md index e98859b..3b21b3e 100644 --- a/README.md +++ b/README.md @@ -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/`) @@ -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: diff --git a/config/api_config.py b/config/api_config.py new file mode 100644 index 0000000..27acab0 --- /dev/null +++ b/config/api_config.py @@ -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" diff --git a/semgrep-rules/gha-dangerous-pr-target-checkout.yml b/semgrep-rules/gha-dangerous-pr-target-checkout.yml new file mode 100644 index 0000000..77cdd3f --- /dev/null +++ b/semgrep-rules/gha-dangerous-pr-target-checkout.yml @@ -0,0 +1,97 @@ +rules: + + # --------------------------------------------------------------- + # Dangerous pull_request_target + PR code checkout + # --------------------------------------------------------------- + # Based on the official Semgrep rule: + # yaml.github-actions.security.pull-request-target-code-checkout + # Source: https://github.com/semgrep/semgrep-rules/blob/develop/yaml/github-actions/security/pull-request-target-code-checkout.yaml + # + # The pull_request_target event runs in the context of the BASE + # (target) repository, which means it has access to all repo + # secrets. Checking out the PR head then executes untrusted + # code from the fork with those secrets available. + # + # Covers all three trigger syntax variants: + # on: + # pull_request_target: ... (mapping) + # on: [push, pull_request_target] (list) + # on: pull_request_target (bare string) + # + # Covers all ref variants via metavariable-pattern: + # github.event.pull_request.head.sha + # github.event.pull_request.head.ref + # github.head_ref + # --------------------------------------------------------------- + - id: gha-pr-target-code-checkout + languages: [yaml] + message: >- + This GitHub Actions workflow uses `pull_request_target` and checks + out code from the incoming pull request. When using + `pull_request_target`, the Action runs in the context of the target + repository, which includes access to all repository secrets. + By checking out the incoming PR code, any build scripts + (npm install, make, pip install) execute attacker-controlled code + with access to those secrets, allowing an attacker to steal them. + Audit your workflow to ensure no incoming PR code is executed. + See https://securitylab.github.com/research/github-actions-preventing-pwn-requests/ + severity: ERROR + metadata: + category: security + owasp: + - "A08:2021 - Software and Data Integrity Failures" + cwe: + - "CWE-829: Inclusion of Functionality from Untrusted Control Sphere" + references: + - https://securitylab.github.com/research/github-actions-preventing-pwn-requests/ + - https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target + likelihood: MEDIUM + impact: HIGH + confidence: HIGH + patterns: + # Match any of the three trigger syntax forms + - pattern-either: + - pattern-inside: | + on: + ... + pull_request_target: ... + ... + ... + - pattern-inside: | + on: [..., pull_request_target, ...] + ... + - pattern-inside: | + on: pull_request_target + ... + # Scope to within a job's steps block + - pattern-inside: | + jobs: + ... + $JOBNAME: + ... + steps: + ... + # Match the checkout action with a ref parameter + - pattern: | + ... + uses: "$ACTION" + with: + ... + ref: $EXPR + # Ensure it's an actions/checkout action + - metavariable-regex: + metavariable: $ACTION + regex: actions/checkout@.* + # Ensure the ref expression references the incoming PR + - metavariable-pattern: + language: generic + metavariable: $EXPR + patterns: + - pattern-inside: "${{ ... }}" + - pattern-either: + - pattern: github.event.pull_request ... + - pattern: github.head_ref ... + paths: + include: + - "*.yml" + - "*.yaml" diff --git a/semgrep-rules/gha-excessive-permissions.yml b/semgrep-rules/gha-excessive-permissions.yml new file mode 100644 index 0000000..0aa4f47 --- /dev/null +++ b/semgrep-rules/gha-excessive-permissions.yml @@ -0,0 +1,125 @@ +rules: + + # --------------------------------------------------------------- + # Rule 1: Workflow-level permissions: write-all + # --------------------------------------------------------------- + # Catches the most obvious anti-pattern: granting the GITHUB_TOKEN + # full write access to every scope at the workflow level. + # + # Example match: + # permissions: write-all + # jobs: + # build: + # runs-on: ubuntu-latest + # --------------------------------------------------------------- + - id: gha-workflow-permissions-write-all + message: > + GitHub Actions workflow grants 'permissions: write-all' at the + workflow level, giving the GITHUB_TOKEN unrestricted write access + to all scopes (contents, packages, pull-requests, etc.). + Apply least-privilege by declaring only the permissions each job + actually needs. + severity: ERROR + languages: [yaml] + metadata: + cwe: + - "CWE-250: Execution with Unnecessary Privileges" + references: + - https://docs.github.com/en/actions/security-for-github-actions/security-guides/automatic-token-authentication#modifying-the-permissions-for-the-github_token + category: security + patterns: + - pattern: | + permissions: write-all + - pattern-not-inside: | + jobs: + ... + $JOB: + ... + permissions: write-all + ... + paths: + include: + - "*.yml" + - "*.yaml" + + # --------------------------------------------------------------- + # Rule 2: Job-level permissions: write-all + # --------------------------------------------------------------- + # Same problem scoped to a single job. Still overly broad since + # it grants write to every scope for that job. + # + # Example match: + # jobs: + # deploy: + # permissions: write-all + # runs-on: ubuntu-latest + # --------------------------------------------------------------- + - id: gha-job-permissions-write-all + message: > + GitHub Actions job grants 'permissions: write-all', giving the + GITHUB_TOKEN unrestricted write access for this job. Narrow the + permissions to only those required (e.g., contents: read). + severity: WARNING + languages: [yaml] + metadata: + cwe: + - "CWE-250: Execution with Unnecessary Privileges" + references: + - https://docs.github.com/en/actions/security-for-github-actions/security-guides/automatic-token-authentication#modifying-the-permissions-for-the-github_token + category: security + pattern: | + jobs: + ... + $JOB: + ... + permissions: write-all + ... + paths: + include: + - "*.yml" + - "*.yaml" + + # --------------------------------------------------------------- + # Rule 3: Workflow-level broad write scopes + # --------------------------------------------------------------- + # Catches explicit per-scope write grants at the workflow level + # that are commonly over-provisioned. The metavariable $SCOPE + # matches any scope key (contents, packages, etc.) set to write. + # + # Example match: + # permissions: + # contents: write + # packages: write + # pull-requests: write + # --------------------------------------------------------------- + - id: gha-workflow-broad-write-permissions + message: > + GitHub Actions workflow grants write access to scope '$SCOPE' + at the workflow level. Verify this is required. Prefer setting + permissions at the job level so each job gets only what it needs. + severity: WARNING + languages: [yaml] + metadata: + cwe: + - "CWE-250: Execution with Unnecessary Privileges" + references: + - https://docs.github.com/en/actions/security-for-github-actions/security-guides/automatic-token-authentication + category: security + patterns: + - pattern: | + permissions: + ... + $SCOPE: write + ... + - pattern-not-inside: | + jobs: + ... + $JOB: + ... + permissions: + ... + ... + paths: + include: + - "*.yml" + - "*.yaml" diff --git a/vulnerable_dockerfiles/Dockerfile.n8n-vulnerable b/vulnerable_dockerfiles/Dockerfile.n8n-vulnerable new file mode 100644 index 0000000..f006ea0 --- /dev/null +++ b/vulnerable_dockerfiles/Dockerfile.n8n-vulnerable @@ -0,0 +1,47 @@ +# Vulnerable n8n Container +# This Dockerfile uses a vulnerable version of n8n affected by CVE-2026-21858 + +FROM n8nio/n8n:1.100.0 + +LABEL maintainer="demo@example.com" +LABEL description="Workflow automation service" +LABEL vulnerability="CVE-2026-21858" + +# Environment configuration +ENV N8N_BASIC_AUTH_ACTIVE=false +ENV N8N_HOST=0.0.0.0 +ENV N8N_PORT=5678 +ENV N8N_PROTOCOL=http +ENV GENERIC_TIMEZONE=UTC + +# Expose the n8n port +EXPOSE 5678 + +# Start n8n +CMD ["n8n", "start"] + +# ============================================================================= +# VULNERABILITY INFORMATION +# ============================================================================= +# +# CVE-2026-21858: Critical Content-Type Confusion RCE in n8n +# +# Affected: n8n versions >= 1.65.0 and < 1.121.0 +# CVSS Score: 10.0 (Critical) +# CWE: CWE-20 (Improper Input Validation) +# +# The n8n:1.100.0 base image contains a critical vulnerability in Form Webhook +# handling that allows unauthenticated attackers to: +# - Read arbitrary files from the container +# - Extract database and session secrets +# - Forge administrator sessions +# - Execute arbitrary commands +# +# REMEDIATION: +# Update to a fixed version: +# FROM n8nio/n8n:1.121.0 +# +# References: +# - https://nvd.nist.gov/vuln/detail/CVE-2026-21858 +# - https://github.com/n8n-io/n8n/security/advisories/GHSA-v4pr-fm98-w9pg +# ============================================================================= diff --git a/vulnerable_packages/README.md b/vulnerable_packages/README.md new file mode 100644 index 0000000..4b63231 --- /dev/null +++ b/vulnerable_packages/README.md @@ -0,0 +1,29 @@ +# Vulnerable Packages + +Examples of applications using vulnerable open-source packages for SCA (Software Composition Analysis) testing. + +## Contents + +### n8n-workflow/ + +Workflow automation service using a vulnerable version of n8n affected by **CVE-2026-21858** (CVSS 10.0 Critical). + +- **Vulnerability**: Content-Type confusion enabling unauthenticated RCE +- **Affected versions**: n8n >= 1.65.0 and < 1.121.0 +- **Fixed version**: 1.121.0 + +## Use Cases + +- Testing SCA scanner detection of critical CVEs +- Validating vulnerability enrichment (CVSS, EPSS, KEV status) +- Demonstrating supply chain risk from vulnerable dependencies +- Training on dependency vulnerability remediation + +## Adding New Examples + +When adding vulnerable package examples: + +1. Create a subdirectory with a descriptive name +2. Include the package manifest (package.json, requirements.txt, go.mod, etc.) +3. Document the specific CVE(s) and affected versions in a README +4. Include remediation guidance diff --git a/vulnerable_packages/n8n-workflow/README.md b/vulnerable_packages/n8n-workflow/README.md new file mode 100644 index 0000000..5a8a4c6 --- /dev/null +++ b/vulnerable_packages/n8n-workflow/README.md @@ -0,0 +1,40 @@ +# N8N Workflow Service + +Example workflow automation service using n8n. + +## Vulnerability + +This package includes **n8n v1.100.0**, which is affected by **CVE-2026-21858** (CVSS 10.0 Critical). + +### CVE-2026-21858: Content-Type Confusion RCE + +- **Affected versions**: n8n >= 1.65.0 and < 1.121.0 +- **Fixed version**: 1.121.0 +- **CWE**: CWE-20 (Improper Input Validation) +- **Attack vector**: Unauthenticated remote code execution via Form Webhook + +### Description + +A content-type confusion vulnerability in n8n's Form Webhook handling allows an unauthenticated attacker to: + +1. Read arbitrary files from the n8n instance +2. Extract database credentials and session secrets +3. Forge administrator sessions +4. Execute arbitrary commands on the host + +### Remediation + +Upgrade to n8n version 1.121.0 or later: + +```json +{ + "dependencies": { + "n8n": "1.121.0" + } +} +``` + +### References + +- [NVD - CVE-2026-21858](https://nvd.nist.gov/vuln/detail/CVE-2026-21858) +- [GitHub Security Advisory GHSA-v4pr-fm98-w9pg](https://github.com/n8n-io/n8n/security/advisories/GHSA-v4pr-fm98-w9pg) diff --git a/vulnerable_packages/n8n-workflow/package.json b/vulnerable_packages/n8n-workflow/package.json new file mode 100644 index 0000000..17616fa --- /dev/null +++ b/vulnerable_packages/n8n-workflow/package.json @@ -0,0 +1,22 @@ +{ + "name": "n8n-workflow-service", + "version": "1.0.0", + "description": "Workflow automation service using n8n", + "main": "index.js", + "scripts": { + "start": "n8n start", + "dev": "n8n start --tunnel" + }, + "keywords": [ + "workflow", + "automation", + "n8n" + ], + "author": "Demo Project", + "license": "MIT", + "dependencies": { + "n8n": "1.100.0", + "express": "^4.18.2", + "dotenv": "^16.3.1" + } +}