Skip to content

fix(security): remove unused vulnerable httpx2 dependency - #558

Draft
seonghobae wants to merge 3 commits into
mainfrom
security-remove-unused-httpx2-20260911
Draft

fix(security): remove unused vulnerable httpx2 dependency#558
seonghobae wants to merge 3 commits into
mainfrom
security-remove-unused-httpx2-20260911

Conversation

@seonghobae

@seonghobae seonghobae commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Finding

Protected main@47c6fd27de13b0da37a7db64697b869941909351 still carries unused httpx2==2.5.0. Exact Trivy evidence from the same protected dependency contract reported CVE-2026-84382 (HIGH) plus CVE-2026-84378/84379/84380 (MEDIUM). Repository source/test search found no httpx2 consumer contract, so retaining and upgrading an unused network client is broader than removing it.

Minimal causal repair

Current exact head: 21824f77e9ee4c26c42bb1f21f00eaa3ae21fb6d.

  • remove httpx2 from requirements.txt and the dev optional dependencies in pyproject.toml;
  • remove httpx2/httpcore2 and their now-unreachable lock attribution from requirements-lock.txt;
  • no production source, workflow, API, or runtime behavior change.

Fresh protected-base compare remains three dependency files only. #557 and security PR #562 must not duplicate or fork this dependency owner; consumers should adopt the immutable protected descendant after this lane is accepted.

Exact-head verification

On unchanged 21824f77e9ee4c26c42bb1f21f00eaa3ae21fb6d:

  • CI 34544999020: SUCCESS across the repository matrix;
  • Security Scan 34544998906: SUCCESS, so the Trivy gate that established the vulnerable dependency RED is GREEN after removal;
  • SAST Semgrep 34544998881: SUCCESS;
  • fuzz 34544998878: SUCCESS;
  • CodeQL PR 34544998904: FAILURE at the central settlement path, not a leaf finding. Python/actions compatibility consumers finished failure at 00:30:36Z / 00:30:32Z, while the same generation's authoritative dispatch job did not start until 00:38:05Z and then succeeded at 00:38:14Z. This is the existing producer/consumer ordering defect owned by .github; no leaf no-op commit, synthetic verdict, alternate scanner, or gate waiver is permitted.

Acceptance

Keep Draft until the unchanged exact head has the required central CodeQL receipt settled successfully and a qualifying independent current-head formal review confirms that no real httpx2 consumer was omitted. The repository CI/Security/SAST/fuzz GREEN above is current-head evidence, not permission to bypass the remaining gates.

If a real consumer is later found, do not preserve removal by force; redesign against a vendor-fixed released contract. No force push, destructive rebase, self-approval, bypass, gate weakening, scanner suppression, or predecessor-GREEN transfer.

@coderabbitai

coderabbitai Bot commented Sep 10, 2026

Copy link
Copy Markdown

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 3628d3c5-3c93-4228-b3d3-85218460bb70

📥 Commits

Reviewing files that changed from the base of the PR and between 47c6fd2 and 21824f7.

📒 Files selected for processing (3)
  • pyproject.toml
  • requirements-lock.txt
  • requirements.txt
💤 Files with no reviewable changes (3)
  • pyproject.toml
  • requirements-lock.txt
  • requirements.txt

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

개발 및 런타임 의존성에서 httpx2를 제거했습니다. 잠금 파일에서 httpx2, httpcore2와 관련 참조를 삭제했습니다. httpx==0.28.1mcp==1.28.1은 유지됩니다.

Changes

의존성 정리

Layer / File(s) Summary
직접 의존성 선언 정리
pyproject.toml, requirements.txt
개발 및 런타임 의존성에서 httpx2==2.5.0 항목을 제거했습니다.
잠금 파일 의존성 그래프 정리
requirements-lock.txt
httpx2, httpcore2 패키지 항목과 anyio, httpcore, idna, typing-extensions의 관련 참조를 제거했습니다.

Priority: ⬆️ High

Estimated code review effort: 1 (Trivial) | ~5 minutes

Merge Risk: ⚪ Minimal · up to 21824

The dependency removal has no supported unresolved merge-blocking risk in the available evidence.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 0…
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 사용되지 않는 취약한 httpx2 의존성을 제거하는 이번 변경의 주요 내용을 정확하고 간결하게 설명합니다.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch security-remove-unused-httpx2-20260911

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.

