Skip to content

Harden auth/upload security, pin dependencies, and add OSS project docs - #11

Merged
zsmithnyc merged 3 commits into
mainfrom
security-hardening
Oct 21, 2026
Merged

zsmithnyc merged 3 commits into
mainfrom
security-hardening

Conversation

@zsmithnyc

Copy link
Copy Markdown
Collaborator

Summary

  • Refuse to start in production with the default/placeholder SECRET_KEY (was a full auth-bypass risk on any misconfigured self-hosted instance)
  • Rate-limit login attempts per IP+email to blunt brute-forcing
  • Enforce a max upload size and content-type allowlist on document uploads, plus an early Content-Length guard middleware
  • Prompt for the admin bootstrap password via getpass instead of a CLI arg (was visible in shell history)
  • Pin all Python dependencies via pip-compile-style lockfiles (requirements.in/requirements-dev.in → generated requirements.txt/requirements-dev.txt) for reproducible installs; along the way, fixed an existing bug where sqladmin's unconditional use of sqlalchemy.ext.asyncio required greenlet, which was never actually declared anywhere
  • Add SECURITY.md, CONTRIBUTING.md, CODE_OF_CONDUCT.md; flag docker-compose.yml creds as dev-only; document the security model (including the single-tenant authorization design) in the README/SECURITY.md

Heads up: this restructures requirements.txt from a hand-edited file into a generated lockfile (source of truth is now requirements.in). The open dependabot PR #10 (bumping sqlalchemy>=2.0 → >=2.1.4 in the old flat requirements.txt) will conflict/go stale once this merges — recommend closing it, since the lockfile already pins sqlalchemy==2.1.6 with the [asyncio] extra.

Testing

  • ruff check . passes
  • Clean-room install (fresh venv, pip install -r requirements.txt -r requirements-dev.txt only, no manual patches) succeeds
  • pytest passes (41 passed) against real Postgres on the pinned dependency versions
  • Manually verified the SECRET_KEY fail-fast, login rate limiting (429 after repeated attempts), and upload rejection (oversized + disallowed content-type) behaviors

🤖 Generated with Claude Code

zsmithnyc and others added 3 commits October 21, 2026 15:11
- Refuse to start in production with the default/placeholder SECRET_KEY
- Rate-limit login attempts per IP+email to blunt brute-forcing
- Enforce a max upload size and content-type allowlist on document uploads,
  plus an early Content-Length guard middleware
- Prompt for the admin bootstrap password via getpass instead of a CLI arg
- Flag docker-compose.yml credentials as dev-only
- Add SECURITY.md, CONTRIBUTING.md, and CODE_OF_CONDUCT.md

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Add requirements.in / requirements-dev.in as the loose, hand-edited source
  of truth; requirements.txt / requirements-dev.txt are now fully pinned
  lockfiles (every direct and transitive dependency) generated via
  `uv pip compile`, so installs are reproducible across dev/CI/prod
- Explicitly depend on sqlalchemy[asyncio] to pull in greenlet, which sqladmin
  requires unconditionally (it imports sqlalchemy.ext.asyncio internally) --
  previously undeclared, so a clean install could fail to import the app
- Fix test suite fallout from the new login rate limiter: reset its
  in-memory state between tests, and add coverage for rate limiting and the
  document upload size/content-type limits
- Document the lockfile regeneration workflow in CONTRIBUTING.md

Verified: clean-room install + ruff + full pytest suite (41 passed) against
the pinned versions on Python 3.12.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@zsmithnyc
zsmithnyc merged commit 6aa44b1 into main Oct 21, 2026
3 checks passed
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