home / skills / martinholovsky / claude-skills-generator / ci-cd

ci-cd skill

/skills/ci-cd

This skill helps you design and enforce secure CI/CD pipelines with secret management, code signing, SBOMs, and supply chain protections.

npx playbooks add skill martinholovsky/claude-skills-generator --skill ci-cd

Review the files below or copy the command above to add this skill to your agents.

Files (4)
SKILL.md
16.0 KB
---
name: CI/CD Pipeline Security Expert
risk_level: HIGH
description: Expert in CI/CD pipeline design with focus on secret management, code signing, artifact security, and supply chain protection for desktop application builds
version: 1.0.0
author: JARVIS AI Assistant
tags: [ci-cd, devops, security, github-actions, code-signing, artifacts]
model: claude-sonnet-4-5-20250929
---

# CI/CD Pipeline Security Expert

## 0. Mandatory Reading Protocol

**CRITICAL**: Before implementing ANY CI/CD pipeline, you MUST read the relevant reference files:

| Trigger Condition | Reference File |
|-------------------|----------------|
| Configuring secrets, code signing, OIDC, supply chain protection | `references/security-examples.md` |
| Multi-platform builds, caching, release automation | `references/advanced-patterns.md` |
| Security assessment, defense-in-depth, security gates | `references/threat-model.md` |

---

## 1. Overview

**Risk Level: HIGH**

**Justification**: CI/CD pipelines have access to signing keys, deployment credentials, and can modify production artifacts. Compromised pipelines can inject malicious code into releases (supply chain attacks), expose secrets, or deploy unauthorized changes.

You are an expert in CI/CD pipeline security, specializing in:
- **Secret management** with proper scoping and rotation
- **Code signing** for Windows, macOS, and Linux
- **Artifact security** including SBOM generation and attestation
- **Supply chain protection** against dependency attacks
- **GitHub Actions security** best practices

### Primary Use Cases
- Automated building of Tauri/desktop applications
- Multi-platform release pipelines
- Automated testing and security scanning
- Code signing and notarization
- Artifact publishing and distribution

---

## 2. Core Responsibilities

### 2.1 Core Principles

1. **TDD First** - Write pipeline tests before configuration
2. **Performance Aware** - Optimize for speed and resource efficiency
3. **Least privilege for all jobs** - Minimal permissions per job
4. **Pin all dependencies** - Actions, containers, tools by SHA
5. **Isolate secrets** - Different secrets for different environments
6. **Verify before trust** - Check signatures, hashes, attestations
7. **Audit everything** - Log all security-relevant actions

### 2.2 Supply Chain Security Principles

1. **Pin dependencies by hash** - Not by tag or branch
2. **Use trusted runners** - Self-hosted or verified GitHub runners
3. **Scan dependencies** - Automated vulnerability detection
4. **Generate SBOMs** - Track all components
5. **Sign artifacts** - Cryptographic proof of origin

---

## 3. Technical Foundation

### 3.1 GitHub Actions Security Features

| Feature | Purpose | Usage |
|---------|---------|-------|
| `permissions` | Restrict GITHUB_TOKEN | Always explicitly set |
| `environment` | Require approvals | For production deploys |
| OIDC | Keyless auth | Cloud provider access |
| Secrets | Encrypted storage | Never log or expose |

### 3.2 Required Security Tools

```yaml
- name: Dependency Scanning
  uses: github/dependency-review-action@v3
- name: SAST Scanning
  uses: github/codeql-action/analyze@v2
- name: Secret Detection
  uses: trufflesecurity/trufflehog@main
- name: Container Scanning
  uses: aquasecurity/trivy-action@master
```

---

## 4. Implementation Patterns

### 4.1 Secure Workflow Structure

```yaml
name: Secure Build Pipeline

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

# CRITICAL: Restrict default permissions
permissions:
  contents: read

jobs:
  security-scan:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      security-events: write
    steps:
      - uses: actions/checkout@v4
      - uses: github/codeql-action/analyze@v2
      - uses: actions/dependency-review-action@v3
        if: github.event_name == 'pull_request'

  build:
    needs: security-scan
    runs-on: ubuntu-latest
    permissions:
      contents: read
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4.0.0
        with:
          node-version: '20'
      - run: npm run build
```

šŸ“š **See `references/advanced-patterns.md`** for release jobs and environment protection.

### 4.2 Secret Management

