Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

security_test.gno

6.15 Kb · 156 lines
  1package home
  2
  3import (
  4	"chain/runtime"
  5	"encoding/base64"
  6	"net/url"
  7	"strconv"
  8	"strings"
  9	"testing"
 10
 11	"gno.land/p/nt/avl/v0"
 12	"gno.land/p/nt/testutils/v0"
 13	"gno.land/p/nt/uassert/v0"
 14)
 15
 16// A guess arrives in the page link, so anyone can write one: none of it may
 17// reach the rendered markdown.
 18func TestGuessIsNeverEchoed(t *testing.T) {
 19	setDay(launchDay)
 20	uassert.True(t, strings.Contains(Render("wordle/1?guess=abc"), "> That guess is not five letters, A to Z."))
 21	for _, guess := range []string{
 22		"[claim your reward](https://evil.example)",
 23		"x\n\n# OFFICIAL NOTICE",
 24		"![](https://evil.example/pixel.png)",
 25		`<gno-form exec="Transfer"><gno-input name="to" /></gno-form>`,
 26	} {
 27		// Any rejected guess renders the very page a harmless rejected one does.
 28		page := Render("wordle/1?guess=" + url.QueryEscape(guess))
 29		uassert.Equal(t, Render("wordle/1?guess=abc"), page, "guess leaked into the page: "+guess)
 30	}
 31}
 32
 33func TestOnlyAdminWrites(cur realm, t *testing.T) {
 34	for _, caller := range []runtime.Realm{
 35		testing.NewUserRealm(testutils.TestAddress("mallory")),
 36		testing.NewCodeRealm("gno.land/r/evil/relay"),
 37	} {
 38		testing.SetRealm(caller)
 39		uassert.AbortsWithMessage(t, cur, "unauthorized", func() { Set(cross(cur), "header", "pwned") })
 40		uassert.AbortsWithMessage(t, cur, "unauthorized", func() { Delete(cross(cur), "header") })
 41	}
 42	uassert.False(t, strings.Contains(Render(""), "pwned"))
 43}
 44
 45func TestChartTextIsEscaped(cur realm, t *testing.T) {
 46	testing.SetRealm(testing.NewUserRealm(admin))
 47	old := Get(reviewActivitySlot)
 48	Set(cross(cur), reviewActivitySlot, `title reviews; series <script>alert(1)</script>,a&b; 2026-01 1 2`)
 49	out := renderChart(reviewActivitySlot)
 50	raw, err := base64.StdEncoding.DecodeString(out[strings.Index(out, "base64,")+7 : len(out)-2])
 51	uassert.NoError(t, err)
 52	uassert.False(t, strings.Contains(string(raw), "<script>"))
 53	uassert.True(t, strings.Contains(string(raw), "&lt;script&gt;"))
 54	uassert.True(t, strings.Contains(string(raw), "a&amp;b"))
 55	Set(cross(cur), reviewActivitySlot, old)
 56}
 57
 58func TestHeartsListIsPaged(t *testing.T) {
 59	hearts = avl.NewTree()
 60	for i := 0; i < 250; i++ {
 61		hearts.Set(testutils.TestAddress("fan"+strconv.Itoa(i)).String(), int64(i))
 62	}
 63	first := Render("hearts")
 64	uassert.Equal(t, heartsPerPage, strings.Count(first, "\n- "))
 65	uassert.True(t, strings.Contains(first, "Page 1 of 3."))
 66	uassert.True(t, strings.Contains(first, "[Next]("+home+":hearts/2)"))
 67	uassert.False(t, strings.Contains(first, "[Previous]"))
 68	last := Render("hearts/3")
 69	uassert.Equal(t, 50, strings.Count(last, "\n- "))
 70	uassert.True(t, strings.Contains(last, "[Previous]("+home+":hearts/2)"))
 71	uassert.False(t, strings.Contains(last, "[Next]"))
 72	for _, bad := range []string{"hearts/0", "hearts/4", "hearts/x", "hearts/-1"} {
 73		uassert.True(t, strings.Contains(Render(bad), "There is no such page."), bad)
 74	}
 75	hearts = avl.NewTree()
 76}
 77
 78func TestOrderSurvivesBadEntries(cur realm, t *testing.T) {
 79	testing.SetRealm(testing.NewUserRealm(admin))
 80	old := Get(orderSlot)
 81	Set(cross(cur), orderSlot, "nosuchsection,,header")
 82	page := Render("")
 83	uassert.True(t, strings.HasPrefix(strings.TrimSpace(page), "I'm a developer engineer"))
 84	Delete(cross(cur), orderSlot)
 85	uassert.True(t, strings.Contains(Render(""), "This page is a contract"))
 86	Set(cross(cur), orderSlot, old)
 87}
 88
 89func TestQueryDoesNotBreakPages(t *testing.T) {
 90	uassert.Equal(t, Render(""), Render("?utm_source=x"))
 91	uassert.Equal(t, Render(""), Render("?fbclid=abc&x=1"))
 92	uassert.Equal(t, Render("hearts"), Render("hearts?x=1"))
 93	// The game keeps its query: it carries the guess.
 94	setDay(launchDay)
 95	uassert.True(t, strings.Contains(Render("wordle/1?guess=crane"), "CRANE"))
 96}
 97
 98func TestLongPathIsRefused(t *testing.T) {
 99	setDay(launchDay)
100	// The longest link the game writes: six guesses and a typed one.
101	longest := "wordle/99999/CRANE.SLATE.ROUTE.TRACE.QUERY.PROXY?guess=" + url.QueryEscape("  crane  ")
102	uassert.True(t, len(longest) <= maxPathLen)
103	uassert.False(t, strings.HasPrefix(Render(longest), "# Not found"))
104	for _, p := range []string{
105		"wordle/1?" + strings.Repeat("a=&", 100),
106		"wordle/1/" + strings.Repeat(".", 300),
107		"hearts/" + strings.Repeat("9", 300),
108	} {
109		uassert.True(t, strings.HasPrefix(Render(p), "# Not found"), p[:20])
110	}
111}
112
113func TestHeroTextIsEditable(cur realm, t *testing.T) {
114	testing.SetRealm(testing.NewUserRealm(admin))
115	old := Get(heroSlot)
116	decode := func() string {
117		out := heroImage()
118		raw, err := base64.StdEncoding.DecodeString(out[strings.Index(out, "base64,")+7 : len(out)-1])
119		uassert.NoError(t, err)
120		return string(raw)
121	}
122	uassert.True(t, strings.Contains(decode(), ">David G.</text>"))
123
124	Set(cross(cur), heroSlot, "title Dave; subtitle <b>&co</b>; bogus x; tagline hello")
125	svg := decode()
126	uassert.True(t, strings.Contains(svg, ">Dave</text>"))
127	uassert.True(t, strings.Contains(svg, ">&lt;b&gt;&amp;co&lt;/b&gt;</text>"))
128	uassert.True(t, strings.Contains(svg, ">hello</text>"))
129	// A key the section leaves out keeps its default.
130	uassert.True(t, strings.Contains(svg, ">gno.land core contributor</text>"))
131	uassert.True(t, strings.HasPrefix(heroImage(), "![David G., developer engineer at Samouraï Coop]("))
132
133	Set(cross(cur), heroSlot, "alt a ]( trap")
134	uassert.True(t, strings.HasPrefix(heroImage(), "![a )( trap]("))
135
136	Set(cross(cur), heroSlot, old)
137}
138
139func TestSetActivity(cur realm, t *testing.T) {
140	oldPR, oldRev, oldAreas := Get(activitySlot), Get(reviewActivitySlot), Get(areasSlot)
141	testing.SetRealm(testing.NewUserRealm(testutils.TestAddress("mallory")))
142	uassert.AbortsWithMessage(t, cur, "unauthorized", func() { SetActivity(cross(cur), "x", "x", "x") })
143	uassert.Equal(t, oldPR, Get(activitySlot))
144
145	testing.SetRealm(testing.NewUserRealm(admin))
146	SetActivity(cross(cur), "title a; series s; 2026-01 1", "", "title c; slice 3 Docs")
147	uassert.Equal(t, "title a; series s; 2026-01 1", Get(activitySlot))
148	uassert.Equal(t, oldRev, Get(reviewActivitySlot)) // empty leaves it as it is
149	uassert.Equal(t, "title c; slice 3 Docs", Get(areasSlot))
150	out := renderChart(activitySlot)
151	raw, err := base64.StdEncoding.DecodeString(out[strings.Index(out, "base64,")+7 : len(out)-2])
152	uassert.NoError(t, err)
153	uassert.True(t, strings.Contains(string(raw), ">1 a</text>"))
154
155	SetActivity(cross(cur), oldPR, oldRev, oldAreas)
156}