Skip to content
Back to skills

Pipeline Security

ASecurity

Security scanning templates and HARD-RULES for CI/CD pipeline configuration. Covers Semgrep SAST and TruffleHog secret scanning across Bitbucket, GitHub Actions, GitLab CI, and Azure DevOps.

  • 5 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added May 28, 2026
developmentjavascripttypescriptpythongojavaphpreactnextjsnodenodejs

Works with

  • api

Security analysis

A100/100

Scanned May 28, 2026

npx -y skills add AppVerk/av-marketplace --skill pipeline-security --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Pipeline Security?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Pipeline Security
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/appverk-pipeline-security/badge)](https://www.skillsdirectory.com/skills/appverk-pipeline-security)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: pipeline-security
description: Security scanning templates and HARD-RULES for CI/CD pipeline configuration. Covers Semgrep SAST and TruffleHog secret scanning across Bitbucket, GitHub Actions, GitLab CI, and Azure DevOps.
---

# Pipeline Security Configuration

Security scanning configuration templates for CI/CD pipelines. This skill provides templates for Semgrep (SAST) and TruffleHog (secret scanning) and mappings for multiple CI/CD providers.

<HARD-RULES>
## HARD-RULES - Non-Negotiable Requirements

These rules MUST be followed in every generated configuration. Violation of any rule makes the output non-compliant.

1. Semgrep MUST always include `p/owasp-top-ten` config for every language
2. Semgrep MUST always include `p/security-audit` config for every language
3. Semgrep MUST always include `p/secrets` config for every language
4. TruffleHog MUST use `--results=verified,unknown` flag
5. Each Semgrep step MUST have two passes:
   - **Soft pass**: generates SARIF report, does NOT fail the build (`|| true`)
   - **Hard pass**: fails on `--severity=ERROR` and `--severity=WARNING` with `--error .`
6. Each detected language MUST get its own separate Semgrep step
7. Framework-specific Semgrep rulesets MUST be added when the framework is detected in project dependencies
</HARD-RULES>

## Security Components

### TruffleHog - Secret Scanning (Universal)

TruffleHog is language-agnostic. One step per pipeline.

**Image:** `trufflesecurity/trufflehog:latest`

**Logic:** The step must detect whether it is running in a PR context or a regular commit context:

- **PR context**: scan full git history, output JSON report, then re-run with `--fail`
- **Commit context**: scan since last commit (`HEAD~1`), output JSON report, then re-run with `--fail`

**Required flags:** `--results=verified,unknown`
**Artifact:** `trufflehog-results.json`

### Semgrep - Python

- **Image:** `semgrep/semgrep:latest`
- **Base configs:** `p/python`, `p/security-audit`, `p/secrets`, `p/owasp-top-ten`
- **Framework configs:** Django -> `p/django`, Flask -> `p/flask`, FastAPI -> `p/fastapi`
- **Excludes:** `tests`, `.venv`
- **Artifact:** `semgrep-results.sarif`
- **Two passes:** soft (SARIF report, `|| true`) + hard (fail on ERROR/WARNING)

### Semgrep - PHP

- **Base configs:** `p/php`, `p/phpcs-security-audit`, `p/security-audit`, `p/secrets`, `p/owasp-top-ten`
- **Framework configs:** Symfony -> `p/symfony`, Laravel -> `p/laravel`, WordPress -> `p/wordpress`
- **Excludes:** `tests`, `vendor`

### Semgrep - JavaScript/TypeScript

- **Base configs:** `p/javascript`, `p/typescript`, `p/nodejs`, `p/security-audit`, `p/secrets`, `p/owasp-top-ten`
- **Framework configs:** React -> `p/react`, Next.js -> `p/nextjs`, Express -> `p/express`
- **Excludes:** `node_modules`, `dist`, `build`, `coverage`

## CI/CD Provider Templates

### Bitbucket Pipelines

**File:** `bitbucket-pipelines.yml`

Uses YAML anchors in `definitions.steps`, referenced in `pipelines.default`.
PR detection via `$BITBUCKET_PR_ID`.

```yaml
definitions:
  steps:
    - step: &trufflehog_scan
        name: Secret Scanning - TruffleHog
        image: trufflesecurity/trufflehog:latest
        script:
          - |
            if [ -n "$BITBUCKET_PR_ID" ]; then
              echo "PR detected - scanning full git history"
              trufflehog git file://. --results=verified,unknown --json > trufflehog-results.json || true
              trufflehog git file://. --results=verified,unknown --fail
            else
              echo "Commit detected - scanning since last commit"
              trufflehog git file://. --since-commit HEAD~1 --results=verified,unknown --json > trufflehog-results.json || true
              trufflehog git file://. --since-commit HEAD~1 --results=verified,unknown --fail
            fi
        artifacts:
          - trufflehog-results.json

    - step: &semgrep_scan_python
        name: SAST - Semgrep Security Scan (Python)
        image: semgrep/semgrep:latest
        script:
          - semgrep scan
              --config p/python
              --config p/security-audit
              --config p/secrets
              --config p/owasp-top-ten
              --exclude tests
              --exclude .venv
              --sarif
              --output semgrep-results.sarif . || true
          - semgrep scan
              --config p/python
              --config p/security-audit
              --config p/secrets
              --config p/owasp-top-ten
              --exclude tests
              --exclude .venv
              --severity=ERROR
              --severity=WARNING
              --error .
        artifacts:
          - semgrep-results.sarif

pipelines:
  default:
    - step: *trufflehog_scan
    - step: *semgrep_scan_python
```

### GitHub Actions

**File:** `.github/workflows/security.yml`

Workflow with `on: [push, pull_request]`. Each tool is a separate job using `container`. PR detection via `github.event_name == 'pull_request'`. Artifacts uploaded via `actions/upload-artifact@v4`.

```yaml
name: Security Scanning

on:
  push:
  pull_request:

jobs:
  trufflehog:
    name: Secret Scanning - TruffleHog
    runs-on: ubuntu-latest
    container:
      image: trufflesecurity/trufflehog:latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Run TruffleHog
        run: |
          if [ "${{ github.event_name }}" = "pull_request" ]; then
            echo "PR detected - scanning full git history"
            trufflehog git file://. --results=verified,unknown --json > trufflehog-results.json || true
            trufflehog git file://. --results=verified,unknown --fail
          else
            echo "Commit detected - scanning since last commit"
            trufflehog git file://. --since-commit HEAD~1 --results=verified,unknown --json > trufflehog-results.json || true
            trufflehog git file://. --since-commit HEAD~1 --results=verified,unknown --fail
          fi
      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: trufflehog-results
          path: trufflehog-results.json

  semgrep-python:
    name: SAST - Semgrep Security Scan (Python)
    runs-on: ubuntu-latest
    container:
      image: semgrep/semgrep:latest
    steps:
      - uses: actions/checkout@v4
      - name: Semgrep Scan (report)
        run: |
          semgrep scan \
            --config p/python \
            --config p/security-audit \
            --config p/secrets \
            --config p/owasp-top-ten \
            --exclude tests \
            --exclude .venv \
            --sarif \
            --output semgrep-results.sarif . || true
      - name: Semgrep Scan (enforce)
        run: |
          semgrep scan \
            --config p/python \
            --config p/security-audit \
            --config p/secrets \
            --config p/owasp-top-ten \
            --exclude tests \
            --exclude .venv \
            --severity=ERROR \
            --severity=WARNING \
            --error .
      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: semgrep-results-python
          path: semgrep-results.sarif
```

### GitLab CI

**File:** `.gitlab-ci.yml`

Uses `stages: [security]`. PR/MR detection via `$CI_MERGE_REQUEST_IID`. SARIF artifacts reported via `artifacts.reports.sast` for native GitLab Security Dashboard integration.

```yaml
stages:
  - security

trufflehog_scan:
  stage: security
  image: trufflesecurity/trufflehog:latest
  script:
    - |
      if [ -n "$CI_MERGE_REQUEST_IID" ]; then
        echo "MR detected - scanning full git history"
        trufflehog git file://. --results=verified,unknown --json > trufflehog-results.json || true
        trufflehog git file://. --results=verified,unknown --fail
      else
        echo "Commit detected - scanning since last commit"
        trufflehog git file://. --since-commit HEAD~1 --results=verified,unknown --json > trufflehog-results.json || true
        trufflehog git file://. --since-commit HEAD~1 --results=verified,unknown --fail
      fi
  artifacts:
    paths:
      - trufflehog-results.json
    when: always

semgrep_scan_python:
  stage: security
  image: semgrep/semgrep:latest
  script:
    - semgrep scan
        --config p/python
        --config p/security-audit
        --config p/secrets
        --config p/owasp-top-ten
        --exclude tests
        --exclude .venv
        --sarif
        --output semgrep-results.sarif . || true
    - semgrep scan
        --config p/python
        --config p/security-audit
        --config p/secrets
        --config p/owasp-top-ten
        --exclude tests
        --exclude .venv
        --severity=ERROR
        --severity=WARNING
        --error .
  artifacts:
    paths:
      - semgrep-results.sarif
    reports:
      sast: semgrep-results.sarif
    when: always
```

### Azure DevOps

**File:** `azure-pipelines.yml`

Uses `stages` with `stage: Security`. PR detection via `Build.Reason`. Artifacts published via `PublishBuildArtifacts@1`.

```yaml
stages:
  - stage: Security
    displayName: Security Scanning
    jobs:
      - job: TruffleHog
        displayName: Secret Scanning - TruffleHog
        container:
          image: trufflesecurity/trufflehog:latest
        steps:
          - checkout: self
            fetchDepth: 0
          - script: |
              if [ "$(Build.Reason)" = "PullRequest" ]; then
                echo "PR detected - scanning full git history"
                trufflehog git file://. --results=verified,unknown --json > trufflehog-results.json || true
                trufflehog git file://. --results=verified,unknown --fail
              else
                echo "Commit detected - scanning since last commit"
                trufflehog git file://. --since-commit HEAD~1 --results=verified,unknown --json > trufflehog-results.json || true
                trufflehog git file://. --since-commit HEAD~1 --results=verified,unknown --fail
              fi
            displayName: Run TruffleHog
          - task: PublishBuildArtifacts@1
            condition: always()
            inputs:
              pathToPublish: trufflehog-results.json
              artifactName: trufflehog-results

      - job: SemgrepPython
        displayName: SAST - Semgrep Security Scan (Python)
        container:
          image: semgrep/semgrep:latest
        steps:
          - checkout: self
          - script: |
              semgrep scan \
                --config p/python \
                --config p/security-audit \
                --config p/secrets \
                --config p/owasp-top-ten \
                --exclude tests \
                --exclude .venv \
                --sarif \
                --output semgrep-results.sarif . || true
            displayName: Semgrep Scan (report)
          - script: |
              semgrep scan \
                --config p/python \
                --config p/security-audit \
                --config p/secrets \
                --config p/owasp-top-ten \
                --exclude tests \
                --exclude .venv \
                --severity=ERROR \
                --severity=WARNING \
                --error .
            displayName: Semgrep Scan (enforce)
          - task: PublishBuildArtifacts@1
            condition: always()
            inputs:
              pathToPublish: semgrep-results.sarif
              artifactName: semgrep-results-python
```

## Adapting Semgrep Templates Per Language

When generating Semgrep steps, adapt the Python template by replacing:

**For PHP:**

- Configs: replace `p/python` with `p/php`, add `p/phpcs-security-audit`
- Excludes: replace `tests, .venv` with `tests, vendor`
- Step name suffix: `(PHP)` instead of `(Python)`
- Anchor/job name: `semgrep_scan_php` instead of `semgrep_scan_python`

**For JavaScript/TypeScript:**

- Configs: replace `p/python` with `p/javascript`, `p/typescript`, `p/nodejs`
- Excludes: replace `tests, .venv` with `node_modules, dist, build, coverage`
- Step name suffix: `(JavaScript/TypeScript)` instead of `(Python)`
- Anchor/job name: `semgrep_scan_js` instead of `semgrep_scan_python`

**Framework configs** are appended as additional `--config p/<framework>` lines to both soft and hard passes.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.