bmersereau ↗ analysis ↗ GitHub bmersereau/mike
A staging ground for upstream fixes: bmersereau is quietly running a security audit on Mike, one pull request at a time.
There's no separate product to try here. This fork carries no changes of its own; it exists so bmersereau can file proposals against the main Mike project. Clone it and run it, and you'd get stock Mike.
The work itself reads like a security audit in slow motion. Over about four weeks, bmersereau filed proposals covering how Mike encrypts the API keys users hand it, how long shared download links stay valid, whether the database can prove which account owns each record, and what happens when a deployment boots with credentials missing. One theme keeps recurring: nothing should fail quietly. A misconfigured server should refuse to start instead of limping along trusting whatever login token shows up.
Not everything landed. Two of the sharper security proposals were closed without merging, including one that would have shut a fail-open login path. Plenty remain open, alongside quieter reliability work (paginated chat history, capped bulk exports, a timeout on hung chat streams), a fix for a sharing bug that locked invited collaborators out, and a draft contributing guide, which is the kind of thing you write when you plan to stick around. We know nothing about bmersereau beyond the handle, but the pattern is consistent: an outside contributor doing unglamorous hardening work on legal software that handles sensitive documents.
What's in it
- Secrets that stand alone Each security function gets its own dedicated secret, checked at startup. No more quietly reusing one master key for unrelated jobs.
- Independently encrypted credentials Stored API keys were all protected by one derived key; cracking it cracked everything. The proposal encrypts each one independently.
- Download links with a shelf life Shared download links used to live forever. Now they expire after 30 days by default, and the verification step was hardened against a subtle timing leak.
- A database that proves ownership Every project, document, chat, and workflow must point at a real user account, enforced by the database itself, with deny-by-default access rules as a second wall.
- Hardened uploads A stored-XSS report on file upload turned into a layered pass: files are validated before acceptance and known vulnerabilities got patched along the way.
- Less PII in the logs Routine logging was linking named users to the specific legal documents they edited. bmersereau wants that stripped out, citing GDPR data minimization.
- Guardrails on heavy operations Chat history loads a page at a time, bulk exports cap at 50 documents, and a stalled AI response gets cut off after three minutes instead of hanging forever.
Direction
securityinfrastructurecompliance
Activity
bmersereau ↗ analysis ↗ GitHub bmersereau ↗ analysis ↗ GitHub bmersereau ↗ analysis ↗ GitHub bmersereau ↗ analysis ↗ GitHub bmersereau ↗ analysis ↗ GitHub bmersereau ↗ analysis ↗ GitHub bmersereau ↗ analysis ↗ GitHub bmersereau ↗ analysis ↗ GitHub bmersereau ↗ analysis ↗ GitHub bmersereau ↗ analysis ↗ GitHub bmersereau ↗ analysis ↗ GitHub bmersereau ↗ analysis ↗ GitHub bmersereau ↗ analysis ↗ GitHub bmersereau ↗ analysis ↗ GitHub bmersereau ↗ analysis ↗ GitHub bmersereau ↗ analysis ↗ GitHub bmersereau ↗ analysis ↗ GitHub bmersereau ↗ analysis ↗ GitHub Show 50 more
bmersereau ↗ analysis ↗ GitHub bmersereau ↗ analysis ↗ GitHub bmersereau ↗ analysis ↗ GitHub bmersereau ↗ analysis ↗ GitHub bmersereau ↗ analysis ↗ GitHub bmersereau ↗ analysis ↗ GitHub bmersereau ↗ analysis ↗ GitHub bmersereau ↗ analysis ↗ GitHub bmersereau ↗ analysis ↗ GitHub bmersereau ↗ analysis ↗ GitHub bmersereau ↗ analysis ↗ GitHub bmersereau ↗ analysis ↗ GitHub bmersereau ↗ analysis ↗ GitHub bmersereau ↗ analysis ↗ GitHub Threads of work (detailed view)
bmersereau adds a second lock to Mike's database
A defense-in-depth rewrite so that one careless permission can't quietly expose a table to the outside world.
bmersereau makes Mike's database prove who owns every record
A proposed schema change would force every project, document, chat, and workflow to point at a real, valid user account - enforced by the database itself, not just trusted by the app.
bmersereau wants every secret to stand on its own
A proposed rework would stop the backend from quietly reusing one master key across unrelated security functions.
bmersereau puts a time limit on hung chat streams
A resilience fix so a stalled AI provider can no longer hold a chat connection open forever.
bmersereau puts a cap on bulk downloads before they can crash the server
A fork of the Mike legal-AI codebase closes a resource-exhaustion hole in its bulk document export.
bmersereau puts a leash on runaway chat-history loads
A proposed backend change stops the app from hauling a user's entire conversation history every time the chat list opens.
bmersereau shores up how Mike stores your files
A quiet reliability pass on the backend layer that holds every uploaded document.
bmersereau tries to stop one secret from guarding two doors
A proposal to make sure the key that encrypts users' stored credentials can't quietly borrow the login-verification secret instead.
bmersereau wants a misconfigured backend to fail loudly, not limp along
A proposal to make the backend refuse to start when its database credentials are missing, instead of booting into a broken state.
bmersereau hardens the locks on Mike's download links
A subtle timing weakness in how the backend checks download tokens gets closed before it can ever be abused.
bmersereau pulls user identities out of the edit logs
Development-era logging was quietly linking named users to the specific legal documents they edited, and bmersereau wants it gone.
bmersereau fixes the sharing bug that locked invited users out
When you share a project, the person you invited should get in - bmersereau found a case where they didn't.
bmersereau locks down Mike's document uploads
A stored-XSS report on file upload becomes a layered security pass across the whole stack.
bmersereau puts a shelf life on Mike's download links
Shared download links stop living forever - by default they now expire after 30 days.
bmersereau closes a one-key-breaks-all hole in how secrets are stored
A proposed change reworks how the backend encrypts the API keys users hand it, so cracking one no longer cracks them all.
bmersereau slams the door on a silent auth bypass
A misconfigured deployment used to quietly trust any login token as proof of identity; this fix makes it fail loudly instead.
CONTRIBUTING.md proposed: bug/PR workflow, migration conventions, security disclosure path
bmersereau opened a PR proposing a `CONTRIBUTING.md` for the upstream repo, framed as a first draft for maintainer review. It codifies conventions that already exist in practice and adds a few explicit requirements - notably test coverage and structured migration scripts. The PR is open.
Convert user_id columns from text to uuid with FK constraints
Nine tables in the Mike backend carry `user_id` as plain text, with no referential link to the auth layer. bmersereau's PR converts all of them to the `uuid` type and adds proper foreign keys against `auth.users(id)`, wrapped in a single transaction so the change is all-or-nothing.
Add 180-second timeout to SSE chat streams via Promise.race
bmersereau proposed wrapping the streaming LLM call in both chat routes with a `Promise.race` against a 180-second deadline. Stalled connections now emit a structured SSE error and close cleanly instead of hanging. The PR is open.
Cap bulk zip downloads at 50 documents to prevent in-memory exhaustion
bmersereau proposed a hard ceiling on the bulk zip download route: requests for more than 50 documents get a 400, preventing an authenticated user from triggering simultaneous in-memory loading of an unbounded file set. The PR is open.
Paginate GET /chat with limit and before-cursor parameters
The `GET /chat` endpoint returns an unbounded list of a user's chats. bmersereau adds a `limit` parameter (default 50, cap 200) and a `before` ISO timestamp cursor, letting callers walk back through chat history one page at a time.
Hoist R2 S3Client to a module-level singleton
The Mike backend was allocating a fresh `S3Client` on every R2 operation. bmersereau's PR hoists it to a module-level singleton with lazy initialization, so the HTTP layer can reuse keep-alive connections instead of reopening sockets per request.
Remove JWT-secret fallback from user API key encryption (closed without merge)
bmersereau proposed removing a fallback in the user-API-key encryption helper that would silently reuse the Supabase JWT verification secret when no dedicated encryption secret was configured. The PR closed after two days without merging.
Fix timingSafeEqStr to run the comparison before checking length
The `timingSafeEqStr` helper guarding download tokens was short-circuiting on length mismatch before invoking the constant-time comparison underneath. That early return undermines the whole contract: an attacker with timing access could distinguish a length mismatch from a content mismatch.
Strip user and document IDs from edit-resolution INFO logs
bmersereau opens a PR against upstream to remove INFO-level log lines in the edit-resolution handler that were emitting user identifiers, document identifiers, and edit identifiers alongside internal handler state - all going to whatever log aggregator the deployment uses.
Normalize shared_with email casing across all project access paths
Project sharing in Mike was silently failing for users whose stored email casing didn't match the lookup. bmersereau traces the root cause to three different normalization rules scattered across four endpoints, extracts them into a single access module, and routes every project route through it.
Harden document uploads against XSS and tighten CSP
bmersereau's PR closes three separate security issues at once: stored XSS via malicious uploads, missing Content-Security-Policy headers, and unpatched `@xmldom/xmldom` CVEs. The core fix is magic-byte validation on both upload paths before a file is accepted.
Add expiry to HMAC-signed download tokens, with 30-day default TTL
bmersereau proposed adding an `exp` Unix timestamp to every HMAC-signed download token and rejecting tokens past their expiry on verification. Legacy tokens without `exp` remain valid, so nothing breaks on deploy. The PR is open against upstream.
Replace SHA-256 key derivation with HKDF and per-row salt for stored API keys
Every stored user API key in the Mike backend is currently encrypted with the same derived key - one SHA-256 hash of the master secret. bmersereau replaces that with HKDF (RFC 5869) and a random 16-byte salt per row, so each encryption is independent.
Make per-purpose secrets mandatory and enforce isolation at boot
bmersereau removes the silent fallbacks that let the download token signing secret and the user API key encryption secret quietly reuse the Supabase service key. Both secrets are now required, must be set explicitly, and must be distinct from each other and from the Supabase key - enforced at startup before the server accepts any traffic.
Remove the dev auth bypass that accepted any Bearer token on misconfigured deployments
bmersereau proposed closing a fail-open auth path in the Supabase server helpers: missing env vars previously treated the deployment as a dev environment and accepted any Bearer token as a valid user ID. The PR was closed without merge, so the bypass stayed in upstream.
Pull requests (detailed view)
🟢 Open (28)
bmersereau · opened 2mo ago bmersereau · opened 2mo ago bmersereau · opened 2mo ago bmersereau · opened 2mo ago bmersereau · opened 2mo ago bmersereau · opened 2mo ago bmersereau · opened 2mo ago bmersereau · opened 2mo ago bmersereau · opened 2mo ago bmersereau · opened 2mo ago Show 18 more open
bmersereau · opened 2mo ago bmersereau · opened 2mo ago bmersereau · opened 2mo ago bmersereau · opened 2mo ago bmersereau · opened 2mo ago bmersereau · opened 2mo ago bmersereau · opened 2mo ago bmersereau · opened 2mo ago bmersereau · opened 2mo ago bmersereau · opened 2mo ago bmersereau · opened 2mo ago bmersereau · opened 2mo ago bmersereau · opened 2mo ago bmersereau · opened 2mo ago bmersereau · opened 2mo ago bmersereau · opened 2mo ago bmersereau · opened 2mo ago bmersereau · opened 2mo ago ⛔ Closed without merge (7)
bmersereau · opened 2mo ago · closed 20d ago bmersereau · opened 2mo ago · closed 2mo ago bmersereau · opened 2mo ago · closed 2mo ago bmersereau · opened 2mo ago · closed 2mo ago bmersereau · opened 2mo ago · closed 2mo ago bmersereau · opened 2mo ago · closed 2mo ago bmersereau · opened 2mo ago · closed 2mo ago