oficina / sheets Public

Clone
git clone https://amadan.net/oficina/sheets

Plain git — no account needed to clone.

Download

Download this file

1package sheets
2
3import (
4 "net/http"
5 "net/http/httptest"
6 "net/url"
7 "regexp"
8 "strings"
9 "testing"
10)
11
12// linkInBody pulls the one /l/<token> address a rendered page shows.
13var linkInBody = regexp.MustCompile(`/l/([A-Za-z0-9_-]{16,})`)
14
15// A share-link token is the whole capability: whoever holds it reads a
16// sealed sheet without signing in, without a member row, and without
17// leaving a trace we can revoke by name. A query string is written into
18// the platform edge's access logs, and AGENTS.md's rule is that those
19// logs are storage we cannot grep — so a token that transits a URL is
20// deposited, in the clear, somewhere we can neither audit nor redact.
21//
22// Browser history was the cost the original code weighed and accepted.
23// The access log is the one it did not, and it is the larger of the two.
24func TestTheLinkTokenNeverReachesAURL(t *testing.T) {
25 app, cookies := signIn(t)
26 s, _ := newSheet(t, app, cookies, "Budget")
27
28 form := url.Values{"role": {string(SheetViewer)}}
29 rec := do(t, app, as(postForm(sharePath(s)+"/link", form), cookies))
30
31 if loc := rec.Header().Get("Location"); loc != "" {
32 t.Fatalf("minting a link redirected to %q — a token cannot travel in a URL", loc)
33 }
34 if rec.Code != http.StatusOK {
35 t.Fatalf("minting a link = %d, want 200 with the screen rendered: %s",
36 rec.Code, rec.Body.String())
37 }
38
39 // Shown once, and it has to be the real thing rather than a
40 // placeholder — so the address on the page is exercised.
41 body := rec.Body.String()
42 m := linkInBody.FindStringSubmatch(body)
43 if m == nil {
44 t.Fatal("minting a link rendered no /l/ address — the token was lost, " +
45 "and only its hash is stored, so it cannot be recovered")
46 }
47 if got := do(t, app, httptest.NewRequest(http.MethodGet, "/l/"+m[1], nil)); got.Code != http.StatusOK {
48 t.Fatalf("the address the screen showed does not open the sheet: %d", got.Code)
49 }
50}
51
52// Closing the channel means the query parameter stops being read at
53// all. While the share screen still echoes a token handed to it, the
54// old URL remains a working way to put one in an access log — a caller
55// only has to build it.
56func TestTheShareScreenIgnoresATokenInItsQuery(t *testing.T) {
57 app, cookies := signIn(t)
58 s, _ := newSheet(t, app, cookies, "Budget")
59
60 token, err := app.createLink(s.ID, SheetViewer, signedInMember(t, app))
61 if err != nil {
62 t.Fatalf("createLink: %v", err)
63 }
64
65 body := do(t, app, as(httptest.NewRequest(http.MethodGet,
66 sharePath(s)+"?notice=link&token="+token, nil), cookies)).Body.String()
67 if strings.Contains(body, token) {
68 t.Fatal("the share screen echoed a token from its query string")
69 }
70 if strings.Contains(body, "/l/") {
71 t.Fatal("the share screen rendered a link address from its query string")
72 }
73}
74