Skip to content

Using Code Snippets

This guide explains how to use the Material for MkDocs code snippet inclusion feature to embed code examples in your documentation.

Overview

Our documentation uses Material for MkDocs with the PyMdown Extensions to provide advanced code block functionality, including:

  • Code syntax highlighting
  • Line numbers and line highlighting
  • Code block annotations
  • Code copying button
  • Code file inclusion

Including Code Files

To include code from existing files in the repository:

```yaml
name: CI/CD Pipeline with CINC Auditor Scanning

on:
  workflow_dispatch:
    inputs:
      image_tag:
        description: 'Tag for the container image'
        required: true
        default: 'latest'
      scan_namespace:
        description: 'Kubernetes namespace for scanning'
        required: true
        default: 'app-scan'
      threshold:
        description: 'Minimum passing score (0-100)'
        required: true
        default: '70'

jobs:
  build-deploy-scan:
    name: Build, Deploy and Scan Container
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Define test application
        run: |
          # Create a simple application for testing
          mkdir -p ./app

          # Create a minimal Dockerfile
          cat > ./app/Dockerfile << 'EOF'
          FROM alpine:latest

          # Add some packages to test vulnerability scanning
          RUN apk add --no-cache bash curl wget

          # Add a sample script
          COPY hello.sh /hello.sh
          RUN chmod +x /hello.sh

          # Set CMD
          CMD ["/bin/sh", "-c", "while true; do /hello.sh; sleep 300; done"]
          EOF

          # Create a simple script file
          cat > ./app/hello.sh << 'EOF'
          #!/bin/bash
          echo "Hello from test container! The time is $(date)"
          echo "Running as user: $(whoami)"
          echo "OS release: $(cat /etc/os-release | grep PRETTY_NAME)"
          EOF

      - name: Set up Minikube
        uses: medyagh/setup-minikube@master
        with:
          driver: docker
          start-args: --nodes=2

      - name: Build container image
        run: |
          # Configure to use minikube's Docker daemon
          eval $(minikube docker-env)

          # Build the image
          docker build -t test-app:${{ github.event.inputs.image_tag }} ./app

          # List images to confirm
          docker images | grep test-app

      - name: Create Kubernetes deployment
        run: |
          # Create namespace
          kubectl create namespace ${{ github.event.inputs.scan_namespace }}

          # Create deployment
          cat <<EOF | kubectl apply -f -
          apiVersion: apps/v1
          kind: Deployment
          metadata:
            name: test-app
            namespace: ${{ github.event.inputs.scan_namespace }}
            labels:
              app: test-app
          spec:
            replicas: 1
            selector:
              matchLabels:
                app: test-app
            template:
              metadata:
                labels:
                  app: test-app
                  security-scan: "enabled"
              spec:
                containers:
                - name: app
                  image: test-app:${{ github.event.inputs.image_tag }}
                  imagePullPolicy: Never
          EOF

          # Wait for deployment to be ready
          kubectl -n ${{ github.event.inputs.scan_namespace }} rollout status deployment/test-app --timeout=120s

          # Get pod name
          POD_NAME=$(kubectl get pods -n ${{ github.event.inputs.scan_namespace }} -l app=test-app -o jsonpath='{.items[0].metadata.name}')
          echo "APP_POD=${POD_NAME}" >> $GITHUB_ENV

          # Show pods
          kubectl get pods -n ${{ github.event.inputs.scan_namespace }} --show-labels

      - name: Set up CINC Auditor
        run: |
          # Install CINC Auditor
          curl -L https://omnitruck.cinc.sh/install.sh | sudo bash -s -- -P cinc-auditor

          # Install train-k8s-container plugin
          cinc-auditor plugin install train-k8s-container

          # Create a custom profile for application scanning
          mkdir -p ./app-scan-profile

          # Create profile files
          cat > ./app-scan-profile/inspec.yml << 'EOF'
          name: app-scan-profile
          title: Custom Application Container Scan
          maintainer: Security Team
          copyright: Security Team
          license: Apache-2.0
          summary: A custom profile for scanning containerized applications
          version: 0.1.0
          supports:
            platform: os
          EOF

          mkdir -p ./app-scan-profile/controls

          cat > ./app-scan-profile/controls/container_checks.rb << 'EOF'
          control 'container-1.1' do
            impact 0.7
            title 'Ensure container is not running as root'
            desc 'Containers should not run as root when possible'

            describe command('whoami') do
              its('stdout') { should_not cmp 'root' }
            end
          end

          control 'container-1.2' do
            impact 0.5
            title 'Check container OS version'
            desc 'Verify the container OS version'

            describe file('/etc/os-release') do
              it { should exist }
              its('content') { should include 'Alpine' }
            end
          end

          control 'container-1.3' do
            impact 0.3
            title 'Check for unnecessary packages'
            desc 'Container should not have unnecessary packages'

            describe package('curl') do
              it { should be_installed }
            end

            describe package('wget') do
              it { should be_installed }
            end
          end

          control 'container-1.4' do
            impact 0.7
            title 'Check for sensitive files'
            desc 'Container should not have sensitive files'

            describe file('/etc/shadow') do
              it { should exist }
              it { should_not be_readable.by('others') }
            end
          end
          EOF

      - name: Setup secure scanning infrastructure
        run: |
          # Create a unique ID for this run
          RUN_ID=$(date +%s)
          echo "RUN_ID=${RUN_ID}" >> $GITHUB_ENV

          # Create service account
          cat <<EOF | kubectl apply -f -
          apiVersion: v1
          kind: ServiceAccount
          metadata:
            name: cinc-scanner-${RUN_ID}
            namespace: ${{ github.event.inputs.scan_namespace }}
          EOF

          # Create role with label-based access
          cat <<EOF | kubectl apply -f -
          apiVersion: rbac.authorization.k8s.io/v1
          kind: Role
          metadata:
            name: cinc-scanner-role-${RUN_ID}
            namespace: ${{ github.event.inputs.scan_namespace }}
          rules:
          - apiGroups: [""]
            resources: ["pods"]
            verbs: ["get", "list"]
          - apiGroups: [""]
            resources: ["pods/exec"]
            verbs: ["create"]
            # No resourceNames restriction - use label selector in code
          - apiGroups: [""]
            resources: ["pods/log"]
            verbs: ["get"]
            # No resourceNames restriction - use label selector in code
          EOF

          # Create rolebinding
          cat <<EOF | kubectl apply -f -
          apiVersion: rbac.authorization.k8s.io/v1
          kind: RoleBinding
          metadata:
            name: cinc-scanner-binding-${RUN_ID}
            namespace: ${{ github.event.inputs.scan_namespace }}
          subjects:
          - kind: ServiceAccount
            name: cinc-scanner-${RUN_ID}
            namespace: ${{ github.event.inputs.scan_namespace }}
          roleRef:
            kind: Role
            name: cinc-scanner-role-${RUN_ID}
            apiGroup: rbac.authorization.k8s.io
          EOF

      - name: Setup SAF-CLI
        run: |
          # Install Node.js (should already be installed on GitHub runners)
          node --version || echo "Node.js not installed"

          # Install SAF-CLI globally
          npm install -g @mitre/saf

          # Verify installation
          saf --version

      - name: Run security scan with CINC Auditor
        run: |
          # Generate token
          TOKEN=$(kubectl create token cinc-scanner-${RUN_ID} -n ${{ github.event.inputs.scan_namespace }} --duration=15m)
          SERVER=$(kubectl config view --minify --output=jsonpath='{.clusters[0].cluster.server}')
          CA_DATA=$(kubectl config view --raw --minify --flatten -o jsonpath='{.clusters[].cluster.certificate-authority-data}')

          # Create kubeconfig
          cat > scan-kubeconfig.yaml << EOF
          apiVersion: v1
          kind: Config
          preferences: {}
          clusters:
          - cluster:
              server: ${SERVER}
              certificate-authority-data: ${CA_DATA}
            name: scanner-cluster
          contexts:
          - context:
              cluster: scanner-cluster
              namespace: ${{ github.event.inputs.scan_namespace }}
              user: scanner-user
            name: scanner-context
          current-context: scanner-context
          users:
          - name: scanner-user
            user:
              token: ${TOKEN}
          EOF

          chmod 600 scan-kubeconfig.yaml

          # Verify we can access the pod with our labels
          POD_NAME=$(KUBECONFIG=scan-kubeconfig.yaml kubectl get pods -n ${{ github.event.inputs.scan_namespace }} -l security-scan=enabled -o jsonpath='{.items[0].metadata.name}')
          if [ -z "$POD_NAME" ]; then
            echo "Error: No pod found with security-scan=enabled label"
            exit 1
          fi
          echo "Found pod to scan: ${POD_NAME}"

          # Run the CINC Auditor scan
          KUBECONFIG=scan-kubeconfig.yaml cinc-auditor exec ./app-scan-profile \
            -t k8s-container://${{ github.event.inputs.scan_namespace }}/${POD_NAME}/app \
            --reporter cli json:scan-results.json

          SCAN_EXIT_CODE=$?
          echo "CINC Auditor scan completed with exit code: ${SCAN_EXIT_CODE}"

          # Also run a standard profile for comparison
          echo "Running standard DevSec Linux Baseline for comparison:"
          KUBECONFIG=scan-kubeconfig.yaml cinc-auditor exec dev-sec/linux-baseline \
            -t k8s-container://${{ github.event.inputs.scan_namespace }}/${POD_NAME}/app \
            --reporter cli json:baseline-results.json || true

      - name: Generate scan summary with SAF-CLI
        run: |
          # Create summary report with SAF-CLI
          echo "Generating scan summary with SAF-CLI:"
          saf summary --input scan-results.json --output-md scan-summary.md

          # Display the summary in the logs
          cat scan-summary.md

          # Create a proper threshold file
          cat > threshold.yml << EOF
compliance:
  min: ${{ github.event.inputs.threshold }}
failed:
  critical:
    max: 0  # No critical failures allowed
EOF

          # Apply threshold check
          echo "Checking against threshold with min compliance of ${{ github.event.inputs.threshold }}%:"
          saf threshold -i scan-results.json -t threshold.yml
          THRESHOLD_EXIT_CODE=$?

          if [ $THRESHOLD_EXIT_CODE -eq 0 ]; then
            echo "✅ Security scan passed threshold requirements"
          else
            echo "❌ Security scan failed to meet threshold requirements"
            # Uncomment to enforce the threshold as a quality gate
            # exit $THRESHOLD_EXIT_CODE
          fi

          # Generate summary for baseline results too
          echo "Generating baseline summary with SAF-CLI:"
          saf summary --input baseline-results.json --output-md baseline-summary.md

          # Create a combined summary for GitHub step summary
          echo "## Custom Application Profile Results" > $GITHUB_STEP_SUMMARY
          cat scan-summary.md >> $GITHUB_STEP_SUMMARY
          echo "## Linux Baseline Results" >> $GITHUB_STEP_SUMMARY
          cat baseline-summary.md >> $GITHUB_STEP_SUMMARY

      - name: Upload scan results
        uses: actions/upload-artifact@v4
        with:
          name: security-scan-results
          path: |
            scan-results.json
            baseline-results.json
            scan-summary.md
            baseline-summary.md

      - name: Cleanup resources
        if: always()
        run: |
          kubectl delete namespace ${{ github.event.inputs.scan_namespace }}
This will render as:

```yaml
name: CI/CD Pipeline with CINC Auditor Scanning

