package e2e import ( "net/url" "os/exec" "regexp" "sort" "strings" "testing" "time" "github.com/barerepo/server/internal/token" ) var formIn = regexp.MustCompile(`]*action="([^"]+)"`) // A form is a control, and a reader must only be shown the ones they may use. Chapter 24. func TestNobodyIsShownAControlTheyMayNotUse(t *testing.T) { if _, err := exec.LookPath("git"); err != nil { t.Skip("git is not installed") } in := newInstance(t) john := in.account("john") lisa := in.account("lisa") seed(t, in, john, "john", "johnbot") post(t, in, "john", "/john/johnbot/threads", url.Values{ "title": {"a thread"}, "body": {"with a body"}}) owner, err := in.db.NewSession(t.Context(), "john") if err != nil { t.Fatal(err) } stranger, err := in.db.NewSession(t.Context(), "lisa") if err != nil { t.Fatal(err) } _ = lisa // An attached machine, last seen long enough ago that the page offers to forget it. _, tok, err := in.db.CreateToken(t.Context(), token.Runner, "john", "john/johnbot", "a runner") if err != nil { t.Fatal(err) } if _, err := in.db.AttachRunner(t.Context(), tok.ID, "john/johnbot", "uproar.local", "linux", "amd64", nil); err != nil { t.Fatal(err) } if _, err := in.db.ExecContext(t.Context(), `UPDATE runners SET last_seen = ?`, time.Now().Add(-time.Hour).Unix()); err != nil { t.Fatal(err) } // Every page a reader can reach on somebody else's repository, and the controls on it. pages := []string{ "/john/johnbot", "/john/johnbot/files", "/john/johnbot/threads", "/john/johnbot/thread/1", "/john/johnbot/runs", "/john/johnbot/runners", "/john/johnbot/releases", "/john/johnbot/config", } want := map[string][]string{ // The owner runs the repository, and is not offered a copy of what is already theirs. "the owner": { "/john/johnbot/delete", "/john/johnbot/rename", "/john/johnbot/runners/forget", "/john/johnbot/thread/1/close", "/john/johnbot/thread/1/reply", "/john/johnbot/transfer", }, // A stranger may talk and may copy. Closing is the author's and the owner's, and she is neither. "a stranger": { "/john/johnbot/copy", "/john/johnbot/thread/1/reply", }, // Signed out there is nothing to press at all, because every control needs an account. "nobody": {}, } for _, who := range []struct{ name, session string }{ {"the owner", owner}, {"a stranger", stranger}, {"nobody", ""}, } { found := map[string]bool{} for _, path := range pages { _, _, body := fetch(t, in, who.session, path) for _, m := range formIn.FindAllStringSubmatch(body, -1) { // A search box is a get form and asks nobody for permission. if m[1] != "/search" { found[m[1]] = true } } } got := make([]string, 0, len(found)) for a := range found { got = append(got, a) } sort.Strings(got) if strings.Join(got, " ") != strings.Join(want[who.name], " ") { t.Errorf("%s is shown\n %v\nand should be shown\n %v", who.name, got, want[who.name]) } } }