Skip to content

fix(auth): compare API keys by value; read note headers as data only - #23

Merged
willynikes2 merged 3 commits into
masterfrom
fix/api-key-bypass
Oct 14, 2026
Merged

willynikes2 merged 3 commits into
masterfrom
fix/api-key-bypass

Conversation

@willynikes2

Copy link
Copy Markdown
Owner

What was wrong

  1. API keys were looked up as properties of a plain object. Every object also has inherited properties (constructor, toString, __proto__, …), so sending one of those names as the key was accepted as a valid key on /api/v1 and on the MCP endpoint.
  2. Note headers were parsed with gray-matter's default engines, which include code languages, and writing a note re-parsed its body.

The fix

  • serviceForKey() compares the given key with each configured key by value, in constant time. The API-key middleware and the Brain API gate both use it. The gate moved to src/middleware/brain-auth.js so it can be tested.
  • readNote() / writeNote() in src/utils/frontmatter.js read note headers as YAML data only, refuse a header that names another language, and write a body unchanged. Every reader goes through them. A first line of four or more dashes is ordinary text.

Tests

  • tests/auth-bypass.test.js (7) and tests/frontmatter-safe.test.js (7); each failed before the fix.
  • Full suite 160/160.
  • A server booted on a spare port refuses every inherited name (403 / 401), and the real key still works.
  • None of the 2,041 notes in the live vault uses a non-YAML header.

Note for self-hosters

Update and restart. The bug did not reveal any key: it let a request in without one. Keys do not need to be changed for this bug.

🤖 Generated with Claude Code

willynikes2 and others added 3 commits October 14, 2026 01:22
The key was used to look up a property of a plain object, which also finds the names every object
inherits. Keys are now compared with each configured key in constant time, in one function used by
the API-key middleware and by the Brain API gate, which moves to its own module so it can be tested.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A note header that names another language is refused, so nothing in a note is ever run. Every reader
of note headers now goes through one function.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ts with a rule line

Writing a note passed its body back through the header parser. The body is now written unchanged.
A first line of four or more dashes is ordinary text again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread src/middleware/brain-auth.js Dismissed
Comment thread src/middleware/brain-auth.js Dismissed
@willynikes2
willynikes2 merged commit 1bc0c09 into master Oct 14, 2026
14 of 16 checks passed
@willynikes2
willynikes2 deleted the fix/api-key-bypass branch October 14, 2026 02:15
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