Skip to content

chore: repo hygiene and security hardening - #12

Merged
mkultraWasHere merged 12 commits into
mainfrom
chore/repo-hygiene
Aug 19, 2026
Merged

chore: repo hygiene and security hardening#12
mkultraWasHere merged 12 commits into
mainfrom
chore/repo-hygiene

Conversation

@mkultraWasHere

Copy link
Copy Markdown
Collaborator

Summary

  • Versioning & licensing: bump to 0.5.0, add template licensing (LPPL-1.3c, ACM dtx source), commit uv.lock
  • Privacy: self-host JetBrains Mono font, remove Google Fonts CDN link
  • SSRF protection: validate DNS resolution against internal ranges, manually follow redirects with per-hop validation
  • Injection fixes: pass paper path via sys.argv in validate.sh (was shell-interpolated into Python), add -norc to all latexmk invocations
  • Command hardening: denylist network-exfiltration binaries (curl, wget, nc, ssh, etc.), scrub credential-shaped env vars from subprocess environments
  • Template fixes: remove stale fancyhdr.sty/natbib.sty from ICLR/ICML extra_files (broke template switching)
  • UI: PDF download names the file after the paper title, download icon uses visible color
  • Docs: add Security section to README documenting mitigations and limitations

Test plan

  • All 155 tests pass (task test)
  • SSRF: verified internal IP blocking, redirect-to-internal blocking, scheme validation (9 formal tests + 6-scenario stress test)
  • Command denylist: verified direct, absolute-path, and bash -c bypass blocking (5 formal tests + 28-case stress test)
  • Env scrub: verified credential stripping, passthrough for S2_API_KEY, non-sensitive var preservation (4 formal tests + 28-case stress test)
  • Template extra_files: verified all 15 templates have matching files on disk
  • Filename sanitization: tested with special chars, path traversal, XSS payloads, unicode, empty/null inputs

🤖 Generated with Claude Code

mkultraWasHere and others added 9 commits August 19, 2026 14:46
- Bump version to 0.5.0 in pyproject.toml (matches README badge)
- Update Node requirement to 20+ (matches pdfjs-dist engine)
- Remove uv.lock from .gitignore and commit lockfile
- Self-host JetBrains Mono font, remove Google Fonts dependency
- Fix false privacy claim in README comparison table
- Add PDF download button to viewer header
- Remove vendored system packages (natbib.sty, fancyhdr.sty) that
  violate LPPL redistribution terms — TeX Live provides them
- Bundle acmart.dtx alongside acmart.cls per LPPL requirements
- Add LPPL-1.3c license text and THIRD_PARTY_LICENSES manifest

Co-Authored-By: Claude <noreply@anthropic.com>
Block requests to private/loopback/link-local/reserved IPs, validate
URL schemes, and catch redirect-based bypasses via aiohttp TraceConfig.
Also upgrades the PDF download icon to SVG with white color.

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Replaces the upstream command tool with a sandboxed version that blocks
network-exfiltration binaries (curl, wget, nc, ssh, etc.) and env-exposure
commands. Adds -norc to all latexmk invocations to prevent .latexmkrc
Perl code execution from untrusted paper directories.

Co-Authored-By: Claude <noreply@anthropic.com>
- Replace trace-based redirect hook with manual redirect following that
  validates each Location header before connecting
- Name downloaded PDFs after the paper title instead of generic paper.pdf
- Remove stale fancyhdr.sty/natbib.sty from ICLR/ICML template configs

Co-Authored-By: Claude <noreply@anthropic.com>
Subprocesses spawned by the agent command tool inherited the full
server environment including API keys. Strip credential-shaped
variables (*_API_KEY, *_TOKEN, *_SECRET, *_PASSWORD, *_CREDENTIAL,
plus AWS keys and DATABASE_URL) while preserving S2_API_KEY for
Semantic Scholar and all non-sensitive vars.

Co-Authored-By: Claude <noreply@anthropic.com>
Document the defense-in-depth mitigations (command denylist, env
scrubbing, SSRF protection, build isolation) and their limitations.
Include recommendations for running against untrusted papers.

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Hardens network and subprocess execution, improves repository licensing/versioning hygiene, and adds privacy-focused UI updates.

Changes:

  • Adds SSRF, command, environment, and LaTeX build protections.
  • Self-hosts fonts and improves PDF downloads.
  • Updates versioning, lockfiles, template assets, licenses, and security documentation.

Reviewed changes

Copilot reviewed 20 out of 24 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
.gitignore Tracks the uv lockfile.
README.md Updates requirements and security guidance.
pyproject.toml Bumps the package version.
uv.lock Adds locked Python dependencies.
scripts/build.sh Disables latexmk configuration files.
scripts/diff.py Hardens diff PDF compilation.
scripts/validate.sh Removes shell interpolation from YAML loading.
tests/test_ui.py Tests SSRF and command protections.
ui/backend/agent.py Adds hardened command execution.
ui/backend/server.py Adds titled PDF downloads.
ui/backend/tools/web.py Adds SSRF checks and redirect validation.
ui/frontend/index.html Removes Google Fonts requests.
ui/frontend/src/index.css Configures the self-hosted font.
ui/frontend/src/fonts/JetBrainsMono-latin.woff2 Provides the local font asset.
ui/frontend/src/components/PdfViewer.tsx Adds the PDF download control.
templates/LPPL-1.3c.txt Adds the LPPL license text.
templates/SOURCES.md Documents template provenance.
templates/THIRD_PARTY_LICENSES Adds the template license manifest.
templates/acm/acmart.dtx Adds ACM class source.
templates/iclr2026/template.yaml Removes stale bundled dependencies.
templates/iclr2026/fancyhdr.sty Removes the vendored package.
templates/iclr2026/natbib.sty Removes the vendored package.
templates/icml2026/template.yaml Removes a stale bundled dependency.
templates/icml2026/fancyhdr.sty Removes the vendored package.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread ui/backend/agent.py
Comment thread README.md Outdated
Comment thread pyproject.toml
Comment thread ui/backend/tools/web.py Outdated
Comment thread ui/backend/tools/web.py
Comment thread tests/test_ui.py
Replace enumerated _is_internal checks with `not ip.is_global or
ip.is_multicast` to catch CGNAT (100.64.0.0/10) and other non-public
ranges. Add _safe_get redirect tests, fix __version__ to 0.5.0, and
scope README SSRF claim to web_fetch only.

Co-Authored-By: Claude <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 21 out of 25 changed files in this pull request and generated 1 comment.

Suppressed comments (6)

tests/test_ui.py:498

  • This test only checks that validation eventually raises; it would still pass if a regression sent the second request before validating the internal redirect. Assert that session.get was awaited exactly once so the security-critical ordering is covered.
        with pytest.raises(ValueError, match="internal address"):
            asyncio.run(_safe_get(mock_session, "https://example.com/go"))

ui/backend/agent.py:135

  • communicate() buffers all stdout in memory until the command exits or the 120-second timeout fires. An agent-invoked high-output command such as yes can therefore exhaust the server's memory well before timeout. Stream stdout with an explicit byte cap and terminate the process once that cap is reached.
    try:
        stdout, _ = await asyncio.wait_for(
            proc.communicate(
                input=input.encode() if input else None,
            ),
            timeout=timeout,

ui/frontend/src/index.css:6

  • The newly vendored JetBrains Mono font is OFL-1.1 third-party software, but the repository contains no OFL license/copyright notice and the root license otherwise presents the project as MIT. Add the font's license and attribution, and clarify that the font is excluded from the root MIT license.
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('./fonts/JetBrainsMono-latin.woff2') format('woff2');

templates/iclr2026/template.yaml:8

  • Existing projects initialized with the previous ICLR manifest already contain fancyhdr.sty and natbib.sty. On a later switch, _get_old_extra_files() reads this updated manifest, so it no longer knows those files are template-owned and leaves them behind to shadow system packages. Add a legacy-removal/migration mechanism before dropping these ownership entries.
extra_files:
  - iclr2026_conference.sty
  - iclr2026_conference.bst

templates/icml2026/template.yaml:9

  • Existing ICML projects created before this change already have a template-owned fancyhdr.sty. Because old ownership is resolved from the current manifest, removing this entry means subsequent template switches never delete that stale file, and it can keep shadowing the system package. Preserve legacy ownership for cleanup or migrate it in the switching logic.
extra_files:
  - icml2026.sty
  - icml2026.bst
  - algorithm.sty

README.md:164

  • This guarantee applies only to the new arbitrary command tool. Fixed workflow tools use tools/subprocess.py:29-34, which starts build/sync/citation subprocesses with the full inherited environment, and S2_API_KEY is also explicitly preserved for command. Scope the documentation accordingly, or centralize scrubbing across both subprocess paths.
- **Environment scrubbing** — credential-shaped environment variables
  (`*_API_KEY`, `*_TOKEN`, `*_SECRET`, `*_PASSWORD`, and AWS keys) are
  stripped from subprocess environments. In-process tools like
  `web_search` and `web_fetch` retain access to their own API keys.

Comment thread ui/backend/agent.py Outdated
mkultraWasHere and others added 2 commits August 19, 2026 18:30
- Filter agent-provided env vars through _is_sensitive_key before
  merging into scrubbed process environment
- Add OFL-1.1 license for vendored JetBrains Mono font
- Scope README env-scrub claim to the command tool only
- Assert session.get call count in redirect-to-internal test

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
@mkultraWasHere
mkultraWasHere merged commit 3116213 into main Aug 19, 2026
4 checks passed
mkultraWasHere added a commit that referenced this pull request Aug 19, 2026
- SSRF protection: URL validation, manual redirect following with
  per-hop checks, CGNAT/multicast blocking via is_global
- Command denylist: block network-exfiltration and env-exposure binaries
- Environment scrubbing: strip credential-shaped vars from subprocess
  env (command tool only; fixed workflow tools inherit full env)
- Build isolation: latexmk -norc to prevent .latexmkrc code execution
- Script injection fix: pass paper path via sys.argv, not f-string
- Self-host JetBrains Mono font (OFL-1.1), remove Google Fonts request
- PDF download button with session-title filename
- Template licensing: LPPL text, SOURCES.md provenance, third-party
  manifest, ACM acmart.dtx per LPPL distribution requirements
- Remove vendored fancyhdr.sty/natbib.sty (use TeX Live system copies)
- Version bump to 0.5.0, add uv.lock, security docs in README
- CODEOWNERS: single owner to reduce review noise

Co-Authored-By: Claude <noreply@anthropic.com>
@mkultraWasHere
mkultraWasHere deleted the chore/repo-hygiene branch August 19, 2026 22:55
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.

2 participants