Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 4 additions & 22 deletions .github/workflows/python-security.yml
Original file line number Diff line number Diff line change
Expand Up @@ -232,25 +232,7 @@ jobs:
- name: Run pip-audit (hard gate on any known vulnerability)
run: |
set -euo pipefail
status=0

# Audit every discovered requirements file.
while IFS= read -r req; do
echo "::group::pip-audit -r ${req}"
pip-audit --strict --desc=on -r "${req}" || status=1
echo "::endgroup::"
done < <(find . -type f -name 'requirements*.txt' -not -path './.git/*')

# Audit the project itself when a PEP 621 / lock manifest exists.
if find . -maxdepth 2 -type f \
\( -name 'pyproject.toml' -o -name 'pylock.*.toml' \) \
-not -path './.git/*' | head -1 | grep -q .; then
echo "::group::pip-audit . (project manifest)"
pip-audit --strict --desc=on . || status=1
echo "::endgroup::"
fi

if [ "${status}" != "0" ]; then
echo "::error::pip-audit reported known-vulnerable Python dependencies."
exit 1
fi
# Hashed complete locks are audited with --disable-pip so pip cannot
# re-apply a stale Requires-Dist bound and label ResolutionImpossible
# as a known vulnerability (ContextualWisdomLab/.github#961).
python3 scripts/ci/pip_audit_requirements.py
8 changes: 7 additions & 1 deletion .github/workflows/strix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -543,7 +543,13 @@ jobs:
# private install umask before creating the credential-bearing Strix
# entry point; the runtime gate still rejects any later relaxation.
umask 022
python3 -m pip install --disable-pip-version-check --no-cache-dir --require-hashes -r requirements-strix-ci-hashes.txt
# The compiled lock is the complete closed set. --no-deps is required
# so pip does not re-apply a stale Requires-Dist bound (strix-agent
# still declares cryptography<49) after a CVE-fixed cryptography pin
# was resolved at compile time. pull_request_target runs this file
# from the base branch, so this flag must land on main before a
# lock that needs it can install.
python3 -m pip install --disable-pip-version-check --no-cache-dir --require-hashes --no-deps -r requirements-strix-ci-hashes.txt
strix_executable="$(command -v strix || true)"
if [ -z "$strix_executable" ] || [[ "$strix_executable" != /* ]] \
|| [ ! -f "$strix_executable" ] || [ -L "$strix_executable" ] \
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ Semantic Versioning where the repository publishes a release.

### Fixed

- Required every package line to carry a `--hash=` pin before pip-audit treats a requirements file as a complete hashed lock. A lone `--require-hashes` directive, a mixed hashed-plus-unhashed file, a filename-only wheel path, a `-r` include, or a hash-shaped pip option no longer receives `--disable-pip`. Resolver-config lines such as `--index-url` beside exact SHA-256 pins still use `--disable-pip`. Invalid UTF-8, symlink/special-file inputs, and directory-symlink parents fail before any audit command, and workflow log paths are escaped. A pylock-shaped symlink or directory is not a project manifest, so the continue-to-next-candidate branch no longer depends on filesystem glob order.
- Landed `--require-hashes --no-deps` on the required Strix installer and taught pip-audit to audit hashed complete locks with `--disable-pip`, so a later strix-agent 1.5.3 + cryptography 50.0.0 lock can install under `pull_request_target` and pip-audit no longer labels pip `ResolutionImpossible` as a known vulnerability (ContextualWisdomLab/.github#961, #952). A `*-hashes.txt` name without hash evidence no longer receives `--disable-pip`, and discovery skips virtualenv trees.
- Materialized base Python locks only when every package line is an exact SHA-256 pin or a bounded relative `-r`/`--requirement` include. A lone `--require-hashes` directive, a dotted include such as `./lock.txt`, or `-r other-hashes.txt` no longer enters the trusted build context.
- Refused a conflict-scope repository root whose immediate parent is a symbolic link, so a swapped parent cannot redirect the canonical worktree after the last-component check (CWE-367).
- Bounded the Strix quality self-test's deterministic timeout fixtures to 3-second process and 5-second fake-sleep budgets so exact-head policy evidence completes inside the existing job limit without changing production Strix scanner timeouts, providers, credentials, or review semantics.
Expand Down
88 changes: 88 additions & 0 deletions docs/doctoring/strix-hashed-lock-no-deps.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Strix hashed-lock install and pip-audit without pip re-resolution

검토 기준일: **2026-08-13**

## Incident

The required Strix workflow is `pull_request_target`: GitHub runs the **base
branch** copy of `.github/workflows/strix.yml` against the pull-request head
tree. ContextualWisdomLab/.github#961 therefore compiled a complete lock of
`strix-agent==1.5.3` plus `cryptography==50.0.0` (override for CVE-2026-39892
and CVE-2026-69247) and added `--no-deps` to the PR copy of the installer, but
the live required job still executed main's installer:

```text
pip install --require-hashes -r requirements-strix-ci-hashes.txt
```

pip re-applied `strix-agent 1.5.3 depends on cryptography<49 and >=48.0.1`
and exited `ResolutionImpossible`. The same resolver path is what
`python-security.yml` used for every `requirements*.txt` file. pip-audit then
printed `::error::pip-audit reported known-vulnerable Python dependencies`
even though no advisory was returned. A buyer watching the required security
dashboard saw two red X marks on an honest lock.

## Decision

1. Land `--require-hashes --no-deps` on **main's** Strix installer first, with
no lock change. The current main lock (`strix-agent==1.0.4` +
`cryptography==50.0.0`) is already a complete hashed set, so `--no-deps`
does not widen the install. After this lands, a later 1.5.3 lock can
install under the required base-branch workflow.
2. Audit hashed locks with `pip-audit --disable-pip` via
`scripts/ci/pip_audit_requirements.py`. Compile-time `*-overrides.txt`
files and unhashed inputs that already have a `*-hashes.txt` sibling are
not separate install sets.
3. Do not drop `cryptography==50.0.0` to satisfy the stale `<49` metadata
bound. Do not treat `ResolutionImpossible` as a vulnerability.

`python-security.yml` is `pull_request` (not `_target`), so the helper takes
effect on the same head that introduces it. Strix still needs this installer
line on the protected base before #961 can go green.

## Trust boundary

- `--no-deps` is not an unhashed install: every wheel remains hash-pinned.
- `--disable-pip` still queries the advisory database for every pinned name
and version; it only skips pip's metadata resolver.
- NVIDIA NIM / OpenCode review-agent credentials are untouched.
- No operational PII is masked.

## Verification contract

`tests/test_pip_audit_requirements.py` reconstructs the #961 lock shape and
requires `--disable-pip` for that file, a skip for the override/input pair,
and the `--no-deps` installer line on `strix.yml`. A `*-hashes.txt` name
or a lone `--require-hashes` directive without `--hash=` is not treated
as a complete lock. A mixed file with one hashed line beside unhashed
packages, a filename-only wheel path, a ``-r`` include, or a pip option
carrying hash-shaped text also stays on the resolver path. A complete lock
that only adds resolver config such as ``--index-url`` still uses
``--disable-pip``. An unhashed compile input is skipped only when its
regular, non-symlink sibling is itself a valid complete lock. Invalid UTF-8,
symlink/special-file inputs, and paths whose intermediate parent is a
directory symlink fail before any audit command. A pylock-shaped symlink
or directory is not a project manifest. Repository-controlled
filenames are JSON-escaped before GitHub Actions group titles. Discovery
skips `.venv` trees.

## References (APA 7th)

GitHub. (2026). *Cryptography vulnerable to buffer overflow if
non-contiguous buffers were passed to APIs (CVE-2026-39892,
GHSA-p423-j2cm-9vmq)*. GitHub Advisory Database.
https://github.com/advisories/GHSA-p423-j2cm-9vmq

GitHub. (2026). *PKCS#7 decryption timing oracle in pyca/cryptography
(CVE-2026-69247, GHSA-g6cj-pr64-35w5)*. GitHub Advisory Database.
https://github.com/advisories/GHSA-g6cj-pr64-35w5

National Institute of Standards and Technology. (2026).
*CVE-2026-69247*. National Vulnerability Database.
https://nvd.nist.gov/vuln/detail/CVE-2026-69247

pypa. (2025). *pip-audit: ``--disable-pip`` (hashed requirements / ``--no-deps``
only)*. https://github.com/pypa/pip-audit

Python Packaging Authority. (n.d.). *Hash-checking mode*. pip documentation.
https://pip.pypa.io/en/stable/topics/secure-installs/#hash-checking-mode
Loading
Loading