fix(security): remediate PDF/image/build dependency CVEs - #467
Conversation
pip-audit against the runtime lock (uv export --no-dev) flagged real advisories in the exact PDF/image parsing path this service depends on. Re-locked with targeted upgrades (all within the existing pyproject.toml version ranges): - pillow 12.2.0 -> 12.3.0 (PYSEC-2026-3451/3452/3453/3454/3493/3494/3495/3496: eight image-decoder advisories). - pypdf 6.13.3 -> 6.14.2 (CVE-2026-59935/59936/59937/59938: PDF parsing). - click 8.3.2 -> 8.4.2 (PYSEC-2026-2132). - setuptools 81.0.0 -> 83.0.0 (CVE-2026-59890, build tool). Verification: `uv export --frozen --no-emit-project --no-dev | pip-audit` -> No known vulnerabilities found. Lock diff is confined to these four packages and their hashes. Residual (documented, not fixable in place): pymdown-extensions 10.21.3 CVE-2026-61632 requires a major bump to 11.0.0, but mkdocs-material 9.6.x caps pymdown-extensions to <11. It is a docs-build-only dependency (mkdocs), not on the runtime/API surface, so it does not affect the shipped image or the `/parse` path; clearing it needs a coordinated docs-toolchain upgrade. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Msz8Dni5zRaqPatNCmZJZ8
📝 WalkthroughWalkthrough보안 수정 버전으로 빌드, 런타임, 문서 도구 의존성을 갱신했습니다. 프로젝트 메타데이터 테스트와 문서 빌드 안내를 새 버전에 맞게 변경했습니다. 의존성 보안 기준과 검증 계약을 문서화했습니다. Changes보안 의존성 및 문서 도구체인
Estimated code review effort: 2 (Simple) | ~10 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
…fting mkdocs-material to 9.7.x The org Security Scan `trivy-fs` job fails on this repo (base branch and every open PR) with: [MEDIUM (security-severity=5.3)] CVE-2026-61632 uv.lock - pymdown-extensions pymdown-extensions 10.21.3 is vulnerable; the fix is 11.0.0+. It could not be upgraded because mkdocs-material 9.6.x pins `pymdown-extensions~=10.2` (i.e. <11). The docs toolchain deliberately held `mkdocs-material<9.7` to avoid the 9.7 "MkDocs 2.0" warning banner (encoded in pyproject, two tests, and CONTRIBUTING.md). That caution-hold now blocks a real security fix and fails required CI, so it is lifted to the minimum extent needed: bump only the theme to `mkdocs-material>=9.7,<9.8` (which relaxes its pymdown-extensions cap to `>=10.2`). The MkDocs *core* stays on the 1.x line (`mkdocs>=1.6,<2.0` unchanged) — the 9.7 theme still builds on MkDocs 1.x. Resulting lock: pymdown-extensions 10.21.3 -> 11.0.1, mkdocs-material 9.6.23 -> 9.7.7 (plus the runtime pillow/pypdf/click and setuptools bumps from the previous commit). Governance re-documented coherently: - CONTRIBUTING.md: explains the theme now tracks 9.7.x for the CVE fix while the MkDocs core stays 1.x; the 9.7 banner is informational and non-fatal under --strict. - tests/test_project_metadata.py: assertions updated to the new pin and the CONTRIBUTING rationale (CVE-2026-61632). Verification: - `uv run --frozen --extra docs mkdocs build --strict` -> builds cleanly (informational MkDocs 2.0 banner only, exit 0). - `uv export --frozen --no-emit-project --all-extras | pip-audit --no-deps` -> No known vulnerabilities found (runtime and full sets). - `PYTHONWARNINGS=error uv run --frozen --all-extras pytest` -> 430 passed. - No src/ change, so the 100% branch-coverage gate is unaffected. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Msz8Dni5zRaqPatNCmZJZ8
|
@coderabbitai review |
✅ Action performedReview finished.
|
|
@claude Please independently review this security dependency remediation against the current |
|
@opencode-agent Please review the current head using the repository's coverage-evidence and docstring requirements, and submit an approving review only if all required evidence and security checks pass. |
Pull request was closed
|
@opencode-agent Please review current head |
Pull request was converted to draft
Pull request was converted to draft
|
@opencode-agent @cwl-noema-review All exact-head repository checks for |
|
@coderabbitai review |
✅ Action performedReview finished.
|
|
@coderabbitai review |
|
@opencode-agent Please review exact current head |
|
@cwl-noema-review Please independently review exact current head |
|
@coderabbitai review Please review exact current head |
Rate Limit Exceeded
|
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@CHANGELOG.md`:
- Line 26: CHANGELOG의 pymdown-extensions 설명에서 mkdocs-material 9.7.x가 `>=10.2`
하한을 완화했다는 부정확한 표현을 수정하세요. `mkdocs-material` 9.6.x의 제약이 `>=10.2,<11`이며 9.7.x에서
제거되는 것은 `<11` 상한이라는 점이 드러나도록 해당 문장을 갱신하고, 나머지 취약점 및 버전 정보는 유지하세요.
In `@tests/test_project_metadata.py`:
- Around line 84-92: Update the pypdf version-floor assertion in
test_security_dependency_floors_exclude_known_vulnerable_ranges to require the
lower bound (6, 14, 2), matching the dependency constraints in pyproject.toml
and uv.lock; leave the other dependency checks unchanged.
- Around line 125-130: Update the docs optional dependency declaration in
pyproject.toml to explicitly include pymdown-extensions>=11, then extend
test_docs_theme_range_tracks_pymdownx_cve_fix to assert that direct constraint
and verify uv.lock resolves pymdown-extensions to version 11 or newer.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 05542c51-f410-4b28-94b2-a6c0e31acb4a
⛔ Files ignored due to path filters (1)
uv.lockis excluded by!**/*.lock
📒 Files selected for processing (4)
CHANGELOG.mdCONTRIBUTING.mdpyproject.tomltests/test_project_metadata.py
There was a problem hiding this comment.
🧹 Nitpick comments (1)
docs/doctoring/dependency-security-baseline.md (1)
98-133: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value참고문헌 항목의 발행 정보가 대부분 정확합니다.
NIST SSDF 1.1과 초안 상태인 1.2 개정판에 대한 구분, CVE-2026-59935 및 CVE-2026-59890에 대한 NVD/OSV 참조, Pillow 12.3.0과 pypdf 6.14.2의 PyPI 참조는 모두 검증된 내용과 일치합니다. 다만 이 문서가 앞으로도 유지될 참고 자료이므로, mkdocs-material 항목이 이번 업그레이드 이후 신규 기능 릴리스를 중단하고 후속 유지보수 위주로 전환한다는 공지 사항을 남기면 추후 버전 상한선 조정 시 참고할 수 있습니다.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@docs/doctoring/dependency-security-baseline.md` around lines 98 - 133, Update the references section around the Python Packaging Authority entries to add a citation or note documenting mkdocs-material’s transition from new feature releases to maintenance-focused support after this upgrade. Preserve the existing verified references and include enough publication or retrieval information for the note to guide future version upper-bound adjustments.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@docs/doctoring/dependency-security-baseline.md`:
- Around line 98-133: Update the references section around the Python Packaging
Authority entries to add a citation or note documenting mkdocs-material’s
transition from new feature releases to maintenance-focused support after this
upgrade. Preserve the existing verified references and include enough
publication or retrieval information for the note to guide future version
upper-bound adjustments.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 8e0e7296-33b4-4a79-a8e8-fb18ee509f87
⛔ Files ignored due to path filters (1)
uv.lockis excluded by!**/*.lock
📒 Files selected for processing (4)
CHANGELOG.mddocs/doctoring/dependency-security-baseline.mdpyproject.tomltests/test_project_metadata.py
🚧 Files skipped from review as they are similar to previous changes (3)
- CHANGELOG.md
- tests/test_project_metadata.py
- pyproject.toml
|
@opencode-agent @cwl-noema-review Please independently review exact current head @coderabbitai review |
🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
repo='ContextualWisdomLab/newsdom-api'
pr='467'
sha='58d8544ffa3ec105bc9e08e594b53fd46c67a778'
echo '== Requested commit and PR head =='
git rev-parse HEAD
git show -s --format='commit=%H%nparents=%P%nsubject=%s' "$sha"
gh api "repos/$repo/pulls/$pr" --jq \
'{number, state, head_sha: .head.sha, base_sha: .base.sha, changed_files, mergeable_state}'
echo '== Changed files in requested commit =='
git diff-tree --no-commit-id --name-status -r "$sha"
echo '== Diff from first parent =='
git diff --no-ext-diff "${sha}^" "$sha" -- \
pyproject.toml uv.lock CONTRIBUTING.md CHANGELOG.md \
tests/test_project_metadata.py docs/doctoring/dependency-security-baseline.md
echo '== Dependency declarations and regression assertions =='
rg -n -C 3 -i \
'pillow|pypdf|click|setuptools|pymdown-extensions|mkdocs-material|mkdocs.*<\s*2|cve-|pip-audit|trivy' \
pyproject.toml CONTRIBUTING.md CHANGELOG.md \
tests/test_project_metadata.py docs/doctoring/dependency-security-baseline.md
echo '== Resolved selected packages =='
python3 - <<'PY'
import tomllib
from pathlib import Path
lock = tomllib.loads(Path("uv.lock").read_text())
wanted = {
"click", "mkdocs", "mkdocs-material", "pillow", "pypdf",
"pymdown-extensions", "setuptools",
}
for package in lock.get("package", []):
if package.get("name") in wanted:
print(f'{package["name"]}=={package.get("version")} source={package.get("source")}')
for dependency in package.get("dependencies", []):
if isinstance(dependency, dict) and dependency.get("name") in wanted:
print(f' dependency={dependency["name"]}')
PY
echo '== Temporary artifact and refresh-marker search =='
rg -n -i \
'refresh marker|refresh-marker|one-shot|temporary workflow|dependency.*refresh' \
.github pyproject.toml uv.lock CONTRIBUTING.md CHANGELOG.md \
tests docs/doctoring 2>/dev/null || true
fd -a -t f .github | sort
echo '== Exact-head check runs =='
gh api "repos/$repo/commits/$sha/check-runs?per_page=100" --paginate --jq \
'.check_runs[] | [.name, .status, .conclusion, .details_url] | `@tsv`' | sort
echo '== Exact-head legacy statuses =='
gh api "repos/$repo/commits/$sha/status" --jq \
'.statuses[]? | [.context, .state, .target_url] | `@tsv`' | sortLength of output: 16173 🏁 Script executed: #!/bin/bash
set -euo pipefail
repo='ContextualWisdomLab/newsdom-api'
pr='467'
sha='58d8544ffa3ec105bc9e08e594b53fd46c67a778'
base='04491c0e9ac38b9f793029683cebfb8210ccfadd'
echo '== Full PR file list and aggregate diff stat =='
git diff --name-status "$base" "$sha"
git diff --stat "$base" "$sha"
echo '== Full PR diff for the six declared files =='
git diff --no-ext-diff "$base" "$sha" -- \
pyproject.toml uv.lock CONTRIBUTING.md CHANGELOG.md \
tests/test_project_metadata.py docs/doctoring/dependency-security-baseline.md
echo '== Locked package stanzas =='
for package in click mkdocs mkdocs-material pillow pypdf pymdown-extensions setuptools; do
echo "--- $package ---"
awk -v package="$package" '
/^\[\[package\]\]/ {
if (printing) exit
printing=0
}
$0 == "name = \"" package "\"" { printing=1 }
printing { print }
' uv.lock
done
echo '== Head-tree checks for removed temporary artifact =='
if git cat-file -e "${sha}:.github/review-refresh.txt" 2>/dev/null; then
echo 'ERROR: .github/review-refresh.txt exists at the requested head'
else
echo 'OK: .github/review-refresh.txt is absent at the requested head'
fi
git ls-tree -r --name-only "$sha" .github | sort
git grep -n -i -E \
'review-refresh|refresh marker|refresh-marker|one-shot workflow|temporary workflow' \
"$sha" -- .github pyproject.toml uv.lock CONTRIBUTING.md CHANGELOG.md tests docs \
|| true
echo '== Exact-head check runs =='
gh api "repos/$repo/commits/$sha/check-runs?per_page=100" --paginate --jq \
'.check_runs[] | [.name, .status, .conclusion, .details_url] | `@tsv`' | sort
echo '== Exact-head legacy statuses =='
gh api "repos/$repo/commits/$sha/status" --jq \
'.statuses[]? | [.context, .state, .target_url] | `@tsv`' | sort
echo '== PR review decision and mergeability fields =='
gh api "repos/$repo/pulls/$pr/reviews?per_page=100" --paginate --jq \
'.[] | [.user.login, .state, .commit_id, .submitted_at] | `@tsv`' | tail -n 30
gh api "repos/$repo/pulls/$pr" --jq \
'{mergeable, mergeable_state, rebaseable, draft, head_sha: .head.sha, base_sha: .base.sha}'Length of output: 84286
The PR changes only the six declared dependency, lock, test, and documentation files. It does not change application source or API behavior. The direct constraints and lock entries agree:
The metadata tests guard the direct security floors, the docs dependency boundary, and the patched I cannot confirm the requested exact-head test, strict-docs, coverage, docstring, and security evidence yet. The relevant checks are still No change is required from the reviewed diff. Complete the queued checks before treating the verification contract as satisfied. ✅ Action performedReview finished.
|
There was a problem hiding this comment.
Pull request overview
OpenCode cannot approve yet because required coverage evidence did not pass.
Review outcome
1. HIGH .github/workflows/opencode-review.yml:1 - Coverage evidence did not prove required test/docstring evidence
-
Problem: The required coverage-evidence job result was
failure, so OpenCode cannot establish approval sufficiency for this head. -
Root cause: Automated approval is only valid when the same-head coverage-evidence job proves supported repository test suites passed and configured docstring gates passed or were advisory, or reports not applicable because no supported source files or package manifests exist. Missing, failed, skipped, unavailable, or unsupported-tooling test evidence is a blocker.
-
Fix: Install or configure the repository test/docstring evidence tooling when source files or package manifests exist, rerun the current-head coverage-evidence job, and approve only after it reports
successwith required evidence or explicit no-source not-applicable evidence. -
Regression test: Keep the approval branch checking
needs.coverage-evidence.result == successbefore posting APPROVE, and publish REQUEST_CHANGES when coverage-evidence blocker states such as cancelled, skipped, failed, unsupported-tooling, or below-100 evidence are present. -
Result: REQUEST_CHANGES
-
Reason: coverage-evidence result was
failure, so required test/docstring evidence was not proven for current head58d8544ffa3ec105bc9e08e594b53fd46c67a778. -
Head SHA:
58d8544ffa3ec105bc9e08e594b53fd46c67a778 -
Workflow run: 30908707737
-
Workflow attempt: 1
Coverage evidence
Coverage Decision
- Result: FAIL
- Test evidence: not proven passing
- Docstring evidence: not proven passing when configured
- Failure count: 1
Changed-File Evidence Map
flowchart LR
PR["PR changed files"] --> Evidence["OpenCode bounded evidence"]
Evidence --> S1["Changed file (4 files)"]
S1 --> I1["repository behavior"]
I1 --> R1["Review risk: Changed file (4 files)"]
R1 --> V1["required checks"]
Evidence --> S2["Docs: dependency-security-baseline.md"]
S2 --> I2["operator or user guidance"]
I2 --> R2["Review risk: Docs: dependency-security-baseline.md"]
R2 --> V2["docs review"]
Evidence --> S3["Test: test_project_metadata.py"]
S3 --> I3["regression suite"]
I3 --> R3["Review risk: Test: test_project_metadata.py"]
R3 --> V3["targeted test run"]
Summary
Supply-chain remediation across the NewsDOM runtime, build, and documentation dependency graph. This PR clears the repository’s active
pip-auditandtrivy-fsfindings and records patched lower bounds so a later lock regeneration cannot resolve back into the vulnerable ranges.Patched dependency set
Regression-resistant floors
Pillow>=12.3,<13.0andpypdf>=6.14.2,<7.0;setuptools>=83;mkdocs>=1.6,<2.0, while the theme is limited tomkdocs-material>=9.7,<9.8;CHANGELOG.md,CONTRIBUTING.md, and metadata tests describe the security rationale and reversible docs-toolchain boundary.Verification
Locally reconstructed exact-branch source after the floor changes:
PYTHONPATH=src:. python3 -m pytest -q tests/test_project_metadata.py→ 15 passed;PYTHONPATH=src:. python3 -m pytest -q→ 431 passed.The current GitHub HEAD must still pass the repository CI, 100% statement/branch coverage, quality gate, CodeQL, Semgrep, dependency review, container validation, ClusterFuzzLite, Scorecard, the repaired central OpenCode coverage path, and independent approval before merge.
Scope
Changed files are limited to dependency policy, the generated lock, documentation, and metadata regression coverage. No application source or API contract changes.
Supersession
This PR now contains the useful direct-floor protection from stacked PR #517; #517 is superseded and should not merge independently.
Git Flow target
fix/*→develop.Summary by CodeRabbit
보안 및 유지보수
문서