Copy link
Copy Markdown
Contributor Author

@jules Please continue this Draft from the live head, preserving the two existing commits. Regenerate requirements-lock.txt from the changed requirements.txt with the exact documented pip-compile command in the lock header; do not hand-edit hashes and do not reintroduce httpx2 unless repository code demonstrates an actual consumer. RED is the current lock still containing httpx2==2.5.0/httpcore2==2.5.0; GREEN is a reproducible lock with neither package, pip install --require-hashes -r requirements-lock.txt succeeding on Python 3.10/3.11/3.12, and exact-head CI + Trivy + SAST/CodeQL GREEN. If pip-compile changes unrelated pins, explain resolver causality and minimize churn rather than weakening gates.

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Sep 11, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

Current-head scope for re-review: 21824f77e9ee4c26c42bb1f21f00eaa3ae21fb6d, exactly the dependency-removal delta. Please specifically verify that removing httpx2/httpcore2 leaves no source/test/tooling consumer contract and that lock attribution is internally consistent. Do not treat the separately owned central CodeQL settlement failure as a source finding.

@coderabbitai

coderabbitai Bot commented Sep 11, 2026

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

target='21824f77e9ee4c26c42bb1f21f00eaa3ae21fb6d'
git rev-parse --verify "${target}^{commit}"
parent="$(git rev-parse "${target}^")"

printf '%s\n' '=== exact commit metadata ==='
git show -s --format='commit=%H%nparent=%P%nsubject=%s' "$target"

printf '%s\n' '=== exact dependency-removal diff ==='
git diff --no-ext-diff --unified=80 "$parent" "$target" -- \
  requirements.txt pyproject.toml requirements-lock.txt

printf '%s\n' '=== lockfile header and package blocks ==='
sed -n '1,80p' requirements-lock.txt
rg -n -i -C 3 '^(httpx2|httpcore2|httpx|httpcore)==|#.*\b(httpx2|httpcore2)\b|\b(httpx2|httpcore2)\b' \
  requirements-lock.txt requirements.txt pyproject.toml || true

printf '%s\n' '=== tracked references outside lock artifacts ==='
git grep -n -i -E '(^|[^[:alnum:]_])(httpx2|httpcore2)([^[:alnum:]_]|$)|from[[:space:]]+(httpx2|httpcore2)([[:space:].]|$)|import[[:space:]]+(httpx2|httpcore2)([[:space:],.]|$)' \
  "$target" -- \
  ':!requirements-lock.txt' ':!*.lock' ':!*.egg-info/*' || true

printf '%s\n' '=== dependency declarations and common import names ==='
git grep -n -i -E '(^|[^[:alnum:]_])(httpx2|httpcore2)([^[:alnum:]_]|$)' \
  "$target" -- \
  requirements.txt pyproject.toml setup.py setup.cfg tox.ini noxfile.py Makefile Dockerfile docker-compose.yml \
  '.github' 2>/dev/null || true

printf '%s\n' '=== direct packages and their lock attribution ==='
rg -n -A 12 -B 3 '^(httpx|httpcore|anyio|idna|typing-extensions)==' requirements-lock.txt

Length of output: 38199


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '=== declared dependency inputs ==='
cat -n requirements.txt
printf '%s\n' '--- pyproject dependency sections ---'
rg -n -A 80 -B 5 '^\[project(\.optional-dependencies)?\]|^(dependencies|dev)[[:space:]]*=' pyproject.toml

printf '%s\n' '=== empty lockfile attribution blocks ==='
python - <<'PY'
from pathlib import Path
import re

lines = Path("requirements-lock.txt").read_text().splitlines()
starts = [i for i, line in enumerate(lines) if re.match(r'^[A-Za-z0-9][A-Za-z0-9_.-]*==', line)]
for pos, start in enumerate(starts):
    end = starts[pos + 1] if pos + 1 < len(starts) else len(lines)
    block = lines[start:end]
    via = [line.strip() for line in block if line.strip().startswith("# via")]
    attribution = [line.strip() for line in block if line.strip().startswith("#   ")]
    if via and not attribution:
        print(f"{start + 1}: {block[0].rstrip(' \\')}")
