Files
git.stella-ops.org/docs/flows/10-cicd-gate-flow.md

22 KiB

CI/CD Gate Flow

Overview

The CI/CD Gate Flow describes how StellaOps integrates into continuous integration and deployment pipelines to provide automated security gates. The flow covers CLI-based scanning, policy evaluation, and pass/fail decisions that control pipeline progression.

Business Value: Shift-left security by catching vulnerabilities before deployment, with deterministic, reproducible verdicts that integrate into existing DevOps workflows.

Actors

Actor Type Role
CI Pipeline System GitHub Actions, GitLab CI, Jenkins, etc.
StellaOps CLI Tool Executes scans from pipeline
Gateway Service API entry point
Scanner Service Performs image analysis
Policy Engine Service Evaluates security policies
Attestor Service Signs scan results

Prerequisites

  • StellaOps CLI installed in CI environment
  • API credentials configured (token or OIDC)
  • Policy set defined for the pipeline
  • Container image built and available

Supported CI/CD Platforms

Platform Integration Method Credentials
GitHub Actions Action + CLI OIDC or PAT
GitLab CI Job template + CLI CI_JOB_TOKEN or PAT
Azure DevOps Task + CLI Service connection
Jenkins Plugin + CLI Credentials binding
CircleCI Orb + CLI Context variables
Tekton Task + CLI Kubernetes secrets

Flow Diagram

┌─────────────────────────────────────────────────────────────────────────────────┐
│                            CI/CD Gate Flow                                       │
└─────────────────────────────────────────────────────────────────────────────────┘

┌────────────┐  ┌───────────┐  ┌─────────┐  ┌─────────┐  ┌────────┐  ┌─────────┐
│ CI Pipeline│  │StellaOps  │  │ Gateway │  │ Scanner │  │ Policy │  │ Attestor│
│            │  │   CLI     │  │         │  │         │  │        │  │         │
└─────┬──────┘  └─────┬─────┘  └────┬────┘  └────┬────┘  └───┬────┘  └────┬────┘
      │               │             │            │           │            │
      │ docker build  │             │            │           │            │
      │───────┐       │             │            │           │            │
      │       │       │             │            │           │            │
      │<──────┘       │             │            │           │            │
      │               │             │            │           │            │
      │ stellaops     │             │            │           │            │
      │ scan          │             │            │           │            │
      │ --policy=prod │             │            │           │            │
      │──────────────>│             │            │           │            │
      │               │             │            │           │            │
      │               │ POST /scans │            │           │            │
      │               │────────────>│            │           │            │
      │               │             │            │           │            │
      │               │             │ Dispatch   │           │            │
      │               │             │───────────>│           │            │
      │               │             │            │           │            │
      │               │             │            │ Analyze   │           │
      │               │             │            │ image     │           │
      │               │             │            │───┐       │           │
      │               │             │            │   │       │           │
      │               │             │            │<──┘       │           │
      │               │             │            │           │            │
      │               │             │            │ Evaluate  │           │
      │               │             │            │──────────>│           │
      │               │             │            │           │            │
      │               │             │            │           │ Apply     │
      │               │             │            │           │ rules     │
      │               │             │            │           │───┐       │
      │               │             │            │           │   │       │
      │               │             │            │           │<──┘       │
      │               │             │            │           │            │
      │               │             │            │ Verdict   │           │
      │               │             │            │<──────────│           │
      │               │             │            │           │            │
      │               │             │            │ Sign      │           │
      │               │             │            │──────────────────────>│
      │               │             │            │           │            │
      │               │             │            │ DSSE      │            │
      │               │             │            │<──────────────────────│
      │               │             │            │           │            │
      │               │             │ Result     │           │            │
      │               │             │<───────────│           │            │
      │               │             │            │           │            │
      │               │ Verdict     │            │           │            │
      │               │<────────────│            │           │            │
      │               │             │            │           │            │
      │ Exit code     │             │            │           │            │
      │ (0=pass,      │             │            │           │            │
      │  1=fail)      │             │            │           │            │
      │<──────────────│             │            │           │            │
      │               │             │            │           │            │
      │ [if pass]     │             │            │           │            │
      │ docker push   │             │            │           │            │
      │───────┐       │             │            │           │            │
      │       │       │             │            │           │            │
      │<──────┘       │             │            │           │            │
      │               │             │            │           │            │

Step-by-Step

1. Pipeline Configuration

GitHub Actions Example

name: Build and Scan

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

