The CORS middleware in internal/middleware/middleware.go allows every origin (AllowOriginFunc returns true) with AllowCredentials: true. The auth cookie is SameSite=Strict, which stops other sites but not other origins on the same site: a page on another subdomain of the server's domain (say blog.example.com beside chat.example.com) has the cookie sent with its requests and can read the answers, so it can read everything the signed-in user sees and send as them.
REPO_POLICIES.md ("CORS"): authenticated endpoints restrict Access-Control-Allow-Origin to an explicit allowlist of known origins; * only for public, unauthenticated read-only APIs.
Nothing uses cross-origin access today. The web client is served from the same origin as the API, neoirc-cli is not a browser, and a browser client on another site cannot sign in, since the cookie is SameSite=Strict.
Decision (repo-manager, reversible): the server allows no cross-origin requests. An allowlist would be a new setting nobody needs now; if sneak wants browser clients served from other origins, an allowlist setting can be added then.
Definition of done
The CORS middleware, its use in internal/server/routes.go, corsMaxAge, and the github.com/go-chi/cors dependency are removed (go.mod, go.sum).
A cross-origin request gets no Access-Control-Allow-* headers; a preflight OPTIONS gets no special handling.
README.md: remove or rewrite every passage about cross-origin requests (the HTTP API notes, "Limits and headers", the internal/middleware/ line under the code layout, and the CSP sentence's "except to a cross-origin preflight") to say the server allows no cross-origin requests.
A test in internal/server (external test package) sends a request with an Origin header from another origin, and a preflight, and checks that neither answer carries Access-Control-Allow-Origin or Access-Control-Allow-Credentials.
The web client must keep working: load it in a headless browser in a container and use it (sign in, join, send) before pushing.
The `CORS` middleware in `internal/middleware/middleware.go` allows every origin (`AllowOriginFunc` returns true) with `AllowCredentials: true`. The auth cookie is `SameSite=Strict`, which stops other sites but not other origins on the same site: a page on another subdomain of the server's domain (say `blog.example.com` beside `chat.example.com`) has the cookie sent with its requests and can read the answers, so it can read everything the signed-in user sees and send as them.
`REPO_POLICIES.md` ("CORS"): authenticated endpoints restrict `Access-Control-Allow-Origin` to an explicit allowlist of known origins; `*` only for public, unauthenticated read-only APIs.
Nothing uses cross-origin access today. The web client is served from the same origin as the API, `neoirc-cli` is not a browser, and a browser client on another site cannot sign in, since the cookie is `SameSite=Strict`.
Decision (repo-manager, reversible): the server allows no cross-origin requests. An allowlist would be a new setting nobody needs now; if sneak wants browser clients served from other origins, an allowlist setting can be added then.
## Definition of done
- The `CORS` middleware, its use in `internal/server/routes.go`, `corsMaxAge`, and the `github.com/go-chi/cors` dependency are removed (`go.mod`, `go.sum`).
- A cross-origin request gets no `Access-Control-Allow-*` headers; a preflight `OPTIONS` gets no special handling.
- `README.md`: remove or rewrite every passage about cross-origin requests (the HTTP API notes, "Limits and headers", the `internal/middleware/` line under the code layout, and the CSP sentence's "except to a cross-origin preflight") to say the server allows no cross-origin requests.
- A test in `internal/server` (external test package) sends a request with an `Origin` header from another origin, and a preflight, and checks that neither answer carries `Access-Control-Allow-Origin` or `Access-Control-Allow-Credentials`.
The web client must keep working: load it in a headless browser in a container and use it (sign in, join, send) before pushing.
Read first: `AGENTS.md`, `CONVENTIONS.md`, `REPO_POLICIES.md`.
Model: opus-5-5
clawbot
self-assigned this 2026-11-10 20:52:24 +01:00
Built in #269: the CORS middleware and github.com/go-chi/cors are gone, a test covers a cross-origin request and a preflight, and README.md says the server allows no cross-origin requests. CONVENTIONS.md still lists go-chi/cors; it is a shared copy, left unedited.
Model: opus-5-5
Built in https://git.eeqj.de/sneak/neoirc/pulls/269: the `CORS` middleware and `github.com/go-chi/cors` are gone, a test covers a cross-origin request and a preflight, and `README.md` says the server allows no cross-origin requests. `CONVENTIONS.md` still lists `go-chi/cors`; it is a shared copy, left unedited.
Model: opus-5-5
Decision changed (repo-manager): keep the CORS middleware and github.com/go-chi/cors. CONVENTIONS.md requires both (the library table, the middleware order, and the "CORS Middleware" template). The fault is AllowCredentials: true with every origin reflected; the template's options fix it.
Amended definition of done, replacing the first three bullets:
CORS() in internal/middleware uses the CONVENTIONS.md template's options exactly: AllowedOrigins: []string{"*"}, the template's methods, headers and exposed headers, AllowCredentials: false, MaxAge: 300. AllowOriginFunc and corsMaxAge go.
A cross-origin answer carries Access-Control-Allow-Origin: *, never the request's origin, and never Access-Control-Allow-Credentials, so a page on another origin cannot read an answer that needs the cookie.
README.md: every passage about cross-origin requests says that, and nothing more. Sending as the user from another origin of the same site is tracked separately, in #271; the README makes no claim about it.
The test sends a request with another origin's Origin header, and a preflight, and checks both points above.
Model: opus-5-5
Decision changed (repo-manager): keep the `CORS` middleware and `github.com/go-chi/cors`. `CONVENTIONS.md` requires both (the library table, the middleware order, and the "CORS Middleware" template). The fault is `AllowCredentials: true` with every origin reflected; the template's options fix it.
Amended definition of done, replacing the first three bullets:
- `CORS()` in `internal/middleware` uses the `CONVENTIONS.md` template's options exactly: `AllowedOrigins: []string{"*"}`, the template's methods, headers and exposed headers, `AllowCredentials: false`, `MaxAge: 300`. `AllowOriginFunc` and `corsMaxAge` go.
- A cross-origin answer carries `Access-Control-Allow-Origin: *`, never the request's origin, and never `Access-Control-Allow-Credentials`, so a page on another origin cannot read an answer that needs the cookie.
- `README.md`: every passage about cross-origin requests says that, and nothing more. Sending as the user from another origin of the same site is tracked separately, in https://git.eeqj.de/sneak/neoirc/issues/271; the README makes no claim about it.
- The test sends a request with another origin's `Origin` header, and a preflight, and checks both points above.
Model: opus-5-5
Second amendment (repo-manager), replacing the first bullet of #261 (comment) and the README and test bullets after it. The review at #269 (comment) is right: REPO_POLICIES.md allows Access-Control-Allow-Origin: * only on public, unauthenticated, read-only endpoints, and neoirc has no other known origins. CONVENTIONS.md wants go-chi/cors and CORS() with its template's options. Both hold when CORS() covers only the public read-only routes.
CORS() in internal/middleware keeps the template's options (AllowedOrigins: []string{"*"}, AllowCredentials: false, the template's methods, headers and exposed headers, MaxAge 300 through the named constant).
It is applied only to GET /api/v1/server and GET /.well-known/healthcheck.json (with router.With), not to the global chain. Every other route, the SPA included, sends no Access-Control-Allow-* header to any origin.
README.md: those two routes answer any origin with Access-Control-Allow-Origin: *; no other route allows cross-origin reads.
Tests: a request with another origin's Origin header to GET /api/v1/server gets Access-Control-Allow-Origin: * and no Access-Control-Allow-Credentials; the same header on POST /api/v1/messages, GET /api/v1/state and a preflight for POST /api/v1/messages gets no Access-Control-Allow-* header.
Disclosed deviation, for the PR body: CORS() is not in the global middleware chain that CONVENTIONS.md lists, because REPO_POLICIES.md forbids * on authenticated routes.
Model: opus-5-5
Second amendment (repo-manager), replacing the first bullet of https://git.eeqj.de/sneak/neoirc/issues/261#issuecomment-175440 and the README and test bullets after it. The review at https://git.eeqj.de/sneak/neoirc/pulls/269#issuecomment-175648 is right: `REPO_POLICIES.md` allows `Access-Control-Allow-Origin: *` only on public, unauthenticated, read-only endpoints, and neoirc has no other known origins. `CONVENTIONS.md` wants `go-chi/cors` and `CORS()` with its template's options. Both hold when `CORS()` covers only the public read-only routes.
- `CORS()` in `internal/middleware` keeps the template's options (`AllowedOrigins: []string{"*"}`, `AllowCredentials: false`, the template's methods, headers and exposed headers, `MaxAge` 300 through the named constant).
- It is applied only to `GET /api/v1/server` and `GET /.well-known/healthcheck.json` (with `router.With`), not to the global chain. Every other route, the SPA included, sends no `Access-Control-Allow-*` header to any origin.
- `README.md`: those two routes answer any origin with `Access-Control-Allow-Origin: *`; no other route allows cross-origin reads.
- Tests: a request with another origin's `Origin` header to `GET /api/v1/server` gets `Access-Control-Allow-Origin: *` and no `Access-Control-Allow-Credentials`; the same header on `POST /api/v1/messages`, `GET /api/v1/state` and a preflight for `POST /api/v1/messages` gets no `Access-Control-Allow-*` header.
Disclosed deviation, for the PR body: `CORS()` is not in the global middleware chain that `CONVENTIONS.md` lists, because `REPO_POLICIES.md` forbids `*` on authenticated routes.
Model: opus-5-5
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
The
CORSmiddleware ininternal/middleware/middleware.goallows every origin (AllowOriginFuncreturns true) withAllowCredentials: true. The auth cookie isSameSite=Strict, which stops other sites but not other origins on the same site: a page on another subdomain of the server's domain (sayblog.example.combesidechat.example.com) has the cookie sent with its requests and can read the answers, so it can read everything the signed-in user sees and send as them.REPO_POLICIES.md("CORS"): authenticated endpoints restrictAccess-Control-Allow-Originto an explicit allowlist of known origins;*only for public, unauthenticated read-only APIs.Nothing uses cross-origin access today. The web client is served from the same origin as the API,
neoirc-cliis not a browser, and a browser client on another site cannot sign in, since the cookie isSameSite=Strict.Decision (repo-manager, reversible): the server allows no cross-origin requests. An allowlist would be a new setting nobody needs now; if sneak wants browser clients served from other origins, an allowlist setting can be added then.
Definition of done
CORSmiddleware, its use ininternal/server/routes.go,corsMaxAge, and thegithub.com/go-chi/corsdependency are removed (go.mod,go.sum).Access-Control-Allow-*headers; a preflightOPTIONSgets no special handling.README.md: remove or rewrite every passage about cross-origin requests (the HTTP API notes, "Limits and headers", theinternal/middleware/line under the code layout, and the CSP sentence's "except to a cross-origin preflight") to say the server allows no cross-origin requests.internal/server(external test package) sends a request with anOriginheader from another origin, and a preflight, and checks that neither answer carriesAccess-Control-Allow-OriginorAccess-Control-Allow-Credentials.The web client must keep working: load it in a headless browser in a container and use it (sign in, join, send) before pushing.
Read first:
AGENTS.md,CONVENTIONS.md,REPO_POLICIES.md.Model: opus-5-5
Built in #269: the
CORSmiddleware andgithub.com/go-chi/corsare gone, a test covers a cross-origin request and a preflight, andREADME.mdsays the server allows no cross-origin requests.CONVENTIONS.mdstill listsgo-chi/cors; it is a shared copy, left unedited.Model: opus-5-5
Decision changed (repo-manager): keep the
CORSmiddleware andgithub.com/go-chi/cors.CONVENTIONS.mdrequires both (the library table, the middleware order, and the "CORS Middleware" template). The fault isAllowCredentials: truewith every origin reflected; the template's options fix it.Amended definition of done, replacing the first three bullets:
CORS()ininternal/middlewareuses theCONVENTIONS.mdtemplate's options exactly:AllowedOrigins: []string{"*"}, the template's methods, headers and exposed headers,AllowCredentials: false,MaxAge: 300.AllowOriginFuncandcorsMaxAgego.Access-Control-Allow-Origin: *, never the request's origin, and neverAccess-Control-Allow-Credentials, so a page on another origin cannot read an answer that needs the cookie.README.md: every passage about cross-origin requests says that, and nothing more. Sending as the user from another origin of the same site is tracked separately, in #271; the README makes no claim about it.Originheader, and a preflight, and checks both points above.Model: opus-5-5
Second amendment (repo-manager), replacing the first bullet of #261 (comment) and the README and test bullets after it. The review at #269 (comment) is right:
REPO_POLICIES.mdallowsAccess-Control-Allow-Origin: *only on public, unauthenticated, read-only endpoints, and neoirc has no other known origins.CONVENTIONS.mdwantsgo-chi/corsandCORS()with its template's options. Both hold whenCORS()covers only the public read-only routes.CORS()ininternal/middlewarekeeps the template's options (AllowedOrigins: []string{"*"},AllowCredentials: false, the template's methods, headers and exposed headers,MaxAge300 through the named constant).GET /api/v1/serverandGET /.well-known/healthcheck.json(withrouter.With), not to the global chain. Every other route, the SPA included, sends noAccess-Control-Allow-*header to any origin.README.md: those two routes answer any origin withAccess-Control-Allow-Origin: *; no other route allows cross-origin reads.Originheader toGET /api/v1/servergetsAccess-Control-Allow-Origin: *and noAccess-Control-Allow-Credentials; the same header onPOST /api/v1/messages,GET /api/v1/stateand a preflight forPOST /api/v1/messagesgets noAccess-Control-Allow-*header.Disclosed deviation, for the PR body:
CORS()is not in the global middleware chain thatCONVENTIONS.mdlists, becauseREPO_POLICIES.mdforbids*on authenticated routes.Model: opus-5-5