on:
  workflow_dispatch:
    inputs:
      image_tag:
        description: 'Tag for the container image'
        required: true
        default: 'latest'
      scan_namespace:
        description: 'Kubernetes namespace for scanning'
        required: true
        default: 'app-scan'
      threshold:
        description: 'Minimum passing score (0-100)'
        required: true
        default: '70'

jobs:
  build-deploy-scan:
    name: Build, Deploy and Scan Container
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Define test application
        run: |
          # Create a simple application for testing
          mkdir -p ./app

          # Create a minimal Dockerfile
          cat > ./app/Dockerfile << 'EOF'
          FROM alpine:latest

          # Add some packages to test vulnerability scanning
          RUN apk add --no-cache bash curl wget

          # Add a sample script
          COPY hello.sh /hello.sh
          RUN chmod +x /hello.sh

          # Set CMD
          CMD ["/bin/sh", "-c", "while true; do /hello.sh; sleep 300; done"]
          EOF

          # Create a simple script file
          cat > ./app/hello.sh << 'EOF'
          #!/bin/bash
          echo "Hello from test container! The time is $(date)"
          echo "Running as user: $(whoami)"
          echo "OS release: $(cat /etc/os-release | grep PRETTY_NAME)"
          EOF

      - name: Set up Minikube
        uses: medyagh/setup-minikube@master
        with:
          driver: docker
          start-args: --nodes=2

      - name: Build container image
        run: |
          # Configure to use minikube's Docker daemon
          eval $(minikube docker-env)

          # Build the image
          docker build -t test-app:${{ github.event.inputs.image_tag }} ./app

          # List images to confirm
          docker images | grep test-app

      - name: Create Kubernetes deployment
        run: |
          # Create namespace
          kubectl create namespace ${{ github.event.inputs.scan_namespace }}

          # Create deployment
          cat <<EOF | kubectl apply -f -
          apiVersion: apps/v1
          kind: Deployment
          metadata:
            name: test-app
            namespace: ${{ github.event.inputs.scan_namespace }}
            labels:
              app: test-app
          spec:
            replicas: 1
            selector:
              matchLabels:
                app: test-app
            template:
              metadata:
                labels:
                  app: test-app
                  security-scan: "enabled"
              spec:
                containers:
                - name: app
                  image: test-app:${{ github.event.inputs.image_tag }}
                  imagePullPolicy: Never
          EOF

          # Wait for deployment to be ready
          kubectl -n ${{ github.event.inputs.scan_namespace }} rollout status deployment/test-app --timeout=120s

          # Get pod name
          POD_NAME=$(kubectl get pods -n ${{ github.event.inputs.scan_namespace }} -l app=test-app -o jsonpath='{.items[0].metadata.name}')
          echo "APP_POD=${POD_NAME}" >> $GITHUB_ENV

          # Show pods
          kubectl get pods -n ${{ github.event.inputs.scan_namespace }} --show-labels

      - name: Set up CINC Auditor
        run: |
          # Install CINC Auditor
          curl -L https://omnitruck.cinc.sh/install.sh | sudo bash -s -- -P cinc-auditor

          # Install train-k8s-container plugin
          cinc-auditor plugin install train-k8s-container

          # Create a custom profile for application scanning
          mkdir -p ./app-scan-profile

          # Create profile files
          cat > ./app-scan-profile/inspec.yml << 'EOF'
          name: app-scan-profile
          title: Custom Application Container Scan
          maintainer: Security Team
          copyright: Security Team
          license: Apache-2.0
          summary: A custom profile for scanning containerized applications
          version: 0.1.0
          supports:
            platform: os
          EOF

          mkdir -p ./app-scan-profile/controls

          cat > ./app-scan-profile/controls/container_checks.rb << 'EOF'
          control 'container-1.1' do
            impact 0.7
            title 'Ensure container is not running as root'
            desc 'Containers should not run as root when possible'

            describe command('whoami') do
              its('stdout') { should_not cmp 'root' }
            end
          end

          control 'container-1.2' do
            impact 0.5
            title 'Check container OS version'
            desc 'Verify the container OS version'

            describe file('/etc/os-release') do
              it { should exist }
              its('content') { should include 'Alpine' }
            end
          end

          control 'container-1.3' do
            impact 0.3
            title 'Check for unnecessary packages'
            desc 'Container should not have unnecessary packages'

            describe package('curl') do
              it { should be_installed }
            end

            describe package('wget') do
              it { should be_installed }
            end
          end

          control 'container-1.4' do
            impact 0.7
            title 'Check for sensitive files'
            desc 'Container should not have sensitive files'

            describe file('/etc/shadow') do
              it { should exist }
              it { should_not be_readable.by('others') }
            end
          end
          EOF

      - name: Setup secure scanning infrastructure
        run: |
          # Create a unique ID for this run
          RUN_ID=$(date +%s)
          echo "RUN_ID=${RUN_ID}" >> $GITHUB_ENV

          # Create service account
          cat <<EOF | kubectl apply -f -
          apiVersion: v1
          kind: ServiceAccount
          metadata:
            name: cinc-scanner-${RUN_ID}
            namespace: ${{ github.event.inputs.scan_namespace }}
          EOF

          # Create role with label-based access
          cat <<EOF | kubectl apply -f -
          apiVersion: rbac.authorization.k8s.io/v1
          kind: Role
          metadata:
            name: cinc-scanner-role-${RUN_ID}
            namespace: ${{ github.event.inputs.scan_namespace }}
          rules:
          - apiGroups: [""]
            resources: ["pods"]
            verbs: ["get", "list"]
          - apiGroups: [""]
            resources: ["pods/exec"]
            verbs: ["create"]
            # No resourceNames restriction - use label selector in code
          - apiGroups: [""]
            resources: ["pods/log"]
            verbs: ["get"]
            # No resourceNames restriction - use label selector in code
          EOF

          # Create rolebinding
          cat <<EOF | kubectl apply -f -
          apiVersion: rbac.authorization.k8s.io/v1
          kind: RoleBinding
          metadata:
            name: cinc-scanner-binding-${RUN_ID}
            namespace: ${{ github.event.inputs.scan_namespace }}
          subjects:
          - kind: ServiceAccount
            name: cinc-scanner-${RUN_ID}
            namespace: ${{ github.event.inputs.scan_namespace }}
          roleRef:
            kind: Role
            name: cinc-scanner-role-${RUN_ID}
            apiGroup: rbac.authorization.k8s.io
          EOF

      - name: Setup SAF-CLI
        run: |
          # Install Node.js (should already be installed on GitHub runners)
          node --version || echo "Node.js not installed"

          # Install SAF-CLI globally
          npm install -g @mitre/saf

          # Verify installation
          saf --version

      - name: Run security scan with CINC Auditor
        run: |
          # Generate token
          TOKEN=$(kubectl create token cinc-scanner-${RUN_ID} -n ${{ github.event.inputs.scan_namespace }} --duration=15m)
          SERVER=$(kubectl config view --minify --output=jsonpath='{.clusters[0].cluster.server}')
          CA_DATA=$(kubectl config view --raw --minify --flatten -o jsonpath='{.clusters[].cluster.certificate-authority-data}')

          # Create kubeconfig
          cat > scan-kubeconfig.yaml << EOF
          apiVersion: v1
          kind: Config
          preferences: {}
          clusters:
          - cluster:
              server: ${SERVER}
              certificate-authority-data: ${CA_DATA}
            name: scanner-cluster
          contexts:
          - context:
              cluster: scanner-cluster
              namespace: ${{ github.event.inputs.scan_namespace }}
              user: scanner-user
            name: scanner-context
          current-context: scanner-context
          users:
          - name: scanner-user
            user:
              token: ${TOKEN}
          EOF

          chmod 600 scan-kubeconfig.yaml

          # Verify we can access the pod with our labels
          POD_NAME=$(KUBECONFIG=scan-kubeconfig.yaml kubectl get pods -n ${{ github.event.inputs.scan_namespace }} -l security-scan=enabled -o jsonpath='{.items[0].metadata.name}')
          if [ -z "$POD_NAME" ]; then
            echo "Error: No pod found with security-scan=enabled label"
            exit 1
          fi
          echo "Found pod to scan: ${POD_NAME}"

          # Run the CINC Auditor scan
          KUBECONFIG=scan-kubeconfig.yaml cinc-auditor exec ./app-scan-profile \
            -t k8s-container://${{ github.event.inputs.scan_namespace }}/${POD_NAME}/app \
            --reporter cli json:scan-results.json

          SCAN_EXIT_CODE=$?
          echo "CINC Auditor scan completed with exit code: ${SCAN_EXIT_CODE}"

          # Also run a standard profile for comparison
          echo "Running standard DevSec Linux Baseline for comparison:"
          KUBECONFIG=scan-kubeconfig.yaml cinc-auditor exec dev-sec/linux-baseline \
            -t k8s-container://${{ github.event.inputs.scan_namespace }}/${POD_NAME}/app \
            --reporter cli json:baseline-results.json || true

      - name: Generate scan summary with SAF-CLI
        run: |
          # Create summary report with SAF-CLI
          echo "Generating scan summary with SAF-CLI:"
          saf summary --input scan-results.json --output-md scan-summary.md

          # Display the summary in the logs
          cat scan-summary.md

          # Create a proper threshold file
          cat > threshold.yml << EOF