jobs:
  build-and-scan:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      id-token: write  # For OIDC

    steps:
      - uses: actions/checkout@v4

      - name: Build Docker image
        run: |
          docker build -t myapp:${{ github.sha }} .

      - name: Install StellaOps CLI
        run: |
          curl -sSL https://get.stellaops.io/cli | sh
          echo "$HOME/.stellaops/bin" >> $GITHUB_PATH

      - name: Authenticate with OIDC
        run: |
          stellaops auth login --oidc \
            --issuer ${{ secrets.STELLAOPS_OIDC_ISSUER }} \
            --client-id ${{ secrets.STELLAOPS_CLIENT_ID }}

      - name: Scan image
        id: scan
        run: |
          stellaops scan myapp:${{ github.sha }} \
            --policy production \
            --format sarif \
            --output results.sarif \
            --attestation \
            --fail-on violation

      - name: Upload SARIF to GitHub Security
        if: always()
        uses: github/codeql-action/upload-sarif@v2
        with:
          sarif_file: results.sarif

      - name: Push to registry
        if: steps.scan.outcome == 'success'
        run: |
          docker tag myapp:${{ github.sha }} ghcr.io/org/myapp:${{ github.sha }}
          docker push ghcr.io/org/myapp:${{ github.sha }}

GitLab CI Example

stages:
  - build
  - scan
  - deploy

variables:
  STELLAOPS_API_URL: https://api.stellaops.local

build:
  stage: build
  script:
    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA

scan:
  stage: scan
  image: stellaops/cli:latest
  script:
    - stellaops auth login --token $STELLAOPS_TOKEN
    - stellaops scan $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
        --policy production
        --fail-on violation
  artifacts:
    reports:
      sast: gl-sast-report.json

deploy:
  stage: deploy
  needs: [scan]
  script:
    - kubectl set image deployment/myapp app=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA

2. CLI Authentication

The CLI supports multiple authentication methods:

Method Command Use Case
Token stellaops auth login --token $TOKEN CI/CD with secrets
OIDC stellaops auth login --oidc GitHub/GitLab OIDC
Interactive stellaops auth login Local development
Keyless stellaops auth login --keyless Sigstore OIDC

3. Scan Execution

CLI submits scan request and waits for completion:

stellaops scan docker.io/myorg/myapp:v1.2.3 \
  --policy production \
  --format json \
  --output scan-results.json \
  --attestation \
  --timeout 5m \
  --fail-on violation

CLI Options

Option Description Default
--policy Policy set to evaluate against default
--format Output format (json, sarif, table) table
--output Write results to file stdout
--attestation Generate DSSE attestation false
--timeout Maximum wait time 10m
--fail-on Exit 1 on: violation, warning, any violation
--quiet Suppress progress output false

3b. AI Code Guard (optional)

Run AI code guard checks on a change set and emit CI-friendly output:

stella guard run \
  --policy .stellaops.yml \
  --format sarif \
  --out guard.sarif

Recommended exit behavior:

  • pass: exit 0
  • review: exit 0 (with warning in report)
  • block: exit 1

4. Policy Evaluation

Policy engine evaluates findings against CI-specific rules:

# Policy Set: production
version: "stella-dsl@1"
name: production

rules:
  - name: block-critical
    condition: severity == 'critical' AND vex_status != 'not_affected'
    action: FAIL

  - name: block-high-unfixed
    condition: severity == 'high' AND fixed_version == null
    action: FAIL

  - name: block-known-exploited
    condition: kev == true
    action: FAIL

  - name: require-sbom
    condition: sbom_complete == false
    action: FAIL
    message: "SBOM must cover all detected packages"

gates:
  ci:
    max_critical: 0
    max_high_unfixed: 0
    require_attestation: true

5. Verdict and Exit Code

CLI translates verdict to exit code:

Verdict Exit Code Pipeline Result
PASS 0 Continue
WARN 0 (or 1 if --fail-on warning) Continue with warning
FAIL 1 Block deployment
ERROR 2 Pipeline failure

5a. DSSE Witness Verification (Required)

Sprint: SPRINT_20260112_004_DOC_cicd_gate_verification

Before deploying, pipelines must verify DSSE witness signatures and Rekor inclusion (or offline ledger). This ensures attestation integrity and provides tamper-evident audit trail.

Online Verification

# Verify DSSE signature and Rekor inclusion
stellaops proof verify \
  --image ghcr.io/org/myapp:$COMMIT_SHA \
  --attestation-type scan-result \
  --check-rekor \
  --fail-on-missing

# Exit codes:
# 0 - Verified successfully
# 1 - Verification failed
# 2 - Missing attestation or Rekor entry

Offline Verification (Air-Gapped Environments)

# Verify against local offline ledger
stellaops proof verify \
  --image myapp:$COMMIT_SHA \
  --attestation-type scan-result \
  --offline \
  --ledger-path /var/lib/stellaops/ledger \
  --fail-on-missing

# Alternative: verify a bundled evidence pack
stellaops evidence-pack verify \
  --bundle /path/to/evidence-pack.tar.gz \
  --check-signatures \
  --check-merkle

Cosign Equivalent Commands

For environments using cosign directly:

# Online: verify with Rekor
cosign verify-attestation \
  --type https://stellaops.io/attestation/scan/v1 \
  --rekor-url https://rekor.sigstore.dev \
  ghcr.io/org/myapp:$COMMIT_SHA

# Offline: verify with bundled certificate
cosign verify-attestation \
  --type https://stellaops.io/attestation/scan/v1 \
  --certificate /path/to/cert.pem \
  --certificate-chain /path/to/chain.pem \
  --offline \
  ghcr.io/org/myapp:$COMMIT_SHA

GitHub Actions Integration

- name: Verify attestation
  run: |
    stellaops proof verify \
      --image ghcr.io/org/myapp:${{ github.sha }} \
      --attestation-type scan-result \
      --check-rekor \
      --fail-on-missing

- name: Push to registry (only if verified)
  if: success()
  run: |
    docker push ghcr.io/org/myapp:${{ github.sha }}

GitLab CI Integration

verify:
  stage: verify
  script:
    - stellaops proof verify
        --image $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
        --attestation-type scan-result
        --check-rekor
        --fail-on-missing
  rules:
    - if: $CI_COMMIT_BRANCH == "main"

Related Documentation:

6. SARIF Integration

CLI outputs SARIF for IDE and GitHub integration:

{
  "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
  "version": "2.1.0",
  "runs": [
    {
      "tool": {
        "driver": {
          "name": "StellaOps",
          "version": "2.1.0",
          "informationUri": "https://stellaops.io"
        }
      },
      "results": [
        {
          "ruleId": "CVE-2024-1234",
          "level": "error",
          "message": {
            "text": "Critical vulnerability in lodash@4.17.20"
          },
          "locations": [
            {
              "physicalLocation": {
                "artifactLocation": {
                  "uri": "package-lock.json"
                }
              }
            }
          ]
        }
      ]
    }
  ]
}

7. Attestation Storage

If --attestation is specified, CLI stores attestation:

# View attestation
stellaops attestation show --scan $SCAN_ID

# Verify attestation

8. PR/MR Comment and Status Integration

StellaOps can post scan results as PR/MR comments and status checks for visibility directly in the SCM platform.

GitHub PR Integration

When scanning PRs, the system can:

  • Post a summary comment with findings count and severity breakdown
  • Create check runs with inline annotations
  • Update commit status with pass/fail verdict
# GitHub Actions with PR comments
- name: Scan with PR feedback
  run: |
    stellaops scan myapp:${{ github.sha }} \
      --policy production \
      --pr-comment \
      --check-run \
      --github-token ${{ secrets.GITHUB_TOKEN }}

Example PR comment format:

## StellaOps Scan Results

**Verdict:** :warning: WARN

| Severity | Count |
|----------|-------|
| Critical | 0 |
| High | 2 |
| Medium | 5 |
| Low | 12 |

### Findings Requiring Attention

| CVE | Severity | Package | Status |
|-----|----------|---------|--------|
| CVE-2026-1234 | High | lodash@4.17.21 | Fix available: 4.17.22 |
| CVE-2026-5678 | High | express@4.18.0 | VEX: Not affected |

<details>
<summary>View full report</summary>

[Download SARIF](https://stellaops.example.com/scans/abc123/sarif)
[View in Console](https://stellaops.example.com/scans/abc123)

</details>

---
*Scan ID: abc123 | Policy: production | [Evidence](https://stellaops.example.com/evidence/abc123)*

GitLab MR Integration

For GitLab Merge Requests:

  • Post MR notes with findings summary
  • Update commit status on the pipeline
  • Create discussion threads for critical findings
# GitLab CI with MR feedback
scan:
  stage: test
  script:
    - stellaops scan $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA \
        --policy production \
        --mr-comment \
        --commit-status \
        --gitlab-token $CI_JOB_TOKEN
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"

Comment Behavior Options

Option Description Default
--pr-comment / --mr-comment Post summary comment false
--check-run Create GitHub check run with annotations false
--commit-status Update commit status false
--update-existing Edit previous comment instead of new true
--collapse-details Use collapsible sections for long output true
--evidence-link Include link to evidence bundle true

Evidence Anchoring in Comments

Comments include evidence references for auditability:

  • Scan ID: Unique identifier for the scan
  • Policy Version: The policy version used for evaluation
  • Attestation Digest: DSSE envelope digest for signed results
  • Rekor Entry: Log index when transparency logging is enabled

Error Handling

Scenario Behavior
No SCM token Skip comment, log warning
API rate limit Retry with backoff, then skip
Comment too long Truncate with link to full report
PR already merged Skip comment

Evidence-First Annotation Format

PR/MR comments use ASCII-only output for determinism and maximum compatibility:

## StellaOps Security Scan

**Verdict:** [BLOCKING] Policy violation detected

| Status | Finding | Package | Action |
| --- | --- | --- | --- |
| [+] New | CVE-2026-1234 | lodash@4.17.21 | Fix: 4.17.22 |
| [-] Fixed | CVE-2025-9999 | express@4.17.0 | Resolved |
| [^] Upgraded | CVE-2026-5678 | axios@1.0.0 | High -> Medium |
| [v] Downgraded | CVE-2026-4321 | react@18.0.0 | Medium -> Low |

### Evidence

| Field | Value |
| --- | --- |
| Attestation Digest | sha256:abc123... |
| Policy Verdict | FAIL |
| Verify Command | `stellaops verify --digest sha256:abc123...` |

---
*[OK] 12 findings unchanged | Policy: production v2.1.0*

ASCII Indicator Reference:

Indicator Meaning
[OK] Pass / Success
[BLOCKING] Fail / Hard gate triggered
[WARNING] Soft gate / Advisory
[+] New finding introduced
[-] Finding fixed / removed
[^] Severity upgraded
[v] Severity downgraded

Offline Mode

In air-gapped environments:

  • Comments are queued locally
  • Export comment payload for manual posting
  • Generate markdown file for offline review stellaops attestation verify --image myapp:v1.2.3 --policy production

Attestation is stored as DSSE envelope:
```json
{
  "payloadType": "application/vnd.in-toto+json",
  "payload": "eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjEi...",
  "signatures": [
    {
      "keyid": "sha256:abc123...",
      "sig": "MEQCI..."
    }
  ]
}

Gate Behaviors

Soft Gate (Warning Only)

# .stellaops.yaml
gates:
  ci:
    mode: soft  # Report but don't fail
    notify:
      - slack://security-channel

Hard Gate (Blocking)

gates:
  ci:
    mode: hard  # Fail pipeline on violations
    exceptions:
      - CVE-2024-9999  # Known false positive

Progressive Gate

gates:
  ci:
    mode: progressive
    thresholds:
      - branch: main
        max_critical: 0
        max_high: 5
      - branch: develop
        max_critical: 2
        max_high: 20
      - branch: feature/*
        mode: soft  # Warn only on feature branches

Data Contracts

CLI Scan Output Schema

interface CliScanOutput {
  scan_id: string;
  image: string;
  digest: string;
  verdict: 'PASS' | 'WARN' | 'FAIL';
  confidence: number;
  summary: {
    critical: number;
    high: number;
    medium: number;
    low: number;
  };
  violations: Array<{
    cve: string;
    severity: string;
    package: string;
    rule: string;
    message: string;
  }>;
  attestation?: {
    digest: string;
    rekor_log_index?: number;
  };
  timing: {
    queued_ms: number;
    scan_ms: number;
    policy_ms: number;
    total_ms: number;
  };
}

Error Handling

Error Exit Code Recovery
Auth failure 2 Check credentials
Image not found 2 Verify image reference
API timeout 2 Retry with --timeout
Policy not found 2 Check policy name
Network error 2 Check connectivity

Observability

Metrics

Metric Type Labels
cli_scan_total Counter verdict, ci_platform
cli_scan_duration_seconds Histogram ci_platform
cli_gate_blocked_total Counter policy, reason

CI/CD Annotations

GitHub Actions annotations:

::error file=package-lock.json::CVE-2024-1234: Critical vulnerability in lodash@4.17.20
::warning file=Dockerfile::Using outdated base image