Skip to content

Add idempotency support for pack execution #337

Add idempotency support for pack execution

Add idempotency support for pack execution #337

Workflow file for this run

# security.yml — Security scanning pipeline for langgraph-agent-stack
#
# Jobs:
# secrets-scan — gitleaks: detect secrets and credentials committed to the repo
# dependency-audit — pip-audit: identify known CVEs in Python dependencies
# sast — bandit: static application security testing for Python code
# image-scan — syft SBOM + trivy container vulnerability scan
#
# Triggers:
# push / pull_request — on every change to the main branch
# schedule — weekly full scan every Monday at 06:00 UTC
#
# All scan results are uploaded as artifacts and retained for 30 days.
# The dependency-audit and sast jobs produce SARIF output when available so
# results can be reviewed in the GitHub Security tab.
name: Security Scanning
on:
push:
branches:
- main
pull_request:
branches:
- main
schedule:
# Every Monday at 06:00 UTC — catches newly disclosed CVEs between PRs
- cron: "0 6 * * 1"
permissions:
contents: read
security-events: write
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
PYTHON_VERSION: "3.12"
UV_VERSION: "0.11.18"
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
# ---------------------------------------------------------------------------
# Job 1 — Secrets scanning with gitleaks
# ---------------------------------------------------------------------------
jobs:
secrets-scan:
name: Secrets Scan (gitleaks)
runs-on: ubuntu-latest
steps:
- name: Checkout source (full history for gitleaks)
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
# Fetch the complete git history so gitleaks can scan all commits,
# not just the files present in the working tree.
fetch-depth: 0
- name: Run gitleaks
uses: gitleaks/gitleaks-action@e0c47f4f8be36e29cdc102c57e68cb5cbf0e8d1e # v3.0.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# GITLEAKS_LICENSE is required for use in private repositories.
# For public repos this environment variable can be omitted.
# GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }}
# gitleaks-action writes a SARIF report when findings exist.
# Upload it so the results surface in the GitHub Security tab.
- name: Upload gitleaks SARIF report
uses: github/codeql-action/upload-sarif@7188fc363630916deb702c7fdcf4e481b751f97a # v4.37.1
if: always()
with:
sarif_file: results.sarif
continue-on-error: true # File may not exist when there are no findings
- name: Upload gitleaks report artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: always()
with:
name: gitleaks-report
path: results.sarif
retention-days: 30
if-no-files-found: ignore
# ---------------------------------------------------------------------------
# Job 2 — Dependency audit with pip-audit
# ---------------------------------------------------------------------------
dependency-audit:
name: Dependency Audit (pip-audit)
runs-on: ubuntu-latest
steps:
- name: Checkout source
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Install uv
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 (SHA verified)
with:
version: ${{ env.UV_VERSION }}
- name: Set up Python ${{ env.PYTHON_VERSION }}
run: uv python install ${{ env.PYTHON_VERSION }}
- name: Cache uv dependencies
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.cache/uv
key: uv-${{ runner.os }}-${{ env.PYTHON_VERSION }}-${{ hashFiles('pyproject.toml', 'uv.lock') }}
restore-keys: |
uv-${{ runner.os }}-${{ env.PYTHON_VERSION }}-
- name: Install production dependencies
# Install only production deps to audit what actually ships in the image.
# pip-audit is added as a temporary tool install, not a project dependency.
run: uv sync --frozen --no-dev --extra anthropic
- name: Install pip-audit
run: uv tool install pip-audit
- name: Run pip-audit (JSON output)
# --require-hashes is skipped because uv lockfiles handle integrity.
# Fail the job on any found vulnerability (exit code != 0).
run: |
uv tool run pip-audit \
--format json \
--output pip-audit-report.json \
--progress-spinner off \
--ignore-vuln PYSEC-2024-278
continue-on-error: true # Always upload the report even on findings
- name: Run pip-audit (human-readable summary)
run: |
uv tool run pip-audit \
--format columns \
--progress-spinner off \
--ignore-vuln PYSEC-2024-278
continue-on-error: true
- name: Upload pip-audit report
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: always()
with:
name: pip-audit-report
path: pip-audit-report.json
retention-days: 30
if-no-files-found: ignore
- name: Fail on vulnerabilities
# Re-run in strict mode after uploading the artifact so the job
# status reflects real findings. The artifact is preserved regardless.
run: |
uv tool run pip-audit \
--format columns \
--progress-spinner off \
--ignore-vuln PYSEC-2024-278
# ---------------------------------------------------------------------------
# Job 3 — Python SAST with bandit
# ---------------------------------------------------------------------------
sast:
name: SAST (bandit)
runs-on: ubuntu-latest
steps:
- name: Checkout source
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Install uv
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 (SHA verified)
with:
version: ${{ env.UV_VERSION }}
- name: Set up Python ${{ env.PYTHON_VERSION }}
run: uv python install ${{ env.PYTHON_VERSION }}
- name: Install bandit
run: uv tool install "bandit[sarif]"
- name: Run bandit (SARIF output for GitHub Security tab)
run: |
uv tool run bandit \
--recursive \
--format sarif \
--output bandit-results.sarif \
--severity-level medium \
--confidence-level medium \
--exclude ".venv,tests" \
api/ core/ agents/ pack_kernel/ domain_packs/ connectors/ control_plane/
continue-on-error: true # Always upload results even when findings exist
- name: Run bandit (human-readable summary for log output)
run: |
uv tool run bandit \
--recursive \
--format screen \
--severity-level medium \
--confidence-level medium \
--exclude ".venv,tests" \
api/ core/ agents/ pack_kernel/ domain_packs/ connectors/ control_plane/
continue-on-error: true
- name: Upload bandit SARIF to GitHub Security tab
uses: github/codeql-action/upload-sarif@7188fc363630916deb702c7fdcf4e481b751f97a # v4.37.1
if: always()
with:
sarif_file: bandit-results.sarif
continue-on-error: true # Requires Advanced Security on private repos
- name: Upload bandit SARIF artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: always()
with:
name: bandit-sarif-report
path: bandit-results.sarif
retention-days: 30
if-no-files-found: ignore
- name: Fail on high/critical findings
# Re-run at HIGH severity to gate the job. Medium findings are
# surfaced in the artifact but do not block merging.
run: |
uv tool run bandit \
--recursive \
--format screen \
--severity-level high \
--confidence-level high \
--exclude ".venv,tests" \
api/ core/ agents/ pack_kernel/ domain_packs/ connectors/ control_plane/
# ---------------------------------------------------------------------------
# Job 4 — SBOM generation (Syft) + container vulnerability scan (Trivy)
# ---------------------------------------------------------------------------
image-scan:
name: Container SBOM & scan (Syft + Trivy)
runs-on: ubuntu-latest
env:
ANTHROPIC_API_KEY: test-key-not-real
steps:
- name: Checkout source
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Build Docker image
run: docker build -f infra/Dockerfile -t langgraph-agent-stack:scan .
- name: Generate SBOM (Syft / SPDX)
uses: anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 # v0
with:
image: langgraph-agent-stack:scan
format: spdx-json
output-file: sbom.spdx.json
- name: Upload container SBOM
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: container-sbom-spdx
path: sbom.spdx.json
retention-days: 30
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
with:
image-ref: langgraph-agent-stack:scan
format: table
exit-code: "1"
ignore-unfixed: true
severity: "HIGH,CRITICAL"
- name: Run Trivy (SARIF for GitHub Security tab)
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
if: always()
with:
image-ref: langgraph-agent-stack:scan
format: sarif
output: trivy-results.sarif
ignore-unfixed: true
severity: "HIGH,CRITICAL"
continue-on-error: true
- name: Upload Trivy SARIF to GitHub Security tab
uses: github/codeql-action/upload-sarif@7188fc363630916deb702c7fdcf4e481b751f97a # v4.37.1
if: always()
with:
sarif_file: trivy-results.sarif
continue-on-error: true
- name: Upload Trivy SARIF artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: always()
with:
name: trivy-sarif-report
path: trivy-results.sarif
retention-days: 30
if-no-files-found: ignore