compliance:
  min: ${{ github.event.inputs.threshold }}
failed:
  critical:
    max: 0  # No critical failures allowed
EOF

          # Apply threshold check
          echo "Checking against threshold with min compliance of ${{ github.event.inputs.threshold }}%:"
          saf threshold -i scan-results.json -t threshold.yml
          THRESHOLD_EXIT_CODE=$?

          if [ $THRESHOLD_EXIT_CODE -eq 0 ]; then
            echo "✅ Security scan passed threshold requirements"
          else
            echo "❌ Security scan failed to meet threshold requirements"
            # Uncomment to enforce the threshold as a quality gate
            # exit $THRESHOLD_EXIT_CODE
          fi

          # Generate summary for baseline results too
          echo "Generating baseline summary with SAF-CLI:"
          saf summary --input baseline-results.json --output-md baseline-summary.md

          # Create a combined summary for GitHub step summary
          echo "## Custom Application Profile Results" > $GITHUB_STEP_SUMMARY
          cat scan-summary.md >> $GITHUB_STEP_SUMMARY
          echo "## Linux Baseline Results" >> $GITHUB_STEP_SUMMARY
          cat baseline-summary.md >> $GITHUB_STEP_SUMMARY

      - name: Upload scan results
        uses: actions/upload-artifact@v4
        with:
          name: security-scan-results
          path: |
            scan-results.json
            baseline-results.json
            scan-summary.md
            baseline-summary.md

      - name: Cleanup resources
        if: always()
        run: |
          kubectl delete namespace ${{ github.event.inputs.scan_namespace }}

Highlighting Specific Lines

You can highlight specific lines in the code:

```yaml hl_lines="3-5 8"
name: CI/CD Pipeline with CINC Auditor Scanning

on:
  workflow_dispatch:
    inputs:
      image_tag:
        description: 'Tag for the container image'
        required: true
        default: 'latest'
      scan_namespace:
        description: 'Kubernetes namespace for scanning'
        required: true
        default: 'app-scan'
      threshold:
        description: 'Minimum passing score (0-100)'
        required: true
        default: '70'

jobs:
  build-deploy-scan:
    name: Build, Deploy and Scan Container
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Define test application
        run: |
          # Create a simple application for testing
          mkdir -p ./app

          # Create a minimal Dockerfile
          cat > ./app/Dockerfile << 'EOF'
          FROM alpine:latest

          # Add some packages to test vulnerability scanning
          RUN apk add --no-cache bash curl wget

          # Add a sample script
          COPY hello.sh /hello.sh
          RUN chmod +x /hello.sh

          # Set CMD
          CMD ["/bin/sh", "-c", "while true; do /hello.sh; sleep 300; done"]
          EOF

          # Create a simple script file
          cat > ./app/hello.sh << 'EOF'
          #!/bin/bash
          echo "Hello from test container! The time is $(date)"
          echo "Running as user: $(whoami)"
          echo "OS release: $(cat /etc/os-release | grep PRETTY_NAME)"
          EOF

      - name: Set up Minikube
        uses: medyagh/setup-minikube@master
        with:
          driver: docker
          start-args: --nodes=2

      - name: Build container image
        run: |
          # Configure to use minikube's Docker daemon
          eval $(minikube docker-env)

          # Build the image
          docker build -t test-app:${{ github.event.inputs.image_tag }} ./app

          # List images to confirm
          docker images | grep test-app

      - name: Create Kubernetes deployment
        run: |
          # Create namespace
          kubectl create namespace ${{ github.event.inputs.scan_namespace }}

          # Create deployment
          cat <<EOF | kubectl apply -f -
          apiVersion: apps/v1
          kind: Deployment
          metadata:
            name: test-app
            namespace: ${{ github.event.inputs.scan_namespace }}
            labels:
              app: test-app
          spec:
            replicas: 1
            selector:
              matchLabels:
                app: test-app
            template:
              metadata:
                labels:
                  app: test-app
                  security-scan: "enabled"
              spec:
                containers:
                - name: app
                  image: test-app:${{ github.event.inputs.image_tag }}
                  imagePullPolicy: Never
          EOF

          # Wait for deployment to be ready
          kubectl -n ${{ github.event.inputs.scan_namespace }} rollout status deployment/test-app --timeout=120s

          # Get pod name
          POD_NAME=$(kubectl get pods -n ${{ github.event.inputs.scan_namespace }} -l app=test-app -o jsonpath='{.items[0].metadata.name}')
          echo "APP_POD=${POD_NAME}" >> $GITHUB_ENV

          # Show pods
          kubectl get pods -n ${{ github.event.inputs.scan_namespace }} --show-labels

      - name: Set up CINC Auditor
        run: |
          # Install CINC Auditor
          curl -L https://omnitruck.cinc.sh/install.sh | sudo bash -s -- -P cinc-auditor

          # Install train-k8s-container plugin
          cinc-auditor plugin install train-k8s-container

          # Create a custom profile for application scanning
          mkdir -p ./app-scan-profile

          # Create profile files
          cat > ./app-scan-profile/inspec.yml << 'EOF'
          name: app-scan-profile
          title: Custom Application Container Scan
          maintainer: Security Team
          copyright: Security Team
          license: Apache-2.0
          summary: A custom profile for scanning containerized applications
          version: 0.1.0
          supports:
            platform: os
          EOF

          mkdir -p ./app-scan-profile/controls

          cat > ./app-scan-profile/controls/container_checks.rb << 'EOF'
          control 'container-1.1' do
            impact 0.7
            title 'Ensure container is not running as root'
            desc 'Containers should not run as root when possible'

            describe command('whoami') do
              its('stdout') { should_not cmp 'root' }
            end
          end

          control 'container-1.2' do
            impact 0.5
            title 'Check container OS version'
            desc 'Verify the container OS version'

            describe file('/etc/os-release') do
              it { should exist }
              its('content') { should include 'Alpine' }
            end
          end

          control 'container-1.3' do
            impact 0.3
            title 'Check for unnecessary packages'
            desc 'Container should not have unnecessary packages'

            describe package('curl') do
              it { should be_installed }
            end

            describe package('wget') do
              it { should be_installed }
            end
          end

          control 'container-1.4' do
            impact 0.7
            title 'Check for sensitive files'
            desc 'Container should not have sensitive files'

            describe file('/etc/shadow') do
              it { should exist }
              it { should_not be_readable.by('others') }
            end
          end
          EOF

      - name: Setup secure scanning infrastructure
        run: |
          # Create a unique ID for this run
          RUN_ID=$(date +%s)
          echo "RUN_ID=${RUN_ID}" >> $GITHUB_ENV

          # Create service account
          cat <<EOF | kubectl apply -f -
          apiVersion: v1
          kind: ServiceAccount
          metadata:
            name: cinc-scanner-${RUN_ID}
            namespace: ${{ github.event.inputs.scan_namespace }}
          EOF

          # Create role with label-based access
          cat <<EOF | kubectl apply -f -
          apiVersion: rbac.authorization.k8s.io/v1
          kind: Role
          metadata:
            name: cinc-scanner-role-${RUN_ID}
            namespace: ${{ github.event.inputs.scan_namespace }}
          rules:
          - apiGroups: [""]
            resources: ["pods"]
            verbs: ["get", "list"]
          - apiGroups: [""]
            resources: ["pods/exec"]
            verbs: ["create"]
            # No resourceNames restriction - use label selector in code
          - apiGroups: [""]
            resources: ["pods/log"]
            verbs: ["get"]
            # No resourceNames restriction - use label selector in code
          EOF

          # Create rolebinding
          cat <<EOF | kubectl apply -f -
          apiVersion: rbac.authorization.k8s.io/v1
          kind: RoleBinding
          metadata:
            name: cinc-scanner-binding-${RUN_ID}
            namespace: ${{ github.event.inputs.scan_namespace }}
          subjects:
          - kind: ServiceAccount
            name: cinc-scanner-${RUN_ID}
            namespace: ${{ github.event.inputs.scan_namespace }}
          roleRef:
            kind: Role
            name: cinc-scanner-role-${RUN_ID}
            apiGroup: rbac.authorization.k8s.io
          EOF

      - name: Setup SAF-CLI
        run: |
          # Install Node.js (should already be installed on GitHub runners)
          node --version || echo "Node.js not installed"

          # Install SAF-CLI globally
          npm install -g @mitre/saf

          # Verify installation
          saf --version

      - name: Run security scan with CINC Auditor
        run: |
          # Generate token
          TOKEN=$(kubectl create token cinc-scanner-${RUN_ID} -n ${{ github.event.inputs.scan_namespace }} --duration=15m)
          SERVER=$(kubectl config view --minify --output=jsonpath='{.clusters[0].cluster.server}')
          CA_DATA=$(kubectl config view --raw --minify --flatten -o jsonpath='{.clusters[].cluster.certificate-authority-data}')

          # Create kubeconfig
          cat > scan-kubeconfig.yaml << EOF
          apiVersion: v1
          kind: Config
          preferences: {}
          clusters:
          - cluster:
              server: ${SERVER}
              certificate-authority-data: ${CA_DATA}
            name: scanner-cluster
          contexts:
          - context:
              cluster: scanner-cluster
              namespace: ${{ github.event.inputs.scan_namespace }}
              user: scanner-user
            name: scanner-context
          current-context: scanner-context
          users:
          - name: scanner-user
            user:
              token: ${TOKEN}
          EOF

          chmod 600 scan-kubeconfig.yaml

          # Verify we can access the pod with our labels
          POD_NAME=$(KUBECONFIG=scan-kubeconfig.yaml kubectl get pods -n ${{ github.event.inputs.scan_namespace }} -l security-scan=enabled -o jsonpath='{.items[0].metadata.name}')
          if [ -z "$POD_NAME" ]; then
            echo "Error: No pod found with security-scan=enabled label"
            exit 1
          fi
          echo "Found pod to scan: ${POD_NAME}"

          # Run the CINC Auditor scan
          KUBECONFIG=scan-kubeconfig.yaml cinc-auditor exec ./app-scan-profile \
            -t k8s-container://${{ github.event.inputs.scan_namespace }}/${POD_NAME}/app \
            --reporter cli json:scan-results.json

          SCAN_EXIT_CODE=$?
          echo "CINC Auditor scan completed with exit code: ${SCAN_EXIT_CODE}"

          # Also run a standard profile for comparison
          echo "Running standard DevSec Linux Baseline for comparison:"
          KUBECONFIG=scan-kubeconfig.yaml cinc-auditor exec dev-sec/linux-baseline \
            -t k8s-container://${{ github.event.inputs.scan_namespace }}/${POD_NAME}/app \
            --reporter cli json:baseline-results.json || true

      - name: Generate scan summary with SAF-CLI
        run: |
          # Create summary report with SAF-CLI
          echo "Generating scan summary with SAF-CLI:"
          saf summary --input scan-results.json --output-md scan-summary.md

          # Display the summary in the logs
          cat scan-summary.md

          # Create a proper threshold file
          cat > threshold.yml << EOF
compliance:
  min: ${{ github.event.inputs.threshold }}
failed:
  critical:
    max: 0  # No critical failures allowed
EOF

          # Apply threshold check
          echo "Checking against threshold with min compliance of ${{ github.event.inputs.threshold }}%:"
          saf threshold -i scan-results.json -t threshold.yml
          THRESHOLD_EXIT_CODE=$?

          if [ $THRESHOLD_EXIT_CODE -eq 0 ]; then
            echo "✅ Security scan passed threshold requirements"
          else
            echo "❌ Security scan failed to meet threshold requirements"
            # Uncomment to enforce the threshold as a quality gate
            # exit $THRESHOLD_EXIT_CODE
          fi

          # Generate summary for baseline results too
          echo "Generating baseline summary with SAF-CLI:"
          saf summary --input baseline-results.json --output-md baseline-summary.md

          # Create a combined summary for GitHub step summary
          echo "## Custom Application Profile Results" > $GITHUB_STEP_SUMMARY
          cat scan-summary.md >> $GITHUB_STEP_SUMMARY
          echo "## Linux Baseline Results" >> $GITHUB_STEP_SUMMARY
          cat baseline-summary.md >> $GITHUB_STEP_SUMMARY

      - name: Upload scan results
        uses: actions/upload-artifact@v4
        with:
          name: security-scan-results
          path: |
            scan-results.json
            baseline-results.json
            scan-summary.md
            baseline-summary.md

      - name: Cleanup resources
        if: always()
        run: |
          kubectl delete namespace ${{ github.event.inputs.scan_namespace }}
## Adding Line Numbers

Line numbers are automatically added to code blocks, but you can disable them if needed:

```markdown
```yaml linenums="1"
name: CI/CD Pipeline with CINC Auditor Scanning

on:
  workflow_dispatch:
    inputs:
      image_tag:
        description: 'Tag for the container image'
        required: true
        default: 'latest'
      scan_namespace:
        description: 'Kubernetes namespace for scanning'
        required: true
        default: 'app-scan'
      threshold:
        description: 'Minimum passing score (0-100)'
        required: true
        default: '70'

jobs:
  build-deploy-scan:
    name: Build, Deploy and Scan Container
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Define test application
        run: |
          # Create a simple application for testing
          mkdir -p ./app

          # Create a minimal Dockerfile
          cat > ./app/Dockerfile << 'EOF'
          FROM alpine:latest

          # Add some packages to test vulnerability scanning
          RUN apk add --no-cache bash curl wget

          # Add a sample script
          COPY hello.sh /hello.sh
          RUN chmod +x /hello.sh

          # Set CMD
          CMD ["/bin/sh", "-c", "while true; do /hello.sh; sleep 300; done"]
          EOF

          # Create a simple script file
          cat > ./app/hello.sh << 'EOF'
          #!/bin/bash
          echo "Hello from test container! The time is $(date)"
          echo "Running as user: $(whoami)"
          echo "OS release: $(cat /etc/os-release | grep PRETTY_NAME)"
          EOF

      - name: Set up Minikube
        uses: medyagh/setup-minikube@master
        with:
          driver: docker
          start-args: --nodes=2

      - name: Build container image
        run: |
          # Configure to use minikube's Docker daemon
          eval $(minikube docker-env)

          # Build the image
          docker build -t test-app:${{ github.event.inputs.image_tag }} ./app

          # List images to confirm
          docker images | grep test-app

      - name: Create Kubernetes deployment
        run: |
          # Create namespace
          kubectl create namespace ${{ github.event.inputs.scan_namespace }}

          # Create deployment
          cat <<EOF | kubectl apply -f -
          apiVersion: apps/v1
          kind: Deployment
          metadata:
            name: test-app
            namespace: ${{ github.event.inputs.scan_namespace }}
            labels:
              app: test-app
          spec:
            replicas: 1
            selector:
              matchLabels:
                app: test-app
            template:
              metadata:
                labels:
                  app: test-app
                  security-scan: "enabled"
              spec:
                containers:
                - name: app
                  image: test-app:${{ github.event.inputs.image_tag }}
                  imagePullPolicy: Never
          EOF

          # Wait for deployment to be ready
          kubectl -n ${{ github.event.inputs.scan_namespace }} rollout status deployment/test-app --timeout=120s

          # Get pod name
          POD_NAME=$(kubectl get pods -n ${{ github.event.inputs.scan_namespace }} -l app=test-app -o jsonpath='{.items[0].metadata.name}')
          echo "APP_POD=${POD_NAME}" >> $GITHUB_ENV

          # Show pods
          kubectl get pods -n ${{ github.event.inputs.scan_namespace }} --show-labels

      - name: Set up CINC Auditor
        run: |
          # Install CINC Auditor
          curl -L https://omnitruck.cinc.sh/install.sh | sudo bash -s -- -P cinc-auditor

          # Install train-k8s-container plugin
          cinc-auditor plugin install train-k8s-container

          # Create a custom profile for application scanning
          mkdir -p ./app-scan-profile

          # Create profile files
          cat > ./app-scan-profile/inspec.yml << 'EOF'
          name: app-scan-profile
          title: Custom Application Container Scan
          maintainer: Security Team
          copyright: Security Team
          license: Apache-2.0
          summary: A custom profile for scanning containerized applications
          version: 0.1.0
          supports:
            platform: os
          EOF

          mkdir -p ./app-scan-profile/controls

          cat > ./app-scan-profile/controls/container_checks.rb << 'EOF'
          control 'container-1.1' do
            impact 0.7
            title 'Ensure container is not running as root'
            desc 'Containers should not run as root when possible'

            describe command('whoami') do
              its('stdout') { should_not cmp 'root' }
            end
          end

          control 'container-1.2' do
            impact 0.5
            title 'Check container OS version'
            desc 'Verify the container OS version'

            describe file('/etc/os-release') do
              it { should exist }
              its('content') { should include 'Alpine' }
            end
          end

          control 'container-1.3' do
            impact 0.3
            title 'Check for unnecessary packages'
            desc 'Container should not have unnecessary packages'

            describe package('curl') do
              it { should be_installed }
            end

            describe package('wget') do
              it { should be_installed }
            end
          end

          control 'container-1.4' do
            impact 0.7
            title 'Check for sensitive files'
            desc 'Container should not have sensitive files'

            describe file('/etc/shadow') do
              it { should exist }
              it { should_not be_readable.by('others') }
            end
          end
          EOF

      - name: Setup secure scanning infrastructure
        run: |
          # Create a unique ID for this run
          RUN_ID=$(date +%s)
          echo "RUN_ID=${RUN_ID}" >> $GITHUB_ENV

          # Create service account
          cat <<EOF | kubectl apply -f -
          apiVersion: v1
          kind: ServiceAccount
          metadata:
            name: cinc-scanner-${RUN_ID}
            namespace: ${{ github.event.inputs.scan_namespace }}
          EOF

          # Create role with label-based access
          cat <<EOF | kubectl apply -f -
          apiVersion: rbac.authorization.k8s.io/v1
          kind: Role
          metadata:
            name: cinc-scanner-role-${RUN_ID}
            namespace: ${{ github.event.inputs.scan_namespace }}
          rules:
          - apiGroups: [""]
            resources: ["pods"]
            verbs: ["get", "list"]
          - apiGroups: [""]
            resources: ["pods/exec"]
            verbs: ["create"]
            # No resourceNames restriction - use label selector in code
          - apiGroups: [""]
            resources: ["pods/log"]
            verbs: ["get"]
            # No resourceNames restriction - use label selector in code
          EOF

          # Create rolebinding
          cat <<EOF | kubectl apply -f -
          apiVersion: rbac.authorization.k8s.io/v1
          kind: RoleBinding
          metadata:
            name: cinc-scanner-binding-${RUN_ID}
            namespace: ${{ github.event.inputs.scan_namespace }}
          subjects:
          - kind: ServiceAccount
            name: cinc-scanner-${RUN_ID}
            namespace: ${{ github.event.inputs.scan_namespace }}
          roleRef:
            kind: Role
            name: cinc-scanner-role-${RUN_ID}
            apiGroup: rbac.authorization.k8s.io
          EOF

      - name: Setup SAF-CLI
        run: |
          # Install Node.js (should already be installed on GitHub runners)
          node --version || echo "Node.js not installed"

          # Install SAF-CLI globally
          npm install -g @mitre/saf

          # Verify installation
          saf --version

      - name: Run security scan with CINC Auditor
        run: |
          # Generate token
          TOKEN=$(kubectl create token cinc-scanner-${RUN_ID} -n ${{ github.event.inputs.scan_namespace }} --duration=15m)
          SERVER=$(kubectl config view --minify --output=jsonpath='{.clusters[0].cluster.server}')
          CA_DATA=$(kubectl config view --raw --minify --flatten -o jsonpath='{.clusters[].cluster.certificate-authority-data}')

          # Create kubeconfig
          cat > scan-kubeconfig.yaml << EOF
          apiVersion: v1
          kind: Config
          preferences: {}
          clusters:
          - cluster:
              server: ${SERVER}
              certificate-authority-data: ${CA_DATA}
            name: scanner-cluster
          contexts:
          - context:
              cluster: scanner-cluster
              namespace: ${{ github.event.inputs.scan_namespace }}
              user: scanner-user
            name: scanner-context
          current-context: scanner-context
          users:
          - name: scanner-user
            user:
              token: ${TOKEN}
          EOF

          chmod 600 scan-kubeconfig.yaml

          # Verify we can access the pod with our labels
          POD_NAME=$(KUBECONFIG=scan-kubeconfig.yaml kubectl get pods -n ${{ github.event.inputs.scan_namespace }} -l security-scan=enabled -o jsonpath='{.items[0].metadata.name}')
          if [ -z "$POD_NAME" ]; then
            echo "Error: No pod found with security-scan=enabled label"
            exit 1
          fi
          echo "Found pod to scan: ${POD_NAME}"

          # Run the CINC Auditor scan
          KUBECONFIG=scan-kubeconfig.yaml cinc-auditor exec ./app-scan-profile \
            -t k8s-container://${{ github.event.inputs.scan_namespace }}/${POD_NAME}/app \
            --reporter cli json:scan-results.json

          SCAN_EXIT_CODE=$?
          echo "CINC Auditor scan completed with exit code: ${SCAN_EXIT_CODE}"

          # Also run a standard profile for comparison
          echo "Running standard DevSec Linux Baseline for comparison:"
          KUBECONFIG=scan-kubeconfig.yaml cinc-auditor exec dev-sec/linux-baseline \
            -t k8s-container://${{ github.event.inputs.scan_namespace }}/${POD_NAME}/app \
            --reporter cli json:baseline-results.json || true

      - name: Generate scan summary with SAF-CLI
        run: |
          # Create summary report with SAF-CLI
          echo "Generating scan summary with SAF-CLI:"
          saf summary --input scan-results.json --output-md scan-summary.md

          # Display the summary in the logs
          cat scan-summary.md

          # Create a proper threshold file
          cat > threshold.yml << EOF
compliance:
  min: ${{ github.event.inputs.threshold }}
failed:
  critical:
    max: 0  # No critical failures allowed
EOF

          # Apply threshold check
          echo "Checking against threshold with min compliance of ${{ github.event.inputs.threshold }}%:"
          saf threshold -i scan-results.json -t threshold.yml
          THRESHOLD_EXIT_CODE=$?

          if [ $THRESHOLD_EXIT_CODE -eq 0 ]; then
            echo "✅ Security scan passed threshold requirements"
          else
            echo "❌ Security scan failed to meet threshold requirements"
            # Uncomment to enforce the threshold as a quality gate
            # exit $THRESHOLD_EXIT_CODE
          fi

          # Generate summary for baseline results too
          echo "Generating baseline summary with SAF-CLI:"
          saf summary --input baseline-results.json --output-md baseline-summary.md

          # Create a combined summary for GitHub step summary
          echo "## Custom Application Profile Results" > $GITHUB_STEP_SUMMARY
          cat scan-summary.md >> $GITHUB_STEP_SUMMARY
          echo "## Linux Baseline Results" >> $GITHUB_STEP_SUMMARY
          cat baseline-summary.md >> $GITHUB_STEP_SUMMARY

      - name: Upload scan results
        uses: actions/upload-artifact@v4
        with:
          name: security-scan-results
          path: |
            scan-results.json
            baseline-results.json
            scan-summary.md
            baseline-summary.md

      - name: Cleanup resources
        if: always()
        run: |
          kubectl delete namespace ${{ github.event.inputs.scan_namespace }}
## Adding Annotations

You can add annotations to specific lines in code blocks:

```markdown
```yaml
name: CI/CD Pipeline with CINC Auditor Scanning

on:
  workflow_dispatch:
    inputs:
      image_tag:
        description: 'Tag for the container image'
        required: true
        default: 'latest'
      scan_namespace:
        description: 'Kubernetes namespace for scanning'
        required: true
        default: 'app-scan'
      threshold:
        description: 'Minimum passing score (0-100)'
        required: true
        default: '70'

jobs:
  build-deploy-scan:
    name: Build, Deploy and Scan Container
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Define test application
        run: |
          # Create a simple application for testing
          mkdir -p ./app

          # Create a minimal Dockerfile
          cat > ./app/Dockerfile << 'EOF'
          FROM alpine:latest

          # Add some packages to test vulnerability scanning
          RUN apk add --no-cache bash curl wget

          # Add a sample script
          COPY hello.sh /hello.sh
          RUN chmod +x /hello.sh

          # Set CMD
          CMD ["/bin/sh", "-c", "while true; do /hello.sh; sleep 300; done"]
          EOF

          # Create a simple script file
          cat > ./app/hello.sh << 'EOF'
          #!/bin/bash
          echo "Hello from test container! The time is $(date)"
          echo "Running as user: $(whoami)"
          echo "OS release: $(cat /etc/os-release | grep PRETTY_NAME)"
          EOF

      - name: Set up Minikube
        uses: medyagh/setup-minikube@master
        with:
          driver: docker
          start-args: --nodes=2

      - name: Build container image
        run: |
          # Configure to use minikube's Docker daemon
          eval $(minikube docker-env)

          # Build the image
          docker build -t test-app:${{ github.event.inputs.image_tag }} ./app

          # List images to confirm
          docker images | grep test-app

      - name: Create Kubernetes deployment
        run: |
          # Create namespace
          kubectl create namespace ${{ github.event.inputs.scan_namespace }}

          # Create deployment
          cat <<EOF | kubectl apply -f -
          apiVersion: apps/v1
          kind: Deployment
          metadata:
            name: test-app
            namespace: ${{ github.event.inputs.scan_namespace }}
            labels:
              app: test-app
          spec:
            replicas: 1
            selector:
              matchLabels:
                app: test-app
            template:
              metadata:
                labels:
                  app: test-app
                  security-scan: "enabled"
              spec:
                containers:
                - name: app
                  image: test-app:${{ github.event.inputs.image_tag }}
                  imagePullPolicy: Never
          EOF

          # Wait for deployment to be ready
          kubectl -n ${{ github.event.inputs.scan_namespace }} rollout status deployment/test-app --timeout=120s

          # Get pod name
          POD_NAME=$(kubectl get pods -n ${{ github.event.inputs.scan_namespace }} -l app=test-app -o jsonpath='{.items[0].metadata.name}')
          echo "APP_POD=${POD_NAME}" >> $GITHUB_ENV

          # Show pods
          kubectl get pods -n ${{ github.event.inputs.scan_namespace }} --show-labels

      - name: Set up CINC Auditor
        run: |
          # Install CINC Auditor
          curl -L https://omnitruck.cinc.sh/install.sh | sudo bash -s -- -P cinc-auditor

          # Install train-k8s-container plugin
          cinc-auditor plugin install train-k8s-container

          # Create a custom profile for application scanning
          mkdir -p ./app-scan-profile

          # Create profile files
          cat > ./app-scan-profile/inspec.yml << 'EOF'
          name: app-scan-profile
          title: Custom Application Container Scan
          maintainer: Security Team
          copyright: Security Team
          license: Apache-2.0
          summary: A custom profile for scanning containerized applications
          version: 0.1.0
          supports:
            platform: os
          EOF

          mkdir -p ./app-scan-profile/controls

          cat > ./app-scan-profile/controls/container_checks.rb << 'EOF'
          control 'container-1.1' do
            impact 0.7
            title 'Ensure container is not running as root'
            desc 'Containers should not run as root when possible'

            describe command('whoami') do
              its('stdout') { should_not cmp 'root' }
            end
          end

          control 'container-1.2' do
            impact 0.5
            title 'Check container OS version'
            desc 'Verify the container OS version'

            describe file('/etc/os-release') do
              it { should exist }
              its('content') { should include 'Alpine' }
            end
          end

          control 'container-1.3' do
            impact 0.3
            title 'Check for unnecessary packages'
            desc 'Container should not have unnecessary packages'

            describe package('curl') do
              it { should be_installed }
            end

            describe package('wget') do
              it { should be_installed }
            end
          end

          control 'container-1.4' do
            impact 0.7
            title 'Check for sensitive files'
            desc 'Container should not have sensitive files'

            describe file('/etc/shadow') do
              it { should exist }
              it { should_not be_readable.by('others') }
            end
          end
          EOF

      - name: Setup secure scanning infrastructure
        run: |
          # Create a unique ID for this run
          RUN_ID=$(date +%s)
          echo "RUN_ID=${RUN_ID}" >> $GITHUB_ENV

          # Create service account
          cat <<EOF | kubectl apply -f -
          apiVersion: v1
          kind: ServiceAccount
          metadata:
            name: cinc-scanner-${RUN_ID}
            namespace: ${{ github.event.inputs.scan_namespace }}
          EOF

          # Create role with label-based access
          cat <<EOF | kubectl apply -f -
          apiVersion: rbac.authorization.k8s.io/v1
          kind: Role
          metadata:
            name: cinc-scanner-role-${RUN_ID}
            namespace: ${{ github.event.inputs.scan_namespace }}
          rules:
          - apiGroups: [""]
            resources: ["pods"]
            verbs: ["get", "list"]
          - apiGroups: [""]
            resources: ["pods/exec"]
            verbs: ["create"]
            # No resourceNames restriction - use label selector in code
          - apiGroups: [""]
            resources: ["pods/log"]
            verbs: ["get"]
            # No resourceNames restriction - use label selector in code
          EOF

          # Create rolebinding
          cat <<EOF | kubectl apply -f -
          apiVersion: rbac.authorization.k8s.io/v1
          kind: RoleBinding
          metadata:
            name: cinc-scanner-binding-${RUN_ID}
            namespace: ${{ github.event.inputs.scan_namespace }}
          subjects:
          - kind: ServiceAccount
            name: cinc-scanner-${RUN_ID}
            namespace: ${{ github.event.inputs.scan_namespace }}
          roleRef:
            kind: Role
            name: cinc-scanner-role-${RUN_ID}
            apiGroup: rbac.authorization.k8s.io
          EOF

      - name: Setup SAF-CLI
        run: |
          # Install Node.js (should already be installed on GitHub runners)
          node --version || echo "Node.js not installed"

          # Install SAF-CLI globally
          npm install -g @mitre/saf

          # Verify installation
          saf --version

      - name: Run security scan with CINC Auditor
        run: |
          # Generate token
          TOKEN=$(kubectl create token cinc-scanner-${RUN_ID} -n ${{ github.event.inputs.scan_namespace }} --duration=15m)
          SERVER=$(kubectl config view --minify --output=jsonpath='{.clusters[0].cluster.server}')
          CA_DATA=$(kubectl config view --raw --minify --flatten -o jsonpath='{.clusters[].cluster.certificate-authority-data}')

          # Create kubeconfig
          cat > scan-kubeconfig.yaml << EOF
          apiVersion: v1
          kind: Config
          preferences: {}
          clusters:
          - cluster:
              server: ${SERVER}
              certificate-authority-data: ${CA_DATA}
            name: scanner-cluster
          contexts:
          - context:
              cluster: scanner-cluster
              namespace: ${{ github.event.inputs.scan_namespace }}
              user: scanner-user
            name: scanner-context
          current-context: scanner-context
          users:
          - name: scanner-user
            user:
              token: ${TOKEN}
          EOF

          chmod 600 scan-kubeconfig.yaml

          # Verify we can access the pod with our labels
          POD_NAME=$(KUBECONFIG=scan-kubeconfig.yaml kubectl get pods -n ${{ github.event.inputs.scan_namespace }} -l security-scan=enabled -o jsonpath='{.items[0].metadata.name}')
          if [ -z "$POD_NAME" ]; then
            echo "Error: No pod found with security-scan=enabled label"
            exit 1
          fi
          echo "Found pod to scan: ${POD_NAME}"

          # Run the CINC Auditor scan
          KUBECONFIG=scan-kubeconfig.yaml cinc-auditor exec ./app-scan-profile \
            -t k8s-container://${{ github.event.inputs.scan_namespace }}/${POD_NAME}/app \
            --reporter cli json:scan-results.json

          SCAN_EXIT_CODE=$?
          echo "CINC Auditor scan completed with exit code: ${SCAN_EXIT_CODE}"

          # Also run a standard profile for comparison
          echo "Running standard DevSec Linux Baseline for comparison:"
          KUBECONFIG=scan-kubeconfig.yaml cinc-auditor exec dev-sec/linux-baseline \
            -t k8s-container://${{ github.event.inputs.scan_namespace }}/${POD_NAME}/app \
            --reporter cli json:baseline-results.json || true

      - name: Generate scan summary with SAF-CLI
        run: |
          # Create summary report with SAF-CLI
          echo "Generating scan summary with SAF-CLI:"
          saf summary --input scan-results.json --output-md scan-summary.md

          # Display the summary in the logs
          cat scan-summary.md

          # Create a proper threshold file
          cat > threshold.yml << EOF
compliance:
  min: ${{ github.event.inputs.threshold }}
failed:
  critical:
    max: 0  # No critical failures allowed
EOF

          # Apply threshold check
          echo "Checking against threshold with min compliance of ${{ github.event.inputs.threshold }}%:"
          saf threshold -i scan-results.json -t threshold.yml
          THRESHOLD_EXIT_CODE=$?

          if [ $THRESHOLD_EXIT_CODE -eq 0 ]; then
            echo "✅ Security scan passed threshold requirements"
          else
            echo "❌ Security scan failed to meet threshold requirements"
            # Uncomment to enforce the threshold as a quality gate
            # exit $THRESHOLD_EXIT_CODE
          fi

          # Generate summary for baseline results too
          echo "Generating baseline summary with SAF-CLI:"
          saf summary --input baseline-results.json --output-md baseline-summary.md

          # Create a combined summary for GitHub step summary
          echo "## Custom Application Profile Results" > $GITHUB_STEP_SUMMARY
          cat scan-summary.md >> $GITHUB_STEP_SUMMARY
          echo "## Linux Baseline Results" >> $GITHUB_STEP_SUMMARY
          cat baseline-summary.md >> $GITHUB_STEP_SUMMARY

      - name: Upload scan results
        uses: actions/upload-artifact@v4
        with:
          name: security-scan-results
          path: |
            scan-results.json
            baseline-results.json
            scan-summary.md
            baseline-summary.md

      - name: Cleanup resources
        if: always()
        run: |
          kubectl delete namespace ${{ github.event.inputs.scan_namespace }}
  1. This line defines the workflow name
  2. These are the events that trigger the workflow
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
## Using Tabs for Multiple Code Examples

You can group related code examples in tabs:

```markdown
=== "GitHub Workflow"
    ```yaml
    name: CI/CD Pipeline with CINC Auditor Scanning

    on:
      workflow_dispatch:
        inputs:
          image_tag:
            description: 'Tag for the container image'
            required: true
            default: 'latest'
          scan_namespace:
            description: 'Kubernetes namespace for scanning'
            required: true
            default: 'app-scan'
          threshold:
            description: 'Minimum passing score (0-100)'
            required: true
            default: '70'

    jobs:
      build-deploy-scan:
        name: Build, Deploy and Scan Container
        runs-on: ubuntu-latest

        steps:
          - name: Checkout code
            uses: actions/checkout@v4

          - name: Set up Docker Buildx
            uses: docker/setup-buildx-action@v3

          - name: Define test application
            run: |
              # Create a simple application for testing
              mkdir -p ./app

              # Create a minimal Dockerfile
              cat > ./app/Dockerfile << 'EOF'
              FROM alpine:latest

              # Add some packages to test vulnerability scanning
              RUN apk add --no-cache bash curl wget

              # Add a sample script
              COPY hello.sh /hello.sh
              RUN chmod +x /hello.sh

              # Set CMD
              CMD ["/bin/sh", "-c", "while true; do /hello.sh; sleep 300; done"]
              EOF

              # Create a simple script file
              cat > ./app/hello.sh << 'EOF'
              #!/bin/bash
              echo "Hello from test container! The time is $(date)"
              echo "Running as user: $(whoami)"
              echo "OS release: $(cat /etc/os-release | grep PRETTY_NAME)"
              EOF

          - name: Set up Minikube
            uses: medyagh/setup-minikube@master
            with:
              driver: docker
              start-args: --nodes=2

          - name: Build container image
            run: |
              # Configure to use minikube's Docker daemon
              eval $(minikube docker-env)

              # Build the image
              docker build -t test-app:${{ github.event.inputs.image_tag }} ./app

              # List images to confirm
              docker images | grep test-app

          - name: Create Kubernetes deployment
            run: |
              # Create namespace
              kubectl create namespace ${{ github.event.inputs.scan_namespace }}

              # Create deployment
              cat <<EOF | kubectl apply -f -
              apiVersion: apps/v1
              kind: Deployment
              metadata:
                name: test-app
                namespace: ${{ github.event.inputs.scan_namespace }}
                labels:
                  app: test-app
              spec:
                replicas: 1
                selector:
                  matchLabels:
                    app: test-app
                template:
                  metadata:
                    labels:
                      app: test-app
                      security-scan: "enabled"
                  spec:
                    containers:
                    - name: app
                      image: test-app:${{ github.event.inputs.image_tag }}
                      imagePullPolicy: Never
              EOF

              # Wait for deployment to be ready
              kubectl -n ${{ github.event.inputs.scan_namespace }} rollout status deployment/test-app --timeout=120s

              # Get pod name
              POD_NAME=$(kubectl get pods -n ${{ github.event.inputs.scan_namespace }} -l app=test-app -o jsonpath='{.items[0].metadata.name}')
              echo "APP_POD=${POD_NAME}" >> $GITHUB_ENV

              # Show pods
              kubectl get pods -n ${{ github.event.inputs.scan_namespace }} --show-labels

          - name: Set up CINC Auditor
            run: |
              # Install CINC Auditor
              curl -L https://omnitruck.cinc.sh/install.sh | sudo bash -s -- -P cinc-auditor

              # Install train-k8s-container plugin
              cinc-auditor plugin install train-k8s-container

              # Create a custom profile for application scanning
              mkdir -p ./app-scan-profile

              # Create profile files
              cat > ./app-scan-profile/inspec.yml << 'EOF'
              name: app-scan-profile
              title: Custom Application Container Scan
              maintainer: Security Team
              copyright: Security Team
              license: Apache-2.0
              summary: A custom profile for scanning containerized applications
              version: 0.1.0
              supports:
                platform: os
              EOF

              mkdir -p ./app-scan-profile/controls

              cat > ./app-scan-profile/controls/container_checks.rb << 'EOF'
              control 'container-1.1' do
                impact 0.7
                title 'Ensure container is not running as root'
                desc 'Containers should not run as root when possible'

                describe command('whoami') do
                  its('stdout') { should_not cmp 'root' }
                end
              end

              control 'container-1.2' do
                impact 0.5
                title 'Check container OS version'
                desc 'Verify the container OS version'

                describe file('/etc/os-release') do
                  it { should exist }
                  its('content') { should include 'Alpine' }
                end
              end

              control 'container-1.3' do
                impact 0.3
                title 'Check for unnecessary packages'
                desc 'Container should not have unnecessary packages'

                describe package('curl') do
                  it { should be_installed }
                end

                describe package('wget') do
                  it { should be_installed }
                end
              end

              control 'container-1.4' do
                impact 0.7
                title 'Check for sensitive files'
                desc 'Container should not have sensitive files'

                describe file('/etc/shadow') do
                  it { should exist }
                  it { should_not be_readable.by('others') }
                end
              end
              EOF

          - name: Setup secure scanning infrastructure
            run: |
              # Create a unique ID for this run
              RUN_ID=$(date +%s)
              echo "RUN_ID=${RUN_ID}" >> $GITHUB_ENV

              # Create service account
              cat <<EOF | kubectl apply -f -
              apiVersion: v1
              kind: ServiceAccount
              metadata:
                name: cinc-scanner-${RUN_ID}
                namespace: ${{ github.event.inputs.scan_namespace }}
              EOF

              # Create role with label-based access
              cat <<EOF | kubectl apply -f -
              apiVersion: rbac.authorization.k8s.io/v1
              kind: Role
              metadata:
                name: cinc-scanner-role-${RUN_ID}
                namespace: ${{ github.event.inputs.scan_namespace }}
              rules:
              - apiGroups: [""]
                resources: ["pods"]
                verbs: ["get", "list"]
              - apiGroups: [""]
                resources: ["pods/exec"]
                verbs: ["create"]
                # No resourceNames restriction - use label selector in code
              - apiGroups: [""]
                resources: ["pods/log"]
                verbs: ["get"]
                # No resourceNames restriction - use label selector in code
              EOF

              # Create rolebinding
              cat <<EOF | kubectl apply -f -
              apiVersion: rbac.authorization.k8s.io/v1
              kind: RoleBinding
              metadata:
                name: cinc-scanner-binding-${RUN_ID}
                namespace: ${{ github.event.inputs.scan_namespace }}
              subjects:
              - kind: ServiceAccount
                name: cinc-scanner-${RUN_ID}
                namespace: ${{ github.event.inputs.scan_namespace }}
              roleRef:
                kind: Role
                name: cinc-scanner-role-${RUN_ID}
                apiGroup: rbac.authorization.k8s.io
              EOF

          - name: Setup SAF-CLI
            run: |
              # Install Node.js (should already be installed on GitHub runners)
              node --version || echo "Node.js not installed"

              # Install SAF-CLI globally
              npm install -g @mitre/saf

              # Verify installation
              saf --version

          - name: Run security scan with CINC Auditor
            run: |
              # Generate token
              TOKEN=$(kubectl create token cinc-scanner-${RUN_ID} -n ${{ github.event.inputs.scan_namespace }} --duration=15m)
              SERVER=$(kubectl config view --minify --output=jsonpath='{.clusters[0].cluster.server}')
              CA_DATA=$(kubectl config view --raw --minify --flatten -o jsonpath='{.clusters[].cluster.certificate-authority-data}')

              # Create kubeconfig
              cat > scan-kubeconfig.yaml << EOF
              apiVersion: v1
              kind: Config
              preferences: {}
              clusters:
              - cluster:
                  server: ${SERVER}
                  certificate-authority-data: ${CA_DATA}
                name: scanner-cluster
              contexts:
              - context:
                  cluster: scanner-cluster
                  namespace: ${{ github.event.inputs.scan_namespace }}
                  user: scanner-user
                name: scanner-context
              current-context: scanner-context
              users:
              - name: scanner-user
                user:
                  token: ${TOKEN}
              EOF

              chmod 600 scan-kubeconfig.yaml

              # Verify we can access the pod with our labels
              POD_NAME=$(KUBECONFIG=scan-kubeconfig.yaml kubectl get pods -n ${{ github.event.inputs.scan_namespace }} -l security-scan=enabled -o jsonpath='{.items[0].metadata.name}')
              if [ -z "$POD_NAME" ]; then
                echo "Error: No pod found with security-scan=enabled label"
                exit 1
              fi
              echo "Found pod to scan: ${POD_NAME}"

              # Run the CINC Auditor scan
              KUBECONFIG=scan-kubeconfig.yaml cinc-auditor exec ./app-scan-profile \
                -t k8s-container://${{ github.event.inputs.scan_namespace }}/${POD_NAME}/app \
                --reporter cli json:scan-results.json

              SCAN_EXIT_CODE=$?
              echo "CINC Auditor scan completed with exit code: ${SCAN_EXIT_CODE}"

              # Also run a standard profile for comparison
              echo "Running standard DevSec Linux Baseline for comparison:"
              KUBECONFIG=scan-kubeconfig.yaml cinc-auditor exec dev-sec/linux-baseline \
                -t k8s-container://${{ github.event.inputs.scan_namespace }}/${POD_NAME}/app \
                --reporter cli json:baseline-results.json || true

          - name: Generate scan summary with SAF-CLI
            run: |
              # Create summary report with SAF-CLI
              echo "Generating scan summary with SAF-CLI:"
              saf summary --input scan-results.json --output-md scan-summary.md

              # Display the summary in the logs
              cat scan-summary.md

              # Create a proper threshold file
              cat > threshold.yml << EOF
    compliance:
      min: ${{ github.event.inputs.threshold }}
    failed:
      critical:
        max: 0  # No critical failures allowed
    EOF

              # Apply threshold check
              echo "Checking against threshold with min compliance of ${{ github.event.inputs.threshold }}%:"
              saf threshold -i scan-results.json -t threshold.yml
              THRESHOLD_EXIT_CODE=$?

              if [ $THRESHOLD_EXIT_CODE -eq 0 ]; then
                echo "✅ Security scan passed threshold requirements"
              else
                echo "❌ Security scan failed to meet threshold requirements"
                # Uncomment to enforce the threshold as a quality gate
                # exit $THRESHOLD_EXIT_CODE
              fi

              # Generate summary for baseline results too
              echo "Generating baseline summary with SAF-CLI:"
              saf summary --input baseline-results.json --output-md baseline-summary.md

              # Create a combined summary for GitHub step summary
              echo "## Custom Application Profile Results" > $GITHUB_STEP_SUMMARY
              cat scan-summary.md >> $GITHUB_STEP_SUMMARY
              echo "## Linux Baseline Results" >> $GITHUB_STEP_SUMMARY
              cat baseline-summary.md >> $GITHUB_STEP_SUMMARY

          - name: Upload scan results
            uses: actions/upload-artifact@v4
            with:
              name: security-scan-results
              path: |
                scan-results.json
                baseline-results.json
                scan-summary.md
                baseline-summary.md

          - name: Cleanup resources
            if: always()
            run: |
              kubectl delete namespace ${{ github.event.inputs.scan_namespace }}
    ```

=== "GitLab CI"
    ```yaml
    stages:
      - deploy
      - scan
      - report
      - cleanup

    variables:
      SCANNER_NAMESPACE: "inspec-test"
      TARGET_LABEL: "app=target-app"
      THRESHOLD_VALUE: "70"  # Minimum passing score (0-100)

    deploy_container:
      stage: deploy
      script:
        - echo "$KUBE_CONFIG" | base64 -d > kubeconfig.yaml
        - export KUBECONFIG=kubeconfig.yaml
        - |
          cat <<EOF | kubectl apply -f -
          apiVersion: v1
          kind: Pod
          metadata:
            name: scan-target-${CI_PIPELINE_ID}
            namespace: ${SCANNER_NAMESPACE}
            labels:
              app: target-app
              pipeline: "${CI_PIPELINE_ID}"
          spec:
            containers:
            - name: target
              image: registry.example.com/my-image:latest
              command: ["sleep", "1h"]
          EOF
        - |
          # Wait for pod to be ready
          kubectl wait --for=condition=ready pod/scan-target-${CI_PIPELINE_ID} \
            -n ${SCANNER_NAMESPACE} --timeout=120s
        - |
          # Save target info for later stages
          echo "TARGET_POD=scan-target-${CI_PIPELINE_ID}" >> deploy.env
          echo "TARGET_CONTAINER=target" >> deploy.env
      artifacts:
        reports:
          dotenv: deploy.env

    create_access:
      stage: scan
      needs: [deploy_container]
      script:
        - echo "$KUBE_CONFIG" | base64 -d > kubeconfig.yaml
        - export KUBECONFIG=kubeconfig.yaml
        - |
          # Create the role for this specific pod
          cat <<EOF | kubectl apply -f -
          apiVersion: rbac.authorization.k8s.io/v1
          kind: Role
          metadata:
            name: scanner-role-${CI_PIPELINE_ID}
            namespace: ${SCANNER_NAMESPACE}
          rules:
          - apiGroups: [""]
            resources: ["pods"]
            verbs: ["get", "list"]
          - apiGroups: [""]
            resources: ["pods/exec"]
            verbs: ["create"]
            resourceNames: ["${TARGET_POD}"]
          - apiGroups: [""]
            resources: ["pods/log"]
            verbs: ["get"]
            resourceNames: ["${TARGET_POD}"]
          EOF
        - |
          # Create service account
          cat <<EOF | kubectl apply -f -
          apiVersion: v1
          kind: ServiceAccount
          metadata:
            name: scanner-sa-${CI_PIPELINE_ID}
            namespace: ${SCANNER_NAMESPACE}
          EOF
        - |
          # Create role binding
          cat <<EOF | kubectl apply -f -
          apiVersion: rbac.authorization.k8s.io/v1
          kind: RoleBinding
          metadata:
            name: scanner-binding-${CI_PIPELINE_ID}
            namespace: ${SCANNER_NAMESPACE}
          subjects:
          - kind: ServiceAccount
            name: scanner-sa-${CI_PIPELINE_ID}
            namespace: ${SCANNER_NAMESPACE}
          roleRef:
            kind: Role
            name: scanner-role-${CI_PIPELINE_ID}
            apiGroup: rbac.authorization.k8s.io
          EOF
        - |
          # Generate token
          TOKEN=$(kubectl create token scanner-sa-${CI_PIPELINE_ID} \
            -n ${SCANNER_NAMESPACE} --duration=30m)
          echo "SCANNER_TOKEN=${TOKEN}" >> scanner.env

          # Save cluster info
          SERVER=$(kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}')
          CA_DATA=$(kubectl config view --raw --minify --flatten \
            -o jsonpath='{.clusters[].cluster.certificate-authority-data}')
          echo "CLUSTER_SERVER=${SERVER}" >> scanner.env
          echo "CLUSTER_CA_DATA=${CA_DATA}" >> scanner.env
      artifacts:
        reports:
          dotenv: scanner.env

    run_scan:
      stage: scan
      needs: [deploy_container, create_access]
      script:
        - |
          # Create a kubeconfig file
          cat > scan-kubeconfig.yaml << EOF
          apiVersion: v1
          kind: Config
          preferences: {}
          clusters:
          - cluster:
              server: ${CLUSTER_SERVER}
              certificate-authority-data: ${CLUSTER_CA_DATA}
            name: scanner-cluster
          contexts:
          - context:
              cluster: scanner-cluster
              namespace: ${SCANNER_NAMESPACE}
              user: scanner-user
            name: scanner-context
          current-context: scanner-context
          users:
          - name: scanner-user
            user:
              token: ${SCANNER_TOKEN}
          EOF
        - |
          # Install CINC Auditor
          curl -L https://omnitruck.cinc.sh/install.sh | sudo bash -s -- -P cinc-auditor

          # Install train-k8s-container plugin
          cinc-auditor plugin install train-k8s-container

          # Install SAF CLI
          npm install -g @mitre/saf

          # Run cinc-auditor scan
          KUBECONFIG=scan-kubeconfig.yaml \
            cinc-auditor exec ${CINC_PROFILE_PATH} \
            -t k8s-container://${SCANNER_NAMESPACE}/${TARGET_POD}/${TARGET_CONTAINER} \
            --reporter json:scan-results.json

          # Generate scan summary using SAF CLI
          saf summary --input scan-results.json --output-md scan-summary.md

          # Display summary in job output
          cat scan-summary.md

          # Check scan against threshold
          saf threshold -i scan-results.json -t ${THRESHOLD_VALUE}
          THRESHOLD_RESULT=$?

          # Save result for later stages
          echo "THRESHOLD_PASSED=${THRESHOLD_RESULT}" >> scan.env

          if [ ${THRESHOLD_RESULT} -eq 0 ]; then
            echo "✅ Security scan passed threshold requirements"
          else
            echo "❌ Security scan failed to meet threshold requirements"
            # Uncomment to enforce threshold as a gate
            # exit ${THRESHOLD_RESULT}
          fi
      artifacts:
        paths:
          - scan-results.json
          - scan-summary.md
        reports:
          dotenv: scan.env

    generate_report:
      stage: report
      needs: [run_scan]
      script:
        - |
          # Install SAF CLI if needed in this stage
          which saf || npm install -g @mitre/saf

          # Generate a more comprehensive report
          saf view -i scan-results.json --output scan-report.html

          # Create a simple markdown report for the MR
          cat > scan-report.md << EOF
          # Security Scan Results

          ## Summary

          $(cat scan-summary.md)

          ## Threshold Check

          ${THRESHOLD_PASSED} -eq 0 && echo "✅ **PASSED**" || echo "❌ **FAILED**"

          Threshold: ${THRESHOLD_VALUE}%

          ## Details

          For full results, see the artifacts.
          EOF
      artifacts:
        paths:
          - scan-report.html
          - scan-report.md
        when: always

    cleanup:
      stage: cleanup
      needs: [run_scan]
      when: always  # Run even if previous stages failed
      script:
        - echo "$KUBE_CONFIG" | base64 -d > kubeconfig.yaml
        - export KUBECONFIG=kubeconfig.yaml
        - |
          # Delete all resources
          kubectl delete pod/${TARGET_POD} -n ${SCANNER_NAMESPACE} --ignore-not-found
          kubectl delete role/scanner-role-${CI_PIPELINE_ID} -n ${SCANNER_NAMESPACE} --ignore-not-found
          kubectl delete sa/scanner-sa-${CI_PIPELINE_ID} -n ${SCANNER_NAMESPACE} --ignore-not-found
          kubectl delete rolebinding/scanner-binding-${CI_PIPELINE_ID} \
            -n ${SCANNER_NAMESPACE} --ignore-not-found
    ```

Best Practices

  1. Use Existing Examples: Reference existing example files rather than duplicating code
  2. Relative Paths: Use relative paths from the docs directory
  3. Context: Always provide explanatory text around code snippets
  4. Highlighting: Use line highlighting to draw attention to important parts
  5. Annotations: Add annotations to explain complex code sections

Available Example Files

GitHub Workflow Examples

  • github-workflow-examples/ci-cd-pipeline.yml
  • github-workflow-examples/setup-and-scan.yml
  • github-workflow-examples/dynamic-rbac-scanning.yml
  • github-workflow-examples/existing-cluster-scanning.yml
  • github-workflow-examples/sidecar-scanner.yml

GitLab CI Examples

  • gitlab-pipeline-examples/gitlab-ci.yml
  • gitlab-pipeline-examples/dynamic-rbac-scanning.yml
  • gitlab-pipeline-examples/existing-cluster-scanning.yml
  • gitlab-pipeline-examples/gitlab-ci-with-services.yml
  • gitlab-pipeline-examples/gitlab-ci-sidecar.yml
  • gitlab-pipeline-examples/gitlab-ci-sidecar-with-services.yml

Further Reading

For more information, see: