Security model

ForkLeaf holds a token that can read and write every repository you granted it. That is the whole security story, and everything below follows from taking it seriously.

Token handling

  • The access token is encrypted into an httpOnly cookie that only the server can open, with authenticated encryption and a key this deployment alone holds.
  • It is never serialised into the page, never returned by an API route, and never placed in a URL or redirect.
  • Every GitHub call is proxied by ForkLeaf’s own server, which attaches the token on the way out.
  • The token expires after eight hours and is renewed server-side with a refresh token held in the same cookie, so the sign-in outlives the token without either value ever reaching the browser. Signing in.
A token in localStorage — the usual shortcut — is readable by any script that ever runs on the page, including a compromised npm dependency. That single decision is why the API proxy exists.

OAuth CSRF

The sign-in round trip carries a single-use random value, held in a short-lived cookie and compared on return. It is consumed and deleted whether or not it matched.

Without it, an attacker can complete an OAuth flow in your browser and bind your session to their account, so your notes start being committed to their repository.

Cross-site scripting

  • Rendered Markdown is sanitised before it reaches the DOM. Raw HTML in a note is not executed.
  • Mermaid SVG output is sanitised before insertion — a diagram is user input like any other.
  • Link and image URLs are restricted to http, https and mailto, in the editor, in pasted content, and in the insert menu. A javascript: URL committed into a note would be a stored XSS affecting everyone who later opens the file.

Your history is not rewritten

ForkLeaf squashes consecutive edits so your history is not one commit per keystroke, which means it amends commits. It amends only a commit it made itself, moments ago, as you, with nothing else having landed since. Anything outside that — a commit from another device, from a collaborator, from a GitHub Action, from you on github.com — is never touched, and neither is a commit old enough to have been read by somebody.

A way to make ForkLeaf rewrite or destroy a commit it did not create is a security bug, not a bug report. Please report it privately rather than opening a public issue — the process is in SECURITY.md.

Known trade-offs

The repo scope is broad

It is the narrowest classic OAuth scope that allows writing to a private repository, and it is the only scope requested — no profile, email or organisation permission is asked for alongside it. If you only keep notes in public repositories, public_repo is offered on the sign-in page as Public repositories only.

Notes are only as private as the repository

ForkLeaf creates the notes repository private, but if you make it public, or connect a public one, your notes are public. The app cannot protect you from your own repository settings.

Local notes are unencrypted

IndexedDB content is not encrypted at rest. Anyone with access to your unlocked machine and browser profile can read it, as with any web app.

Reporting a vulnerability

Please do not open a public issue. The disclosure process, the response timeline and what is in scope are documented in SECURITY.md.