Skip to content

fix(deps): raise parser and TestClient security floors - #822

Draft
seonghobae wants to merge 48 commits into
developfrom
sentinel/fix-dos-risk-in-form-fields-10499323731269682757
Draft

fix(deps): raise parser and TestClient security floors#822
seonghobae wants to merge 48 commits into
developfrom
sentinel/fix-dos-risk-in-form-fields-10499323731269682757

Conversation

@seonghobae

@seonghobae seonghobae commented Sep 7, 2026

Copy link
Copy Markdown
Collaborator

문제와 current dependency authority

이 lane은 repository-wide dependency-security foundation입니다. Protected develop539528f9667524f6b65de0ee7b8b21fbdd97c380, current exact head는 **ce66ad092c14f6002e67c7b30ea5742a4968b5c1**이며 Draft/open/mergeable입니다. Fresh compare는 behind 0, effective files 8개입니다.

현재 선언/lock contract:

  • pypdf>=6.16.1,<7.0, resolved 6.18.0;
  • httpx2>=2.12.0, resolved 2.12.0;
  • httpcore2>=2.12.0, resolved 2.12.0;
  • plain httpx>=0.28,<1.0은 기존 dev consumer와 별도이며 이 lane이 임의 제거하지 않습니다.

Owner paths는 pyproject.toml, uv.lock, focused dependency regressions, CHANGELOG.md, dependency-security doctoring, docs/product-technical-gap-baseline.md로 한정됩니다. Auth/parser/product source는 이 dependency owner에 섞지 않습니다.

RED → repair lineage

Protected tree의 known-vulnerable lock을 causal RED로 사용했습니다. pypdf advisory floor에 더해 HTTPX2 family의 strongest current patched floor가 2.12.0이므로 direct dev floor와 resolver-generated lock을 그 경계로 올렸습니다. Historical form/auth/generated-doctrine churn은 ordinary descendants에서 제거했습니다.

Fresh exact-head source confirms direct floors above, and current lock resolves pypdf 6.18.0 / HTTPX2 2.12.0. Security Scan run 34666601147 independently distinguishes dependency payload from central evidence availability:

  • OSV 103479657369: SUCCESS;
  • Trivy fs 103479657440: SUCCESS;
  • Scorecard 103479657384: SUCCESS;
  • dependency-review 103479657430: FAILURE only at Check dependency review support; the pinned review action is skipped because the authoritative GitHub dependency comparison is unavailable. This exact canary was delivered to central availability owner .github#810; no leaf shim or skip-as-success is allowed.

Thus there is no current Trivy/OSV evidence requiring a pypdf 6.18.1 or another dependency bump on this head. Scanner-clean results are not promoted as substitutes for Dependency Review.

Remaining exact-head blockers

  • Repository tests 34666601138, SAST 34666601121, container 34666601149, scorecards 34666601108, ClusterFuzzLite 34666601125 are terminal GREEN.
  • Security Scan is non-passing solely at central dependency-review support as above.
  • Repository CodeQL run 34666601100, job 103479623278, is terminal FAILURE, but fresh log RCA shows this is not a source-analysis finding. Python/Actions extraction and query execution completed; SARIF combine/fingerprinting reached publication, then GitHub returned API rate limit exceeded for installation at 2026-09-12T02:06:32Z (request C040:201F70:A6D086:21BD9F2:6AA4B3A8). Earlier telemetry/cache API calls in the same job also hit the installation quota. Exact evidence and RED/GREEN acceptance were delivered to central owner .github#1929. The leaf must still remain non-passing until authoritative SARIF publication succeeds; no waiver or synthetic GREEN is implied.
  • Required CodeQL PR 34666601172 is terminal FAILURE; Python/actions compatibility receivers fail and the later same-run dispatch itself also fails. That is a distinct central control-plane path and remains owner work; this leaf does not no-op retrigger, synthesize status, or copy central workflow source.
  • No qualifying current-head independent approval is claimed.

Keep Draft. Normal merge requires one unchanged exact head with the dependency contracts above, focused/full tests and owned coverage/docstrings, authoritative Dependency Review, SAST/security/repository+required CodeQL, container/fuzz/scorecard, zero valid unresolved findings, and independent current-head review all terminally acceptable.

After normal protected merge, dependent auth/parser/export lanes adopt the new protected base by ordinary non-force descendants and reacquire exact-head evidence; mutable #822 head is never a consumer dependency.

No force push, destructive rebase, source-neutral/no-op retrigger, self-approval, bypass/gate weakening, scanner substitution, mutable sibling source copy, or predecessor evidence transfer.

Traceability

🚨 Severity: MEDIUM
💡 Vulnerability: /parse 엔드포인트의 language 및 mode Form 필드에 최대 길이(max_length) 제한이 설정되어 있지 않았습니다.
🎯 Impact: python-multipart가 폼 데이터를 메모리에 로드하므로, 악의적인 사용자가 매우 긴 문자열을 전송하여 메모리 고갈(OOM)을 통한 서비스 거부(DoS) 공격을 유발할 수 있습니다.
🔧 Fix: FastAPI의 Form 필드에 max_length=50 제약 조건을 명시적으로 추가했습니다.
✅ Verification: 비정상적으로 긴 폼 데이터를 전송했을 때 HTTP 422 응답이 반환되는지 확인하는 테스트 코드를 추가하고 테스트 스위트를 통과했습니다.
@google-labs-jules

