Skip to content

deps(security): cryptography >= 46.0.7 (GHSA-p423-j2cm-9vmq) #331

deps(security): cryptography >= 46.0.7 (GHSA-p423-j2cm-9vmq)

deps(security): cryptography >= 46.0.7 (GHSA-p423-j2cm-9vmq) #331

Workflow file for this run

# SPDX-License-Identifier: MIT
#
# PR Gate — synchronous merge contract for `main`.
#
# Every job declared here is a required status check in the branch-protection
# ruleset for `main` (see .github/BRANCH_PROTECTION_MAIN.md). Jobs are
# fail-closed (`continue-on-error: false`) so a green tick deterministically
# means "all gates satisfied".
#
# Invariants:
# * Jobs are content-aware: they exit successfully when no relevant files
# changed, preserving merge queue throughput without losing enforcement.
# * All third-party actions are pinned by 40-character commit SHA
# (repo-policy validates this on every run).
# * Permissions follow principle of least privilege.
name: PR Gate
on:
pull_request:
branches: [main]
merge_group:
permissions:
contents: read
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: 'true'
concurrency:
group: pr-gate-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true
jobs:
# ---------------------------------------------------------------------------
# repo-policy — workflow hygiene invariants (permissions, pinning, actionlint)
# ---------------------------------------------------------------------------
repo-policy:
name: repo-policy
runs-on: ubuntu-latest
continue-on-error: false
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
- name: Validate workflow policy invariants
run: |
set -euo pipefail
for wf in .github/workflows/*.yml; do
grep -Eq '^permissions:' "$wf" || { echo "ERROR: $wf missing permissions"; exit 1; }
if grep -Eq '^\s*pull_request_target:' "$wf"; then
echo "ERROR: $wf uses forbidden pull_request_target"
exit 1
fi
done
- name: Enforce pinned actions policy
run: |
set -euo pipefail
python - <<'PY'
import re
from pathlib import Path
pattern = re.compile(r'^\s*uses:\s*([^\s#]+)')
violations = []
for wf in sorted(Path('.github/workflows').glob('*.yml')):
for lineno, line in enumerate(wf.read_text(encoding='utf-8').splitlines(), start=1):
m = pattern.search(line)
if not m:
continue
ref = m.group(1)
if ref.startswith('./') or ref.startswith('docker://'):
continue
if not re.search(r'@[a-f0-9]{40}$', ref):
violations.append(f"{wf}:{lineno}: unpinned action reference: {ref}")
if violations:
print('\n'.join(violations))
raise SystemExit(1)
print('Pinned action policy passed.')
PY
- name: Actionlint validation
run: |
set -euo pipefail
docker run --rm -v "$PWD":/repo -w /repo rhysd/actionlint:1.7.8 -color
- name: Inventory and manifest integrity checks
run: |
set -euo pipefail
python scripts/ci/check_inventory_sync.py
python scripts/ci/check_manifest_hashes.py
# ---------------------------------------------------------------------------
# python-quality — ruff + black + mypy on changed Python files
# ---------------------------------------------------------------------------
python-quality:
name: python-quality
runs-on: ubuntu-latest
continue-on-error: false
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
with:
fetch-depth: 0
- name: Setup Python environment
uses: ./.github/actions/setup-geosync
with:
python-version: '3.11'
cache-prefix: pr-quality
- name: Verify pip executable and Python contract
run: |
set -euo pipefail
.venv/bin/python -m pip --version
.venv/bin/python -m pip check
.venv/bin/python - <<'PY'
import tomllib
from pathlib import Path
requires = tomllib.loads(Path('pyproject.toml').read_text(encoding='utf-8'))['project']['requires-python']
if requires != '>=3.11,<3.13':
raise SystemExit(f'Unexpected requires-python: {requires}')
print(f'requires-python validated: {requires}')
PY
- name: Run lint/type gate on changed Python files
run: |
set -euo pipefail
if [[ "${GITHUB_EVENT_NAME}" == "pull_request" ]]; then
git fetch --no-tags origin "${{ github.base_ref }}"
base_ref="origin/${{ github.base_ref }}"
changed_files=$(git diff --name-only "$base_ref...$GITHUB_SHA")
else
changed_files=$(git diff-tree --no-commit-id --name-only -r "$GITHUB_SHA")
fi
mapfile -t py_files < <(
printf '%s\n' "$changed_files" \
| awk '/\.py$/' \
| while IFS= read -r f; do
[[ -f "$f" ]] && printf '%s\n' "$f"
done
)
if [[ ${#py_files[@]} -eq 0 ]]; then
echo "No Python file changes detected; python-quality passes."
exit 0
fi
.venv/bin/ruff check "${py_files[@]}"
.venv/bin/black --check "${py_files[@]}"
.venv/bin/mypy "${py_files[@]}"
# ---------------------------------------------------------------------------
# python-fast-tests — fast deterministic pytest suite
# ---------------------------------------------------------------------------
python-fast-tests:
name: python-fast-tests
runs-on: ubuntu-latest
needs: python-quality
continue-on-error: false
timeout-minutes: 20
steps:
- name: Checkout
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
with:
fetch-depth: 0
- name: Setup Python environment
uses: ./.github/actions/setup-geosync
with:
python-version: '3.11'
cache-prefix: pr-fast-tests
- name: Verify pip executable
run: .venv/bin/python -m pip --version
- name: Verify test-level classification (fail-fast)
run: |
set -euo pipefail
[[ -x .venv/bin/pytest ]] || { echo ".venv/bin/pytest missing"; exit 1; }
.venv/bin/pytest --collect-only -q \
tests/connectors/test_fail_closed_connectors.py \
tests/connectors/test_polygon_adapter_reproducible.py \
scripts/tests/test_generate_stakeholder_assets.py
- name: Run fast deterministic pytest gate
run: |
set -euo pipefail
if [[ "${GITHUB_EVENT_NAME}" == "pull_request" ]]; then
git fetch --no-tags origin "${{ github.base_ref }}"
base_ref="origin/${{ github.base_ref }}"
changed_files=$(git diff --name-only "$base_ref...$GITHUB_SHA")
else
changed_files=$(git diff-tree --no-commit-id --name-only -r "$GITHUB_SHA")
fi
if ! printf '%s\n' "$changed_files" | awk '
/^tests\// || /^core\// || /^backtest\// || /^execution\// || /^src\// ||
/^scripts\// || /^pyproject\.toml$/ || /^requirements(-dev)?\.(txt|lock)$/ ||
/^pytest\.ini$/ || /^\.github\/workflows\/pr-gate\.yml$/ || /^\.pre-commit-config\.yaml$/ {
found=1; exit
}
END { exit !found }
'; then
echo "No Python runtime/test surface changes detected; python-fast-tests passes."
exit 0
fi
[[ -x .venv/bin/pytest ]] || { echo ".venv/bin/pytest missing"; exit 1; }
timeout 1200s .venv/bin/pytest tests/ \
-m "not slow and not heavy_math and not nightly and not flaky" \
--maxfail=1 \
-q
- name: Run mandatory connector hygiene tests
run: |
set -euo pipefail
[[ -x .venv/bin/pytest ]] || { echo ".venv/bin/pytest missing"; exit 1; }
timeout 300s .venv/bin/pytest \
tests/connectors/test_fail_closed_connectors.py \
tests/connectors/test_polygon_adapter_reproducible.py \
scripts/tests/test_generate_stakeholder_assets.py \
--maxfail=1 \
-q
- name: Run formal verification gate
run: |
set -euo pipefail
if [[ "${GITHUB_EVENT_NAME}" == "pull_request" ]]; then
git fetch --no-tags origin "${{ github.base_ref }}"
base_ref="origin/${{ github.base_ref }}"
changed_files=$(git diff --name-only "$base_ref...$GITHUB_SHA")
else
changed_files=$(git diff-tree --no-commit-id --name-only -r "$GITHUB_SHA")
fi
if ! printf '%s\n' "$changed_files" | awk '
/^formal\// || /^cortex_service\// || /^src\// || /^tests\// ||
/^Makefile$/ || /^requirements(-dev)?\.(txt|lock)$/ {
found=1; exit
}
END { exit !found }
'; then
echo "No formal-sensitive changes detected; skipping formal-verify."
exit 0
fi
make formal-verify
# ---------------------------------------------------------------------------
# python-heavy-tests — heavy compute test lane
# ---------------------------------------------------------------------------
python-heavy-tests:
name: python-heavy-tests
runs-on: ubuntu-latest
needs: python-fast-tests
continue-on-error: false
timeout-minutes: 30
steps:
- name: Checkout
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
with:
fetch-depth: 0
- name: Setup Python environment
uses: ./.github/actions/setup-geosync
with:
python-version: '3.11'
cache-prefix: pr-heavy-tests
- name: Run heavy invariant gate
run: |
set -euo pipefail
if [[ "${GITHUB_EVENT_NAME}" == "pull_request" ]]; then
git fetch --no-tags origin "${{ github.base_ref }}"
base_ref="origin/${{ github.base_ref }}"
changed_files=$(git diff --name-only "$base_ref...$GITHUB_SHA")
else
changed_files=$(git diff-tree --no-commit-id --name-only -r "$GITHUB_SHA")
fi
if ! printf '%s\n' "$changed_files" | awk '
/^formal\// || /^cortex_service\// || /^src\// || /^tests\// ||
/^scripts\// || /^Makefile$/ || /^core\// {
found=1; exit
}
END { exit !found }
'; then
echo "No heavy-test-sensitive changes detected; skipping test-heavy."
exit 0
fi
[[ -x .venv/bin/pytest ]] || { echo ".venv/bin/pytest missing"; exit 1; }
timeout 1200s .venv/bin/pytest tests/ \
-m "slow or heavy_math or nightly" \
--maxfail=3 \
-q
# ---------------------------------------------------------------------------
# frontend-gate — Next.js lint/typecheck/tests when apps/web or root manifests change
# ---------------------------------------------------------------------------
frontend-gate:
name: frontend-gate
runs-on: ubuntu-latest
continue-on-error: false
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
with:
fetch-depth: 0
- name: Determine if frontend gate applies
id: changes
run: |
set -euo pipefail
if [[ "${GITHUB_EVENT_NAME}" == "pull_request" ]]; then
git fetch --no-tags origin "${{ github.base_ref }}"
base_ref="origin/${{ github.base_ref }}"
changed_files=$(git diff --name-only "$base_ref...$GITHUB_SHA")
else
changed_files=$(git diff-tree --no-commit-id --name-only -r "$GITHUB_SHA")
fi
if printf '%s\n' "$changed_files" | awk '
/^apps\/web\// || /^package\.json$/ || /^package-lock\.json$/ { found=1; exit }
END { exit !found }
'; then
echo "frontend_changed=true" >> "$GITHUB_OUTPUT"
else
echo "frontend_changed=false" >> "$GITHUB_OUTPUT"
fi
- name: Verify frontend lockfile
if: steps.changes.outputs.frontend_changed == 'true'
run: test -f apps/web/package-lock.json
- name: Setup Node.js
if: steps.changes.outputs.frontend_changed == 'true'
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: '24'
cache: npm
cache-dependency-path: apps/web/package-lock.json
- name: Run frontend quality and tests
if: steps.changes.outputs.frontend_changed == 'true'
working-directory: apps/web
run: |
npm ci
npm run format:check
npm run lint
npm run typecheck
npm run test -- --ci
- name: Frontend gate not applicable
if: steps.changes.outputs.frontend_changed != 'true'
run: echo 'No frontend changes detected; frontend gate passed deterministically.'
# ---------------------------------------------------------------------------
# dependency-review — GitHub-native vulnerability/license gate on PR diffs
# ---------------------------------------------------------------------------
dependency-review:
name: dependency-review
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
continue-on-error: false
timeout-minutes: 15
permissions:
contents: read
pull-requests: write
steps:
- name: Checkout
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
- name: GitHub dependency review
uses: actions/dependency-review-action@2031cfc080254a8a887f58cffee85186f0e49e48 # v4.9.0
with:
fail-on-severity: critical
deny-licenses: GPL-2.0, GPL-3.0, AGPL-3.0
comment-summary-in-pr: on-failure
# ---------------------------------------------------------------------------
# secrets-supply-chain — bandit SAST + detect-secrets baseline enforcement
# ---------------------------------------------------------------------------
secrets-supply-chain:
name: secrets-supply-chain
runs-on: ubuntu-latest
continue-on-error: false
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
with:
fetch-depth: 0
- name: Setup Python environment
uses: ./.github/actions/setup-geosync
with:
python-version: '3.11'
cache-prefix: pr-security
- name: Verify pip executable
run: .venv/bin/python -m pip --version
- name: Ensure security tooling
run: |
set -euo pipefail
missing=()
.venv/bin/python -m pip show bandit >/dev/null 2>&1 || missing+=(bandit)
.venv/bin/python -m pip show detect-secrets >/dev/null 2>&1 || missing+=(detect-secrets)
if [[ ${#missing[@]} -gt 0 ]]; then
.venv/bin/python -m pip install -c constraints/security.txt "${missing[@]}"
fi
- name: Collect changed files
id: changed
run: |
set -euo pipefail
if [[ "${GITHUB_EVENT_NAME}" == "pull_request" ]]; then
git fetch --no-tags origin "${{ github.base_ref }}"
base_ref="origin/${{ github.base_ref }}"
git diff --name-only "$base_ref...$GITHUB_SHA" > /tmp/changed-files.txt
else
git diff-tree --no-commit-id --name-only -r "$GITHUB_SHA" > /tmp/changed-files.txt
fi
cat /tmp/changed-files.txt
- name: Bandit on changed security-critical Python files
run: |
set -euo pipefail
mapfile -t bandit_targets < <(awk '/^(core|backtest|execution|src)\/.*\.py$/' /tmp/changed-files.txt)
if [[ ${#bandit_targets[@]} -eq 0 ]]; then
echo "No security-critical Python changes detected; Bandit gate passes."
else
.venv/bin/bandit -ll "${bandit_targets[@]}"
fi
- name: Detect-secrets using baseline
run: |
set -euo pipefail
mapfile -t existing_changed_files < <(
while IFS= read -r f; do
[[ -f "$f" ]] && echo "$f"
done < /tmp/changed-files.txt
)
if [[ ${#existing_changed_files[@]} -eq 0 ]]; then
echo "No file additions/modifications to scan; secrets gate passes."
exit 0
fi
.venv/bin/detect-secrets-hook \
--baseline .github/detect-secrets.baseline \
--exclude-files '^(INVENTORY\.json|stakeholders/manifest\.json|\.github/detect-secrets\.baseline)$' \
"${existing_changed_files[@]}"
go-workspace-integrity:
name: go-workspace-integrity
runs-on: ubuntu-latest
continue-on-error: false
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
with:
fetch-depth: 0
- name: Detect Go changes
id: go-changes
run: |
set -euo pipefail
if [[ "${GITHUB_EVENT_NAME}" == "pull_request" ]]; then
git fetch --no-tags origin "${{ github.base_ref }}"
changed=$(git diff --name-only "origin/${{ github.base_ref }}...$GITHUB_SHA")
else
changed=$(git diff-tree --no-commit-id --name-only -r "$GITHUB_SHA")
fi
printf '%s\n' "$changed" | grep -qE '(go\.work|go\.mod|go\.sum|\.go$)' \
&& echo "applicable=true" >> "$GITHUB_OUTPUT" \
|| echo "applicable=false" >> "$GITHUB_OUTPUT"
- name: Set up Go
if: steps.go-changes.outputs.applicable == 'true'
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v5
with:
go-version-file: 'go.work'
cache: false
- name: go work sync check
if: steps.go-changes.outputs.applicable == 'true'
run: |
set -euo pipefail
go work sync
git diff --exit-code go.work || {
echo "go.work out of sync — run 'go work sync' locally"
exit 1
}
- name: go vet + build (workspace modules only)
if: steps.go-changes.outputs.applicable == 'true'
run: |
set -euo pipefail
cd examples/go-client
go mod tidy
git diff --exit-code go.mod || {
echo "go.mod out of sync — run 'cd examples/go-client && go mod tidy' locally"
exit 1
}
go vet ./...
go build ./...
- name: go test (workspace modules only)
if: steps.go-changes.outputs.applicable == 'true'
run: |
set -euo pipefail
cd examples/go-client
go test -race -count=1 ./...
- name: Skip (no Go changes)
if: steps.go-changes.outputs.applicable != 'true'
run: echo "No Go manifest changes — go-workspace-integrity passes."