Scope jobs and snapshots to a browser session
An HttpOnly, SameSite=Lax session cookie is issued when a job is created. Job and snapshot routes return 404 to any other session, so indexed source is only readable by the browser that loaded it.
This commit is contained in:
+36
-5
@@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/cookiejar"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -67,14 +68,21 @@ func TestPathValidation(t *testing.T) {
|
||||
}
|
||||
|
||||
type client struct {
|
||||
t *testing.T
|
||||
url string
|
||||
t *testing.T
|
||||
url string
|
||||
http *http.Client
|
||||
}
|
||||
|
||||
// stranger is a different browser session against the same server.
|
||||
func (c client) stranger() client {
|
||||
jar, _ := cookiejar.New(nil)
|
||||
return client{c.t, c.url, &http.Client{Jar: jar}}
|
||||
}
|
||||
|
||||
func (c client) do(method, path, body string) (int, []byte) {
|
||||
c.t.Helper()
|
||||
req, _ := http.NewRequest(method, c.url+path, strings.NewReader(body))
|
||||
res, err := http.DefaultClient.Do(req)
|
||||
res, err := c.http.Do(req)
|
||||
if err != nil {
|
||||
c.t.Fatal(err)
|
||||
}
|
||||
@@ -100,7 +108,7 @@ func newTestServer(t *testing.T) (*Server, client) {
|
||||
server := NewServer(filepath.Join(t.TempDir(), "codenavigator"))
|
||||
ts := httptest.NewServer(server)
|
||||
t.Cleanup(ts.Close)
|
||||
return server, client{t, ts.URL}
|
||||
return server, client{t, ts.URL, nil}.stranger()
|
||||
}
|
||||
|
||||
func TestLocalJobLifecycle(t *testing.T) {
|
||||
@@ -118,7 +126,7 @@ func TestLocalJobLifecycle(t *testing.T) {
|
||||
c.json("POST", "/api/jobs/"+created.JobID+"/files?path=..%2Fescape.js", "x", 400, nil)
|
||||
c.json("POST", "/api/jobs/"+created.JobID+"/commit", "", 202, nil)
|
||||
|
||||
res, err := http.Get(c.url + "/api/jobs/" + created.JobID + "/events")
|
||||
res, err := c.http.Get(c.url + "/api/jobs/" + created.JobID + "/events")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -139,6 +147,12 @@ func TestLocalJobLifecycle(t *testing.T) {
|
||||
}
|
||||
snapshotURL := "/api/snapshots/" + *job.SnapshotID
|
||||
|
||||
other := c.stranger()
|
||||
other.json("GET", "/api/jobs/"+created.JobID, "", 404, nil)
|
||||
other.json("POST", "/api/jobs/"+created.JobID+"/files?path=evil.js", "x", 404, nil)
|
||||
other.json("GET", snapshotURL+"/scene", "", 404, nil)
|
||||
other.json("GET", snapshotURL+"/entities/1/source", "", 404, nil)
|
||||
|
||||
var summary map[string]any
|
||||
c.json("GET", snapshotURL, "", 200, &summary)
|
||||
if summary["fileCount"] != 2.0 || summary["references"] != 1.0 || summary["name"] != "demo" {
|
||||
@@ -255,3 +269,20 @@ func must[T any](v T, err error) T {
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func TestSessionCookieFlags(t *testing.T) {
|
||||
_, c := newTestServer(t)
|
||||
req, _ := http.NewRequest("POST", c.url+"/api/jobs", strings.NewReader(`{"kind":"local"}`))
|
||||
req.Header.Set("X-Forwarded-Proto", "https")
|
||||
res, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
res.Body.Close()
|
||||
cookie := res.Header.Get("Set-Cookie")
|
||||
for _, want := range []string{sessionCookie + "=", "HttpOnly", "Secure", "SameSite=Lax", "Path=/"} {
|
||||
if !strings.Contains(cookie, want) {
|
||||
t.Errorf("Set-Cookie %q missing %q", cookie, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user