PY

printf '%s\n' '=== exact tracked-file consumer scan ==='
git ls-tree -r --name-only 21824f77e9ee4c26c42bb1f21f00eaa3ae21fb6d \
  | grep -E '\.(py|pyi|toml|txt|ini|cfg|yml|yaml|md|sh|json)$|(^|/)(Dockerfile|Makefile)$' \
  | while IFS= read -r file; do
      git show "21824f77e9ee4c26c42bb1f21f00eaa3ae21fb6d:${file}" \
        | grep -n -i -E '(^|[^[:alnum:]_])(httpx2|httpcore2)([^[:alnum:]_]|$)|^[[:space:]]*(from|import)[[:space:]]+(httpx2|httpcore2)([[:space:].,]|$)' \
        && printf '  file=%s\n' "$file" || true
    done

printf '%s\n' '=== dependency-install and pip-compile workflow references ==='
rg -n -i -C 3 'pip-compile|requirements-lock\.txt|require-hashes|requirements\.txt' \
  .github .  -g '!requirements-lock.txt' -g '!**/.git/**' 2>/dev/null | head -n 300

Length of output: 6156


🏁 Script executed:

#!/bin/bash
set -euo pipefail

target='21824f77e9ee4c26c42bb1f21f00eaa3ae21fb6d'

printf '%s\n' '=== empty lockfile attribution blocks ==='
python - <<'PY'
from pathlib import Path
import re

lines = Path("requirements-lock.txt").read_text().splitlines()
starts = [i for i, line in enumerate(lines) if re.match(r'^[A-Za-z0-9][A-Za-z0-9_.-]*==', line)]
for pos, start in enumerate(starts):
    end = starts[pos + 1] if pos + 1 < len(starts) else len(lines)
    block = lines[start:end]
    has_via = any(line.strip().startswith("# via") for line in block)
    attribution = [line.strip() for line in block if line.strip().startswith("#   ")]
    if has_via and not attribution:
        package = block[0].rstrip().rstrip("\\").rstrip()
        print(f"{start + 1}: {package}")
PY

printf '%s\n' '=== exact tracked-file consumer scan ==='
matches=0
while IFS= read -r file; do
  if git show "${target}:${file}" |
      grep -n -i -E '(^|[^[:alnum:]_])(httpx2|httpcore2)([^[:alnum:]_]|$)|^[[:space:]]*(from|import)[[:space:]]+(httpx2|httpcore2)([[:space:].,]|$)'; then
    printf 'file=%s\n' "$file"
    matches=1
  fi
done < <(
  git ls-tree -r --name-only "$target" |
    grep -E '\.(py|pyi|toml|txt|ini|cfg|yml|yaml|md|sh|json)$|(^|/)(Dockerfile|Makefile)$' || true
)
printf 'consumer_match_found=%s\n' "$matches"

printf '%s\n' '=== truststore declarations and exact package block ==='
git grep -n -i -E '(^|[^[:alnum:]_-])truststore([^[:alnum:]_-]|$)' "$target" -- \
  ':!requirements-lock.txt' ':!*.lock' ':!*.egg-info/*' || true
sed -n '550,562p' requirements-lock.txt

Length of output: 2584


@seonghobae Review complete for commit 21824f77e9ee4c26c42bb1f21f00eaa3ae21fb6d.

I found no source, test, or tooling consumer of httpx2 or httpcore2. Their removal does not break a repository consumer contract.

Finding — requirements-lock.txt Line 556: truststore==0.10.4 remains in the lockfile, but its # via section is now empty. The removed httpx2 and httpcore2 entries were its only listed consumers. truststore is also absent from requirements.txt and pyproject.toml.

Regenerate the lockfile with the header command. This should remove the orphaned truststore entry and restore internal lockfile consistency. I excluded the separately owned central CodeQL settlement failure from this review.

⚠️ Action not completed

Already reviewed the last commit. Use @coderabbitai full review to rerun a review of the entire changeset.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

Please review only the current exact head 21824f77e9ee4c26c42bb1f21f00eaa3ae21fb6d and the three dependency files. Verify that httpx2/httpcore2 have no retained repository consumer contract and that the lockfile cleanup does not remove a still-required transitive dependency. Do not treat the separately owned central CodeQL settlement failure as a leaf-source finding.

@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

target='21824f77e9ee4c26c42bb1f21f00eaa3ae21fb6d'
git rev-parse --verify "${target}^{commit}" >/dev/null
parent="$(git rev-parse "${target}^")"

printf '%s\n' '=== exact commit ==='
git show -s --format='commit=%H%nparent=%P%nsubject=%s' "$target"

printf '%s\n' '=== exact three-file diff ==='
git diff --no-ext-diff --unified=40 "$parent" "$target" -- \
  requirements.txt pyproject.toml requirements-lock.txt

printf '%s\n' '=== dependency declarations ==='
cat -n requirements.txt
printf '%s\n' '--- pyproject dependency sections ---'
rg -n -A 100 -B 4 '^\[project(\.optional-dependencies)?\]|^(dependencies|dev)[[:space:]]*=' pyproject.toml

printf '%s\n' '=== lockfile header and relevant blocks ==='
sed -n '1,70p' requirements-lock.txt
rg -n -A 18 -B 3 \
  '^(httpx2|httpcore2|httpx|httpcore|anyio|idna|typing-extensions|truststore)==' \
  requirements-lock.txt || true

printf '%s\n' '=== removed-package references in tracked files at exact head ==='
git grep -n -i -E \
  '(^|[^[:alnum:]_])(httpx2|httpcore2)([^[:alnum:]_]|$)|^[[:space:]]*(from|import)[[:space:]]+(httpx2|httpcore2)([[:space:].,]|$)' \
  "$target" || true

printf '%s\n' '=== lock attribution integrity ==='
python - <<'PY'
from pathlib import Path
import re

path = Path("requirements-lock.txt")
lines = path.read_text(encoding="utf-8").splitlines()
starts = [i for i, line in enumerate(lines)
          if re.match(r'^[A-Za-z0-9][A-Za-z0-9_.-]*==', line)]
empty_via = []
for n, start in enumerate(starts):
    end = starts[n + 1] if n + 1 < len(starts) else len(lines)
    block = lines[start:end]
    package = block[0].split("==", 1)[0]
    has_via = any(line.strip() == "# via" for line in block)
    sources = [line.strip()[3:] for line in block if line.strip().startswith("#   ")]
    if has_via and not sources:
        empty_via.append((start + 1, package))
print("empty_via_blocks=", empty_via)

for pkg in ("httpx2", "httpcore2", "truststore"):
    found = [(i + 1, line) for i, line in enumerate(lines)
             if line.lower().startswith(pkg + "==")]
    print(f"{pkg}_entries=", found)
PY

Length of output: 28636


@seonghobae Review complete for exact head 21824f77e9ee4c26c42bb1f21f00eaa3ae21fb6d.

I found no tracked source, test, or tooling reference to httpx2 or httpcore2. Their removal does not break a retained repository consumer contract.

httpcore==1.0.9 remains required by httpx==0.28.1. The cleanup does not remove that required transitive dependency.

Finding — requirements-lock.txt Line 556: truststore==0.10.4 remains in the lockfile with an empty # via block. It is absent from requirements.txt and pyproject.toml. Its only prior attribution was httpx2 and httpcore2.

Regenerate requirements-lock.txt with the documented pip-compile command. The resolver should remove the orphaned truststore entry.

I excluded the separately owned central CodeQL settlement failure from this review.

You are interacting with an AI system.

Copy link
Copy Markdown
Contributor Author

Fresh independent consumer evidence from #562 exact head 6e371b9c9e6e3f72b9be6cb5949d77dbed24e19f: Security Scan run 34638199629, trivy-fs job 103391244310, checked out the exact head and failed only at the finding gate with the protected-base httpx2 contract: CVE-2026-84382 HIGH (7.5), CVE-2026-84378 MEDIUM (5.9), CVE-2026-84379 MEDIUM (5.3), CVE-2026-84380 MEDIUM (5.6), all at requirements.txt:7. This corroborates #558 as the causal dependency foundation. #562 intentionally restored its dependency files to protected-base blobs rather than duplicating this fix; after #558 reaches protected immutable ancestry, #562/#543 should non-force restack and regenerate exact-head Security evidence.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant