Repository navigation
Security audit 2026-10: certifi fix, CI hardening, chunk cap, redirect guard, markup escaping - #17
Merged
Merged
Conversation
pip-audit against the previous lock reported one known vulnerability: certifi 2026.7.22 bundles the "emSign ECC Root CA - G3" root, whose eMudhra intermediates have compromised private keys. An on-path attacker holding one of those keys could forge TLS for the registry and feed hosts this tool talks to. certifi 2026.10.19 removes the affected roots. The rest of the refresh moves the lock to the same resolution CI already installs from scratch (the project pins only lower bounds): anthropic 1.4.0 -> 2.1.0 (released 2026-10-21; the mocked AsyncAnthropic paths and the TextBlock import are unchanged), pydantic 2.13.5 -> 2.14.1, cryptography 50.0.1 -> 50.0.2, and patch bumps elsewhere. Verified on the refreshed lock: 307 tests pass, coverage 90.8%, ruff and mypy clean, pip-audit reports no known vulnerabilities. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SrGsKknvZvNgFwvTVnHdUA
CI: actions pinned to commit SHAs instead of mutable major tags (checkout v7.0.1, setup-python v7.1.0 -- both were two majors behind), a read-only GITHUB_TOKEN via top-level `permissions: contents: read`, and Python 3.13 and 3.14 added to the matrix (3.14 verified locally, 307 tests). For a tool whose subject is supply-chain compromise, a tag that can be moved to different code is the wrong kind of pin. .github/dependabot.yml: weekly grouped PRs for pip and github-actions so the lockfile and the action SHAs stop drifting silently; the last CI run on main was five weeks old and had never seen the current SDK major. SECURITY.md: how to report, what is in scope (the archive and feed boundary, secret leakage, prompt injection that changes behaviour rather than scores), what is out of scope (model mis-scoring is a research finding, not a vulnerability), and the one-release support window. pyproject classifiers now list 3.13 and 3.14 to match the matrix. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SrGsKknvZvNgFwvTVnHdUA
…ndering Three findings from the 2026-10-22 review of the archive, feed, LLM, and output surfaces, plus one correctness fix it noticed in passing. High — unbounded LLM calls per analysis. Each chunk is one billed call, and the count was bounded only by byte limits, never by file count: a hostile release touching thousands of small source files (well under max_archive_files) would cost thousands of calls from a single `diff`, multiplied again under `scan`. New CHAINWATCH_MAX_CHUNKS_PER_ANALYSIS (default 40) is enforced in chunk_diff(); files past the cap are listed in diff_summary.files_not_analyzed (schema 0.3.0 -> 0.4.0, additive) and named in a caveat, matching how skipped and truncated files are already recorded. Medium — redirects on the shared HTTP client. download_with_limit() already re-validated every redirect hop against https-only / no-credentials / no-localhost / no-non-public-IP rules, but the client used for registry metadata, OSV, Rekor, and Scorecard had follow_redirects=True with no check, so a compromised mirror or feed could steer a request at localhost or a cloud metadata address. The same rules now run as a request event hook on every request and hop (validate_outbound_url), and build_http_client() accepts a transport so tests can exercise the hook. Medium — Rich markup injection. The LLM quotes attacker-controlled diff text verbatim, and Rich parses plain strings for [link=...] and style tags, so a hostile comment could plant a real terminal hyperlink or a styled "all clear" inside the tool's own framing. Reasoning, summary, feed details and URLs, package names, hook names, caveat file paths, and scan errors now render as Text() or through markup.escape(). Low — scoped package names were not URL-encoded in the two _resolve_github_repo URLs, silently degrading Scorecard to no_data for @scope/name packages. .envrc joins .env in .gitignore. Tests: 323 passed (16 new: chunk cap, redirect guard, markup rendering), coverage 90.9%, ruff and mypy clean; all 34 dataset reports validate. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SrGsKknvZvNgFwvTVnHdUA
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Security audit of 2026-10-22: dependency vulnerabilities, secrets history, GitHub-side settings, CI supply-chain posture, and a static code review with limited offline probing of the archive, feed, LLM, and output surfaces. Three commits:
GITHUB_TOKEN, Python 3.13 and 3.14 in the matrix (3.14 verified locally),dependabot.ymlfor pip and GitHub Actions,SECURITY.md.CHAINWATCH_MAX_CHUNKS_PER_ANALYSIS, default 40). A hostile release touching thousands of small files could force thousands of billed LLM calls from onediff. Dropped files are recorded indiff_summary.files_not_analyzed(schema 0.4.0) and named in a caveat.[link=...]/style injection via LLM-quoted package comments..envrcgitignored.What the review found solid
Tar and zip extraction (traversal, symlink, hardlink, absolute-path, oversized-member and lied-size zip-bomb probes all rejected with nothing written), integrity verification fails closed, setup.py is AST-only, the API key is unwrapped once and never logged, lockfile parsers and the chunker scale linearly on adversarial input.
Needs the repository owner (cannot be done from a token without admin scopes)
dependabot.ymlonly requests version-update PRs.SECURITY.mdnow points to.mainrequiring the CI check before merge.Test plan
🤖 Generated with Claude Code
https://claude.ai/code/session_01SrGsKknvZvNgFwvTVnHdUA