Copy link
Copy Markdown

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@coderabbitai

coderabbitai Bot commented Sep 7, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

pypdf 최소 버전이 6.16.1로 상향되었다. 잠금 버전과 세 보안 권고를 문서와 테스트에 반영했다. 프로젝트 메타데이터 및 보안 기준 검증도 새 버전에 맞춰 갱신했다.

Changes

의존성 보안 기준

Layer / File(s) Summary
보안 기준과 검증 계약
CHANGELOG.md, docs/doctoring/dependency-security-baseline.md, docs/product-technical-gap-baseline.md
pypdf 기준을 >=6.16.1,<7.0으로 변경하고, 해결 버전을 6.18.0으로 갱신했다. 세 CVE와 GHSA 식별자, SSDF 근거 및 검증 조건을 문서화했다.
의존성 기준과 보안 테스트
pyproject.toml, tests/test_project_metadata.py, tests/test_pypdf_security_floor.py
pypdf 선언과 테스트 기준을 6.16.1에 맞췄다. 세 CVE/GHSA 쌍의 비억제 및 문서 기록을 검증한다.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

Merge Risk: 🔵 Low · up to f0f47

This change raises the pypdf security floor and records the patched lock version, but its release documentation identifies an older candidate commit and some regression tests could allow security-documentation or lock-floor regressions. Update the SHA and test assertions before merging.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 85.71% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 7 functions across 2 files. (4 skipped: 4 u…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed 제목은 의존성 보안 기준 상향이라는 변경 목적과 관련됩니다. 그러나 실제 핵심 변경인 pypdf 보안 기준 상향을 명시하지 않고 TestClient 변경을 언급하므로 정확성이 일부 부족합니다.
Description check ✅ Passed 설명은 변경 목적, 의존성 기준, 검증 상태, 차단 항목, 후속 작업을 충분히 설명합니다. 템플릿의 명시적 Git Flow target, Verification, Notes 제목과 체크박스 형식은 사용하지 않았지만 필요한 정보는 대부분 포함합니다.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch sentinel/fix-dos-risk-in-form-fields-10499323731269682757

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

seonghobae and others added 5 commits September 7, 2026 21:17
🚨 Severity: MEDIUM
💡 Vulnerability: /parse 엔드포인트의 language 및 mode Form 필드에 최대 길이(max_length) 제한이 설정되어 있지 않았습니다.
🎯 Impact: python-multipart가 폼 데이터를 메모리에 로드하므로, 악의적인 사용자가 매우 긴 문자열을 전송하여 메모리 고갈(OOM)을 통한 서비스 거부(DoS) 공격을 유발할 수 있습니다.
🔧 Fix: FastAPI의 Form 필드에 max_length=50 제약 조건을 명시적으로 추가했습니다.
✅ Verification: 비정상적으로 긴 폼 데이터를 전송했을 때 HTTP 422 응답이 반환되는지 확인하는 테스트 코드를 추가하고 테스트 스위트를 통과했습니다.
@seonghobae seonghobae changed the title 🛡️ Sentinel: [MEDIUM] Fix 폼 필드 길이 제한 누락 취약점 fix(deps): lock pypdf 6.18.0 and enforce current security floor Sep 7, 2026
@seonghobae
seonghobae marked this pull request as draft September 7, 2026 21:49
seonghobae and others added 8 commits September 7, 2026 22:10
🚨 Severity: MEDIUM
💡 Vulnerability: /parse 엔드포인트의 language 및 mode Form 필드에 최대 길이(max_length) 제한이 설정되어 있지 않았습니다.
🎯 Impact: python-multipart가 폼 데이터를 메모리에 로드하므로, 악의적인 사용자가 매우 긴 문자열을 전송하여 메모리 고갈(OOM)을 통한 서비스 거부(DoS) 공격을 유발할 수 있습니다.
🔧 Fix: FastAPI의 Form 필드에 max_length=50 제약 조건을 명시적으로 추가했습니다.
✅ Verification: 비정상적으로 긴 폼 데이터를 전송했을 때 HTTP 422 응답이 반환되는지 확인하는 테스트 코드를 추가하고 테스트 스위트를 통과했습니다.
🚨 Severity: MEDIUM
💡 Vulnerability: /parse 엔드포인트의 language 및 mode Form 필드에 최대 길이(max_length) 제한이 설정되어 있지 않았습니다.
🎯 Impact: python-multipart가 폼 데이터를 메모리에 로드하므로, 악의적인 사용자가 매우 긴 문자열을 전송하여 메모리 고갈(OOM)을 통한 서비스 거부(DoS) 공격을 유발할 수 있습니다.
🔧 Fix: FastAPI의 Form 필드에 max_length=50 제약 조건을 명시적으로 추가했습니다.
✅ Verification: 비정상적으로 긴 폼 데이터를 전송했을 때 HTTP 422 응답이 반환되는지 확인하는 테스트 코드를 추가하고 테스트 스위트를 통과했습니다.

Copy link
Copy Markdown
Collaborator Author

@jules Current exact 0062273e660f6d0ef0ae7e74fe02b3e03f4d3ad8 now has a stricter supply-chain RED and the unrelated Form/OOM source/test/Sentinel delta has been removed by ordinary descendants. Please continue from this exact head; inspect any intervening child before writing and do not force-push/rebase.

RCA: the declared pypdf>=6.16.0,<7.0 floor is insufficient for the three current findings named by this PR. Upstream py-pdf advisories establish: GHSA-jp53-mhqp-8xcg is patched in 6.16.0, while GHSA-23w6-3w8w-8484 and GHSA-763m-79hh-57f2 are affected <6.16.1 and patched >=6.16.1. Current lock resolves 6.18.0, but its editable-project metadata still records pypdf>=6.16.0,<7.0, so a later lock refresh can legally select 6.16.0. The current tests/test_pypdf_security_floor.py intentionally REDs that mismatch and also requires all three findings to remain unsuppressed.

Minimal causal GREEN:

  1. Regenerate uv.lock from current pyproject.toml so project metadata is pypdf>=6.16.1,<7.0; keep a hash-locked resolved pypdf >=6.16.1 (current 6.18.0 is acceptable if regeneration retains it).
  2. Update CHANGELOG.md and docs/doctoring/dependency-security-baseline.md (and any product technical baseline that states the old floor) to distinguish 6.16.0's GHSA-jp53 fix from the two 6.16.1 fixes. Current doctoring text is stale: it says the direct floor is 6.16.0, discusses only two Aug findings, and even says the floor/lock move to 6.15.0. Remove those contradictions rather than adding exceptions.
  3. Preserve the restored protected route/endpoint-test/Sentinel blobs; do not reintroduce the unsupported multipart Form(max_length=...) OOM claim.
  4. Exact descendant must make tests/test_pypdf_security_floor.py GREEN, then run the full locked test/100%-coverage/package/docs/security set. Security Scan must show no CVE-2026-84309/84310/84311 finding and no suppression. Keep Draft until all current-head lanes and independent review are terminal; no predecessor receipts or synthetic status.

Primary upstream references:

NIST currently still lists SP 800-218 Rev.1 / SSDF 1.2 as Draft (published 2025-12-17), so the doctoring record may keep SSDF 1.1 as normative and 1.2 as informative, but should use the current publication date/status rather than the older 2025 wording.

seonghobae and others added 7 commits September 8, 2026 10:48
🚨 Severity: MEDIUM
💡 Vulnerability: /parse 엔드포인트의 language 및 mode Form 필드에 최대 길이(max_length) 제한이 설정되어 있지 않았습니다.
🎯 Impact: python-multipart가 폼 데이터를 메모리에 로드하므로, 악의적인 사용자가 매우 긴 문자열을 전송하여 메모리 고갈(OOM)을 통한 서비스 거부(DoS) 공격을 유발할 수 있습니다.
🔧 Fix: FastAPI의 Form 필드에 max_length=50 제약 조건을 명시적으로 추가했습니다.
✅ Verification: 비정상적으로 긴 폼 데이터를 전송했을 때 HTTP 422 응답이 반환되는지 확인하는 테스트 코드를 추가하고 테스트 스위트를 통과했습니다.
🚨 Severity: MEDIUM
💡 Vulnerability: /parse 엔드포인트의 language 및 mode Form 필드에 최대 길이(max_length) 제한이 설정되어 있지 않았습니다.
🎯 Impact: python-multipart가 폼 데이터를 메모리에 로드하므로, 악의적인 사용자가 매우 긴 문자열을 전송하여 메모리 고갈(OOM)을 통한 서비스 거부(DoS) 공격을 유발할 수 있습니다.
🔧 Fix: FastAPI의 Form 필드에 max_length=50 제약 조건을 명시적으로 추가했습니다.
✅ Verification: 비정상적으로 긴 폼 데이터를 전송했을 때 HTTP 422 응답이 반환되는지 확인하는 테스트 코드를 추가하고 테스트 스위트를 통과했습니다.
@seonghobae seonghobae added bug Something isn't working priority: high High-priority or P1 work labels Sep 8, 2026 — with ChatGPT Codex Connector
seonghobae and others added 2 commits September 11, 2026 00:57
🚨 Severity: MEDIUM
💡 Vulnerability: /parse 엔드포인트의 language 및 mode Form 필드에 최대 길이(max_length) 제한이 설정되어 있지 않았습니다.
🎯 Impact: python-multipart가 폼 데이터를 메모리에 로드하므로, 악의적인 사용자가 매우 긴 문자열을 전송하여 메모리 고갈(OOM)을 통한 서비스 거부(DoS) 공격을 유발할 수 있습니다.
🔧 Fix: FastAPI의 Form 필드에 max_length=50 제약 조건을 명시적으로 추가했습니다.
✅ Verification: 비정상적으로 긴 폼 데이터를 전송했을 때 HTTP 422 응답이 반환되는지 확인하는 테스트 코드를 추가하고 테스트 스위트를 통과했습니다.

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

현재 exact head ac0129a2affcfea3a5c782035527502566016f7c만 다시 검토해 주세요. 직전 41f9f747...는 pypdf floor/evidence 약화, baseline 삭제, unrelated multipart Form/Sentinel delta를 섞어 ordinary descendant에서 canonical tree로 복구했습니다. 특히 pypdf>=6.16.1,<7.0 + lock 6.18.0, 세 CVE no-suppression contract, canonical gap baseline 복원, unrelated /parse source/test delta 제거를 확인해 주세요.

@coderabbitai

coderabbitai Bot commented Sep 11, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

expected='ac0129a2affcfea3a5c782035527502566016f7c'
echo '== Repository and requested-head identity =='
printf 'HEAD='
git rev-parse HEAD
printf 'expected=%s\n' "$expected"
git status --short
gh pr view 822 --json number,state,isDraft,headRefOid,baseRefName,headRefName,url
echo

echo '== Merge base and changed paths =='
base_ref="$(gh pr view 822 --json baseRefName --jq .baseRefName)"
base="$(git merge-base HEAD "origin/$base_ref")"
printf 'merge-base=%s\n' "$base"
git diff --name-status "$base...HEAD"
echo

echo '== Dependency declarations, lock metadata, tests, and documentation diffs =='
git diff --unified=30 "$base...HEAD" -- \
  pyproject.toml uv.lock \
  tests/test_project_metadata.py tests/test_pypdf_security_floor.py \
  CHANGELOG.md docs/doctoring/dependency-security-baseline.md \
  docs/product-technical-gap-baseline.md \
  src/newsdom_api/main.py tests/test_parse_endpoint.py .jules/sentinel.md
echo

echo '== Current required dependency and advisory evidence =='
for file in \
  pyproject.toml uv.lock \
  tests/test_project_metadata.py tests/test_pypdf_security_floor.py \
  CHANGELOG.md docs/doctoring/dependency-security-baseline.md \
  docs/product-technical-gap-baseline.md
do
  echo "--- $file"
  rg -n -i -C 3 \
    'pypdf|6\.15\.0|6\.16\.0|6\.16\.1|6\.18\.0|CVE-2026-84309|CVE-2026-84310|CVE-2026-84311|GHSA-jp53-mhqp-8xcg|GHSA-23w6-3w8w-8484|GHSA-763m-79hh-57f2|suppress|SSDF|Current authority|candidate|commit' \
    "$file" || true
done
echo

echo '== Unrelated Form/Sentinel evidence at requested head =='
rg -n -i -C 3 'Form\s*\(\s*max_length\s*=|multipart.*OOM|parser.*OOM|form-admission' \
  src/newsdom_api/main.py tests/test_parse_endpoint.py .jules/sentinel.md 2>/dev/null || true
echo

echo '== Diff against protected base for unrelated paths =='
git diff --unified=20 "$base...HEAD" -- \
  src/newsdom_api/main.py tests/test_parse_endpoint.py .jules/sentinel.md
echo

echo '== Current PR checks =='
gh pr checks 822 --json name,state,workflow,link 2>&1 || true
echo

echo '== Unresolved review threads =='
gh api graphql -F owner='ContextualWisdomLab' -F name='newsdom-api' -F number='822' -f query='
query($owner: String!, $name: String!, $number: Int!) {
  repository(owner: $owner, name: $name) {
    pullRequest(number: $number) {
      reviewThreads(first: 100) {
        nodes {
          isResolved
          path
          line
          comments(first: 1) { nodes { author { login } body } }
        }
      }
    }
  }
}' --jq '.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false) | {path, line, author: .comments.nodes[0].author.login, body: .comments.nodes[0].body}' 2>&1 || true