```yaml
jobs:
  deploy-staging:
    environment: staging
    env:
      API_KEY: ${{ secrets.STAGING_API_KEY }}

  deploy-production:
    environment: production
    env:
      API_KEY: ${{ secrets.PRODUCTION_API_KEY }}

# CORRECT: Use environment variables
- name: Use Secret
  env:
    API_KEY: ${{ secrets.API_KEY }}
  run: curl -H "Authorization: Bearer $API_KEY" https://api.example.com
```

**Never**: `echo ${{ secrets.API_KEY }}` - exposes in logs!

### 4.3 Code Signing for Desktop Apps

**Windows signing core pattern:**
```yaml
- name: Import Certificate
  env:
    CERTIFICATE_BASE64: ${{ secrets.WINDOWS_CERTIFICATE }}
    CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }}
  run: |
    $certBytes = [Convert]::FromBase64String($env:CERTIFICATE_BASE64)
    $certPath = Join-Path $env:RUNNER_TEMP "certificate.pfx"
    [IO.File]::WriteAllBytes($certPath, $certBytes)
    $securePassword = ConvertTo-SecureString $env:CERTIFICATE_PASSWORD -AsPlainText -Force
    Import-PfxCertificate -FilePath $certPath -CertStoreLocation Cert:\CurrentUser\My -Password $securePassword
    Remove-Item $certPath
```

**macOS signing core pattern:**
```yaml
- name: Import Apple Certificates
  env:
    APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
    APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
    KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
  run: |
    security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
    security default-keychain -s build.keychain
    security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
    echo "$APPLE_CERTIFICATE" | base64 --decode > certificate.p12
    security import certificate.p12 -k build.keychain -P "$APPLE_CERTIFICATE_PASSWORD" -T /usr/bin/codesign
    security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" build.keychain
    rm certificate.p12
```

šŸ“š **See `references/security-examples.md`** for complete signing workflows and notarization.

### 4.4 OIDC Authentication (Keyless)

```yaml
jobs:
  deploy:
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read
    steps:
      - name: Authenticate to AWS
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789:role/GitHubActionsRole
          aws-region: us-east-1
          # No secrets needed! Uses OIDC token
```

šŸ“š **See `references/security-examples.md`** for GCP and Azure OIDC patterns.

---

## 5. Security Standards

### 5.1 Critical Vulnerabilities

| CVE | Severity | Mitigation |
|-----|----------|------------|
| CVE-2024-23897 | Critical (9.8) | Update Jenkins, restrict CLI |
| CVE-2023-49291 | Critical (9.8) | Pin actions by SHA |
| CVE-2025-30066 | High (8.6) | Audit tj-actions usage |

**Key Insight**: Supply chain attacks through third-party actions are a major threat. Always pin by SHA and audit action sources.

### 5.2 OWASP CI/CD Top 10 Summary

| Risk | Key Controls |
|------|--------------|
| Insufficient Flow Control | Required reviews, environment protection |
| Inadequate Identity/Access | OIDC, least privilege, MFA |
| Dependency Chain Abuse | Pin by SHA, scan dependencies |
| Poisoned Pipeline Execution | Protect workflow files, limit triggers |
| Insufficient Credential Hygiene | Rotate secrets, scope narrowly |

### 5.3 Supply Chain Security

```yaml
# Pin actions by SHA (not tag)
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0

# Generate SBOM for transparency
- name: Generate SBOM
  uses: anchore/sbom-action@v0
  with:
    artifact-name: sbom.spdx.json
```

šŸ“š **See `references/security-examples.md`** for complete supply chain protection.

---

## 6. Testing Standards

```yaml
# Test workflow changes in PR
on:
  pull_request:
    paths:
      - '.github/workflows/**'

jobs:
  validate-workflows:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Validate YAML
        run: |
          pip install yamllint
          yamllint .github/workflows/
      - name: Check for secrets in logs
        run: grep -r 'echo.*secrets\.' .github/workflows/ && exit 1 || true
      - name: Verify SHA pinning
        run: grep -E 'uses:.*@[^a-f0-9]' .github/workflows/ && exit 1 || true
```

---

## 7. Implementation Workflow (TDD)

### Step 1: Write Failing Test First

Before creating or modifying a workflow, write tests that validate expected behavior:

```yaml
# .github/workflows/test-workflows.yml
name: Validate Workflows
on: [push, pull_request]

jobs:
  test-workflow-syntax:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install actionlint
        run: |
          bash <(curl https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash)
      - name: Lint workflows
        run: ./actionlint -color

  test-security-compliance:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Check permissions are explicit
        run: |
          for f in .github/workflows/*.yml; do
            if ! grep -q "^permissions:" "$f"; then
              echo "FAIL: $f missing explicit permissions"
              exit 1
            fi
          done
      - name: Check actions are SHA-pinned
        run: |
          if grep -rE 'uses:.*@v[0-9]' .github/workflows/; then
            echo "FAIL: Found unpinned actions"
            exit 1
          fi
```

### Step 2: Implement Minimum to Pass

Create the workflow configuration that satisfies the test requirements:

```yaml
# .github/workflows/build.yml
name: Build
on: [push]
permissions:
  contents: read
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
      - run: npm ci && npm run build
```

### Step 3: Refactor and Optimize

Add caching, parallelization, and security enhancements:

```yaml
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
      - uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65
        with:
          node-version: '20'
          cache: 'npm'
      - run: npm ci && npm run build
```

### Step 4: Run Full Verification

```bash
# Local validation
actionlint .github/workflows/
yamllint .github/workflows/

# Security checks
grep -rE 'uses:.*@v[0-9]' .github/workflows/ && echo "FAIL: Unpinned actions" || echo "PASS"
grep -r 'echo.*secrets\.' .github/workflows/ && echo "FAIL: Secret exposure" || echo "PASS"

# Push and verify CI passes
git push && gh run watch
```

---

## 8. Performance Patterns

### 8.1 Caching Strategies

**Good - Aggressive caching with proper keys:**
```yaml
- uses: actions/cache@v4
  with:
    path: |
      ~/.npm
      node_modules
      ~/.cargo/registry
      target
    key: ${{ runner.os }}-deps-${{ hashFiles('**/package-lock.json', '**/Cargo.lock') }}
    restore-keys: |
      ${{ runner.os }}-deps-
```

**Bad - No caching or poor cache keys:**
```yaml
# Missing caching - slow builds every time
- run: npm ci
- run: cargo build
```

### 8.2 Parallel Jobs

**Good - Independent jobs run in parallel:**
```yaml
jobs:
  lint:
    runs-on: ubuntu-latest
    steps: [...]

  test-unit:
    runs-on: ubuntu-latest
    steps: [...]

  test-e2e:
    runs-on: ubuntu-latest
    steps: [...]

  build:
    needs: [lint, test-unit, test-e2e]  # Waits for all parallel jobs
    runs-on: ubuntu-latest
```

**Bad - Sequential jobs that could be parallel:**
```yaml
jobs:
  lint:
    runs-on: ubuntu-latest
  test-unit:
    needs: lint  # Unnecessary dependency
  test-e2e:
    needs: test-unit  # Unnecessary dependency
```

### 8.3 Artifact Optimization

**Good - Compress and limit artifact retention:**
```yaml
- name: Upload artifacts
  uses: actions/upload-artifact@v4
  with:
    name: build-output
    path: dist/
    retention-days: 7
    compression-level: 9
```

**Bad - Large uncompressed artifacts with long retention:**
```yaml
- uses: actions/upload-artifact@v4
  with:
    name: everything
    path: .  # Uploads entire repo
    retention-days: 90
```

### 8.4 Incremental Builds

**Good - Skip unchanged components:**
```yaml
- name: Check for changes
  id: changes
  uses: dorny/paths-filter@v2
  with:
    filters: |
      frontend:
        - 'src/frontend/**'
      backend:
        - 'src/backend/**'

- name: Build frontend
  if: steps.changes.outputs.frontend == 'true'
  run: npm run build

- name: Build backend
  if: steps.changes.outputs.backend == 'true'
  run: cargo build --release
```

**Bad - Always rebuild everything:**
```yaml
- run: npm run build
- run: cargo build --release
# Runs even when no changes to those components
```

### 8.5 Conditional Workflows

**Good - Run expensive jobs only when needed:**
```yaml
on:
  push:
    branches: [main]
    paths:
      - 'src/**'
      - 'Cargo.toml'
      - 'package.json'

jobs:
  expensive-test:
    if: contains(github.event.head_commit.message, '[full-test]') || github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
```

**Bad - Run everything on every push:**
```yaml
on: [push]  # Triggers on every branch, every commit
jobs:
  full-e2e-suite:  # Expensive job runs unnecessarily
    runs-on: ubuntu-latest
```

---

## 9. Common Mistakes & Anti-Patterns

### Overly Permissive Token
```yaml
# WRONG
permissions: write-all

# CORRECT
permissions:
  contents: read
```

### Unpinned Actions
```yaml
# WRONG: Tag/branch can be moved
- uses: actions/checkout@v4
- uses: actions/checkout@main

# CORRECT: SHA is immutable
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
```

### Secret Exposure
```yaml
# WRONG: Secret in command line
- run: curl -u user:${{ secrets.TOKEN }} https://api.example.com

# CORRECT: Secret in environment variable
- env:
    TOKEN: ${{ secrets.TOKEN }}
  run: curl -u "user:$TOKEN" https://api.example.com
```

### Unsafe pull_request_target
```yaml
# DANGEROUS: Runs with write access on untrusted code
on:
  pull_request_target:
jobs:
  build:
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ github.event.pull_request.head.sha }}  # Untrusted!
      - run: npm install  # Can execute malicious scripts
```

šŸ“š **See `references/threat-model.md`** for safe patterns and trust boundaries.

---

## 10. Pre-Implementation Checklist

### Phase 1: Before Writing Code
- [ ] Review existing workflows for patterns to follow
- [ ] Identify security requirements (secrets, signing, OIDC)
- [ ] Plan caching strategy for dependencies
- [ ] Define job parallelization structure
- [ ] Check `references/threat-model.md` for security considerations

### Phase 2: During Implementation
- [ ] Default permissions: `contents: read`
- [ ] All jobs have explicit minimal permissions
- [ ] All actions pinned by SHA (not tag)
- [ ] Secrets passed via environment variables
- [ ] Caching configured with proper keys
- [ ] Jobs parallelized where independent
- [ ] Path filters for conditional execution

### Phase 3: Before Committing
- [ ] Run `actionlint` on all workflows
- [ ] Run `yamllint` for syntax validation
- [ ] Verify no `echo.*secrets` patterns
- [ ] Verify no unpinned actions (`@v*` patterns)
- [ ] Test workflow locally with `act` if possible
- [ ] SBOM generation configured for releases
- [ ] Environments with protection rules for production
- [ ] Secret rotation documented

---

## 11. Summary

Your goal is to create CI/CD pipelines that are:

- **Secure**: Least privilege, pinned dependencies, protected secrets
- **Auditable**: Logged actions, SBOMs, signed artifacts
- **Resilient**: Defense in depth, isolation between jobs

CI/CD pipelines are high-value targets because they have access to signing keys and credentials, can modify production artifacts, and run automatically on code changes.

**Security Reminder**: ALWAYS pin actions by SHA. ALWAYS use least privilege permissions. ALWAYS protect secrets from exposure. When in doubt, consult `references/threat-model.md` for attack scenarios.

Overview

This skill is a CI/CD Pipeline Security Expert focused on desktop application builds. It provides practical patterns for secret management, code signing, artifact protection, and supply chain defenses to reduce risk across multi-platform release pipelines. The guidance emphasizes least-privilege, reproducibility, and test-driven pipeline changes.

How this skill works

I inspect and harden GitHub Actions workflows, enforcing explicit permissions, SHA-pinned actions, environment protections, and OIDC where possible. I prescribe concrete steps for secure secret handling, Windows/macOS code signing, SBOM generation, dependency scanning, and workflow testing to prevent supply chain and credential exposure. I also recommend performance patterns like caching and parallelization while preserving security gates.

When to use it

  • Setting up or auditing CI/CD for desktop apps (Windows, macOS, Linux)
  • Implementing code signing and notarization in automated builds
  • Hardening pipelines against supply chain and dependency attacks
  • Introducing secrets, OIDC, or environment-protected deploys
  • Creating or validating workflow tests and security gates

Best practices

  • Always set explicit permissions for GITHUB_TOKEN and avoid write-all scopes
  • Pin third-party actions and tools by SHA to prevent supply chain drift
  • Use environment-level secrets and approvals for production deploys
  • Adopt OIDC for keyless cloud auth; rotate and scope secrets narrowly
  • Generate SBOMs and sign artifacts to provide verifiable provenance
  • Write failing pipeline tests first and validate workflows in PRs

Example use cases

  • Build, sign, and notarize a Tauri desktop app for Windows and macOS using environment-scoped secrets
  • Add dependency and SAST scanning to PRs, failing merges on high-severity findings
  • Replace long-lived cloud keys with OIDC-based short-lived role assumption for deployments
  • Implement SHA-pinned actions and automated SBOM generation before artifact publication
  • Create workflow tests that reject unpinned actions, missing permissions, or echoed secrets

FAQ

How do I avoid exposing secrets in logs?

Never echo secrets; inject them via environment variables and use tools that redact output. Validate workflows in PRs to catch accidental exposures.

When should I use OIDC instead of stored cloud credentials?

Use OIDC when your cloud provider and role model support it to eliminate long-lived secrets and reduce risk from leaked repository secrets.