Skip to content

Security: studypilot/squawk

SECURITY.md

Security

Reporting a vulnerability

Please report vulnerabilities privately through GitHub: Security → Report a vulnerability on studypilot/squawk. Do not open a public issue. Include the affected version or commit, the steps to reproduce, and what an attacker gains.

We confirm reports within a few working days and keep you posted until a fix is released. There is no bug bounty. We are happy to credit you in the advisory.

Only the latest release and main receive fixes.

Security model

Squawk is self-hosted. Whoever runs the server controls its data; there is no central service.

  • Access is an email-domain allowlist. Users sign in through the operator's OpenID Connect provider. Only addresses with email_verified: true from OIDC_ALLOWED_EMAIL_DOMAINS (and, with OIDC_HOSTED_DOMAIN, the matching Google Workspace hd claim) get in. Every signed-in user can read and write every site. There are no roles.
  • Sessions are 12-hour HS256 JWTs signed with SESSION_SECRET, in an HttpOnly, SameSite=Lax cookie that is Secure when PUBLIC_URL is https.
  • Tokens (sqk_…) for the overlay, the CLI and agents are stored as SHA-256 hashes; the secret is shown once. Login tokens expire after 30 days, agent tokens when their creator chose, and all can be revoked on the inbox's Tokens page. Agent tokens cannot create or revoke tokens and can be restricted to one site.
  • CSRF. Writes authenticated by the session cookie must come from the server's own origin and carry a JSON body (or the multipart squawk upload). CORS on /api never allows credentials; other origins can only call the API with a bearer token.
  • Popup sign-in. The /connect page issues an overlay token only to the page origin that opened it: it posts the token with that origin as the target origin, and asks the user first unless the origin matches SQUAWK_TRUSTED_ORIGINS. The overlay accepts the answer only from the server origin and from the popup it opened.
  • Device codes are stored hashed, expire after 10 minutes, and need approval by a signed-in user. Starting device logins and popup logins is rate-limited per client address.
  • Drafts are visible only to their author; other callers get a 404.
  • Screenshots are served only to authenticated callers. File paths are generated by the server, never taken from input.
  • The Chrome extension captures only the visible area of the active tab, only when a page that announced the Squawk overlay asks for it right after a real user gesture, and hands the image only to that page. It sends nothing anywhere itself. Details: packages/extension/README.md.
  • SQUAWK_DEV_USER disables authentication for local development. The server refuses to start with it when NODE_ENV=production, which the image sets.

Things that are out of scope by design: users of an allowed domain seeing all sites, the overlay token living in the preview origin's localStorage (a script on the preview page can read it), and anything that needs control of the operator's identity provider.

There aren't any published security advisories