Length of output: 83653


@seonghobae 검토 대상은 exact head ac0129a2affcfea3a5c782035527502566016f7c입니다.

유효한 finding이 1건 있습니다.

[P1] Product baseline에 현재 exact candidate SHA를 기록하세요 — docs/product-technical-gap-baseline.md Line 9

Line 9는 8595855f358009583d396a8908e290c9e863feb7를 “before this document update”로만 기록합니다. 이 표현은 이전 후보임을 일부 나타내지만, 현재 승인 및 검사 증거가 연결되어야 할 exact candidate를 식별하지 않습니다. 이 문서는 current authority와 exact-head GREEN acceptance를 정의합니다.

ac0129a2affcfea3a5c782035527502566016f7c를 현재 pypdf candidate로 기록하세요. 이전 SHA를 유지해야 하면 predecessor 또는 repair lineage로 명시하세요.

나머지 요청 범위는 확인했습니다.

  • pyproject.tomluv.lock editable-project metadata는 pypdf>=6.16.1,<7.0입니다.
  • uv.lock은 해시와 함께 pypdf 6.18.0을 resolve합니다.
  • tests/test_pypdf_security_floor.py는 세 CVE/GHSA와 no-suppression 계약을 확인합니다.
  • CHANGELOG.md, dependency baseline, product baseline은 6.16.0 및 6.16.1 advisory boundary를 설명합니다.
  • src/newsdom_api/main.py, tests/test_parse_endpoint.py, .jules/sentinel.md에는 unrelated Form(max_length=...) 또는 parser OOM claim delta가 없습니다.

