1 Commits
Author SHA1 Message Date
sneak 521cdcb343 Reports are taken only as application/json (closes #126)
check / check (push) Canceled after 0s
A page on another origin can POST text/plain or a form without a CORS
preflight, so its visitors' browsers could store reports although the
server sends no CORS headers. decodeJSON now refuses a request whose
Content-Type is not application/json (parameters such as charset
allowed), and the report handler answers it 415 with the usual error
body, storing nothing. A JSON POST waits for the preflight, which only
an origin in CORS_ALLOWED_ORIGINS passes; the page already sends
application/json.

Model: opus-5-5
2026-10-14 09:48:58 +00:00
3 changed files with 18 additions and 5 deletions
+3 -2
View File
@@ -29,8 +29,9 @@ Decide whether the repo moves to the layout `REPO_POLICIES.md` gives, with
origin can send a `text/plain` POST without a CORS preflight, so any site
could make its visitors' browsers store reports; a JSON POST waits for the
preflight, which only an origin in `CORS_ALLOWED_ORIGINS` passes. The page
already sent `application/json`. A test posts a report as `text/plain`, with
no `Content-Type`, and as `application/json` with and without a charset, and
already sent `application/json`. A test posts a report as `text/plain`, as
`application/x-www-form-urlencoded`, as `multipart/form-data`, with no
`Content-Type`, and as `application/json` with and without a charset, and
checks which are stored
- 2026-10-07: the files shared from `sneak/prompts` are its copies at commit
`dd4027b` ([#113](https://git.eeqj.de/sneak/netwatch/issues/113)), with this
+1 -1
View File
@@ -150,7 +150,7 @@ credentials, so it is bounded instead. Each refusal below answers with the same
parameters such as `charset` allowed, as the page sends it; any other is
refused with 415, and nothing of it is stored. A page on another origin can
send a `text/plain` POST, or a form, without a CORS preflight; refusing them
keeps any site from making its visitors' browsers send reports (see
keeps any site from making its visitors' browsers store reports (see
[CORS](#cors)).
- **Rate limit.** Each client address, resolved through `TRUSTED_PROXIES`, may
send `REPORTS_PER_MINUTE` reports a minute; past that it gets 429 with
+14 -2
View File
@@ -117,8 +117,8 @@ func TestHandleReportAcceptsValidReports(t *testing.T) {
// TestHandleReportStoresOnlyJSON checks that a report is stored only
// when sent as application/json, as the page sends it. One sent as
// text/plain, which a page on another origin can send without a CORS
// preflight, is refused with 415.
// text/plain or as a form, which a page on another origin can send
// without a CORS preflight, is refused with 415.
func TestHandleReportStoresOnlyJSON(t *testing.T) {
t.Parallel()
@@ -134,6 +134,18 @@ func TestHandleReportStoresOnlyJSON(t *testing.T) {
wantStatus: http.StatusUnsupportedMediaType,
wantStored: 0,
},
{
name: "a urlencoded form",
contentType: "application/x-www-form-urlencoded",
wantStatus: http.StatusUnsupportedMediaType,
wantStored: 0,
},
{
name: "a multipart form",
contentType: "multipart/form-data; boundary=x",
wantStatus: http.StatusUnsupportedMediaType,
wantStored: 0,
},
{
name: "no Content-Type",
contentType: "",