stringify_proposal_00_filetest.gno
3.77 Kb · 116 lines
1// PKGPATH: gno.land/r/test
2package test
3
4import (
5 "strings"
6 "testing"
7
8 "gno.land/p/nt/testutils/v0"
9 "gno.land/r/gov/dao"
10 "gno.land/r/gov/dao/impl/v0"
11 "gno.land/r/gov/dao/memberstore/v0"
12)
13
14var (
15 testUser = testutils.TestAddress("test")
16)
17
18// StringifyProposal carries its own copy of the disclosure expression, separate
19// from the one in render.gno. It has no production caller today, but it is
20// exported, and an untested copy of a security-relevant expression is free to
21// drift from the tested one. This executor pins it: CreationRealm is reached
22// through the public interface, so it can return anything, and the run of two
23// backticks closes a narrower fence.
24type hostileExec struct{}
25
26func (e *hostileExec) Execute(cur realm) error { return nil }
27
28func (e *hostileExec) String() string { return "" }
29
30// Padded on both ends as well as fenced: without the TrimSpace, InlineCode
31// pads the fence to protect the leading and trailing spaces, and the expected
32// span below no longer matches. That makes one payload cover both the escaping
33// and the trim.
34func (e *hostileExec) CreationRealm() string { return " gno.land/r/evil`` **INJECTED** " }
35
36func memberByTier(tier string) *memberstore.Member {
37 switch tier {
38 case memberstore.T1:
39 t, _ := memberstore.GetTier(memberstore.T1)
40 return memberstore.NewMember(t.InvitationPoints)
41 default:
42 panic("unsupported tier: " + tier)
43 }
44}
45
46func init(cur realm) {
47 // Load members for testing
48 mstore := memberstore.Get(0, cur)
49 mstore.DeleteAll()
50 mstore.SetTier(memberstore.T1)
51 mstore.SetMember(memberstore.T1, testUser, memberByTier(memberstore.T1))
52
53 // Set up the DAO implementation using proper constructor
54 govDAO := impl.NewGovDAO()
55 dao.UpdateImpl(cross(cur), dao.NewUpdateRequest(govDAO, []string{"gno.land/r/test", "gno.land/r/gov/dao/impl/v0"}))
56}
57
58func main(cur realm) {
59 // Create an executor in a specific realm to test creation realm tracking
60 testing.SetRealm(testing.NewCodeRealm("gno.land/r/template/contract"))
61 executor := dao.NewSimpleExecutor(0, cur, func(realm) error { return nil }, "Test executor description")
62
63 // Create a proposal request
64 proposalRequest := dao.NewProposalRequest(
65 "Test Proposal Title",
66 "This is a test proposal description to verify StringifyProposal works correctly.",
67 executor,
68 )
69
70 // Switch to user realm to create the proposal
71 testing.SetOriginCaller(testUser)
72 testing.SetRealm(testing.NewUserRealm(testUser))
73 pid := dao.MustCreateProposal(cross(cur), proposalRequest)
74
75 // Get the proposal and test the core functionality
76 prop := dao.MustGetProposal(pid)
77
78 // Test that executor string is captured correctly
79 println("Executor string:", prop.ExecutorString())
80
81 // Test that executor creation realm is captured correctly
82 println("Executor creation realm:", prop.ExecutorCreationRealm())
83
84 // Stringify
85 println("----")
86 println(impl.StringifyProposal(prop))
87
88 // The same disclosure, through a hostile executor.
89 hpid := dao.MustCreateProposal(cross(cur), dao.NewProposalRequest(
90 "Hostile", "creation realm attacks the code span", &hostileExec{}))
91 hout := impl.StringifyProposal(dao.MustGetProposal(hpid))
92
93 println("stringify widens the fence too:",
94 strings.Contains(hout, "```gno.land/r/evil`` **INJECTED**```"))
95 println("and no bold escapes it:", !strings.Contains(hout, "\n**INJECTED**"))
96}
97
98// Output:
99// Executor string: Test executor description
100// Executor creation realm: gno.land/r/template/contract
101// ----
102//
103// ### Title: Test Proposal Title
104//
105// ### Proposed by: g1w3jhxazlta047h6lta047h6lta047h6lwmjv0n
106//
107// This is a test proposal description to verify StringifyProposal works correctly.
108//
109// This proposal contains the following metadata:
110//
111// Test executor description
112//
113// Executor created in: `gno.land/r/template/contract`
114//
115// stringify widens the fence too: true
116// and no bold escapes it: true