현재 GREEN acceptance는 아직 성립하지 않습니다. Security Scan의 trivy-fs가 실패했습니다. CodeQL, SAST, container, fuzz, Noema, Strix 검사도 아직 진행 중입니다.

You are interacting with an AI system.

Copy link
Copy Markdown
Collaborator Author

Fresh owner split after exact-head Security run 34626571301: trivy-fs is still FAILURE on ac0129a2affcfea3a5c782035527502566016f7c, while Dependency Review/OSV base+head comparison are GREEN. The public job metadata/artifacts expose only osv-scan-debug, not the Trivy finding text, so I am not guessing the remaining CVE/package.

Separately, protected ancestry still has the verified unused httpx2 gap. Closed-unmerged #518 carried a valid two-file removal but its branch is gone and the delta never reached protected ancestry. I opened canonical successor issue #843 for a resolver-generated removal after #822 is normally integrated. Do not fold #843 into this pypdf PR or claim httpx2 caused the current Trivy failure until the Print Trivy findings that failed the gate/SARIF evidence says so.

#822 stays Draft: obtain the exact Trivy finding, repair it in its causal owner if needed, and keep the pypdf floor/lock contract unchanged unless authoritative advisory evidence requires otherwise.

🚨 Severity: MEDIUM
💡 Vulnerability: /parse 엔드포인트의 language 및 mode Form 필드에 최대 길이(max_length) 제한이 설정되어 있지 않았습니다.
🎯 Impact: python-multipart가 폼 데이터를 메모리에 로드하므로, 악의적인 사용자가 매우 긴 문자열을 전송하여 메모리 고갈(OOM)을 통한 서비스 거부(DoS) 공격을 유발할 수 있습니다.
🔧 Fix: FastAPI의 Form 필드에 max_length=50 제약 조건을 명시적으로 추가했습니다.
✅ Verification: 비정상적으로 긴 폼 데이터를 전송했을 때 HTTP 422 응답이 반환되는지 확인하는 테스트 코드를 추가하고 테스트 스위트를 통과했습니다.

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

Current exact head is d9d48ec43918931e995fc1a8bacac3ddc1d568f6. Please review this exact generation after the ordinary-forward canonical-tree repair. Effective protected-base diff is seven pypdf security/traceability paths only; form-field changes, generated Sentinel doctrine, and httpx2/httpcore2 dependency churn were removed from this owner.

@coderabbitai

coderabbitai Bot commented Sep 12, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

expected='d9d48ec43918931e995fc1a8bacac3ddc1d568f6'

echo '== Requested head identity and PR metadata =='
printf 'HEAD='
git rev-parse HEAD
printf 'expected=%s\n' "$expected"
git status --short
gh pr view 822 --json number,state,isDraft,headRefOid,baseRefName,headRefName,url
echo

echo '== Protected-base diff paths =='
base_ref="$(gh pr view 822 --json baseRefName --jq .baseRefName)"
base="$(git merge-base HEAD "origin/$base_ref")"
printf 'merge-base=%s\n' "$base"
git diff --name-status "$base...HEAD"
echo

echo '== Dependency and evidence diff =='
git diff --unified=40 "$base...HEAD" -- \
  pyproject.toml \
  uv.lock \
  tests/test_project_metadata.py \
  tests/test_pypdf_security_floor.py \
  CHANGELOG.md \
  docs/doctoring/dependency-security-baseline.md \
  docs/product-technical-gap-baseline.md
echo

echo '== Current dependency, advisory, and candidate-head evidence =='
for file in \
  pyproject.toml \
  uv.lock \
  tests/test_project_metadata.py \
  tests/test_pypdf_security_floor.py \
  CHANGELOG.md \
  docs/doctoring/dependency-security-baseline.md \
  docs/product-technical-gap-baseline.md
do
  echo "--- $file"
  rg -n -i -C 3 \
    'pypdf|6\.15\.0|6\.16\.0|6\.16\.1|6\.18\.0|CVE-2026-84309|CVE-2026-84310|CVE-2026-84311|GHSA-jp53-mhqp-8xcg|GHSA-23w6-3w8w-8484|GHSA-763m-79hh-57f2|suppress|current authority|candidate|exact head|commit|SSDF' \
    "$file" || true
done
echo

echo '== Unrelated-path diff and current content checks =='
git diff --name-status "$base...HEAD" -- \
  src/newsdom_api/main.py \
  tests/test_parse_endpoint.py \
  .jules/sentinel.md || true
rg -n -i -C 3 \
  'Form\s*\(\s*max_length\s*=|multipart.*OOM|parser.*OOM|httpx2|httpcore2' \
  src/newsdom_api/main.py \
  tests/test_parse_endpoint.py \
  .jules/sentinel.md \
  pyproject.toml \
  uv.lock 2>/dev/null || true
echo

echo '== Current checks =='
gh pr checks 822 --json name,state,workflow,link 2>&1 || true
echo

echo '== Unresolved review threads =='
gh api graphql -F owner='ContextualWisdomLab' -F name='newsdom-api' -F number='822' -f query='
query($owner: String!, $name: String!, $number: Int!) {
  repository(owner: $owner, name: $name) {
    pullRequest(number: $number) {
      reviewThreads(first: 100) {
        nodes {
          isResolved
          path
          line
          comments(first: 1) {
            nodes { author { login } body }
          }
        }
      }
    }
  }
}' --jq '.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false) | {path, line, author: .comments.nodes[0].author.login, body: .comments.nodes[0].body}' 2>&1 || true

Length of output: 96892


@seonghobae 검토를 완료했습니다. 유효한 finding은 1건입니다.

[P1] 현재 pypdf candidate SHA를 명시하세요 — docs/product-technical-gap-baseline.md Line 9

