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
54 changes: 4 additions & 50 deletions .github/workflows/python-security.yml
Original file line number Diff line number Diff line change
Expand Up @@ -232,53 +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
# A matching requirements-<tool>-ci-overrides.txt (a `uv pip compile --override`
# input, e.g. requirements-strix-ci-overrides.txt) means the *-hashes.txt this
# override applies to pins a version whose declared metadata range intentionally
# conflicts with another pin in the same file (verified safe at override time, not a
# resolution mistake). pip's own dependency resolver -- which pip-audit's default
# `-r` mode still calls even for fully hash-pinned files -- fails on that same
# declared-range conflict regardless of --require-hashes, and plain --no-deps does
# not suppress it (confirmed: --no-deps only skips fetching undeclared transitive
# packages, pip's resolver still cross-checks the packages that *are* listed
# together). --disable-pip bypasses pip's resolver entirely and audits the exact
# pins directly, but it requires every requirement to be an exact version (raises on
# any bare range) -- true for the compiled *-hashes.txt, not necessarily true for the
# hand-maintained raw input (e.g. requirements-strix-ci.txt intentionally leaves
# protobuf as a range). So: hashed output files with an override get
# --disable-pip --no-deps; their raw, non-hash input counterpart is skipped here
# (it is never itself a `pip install --require-hashes` target -- only its compiled
# *-hashes.txt is installed -- and that compiled file is the one audited with full
# transitive coverage).
base="${req%.txt}"
unhashed_base="${base%-hashes}"
if [ "$base" != "$unhashed_base" ] && [ -f "${unhashed_base}-overrides.txt" ]; then
echo "::group::pip-audit -r ${req} (--disable-pip --no-deps: overridden lock)"
pip-audit --strict --desc=on --no-deps --disable-pip -r "${req}" || status=1
echo "::endgroup::"
elif [ "$base" = "$unhashed_base" ] && [ -f "${unhashed_base}-overrides.txt" ]; then
echo "::notice::Skipping pip-audit for ${req}: it is the raw input to an overridden lock (${unhashed_base}-hashes.txt), never itself a pip install --require-hashes target, and its compiled hashes file is audited separately with full resolution."
else
echo "::group::pip-audit -r ${req}"
pip-audit --strict --desc=on -r "${req}" || status=1
echo "::endgroup::"
fi
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
3 changes: 3 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,7 @@ Materialize accepts only exact SHA-256 pins or a bounded relative `-r` include (
Conflict-scope roots fail closed when the immediate parent directory is a symbolic link.
OriginWeave hourly NVIDIA NIM repair is a thin caller at minute 10. See [`docs/doctoring/originweave-hourly-review-caller.md`](docs/doctoring/originweave-hourly-review-caller.md).
nonnest2 hourly NVIDIA NIM repair is a thin caller at minute 16. See [`docs/doctoring/nonnest2-hourly-review-caller.md`](docs/doctoring/nonnest2-hourly-review-caller.md).

Hashed-lock pip-audit uses `--disable-pip` only when every package line is an exact SHA-256 pin; `--index-url` is configuration; directory-symlink hops fail closed. See [`docs/doctoring/strix-hashed-lock-no-deps.md`](docs/doctoring/strix-hashed-lock-no-deps.md).

The materialization contract is also covered by [`docs/doctoring/exact-artifact-sbom-attestation.md`](docs/doctoring/exact-artifact-sbom-attestation.md).
9 changes: 9 additions & 0 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@ only established scheduler credentials, and grants job-scoped
only established scheduler credentials, and grants job-scoped
`id-token: write`. The reusable engine stays product-neutral.

## Hashed-lock pip-audit

`scripts/ci/pip_audit_requirements.py` audits complete hashed locks with
`--disable-pip`. Resolver flags such as `--index-url` are not package
lines. `-r` includes and directory-symlink parents fail closed so pip
cannot relabel `ResolutionImpossible` as a known vulnerability.

## Hourly NVIDIA NIM repair gate

```mermaid
Expand Down Expand Up @@ -147,5 +154,7 @@ trusted `uv` exporter is downloaded from the literal GitHub Releases URL for
— current increment's repair-worker decision and APA 7th citations.
- [`docs/doctoring/fast-mlsirm-hourly-review-caller.md`](docs/doctoring/fast-mlsirm-hourly-review-caller.md)
— product-specific psychometric repair heartbeat and scientific gates.


- [`docs/doctoring/exact-artifact-sbom-attestation.md`](docs/doctoring/exact-artifact-sbom-attestation.md)
— current increment's attestation decision and APA 7th citations.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,9 @@ Semantic Versioning where the repository publishes a release.
- Download the pinned `uv` 0.12.1 exporter from the official GitHub Releases URL instead of `releases.astral.sh`, which now returns HTTP 403 and blocks org-wide OpenCode `coverage-evidence`. The SHA-256 pin is unchanged. The opener may follow one hop onto `release-assets.githubusercontent.com` or `objects.githubusercontent.com` and still rejects every other host, userinfo, non-HTTPS scheme, and nondefault port (ContextualWisdomLab/.github#1109).
- Compared the trusted `uv` executable's post-install `--version` output against the real GitHub Releases build's full string, `uv 0.12.1 (x86_64-unknown-linux-gnu)`, instead of the bare `uv 0.12.1` the prior check required; the genuine release binary always prints the target triple, so every installation was failing the pin check immediately after the archive download itself was fixed (ContextualWisdomLab/.github#1109).
- Excluded relative `-r` and `--requirement` referrers from generated flat base-lock publication while retaining bounded include syntax diagnostics and discovering independently complete direct `.txt` children of `requirements` directories.
- 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).
- Audit complete hashed Python locks with `--disable-pip` so `--index-url` stays configuration, `-r` includes do not earn resolver bypass, and directory-symlink parents fail closed before pip-audit can label `ResolutionImpossible` as a known vulnerability.
- 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.
Comment thread
seonghobae marked this conversation as resolved.
- 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.
- Allowed commas and ASCII parentheses in the bounded Strix changed-file path policy so legal tracked Packrat fixtures can receive exact-head security analysis, while rejecting raw `..` components before normalization and keeping controls, backslashes, whitespace ambiguity, and shell punctuation fail-closed.
- Bound each review-agent invocation key to the wrapper's complete canonical payload, including the base branch and requesting actor; altered fields with a valid-format key now fail before durable-leader election or forwarding, and wrapper write permission is job-scoped.
Expand Down
2 changes: 2 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,8 @@ repeatable compile command.
- **Product hourly callers** stay thin. Do not hard-code OriginWeave, naruon, or Keyverse
into `pr-review-fix-scheduler.yml`. The model credential remains `NVIDIA_NIM_API_KEY`
on the worker, never `COPILOT_GITHUB_TOKEN`.
- **pip-audit** of hashed locks goes through `scripts/ci/pip_audit_requirements.py`.
Do not call `pip-audit -r` on a complete lock (that re-resolves and can false-fail).
- **`pull_request_target` trust boundary.** The required review workflows run the *base branch's*
trusted scripts. A PR that edits the trusted review workflows can fail its own checks until the
base branch catches up; a same-head manual `workflow_dispatch` Strix run may supply review evidence
Expand Down
95 changes: 95 additions & 0 deletions docs/doctoring/strix-hashed-lock-no-deps.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# Strix hashed-lock install and pip-audit without pip re-resolution

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

## 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`/`-c`/`-e` include, or a pip
option carrying hash-shaped text also stays on the resolver path. A
complete lock that only adds resolver configuration such as `--index-url`
still receives `--disable-pip`. An unhashed compile input is skipped
only when its regular, non-symlink sibling is itself a valid complete lock.
Invalid UTF-8 and symlink/special-file requirement inputs fail before any
audit command. Discovery does not descend directory symbolic links, and a
presented path whose intermediate parent is a symlink or whose resolved
target leaves the audit root fails closed (CWE-22 / CWE-59).
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

MITRE. (2026). *CWE-22: Improper limitation of a pathname to a restricted
directory ('Path Traversal')*. https://cwe.mitre.org/data/definitions/22.html

MITRE. (2026). *CWE-59: Improper link resolution before file access
('Link Following')*. https://cwe.mitre.org/data/definitions/59.html

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