Skip to content

Harden the public release: API exposure, path handling, cookie export, contributor address - #38

Merged
lfnothias merged 4 commits into
mainfrom
fix/public-release-hardening
Aug 21, 2026
Merged

Harden the public release: API exposure, path handling, cookie export, contributor address#38
lfnothias merged 4 commits into
mainfrom
fix/public-release-hardening

Conversation

@lfnothias

Copy link
Copy Markdown
Collaborator

Harden the public release: API exposure, path handling, cookie export, contributor address

Four independent items from the release audit, each with regression tests that fail
without the fix.

An unauthenticated API was reachable from the network

AuthConfig(enabled=True) existed but its only consumer was a to_dict() mask — nothing
enforced it, and no middleware or dependency was wired anywhere in web/. The shipped
configs bound 0.0.0.0. That combination published POST /api/llm/proxy — which takes an
arbitrary prompt and streams it through the operator's own API keys — plus read/write
access to every knowledge base, to anyone who could reach the port.

The guard has two halves, in web/auth.py:

  • Start time. The server refuses to bind a non-loopback interface unless a token is
    configured. This is what protects the default install; the dangerous configuration now
    fails loudly instead of serving. Escape hatch: auth.allow_insecure_network_bind, for
    when a reverse proxy in front already authenticates callers.
  • Request time. Once a token is set, /api/* requires Authorization: Bearer <token>.
    GET /api/health stays open so liveness probes work.

With no token and a loopback bind — the new default — nothing changes.

Shipped configs move from 0.0.0.0 to 127.0.0.1, and the docs that described a bearer
scheme which did not exist now describe the one that does.

A BibTeX file field could read any PDF on the server

Three sites parsed the file entry of an uploaded .bib directly, bypassing the
validate_local_path allow-list the local-docs routes already used. file = {/…/private.pdf}
made the server parse that file and store its text as a KB chunk, which whoever uploaded the
.bib could then read back through search.

All three now go through one helper that validates against local_docs.allowed_roots. It
fails soft: a rejected path skips the local-PDF shortcut and lets the DOI download proceed,
rather than failing the import.

import-browser-cookies dumped the whole jar by default

With no --domain the command wrote every cookie the browser held — mail, bank, everything
— to a plaintext file. Its own help string said "NOT recommended" and did it anyway. A
domain filter is now required, with --all-domains as the explicit opt-in.

A contributor's address shipped in two config presets

configs/embedders/code_kb.yml and openai_large.yml carried a real address as
unpaywall_email, which is transmitted to Unpaywall and PubMed on every request — so every
user of those presets impersonated them. Both are placeholders now.

tests/unit/test_repo_hygiene.py keeps this from drifting back, and also guards against
re-introducing a shadow-library reference into the tracked tree.

Behaviour changes to be aware of

  • A non-loopback bind without a token now refuses to start. Local configs that set
    server.host: "0.0.0.0" need 127.0.0.1, a token, or the opt-out flag.
  • BibTeX local-PDF ingest now requires local_docs.allowed_roots to cover the PDF
    location. Outside it, the entry falls through to the DOI download path and logs why.

Not changed

CORSMiddleware is still not instantiated. That is the safe state — browsers block
cross-origin by default, no user-facing doc claims otherwise, and adding a permissive
policy would be a regression.

Verification

  • Full unit suite: 2623 passed, 8 skipped, 0 failed.
  • CI simulation, private indicium hidden and ci.yml's ignore/deselect list applied:
    2503 passed, 8 skipped, 17 deselected, 0 failed.
  • Each guard was re-checked by reverting it and confirming its tests fail.

@lfnothias
lfnothias merged commit 89c2e3e into main Aug 21, 2026
3 checks passed
@lfnothias
lfnothias deleted the fix/public-release-hardening branch August 21, 2026 18:13
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