Line 9는 8595855f358009583d396a8908e290c9e863feb7를 문서 갱신 전 candidate로만 기록합니다. 그러나 이 문서는 current authority와 exact-head GREEN acceptance를 정의합니다. 현재 PR head인 d9d48ec43918931e995fc1a8bacac3ddc1d568f6를 current pypdf candidate로 기록해야 합니다. 이전 SHA는 predecessor 또는 repair lineage로 유지할 수 있습니다.

이 식별자가 없으면 이전 candidate의 검사 결과가 현재 generation의 증거로 연결될 수 있습니다.

나머지 요청 범위는 확인했습니다.

  • protected develop 기준 diff는 7개 pypdf security/traceability 경로만 포함합니다.
  • pyproject.tomluv.lock editable metadata는 pypdf>=6.16.1,<7.0을 선언합니다.
  • uv.lock은 hash-locked pypdf 6.18.0을 resolve합니다.
  • 세 CVE/GHSA와 no-suppression 계약은 security-floor test 및 baseline 문서에 있습니다.
  • /parse form-field, Sentinel, httpx2/httpcore2 변경은 protected-base diff에 없습니다.

현재 GREEN acceptance는 아직 성립하지 않습니다. 이 exact head에서 trivy-fs가 실패했습니다. CodeQL, SAST, fuzz, container, Strix, Noema 검사도 아직 진행 중입니다.

You are interacting with an AI system.

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

Protected develop advanced through normal merge #759, so this lane was non-force restacked. Please review exact head c6153f707fd770a4e0191763d7b7681d47034d28 only. Fresh compare against develop@539528f9667524f6b65de0ee7b8b21fbdd97c380 is behind 0 with exactly seven pypdf security/traceability paths.

@coderabbitai

coderabbitai Bot commented Sep 12, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

expected='c6153f707fd770a4e0191763d7b7681d47034d28'
base_expected='539528f9667524f6b65de0ee7b8b21fbdd97c380'

echo '== exact-head identity =='
printf 'HEAD='; git rev-parse HEAD
printf 'expected=%s\n' "$expected"
printf 'develop expected=%s\n' "$base_expected"
git status --short
gh pr view 822 --json number,state,isDraft,headRefOid,baseRefName,headRefName,url
echo

echo '== ancestry and effective comparison =='
git merge-base --is-ancestor "$base_expected" HEAD && echo 'base_is_ancestor=true' || echo 'base_is_ancestor=false'
git rev-list --left-right --count "$base_expected...HEAD"
git diff --name-status "$base_expected...HEAD"
echo

echo '== changed dependency, tests, and evidence =='
git diff --unified=50 "$base_expected...HEAD" -- \
  pyproject.toml \
  uv.lock \
  tests/test_project_metadata.py \
  tests/test_pypdf_security_floor.py \
  CHANGELOG.md \
  docs/doctoring/dependency-security-baseline.md \
  docs/product-technical-gap-baseline.md
echo

echo '== exact evidence lines =='
for file in \
  pyproject.toml \
  uv.lock \
  tests/test_project_metadata.py \
  tests/test_pypdf_security_floor.py \
  CHANGELOG.md \
  docs/doctoring/dependency-security-baseline.md \
  docs/product-technical-gap-baseline.md
do
  echo "--- $file"
  rg -n -i -C 3 \
    'pypdf|6\.15\.0|6\.16\.0|6\.16\.1|6\.18\.0|CVE-2026-84309|CVE-2026-84310|CVE-2026-84311|GHSA-jp53-mhqp-8xcg|GHSA-23w6-3w8w-8484|GHSA-763m-79hh-57f2|suppress|current authority|candidate|exact head|commit|SSDF' \
    "$file" || true
done
echo

echo '== unrelated paths absent from comparison =='
git diff --name-status "$base_expected...HEAD" -- \
  src/newsdom_api/main.py \
  tests/test_parse_endpoint.py \
  .jules/sentinel.md || true
git diff --name-only "$base_expected...HEAD" | grep -E '(^|/)(main\.py|test_parse_endpoint\.py|sentinel\.md)$|httpx2|httpcore2' || true
echo

echo '== current check states =='
gh pr checks 822 --json name,state,workflow,link 2>&1 || true
echo

echo '== unresolved review threads =='
gh api graphql -F owner='ContextualWisdomLab' -F name='newsdom-api' -F number='822' -f query='
query($owner: String!, $name: String!, $number: Int!) {
  repository(owner: $owner, name: $name) {
    pullRequest(number: $number) {
      reviewThreads(first: 100) {
        nodes {
          isResolved
          path
          line
          comments(first: 1) {
            nodes { author { login } body }
          }
        }
      }
    }
  }
}' --jq '.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false) | {path, line, author: .comments.nodes[0].author.login, body: .comments.nodes[0].body}' 2>&1 || true

Length of output: 101292


@seonghobae 검토를 완료했습니다. 유효한 finding은 1건입니다.

[P1] Product baseline에 현재 pypdf candidate SHA를 명시하세요 — docs/product-technical-gap-baseline.md Line 9

Line 9는 8595855f358009583d396a8908e290c9e863feb7를 문서 갱신 전 candidate로만 기록합니다. 그러나 이 문서는 current authority와 exact-head GREEN acceptance를 정의합니다. 현재 검토한 PR head인 c6153f707fd770a4e0191763d7b7681d47034d28를 현재 pypdf candidate로 기록해야 합니다. 이전 SHA를 유지해야 하면 predecessor 또는 repair lineage로 명시하세요.

그 외 요청 범위는 확인했습니다.

  • develop@539528f9667524f6b65de0ee7b8b21fbdd97c380은 head의 조상입니다. 비교 결과는 behind 0, ahead 38입니다.
  • 비교 diff는 지정한 7개 pypdf security/traceability 경로만 변경합니다.
  • pyproject.tomluv.lock editable metadata는 pypdf>=6.16.1,<7.0을 선언합니다.
  • uv.lock은 해시가 있는 pypdf 6.18.0을 resolve합니다.
  • security-floor test는 세 CVE/GHSA 및 no-suppression 계약을 확인합니다.
  • /parse Form 변경, Sentinel, httpx2, httpcore2 변경은 이 비교에 없습니다.

현재 GREEN acceptance는 성립하지 않습니다. trivy-fs가 실패했습니다. CodeQL, Strix, Noema, container image, fuzz, OpenCode review도 아직 완료되지 않았습니다.

You are interacting with an AI system.

@seonghobae seonghobae changed the title fix(deps): lock pypdf 6.18.0 and enforce current security floor fix(deps): raise parser and TestClient security floors Sep 12, 2026

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

Please review exact head 21c8d4271106d7ed568a8183c7a5e82de7fbe56a only. Protected base is develop@539528f9667524f6b65de0ee7b8b21fbdd97c380; fresh compare is behind 0 with eight dependency-security/traceability paths. The current RED→repair sequence is the terminal Trivy failure at c6153f7..., focused HTTPX2 floor regression at 5b4f05c..., resolver-generated HTTPX2/httpcore2 2.12.0 lock adoption at 836a7ee..., followed only by code-current doctoring/CHANGELOG/gap updates. No form/auth/generated Sentinel source delta is in the effective diff.

@coderabbitai

coderabbitai Bot commented Sep 12, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

expected_head='21c8d4271106d7ed568a8183c7a5e82de7fbe56a'
expected_base='539528f9667524f6b65de0ee7b8b21fbdd97c380'

echo '== Exact commit identity =='
printf 'HEAD='; git rev-parse HEAD
printf 'expected_head=%s\n' "$expected_head"
printf 'expected_base=%s\n' "$expected_base"
gh pr view 822 --json number,state,isDraft,headRefOid,baseRefName,headRefName,url
echo

echo '== Ancestry and effective diff =='
git merge-base --is-ancestor "$expected_base" HEAD && echo 'base_is_ancestor=true' || echo 'base_is_ancestor=false'
git rev-list --left-right --count "$expected_base...HEAD"
git diff --name-status "$expected_base...HEAD"
echo

echo '== Targeted diff =='
git diff --unified=50 "$expected_base...HEAD" -- \
  pyproject.toml \
  uv.lock \
  tests/test_project_metadata.py \
  tests/test_pypdf_security_floor.py \
  CHANGELOG.md \
  docs/doctoring/dependency-security-baseline.md \
  docs/product-technical-gap-baseline.md \
  .trivyignore.yaml
echo

echo '== Dependency, advisory, and current-candidate evidence =='
for file in \
  pyproject.toml \
  uv.lock \
  tests/test_project_metadata.py \
  tests/test_pypdf_security_floor.py \
  CHANGELOG.md \
  docs/doctoring/dependency-security-baseline.md \
  docs/product-technical-gap-baseline.md \
  .trivyignore.yaml
do
  if [ -f "$file" ]; then
    echo "--- $file"
    rg -n -i -C 3 \
      'pypdf|httpx2|httpcore2|6\.15\.0|6\.16\.0|6\.16\.1|6\.18\.0|2\.12\.0|CVE-2026-84309|CVE-2026-84310|CVE-2026-84311|GHSA-jp53-mhqp-8xcg|GHSA-23w6-3w8w-8484|GHSA-763m-79hh-57f2|suppress|candidate|exact head|current authority|trivy|SSDF' \
      "$file" || true
  fi
done
echo

echo '== Check dependency declarations and lock package metadata =='
rg -n -C 4 'pypdf>=6\.16\.1,<7\.0|httpx2|httpcore2' pyproject.toml uv.lock || true
echo

echo '== Verify no unrelated form/auth/Sentinel changes =='
git diff --name-status "$expected_base...HEAD" -- \
  src/newsdom_api/main.py \
  tests/test_parse_endpoint.py \
  .jules/sentinel.md || true
git diff --name-only "$expected_base...HEAD" | \
  grep -E '(^|/)(main\.py|test_parse_endpoint\.py|sentinel\.md)$|form|auth' || true
rg -n -i -C 2 'Form\s*\(\s*max_length\s*=|multipart.*OOM|parser.*OOM' \
  src/newsdom_api/main.py tests/test_parse_endpoint.py .jules/sentinel.md 2>/dev/null || true
echo

echo '== Current PR checks =='
gh pr checks 822 --json name,state,workflow,link 2>&1 || true
echo

echo '== Unresolved review threads =='
gh api graphql -F owner='ContextualWisdomLab' -F name='newsdom-api' -F number='822' -f query='
query($owner: String!, $name: String!, $number: Int!) {
  repository(owner: $owner, name: $name) {
    pullRequest(number: $number) {
      reviewThreads(first: 100) {
        nodes {
          isResolved
          path
          line
          comments(first: 1) {
            nodes { author { login } body }
          }
        }
      }
    }
  }
}' --jq '.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false) | {path, line, author: .comments.nodes[0].author.login, body: .comments.nodes[0].body}' 2>&1 || true

Length of output: 134664


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== HTTPX2 security-floor test structure =='
ast-grep outline tests/test_httpx2_security_floor.py --items all
echo
nl -ba tests/test_httpx2_security_floor.py | sed -n '1,260p'
echo

echo '== HTTPX2 lock metadata and direct-dependency consistency =='
rg -n -C 5 \
  'name = "(httpx2|httpcore2)"|specifier = ">=2\.12\.0"|version = "2\.12\.0"|httpx2>=2\.12\.0|httpcore2>=2\.12\.0|CVE-2026-8437[8-9]|CVE-2026-8438[0-2]|GHSA-(f2fp-rgf2-35cp|h4x7-gw46-3wm6|pf96-p4fj-6566|7mj9-2mp8-4m2p|8xx6-hgc6-gc2m)' \
  pyproject.toml uv.lock tests/test_httpx2_security_floor.py CHANGELOG.md docs/doctoring/dependency-security-baseline.md docs/product-technical-gap-baseline.md .trivyignore.yaml || true
echo

echo '== Current candidate references and prior product-baseline values =='
git log --format='%H %s' --all -- docs/product-technical-gap-baseline.md | head -20
git show "$expected_head:docs/product-technical-gap-baseline.md" | nl -ba | sed -n '1,65p'
echo

echo '== Current diff for HTTPX2 regression test =='
git diff --unified=60 539528f9667524f6b65de0ee7b8b21fbdd97c380...HEAD -- tests/test_httpx2_security_floor.py

Length of output: 24752


@seonghobae 검토를 완료했습니다. Exact head는 21c8d4271106d7ed568a8183c7a5e82de7fbe56a입니다.

유효한 finding은 2건입니다.

  1. [P1] 현재 candidate SHA를 기록하세요 — docs/product-technical-gap-baseline.md Line 9

    Line 9는 0a888dabfa36adb1973894453f61f80fa72b7656만 “before this document update” candidate로 기록합니다. 이 문서는 exact-head GREEN acceptance를 정의합니다. 그러나 현재 candidate SHA를 식별하지 않습니다.

    21c8d4271106d7ed568a8183c7a5e82de7fbe56a를 current dependency-security candidate로 명시하세요. 이전 SHA는 predecessor 또는 repair-lineage SHA로 유지할 수 있습니다. 이 변경이 없으면 이전 generation의 검사 결과가 현재 generation의 증거로 연결될 수 있습니다.

  2. [P1] HTTPX2 lock metadata도 security floor로 검증하세요 — tests/test_httpx2_security_floor.py Line 21

    test_lock_resolves_httpx2_family_to_2_12_0()는 resolved package version만 확인합니다. 이 검사는 uv.lock의 editable-project requires-dist metadata를 확인하지 않습니다.

    따라서 향후 pyproject.tomlhttpx2>=2.12.0httpcore2>=2.12.0을 선언해도, lock metadata가 이전 floor를 유지하는 상태가 통과할 수 있습니다. pypdf test와 같이 두 requires-dist specifier를 직접 검증하세요. 이는 future lock refresh가 취약한 version range를 허용하지 않게 합니다.

확인한 범위는 다음과 같습니다.

  • develop@539528f9667524f6b65de0ee7b8b21fbdd97c380은 head의 조상입니다. 비교는 behind 0, ahead 43입니다.
  • effective diff는 8개 dependency-security/traceability path만 변경합니다.
  • pyproject.tomluv.lock metadata는 pypdf>=6.16.1,<7.0을 선언합니다.
  • lock은 hash-locked pypdf 6.18.0, httpx2 2.12.0, httpcore2 2.12.0을 resolve합니다.
  • pypdf CVE/GHSA와 no-suppression contract는 test와 baseline 문서에 있습니다.
  • form, auth, generated Sentinel source delta는 effective diff에 없습니다.
  • trivy-fs, OSV, pytest, Semgrep은 성공했습니다.

GREEN acceptance는 아직 성립하지 않습니다. CodeQL compatibility, container image, fuzz, Strix, Noema, 및 CodeQL 일부 lane이 아직 진행 중입니다.

You are interacting with an AI system.

seonghobae and others added 5 commits September 12, 2026 01:55
🚨 Severity: MEDIUM
💡 Vulnerability: /parse 엔드포인트의 language 및 mode Form 필드에 최대 길이(max_length) 제한이 설정되어 있지 않았습니다.
🎯 Impact: python-multipart가 폼 데이터를 메모리에 로드하므로, 악의적인 사용자가 매우 긴 문자열을 전송하여 메모리 고갈(OOM)을 통한 서비스 거부(DoS) 공격을 유발할 수 있습니다.
🔧 Fix: FastAPI의 Form 필드에 max_length=50 제약 조건을 명시적으로 추가했습니다.
✅ Verification: 비정상적으로 긴 폼 데이터를 전송했을 때 HTTP 422 응답이 반환되는지 확인하는 테스트 코드를 추가하고 테스트 스위트를 통과했습니다.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working priority: high High-priority or P1 work

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant