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.
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: truefromOIDC_ALLOWED_EMAIL_DOMAINS(and, withOIDC_HOSTED_DOMAIN, the matching Google Workspacehdclaim) 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 anHttpOnly,SameSite=Laxcookie that isSecurewhenPUBLIC_URLishttps. - 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
/apinever allows credentials; other origins can only call the API with a bearer token. - Popup sign-in. The
/connectpage 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 matchesSQUAWK_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_USERdisables authentication for local development. The server refuses to start with it whenNODE_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.