Skip to content

fix(core): widen cryptography upper bound to <50.0 #1083

fix(core): widen cryptography upper bound to <50.0

fix(core): widen cryptography upper bound to <50.0 #1083

name: Supply Chain Check
on:
pull_request:
paths:
# Manifest formats that declare runtime/build dependencies.
- '**/package.json'
- '**/package-lock.json'
- '**/npm-shrinkwrap.json'
- '**/Cargo.toml'
- '**/Cargo.lock'
- '**/pyproject.toml'
- '**/requirements*.txt'
- 'requirements*.txt'
- '**/setup.py'
- '**/setup.cfg'
- '**/Pipfile'
- '**/Pipfile.lock'
- '**/poetry.lock'
- '**/uv.lock'
- '**/*.csproj'
- '**/packages.config'
- '**/go.mod'
- '**/go.sum'
- '**/build.rs'
# If the scanner code itself changes, the trip-wire job must run.
- 'scripts/check_release_age.py'
- 'scripts/check_install_scripts.py'
- 'scripts/check_build_hooks.py'
- 'scripts/_supply_chain_common.py'
- 'scripts/tests/test_check_release_age.py'
- 'scripts/tests/test_check_install_scripts.py'
- 'scripts/tests/test_check_build_hooks.py'
- 'scripts/tests/test_supply_chain_common.py'
- '.github/workflows/supply-chain-check.yml'
permissions:
contents: read
jobs:
# ------------------------------------------------------------------
# Trip-wire: refuse to let a PR modify its own gates AND its
# dependencies in the same change. A PR that wants to update the
# scanner code MUST do so in a separate scoped PR; the dep-changing
# PR then rebases on top.
# ------------------------------------------------------------------
scanner-trip-wire:
name: Scanner trip-wire
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
fetch-depth: 0
persist-credentials: false
- name: Refuse co-modification of scanner code and dependencies
env:
BASE_REF: ${{ github.event.pull_request.base.ref }}
run: |
set -euo pipefail
CHANGED="$(git diff --name-only "origin/${BASE_REF}...HEAD")"
SCANNER_HITS="$(printf '%s\n' "$CHANGED" \
| grep -E '^(scripts/(check_release_age|check_install_scripts|check_build_hooks|_supply_chain_common)\.py|scripts/tests/test_(check_release_age|check_install_scripts|check_build_hooks|supply_chain_common)\.py|\.github/workflows/supply-chain-check\.yml)$' \
|| true)"
DEP_HITS="$(printf '%s\n' "$CHANGED" \
| grep -E '(^|/)(package\.json|package-lock\.json|npm-shrinkwrap\.json|Cargo\.toml|Cargo\.lock|pyproject\.toml|requirements[^/]*\.txt|setup\.py|setup\.cfg|Pipfile|Pipfile\.lock|poetry\.lock|uv\.lock|.*\.csproj|packages\.config|go\.mod|go\.sum|build\.rs)$' \
|| true)"
if [ -n "$SCANNER_HITS" ] && [ -n "$DEP_HITS" ]; then
echo "::error::This PR modifies BOTH supply-chain scanner code AND dependency manifests."
echo "::error::Split into two PRs: land scanner updates first, then rebase the dep-changing PR on top."
echo "Scanner files touched:"
echo "$SCANNER_HITS" | sed 's/^/ - /'
echo "Dependency files touched:"
echo "$DEP_HITS" | sed 's/^/ - /'
exit 1
fi
echo "OK: no scanner/dependency co-modification detected."
# ------------------------------------------------------------------
# Version-pinning gate (legacy in-line check, structural rewrite).
# ------------------------------------------------------------------
check-version-pinning:
name: Version-pinning gate
runs-on: ubuntu-latest
timeout-minutes: 5
needs: scanner-trip-wire
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.11"
- name: Check for unpinned npm versions
run: |
set -euo pipefail
# We feed the file list to python via stdin and use sys.argv to
# avoid any shell interpolation of paths into a python-c string
# (that was the M1 path-injection finding).
VIOLATIONS=0
while IFS= read -r f; do
if ! python3 - "$f" <<'PY' 2>/dev/null
import json, sys
path = sys.argv[1]
try:
with open(path, encoding="utf-8") as fh:
data = json.load(fh)
except Exception:
sys.exit(0)
for section in ("dependencies", "devDependencies", "peerDependencies"):
for pkg, ver in (data.get(section) or {}).items():
if not isinstance(ver, str):
continue
if ver.startswith("^") or ver.startswith("~"):
print(f' "{pkg}": "{ver}"')
sys.exit(1)
PY
then
echo "VIOLATION: $f has unpinned version ranges"
VIOLATIONS=$((VIOLATIONS + 1))
fi
done < <(find packages -name package.json -not -path '*/node_modules/*' -not -path '*/dist/*' 2>/dev/null || true)
if [ "$VIOLATIONS" -gt 0 ]; then
echo ""
echo "Found $VIOLATIONS package.json files with ^ or ~ version ranges."
echo "Pin to exact versions (e.g., \"1.2.3\" not \"^1.2.3\")."
exit 1
fi
echo "OK: All package.json files use exact versions"
- name: Check lockfile presence
run: |
set -euo pipefail
while IFS= read -r f; do
DIR=$(dirname "$f")
if [ ! -f "$DIR/package-lock.json" ] && [ ! -f "$DIR/pnpm-lock.yaml" ] && [ ! -f "$DIR/yarn.lock" ]; then
if grep -q '"dependencies"' "$f" 2>/dev/null; then
echo "WARNING: $DIR has dependencies but no lockfile"
fi
fi
done < <(find packages -name package.json -not -path '*/node_modules/*' -not -path '*/dist/*' 2>/dev/null || true)
echo "Lockfile check complete"
# ------------------------------------------------------------------
# G1: 7-day release-age cooling-off.
# ------------------------------------------------------------------
release-age:
name: 7-day cooling-off check
runs-on: ubuntu-latest
timeout-minutes: 10
needs: scanner-trip-wire
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
fetch-depth: 0
persist-credentials: false
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.11"
- name: Check newly-pinned versions against 7-day rule
env:
BASE_REF: ${{ github.event.pull_request.base.ref }}
run: |
python3 scripts/check_release_age.py --base "origin/${BASE_REF}" --min-age-days 7
# ------------------------------------------------------------------
# G2: npm install-script flagger (strict — blocks merge by default).
# ------------------------------------------------------------------
install-scripts:
name: npm install-script audit
runs-on: ubuntu-latest
timeout-minutes: 10
needs: scanner-trip-wire
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
fetch-depth: 0
persist-credentials: false
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.11"
- name: Audit added npm deps for install scripts
env:
BASE_REF: ${{ github.event.pull_request.base.ref }}
run: |
python3 scripts/check_install_scripts.py --base "origin/${BASE_REF}" --strict
# ------------------------------------------------------------------
# G3 (H5): build-hook (setup.py / build.rs) addition flagger.
# ------------------------------------------------------------------
build-hooks:
name: Build-hook audit
runs-on: ubuntu-latest
timeout-minutes: 5
needs: scanner-trip-wire
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
fetch-depth: 0
persist-credentials: false
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.11"
- name: Flag setup.py and build.rs additions
env:
BASE_REF: ${{ github.event.pull_request.base.ref }}
run: |
python3 scripts/check_build_hooks.py --base "origin/${BASE_REF}" --strict
# ------------------------------------------------------------------
# Lockfile integrity: verify lockfile hashes match upstream registries.
# ------------------------------------------------------------------
lockfile-integrity:
name: Verify lockfile hashes match upstream registries
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
fetch-depth: 0
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: '3.12'
- name: Resolve base ref
env:
BASE_REF: ${{ github.event.pull_request.base.ref }}
run: |
set -euo pipefail
if [ -n "${BASE_REF:-}" ]; then
git fetch --no-tags --depth=1 origin "${BASE_REF}" || true
echo "AGT_BASE_REF=origin/${BASE_REF}" >> "${GITHUB_ENV}"
else
echo "AGT_BASE_REF=origin/main" >> "${GITHUB_ENV}"
fi
- name: Run lockfile integrity check
env:
AGT_BASE_REF: ${{ env.AGT_BASE_REF }}
run: |
set -euo pipefail
python scripts/check_lockfile_integrity.py --base "${AGT_BASE_REF}" --max-deps 2000