Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/git/vole.git

Write over SSH with a registered key:

git clone ssh://git@0.0.0.0:2222/vole.git

A free and open-source forge that lives in your Git repo.

Code

392 commit(s) on main

Merge change request 01M70WES5ZZDJ6RP226WB11BBZ: Let people rename and remove their own passkeys keenan · 53m ago 4b8c9e1d
history blame raw 5.1 KiB
1package agent
2
3import (
4 "os"
5 "strings"
6 "testing"
7
8 "rodent.software/vole/ci/redact"
9)
10
11func TestSpoolKeepsWhatFits(t *testing.T) {
12 t.Parallel()
13 s, err := newSpool(1024)
14 if err != nil {
15 t.Fatal(err)
16 }
17 defer s.Close()
18 if _, err := s.Write([]byte("hello\n")); err != nil {
19 t.Fatal(err)
20 }
21 if s.Len() != 6 {
22 t.Fatalf("Len = %d, want 6", s.Len())
23 }
24 if err := s.seal(); err != nil {
25 t.Fatal(err)
26 }
27 raw, err := os.ReadFile(s.Path())
28 if err != nil {
29 t.Fatal(err)
30 }
31 if string(raw) != "hello\n" {
32 t.Fatalf("log = %q", raw)
33 }
34}
35
36// A capped log reports the whole write as taken, because a short write would kill
37// the process producing it; the excess is dropped and the file says so.
38func TestSpoolTruncatesAndSaysSo(t *testing.T) {
39 t.Parallel()
40 s, err := newSpool(16)
41 if err != nil {
42 t.Fatal(err)
43 }
44 defer s.Close()
45 if n, err := s.Write(make([]byte, 100)); err != nil || n != 100 {
46 t.Fatalf("Write = %d, %v; want 100, nil", n, err)
47 }
48 if s.Len() != 16 {
49 t.Fatalf("Len = %d, want the cap", s.Len())
50 }
51 if err := s.seal(); err != nil {
52 t.Fatal(err)
53 }
54 raw, err := os.ReadFile(s.Path())
55 if err != nil {
56 t.Fatal(err)
57 }
58 if len(raw) <= 16 {
59 t.Fatalf("the truncation note was not appended: %d bytes", len(raw))
60 }
61 if !strings.Contains(string(raw), "log truncated") {
62 t.Fatalf("the log does not say it was truncated: %q", raw)
63 }
64 // Step ranges are computed from Len, which never exceeds the cap, so a range can
65 // never name bytes the note overwrote.
66 if s.Len() > 16 {
67 t.Fatalf("Len = %d after sealing, want at most the cap", s.Len())
68 }
69}
70
71func TestSpoolCloseRemovesTheFile(t *testing.T) {
72 t.Parallel()
73 s, err := newSpool(1024)
74 if err != nil {
75 t.Fatal(err)
76 }
77 if _, err := s.Write([]byte("x")); err != nil {
78 t.Fatal(err)
79 }
80 name := s.Path()
81 if err := s.Close(); err != nil {
82 t.Fatal(err)
83 }
84 if _, err := os.Stat(name); !os.IsNotExist(err) {
85 t.Fatalf("the log file survived Close: %v", err)
86 }
87}
88
89func TestCheckArtifactLimits(t *testing.T) {
90 t.Parallel()
91 work := t.TempDir()
92 write := func(name, body string) {
93 if err := os.WriteFile(work+"/"+name, []byte(body), 0o644); err != nil {
94 t.Fatal(err)
95 }
96 }
97 write("a", "12345")
98 files := []artifactFile{{Path: "a", Name: "a"}}
99 if err := checkArtifactLimits(work, files); err != nil {
100 t.Fatalf("a small artifact set was refused: %v", err)
101 }
102 many := make([]artifactFile, MaxRunArtifactFiles+1)
103 for i := range many {
104 many[i] = artifactFile{Path: "a", Name: "a"}
105 }
106 if err := checkArtifactLimits(work, many); err == nil {
107 t.Fatal("too many artifact files were accepted")
108 }
109 // A sparse file is a 4 GiB stat result that costs no disk, which is how the byte
110 // limit can be tested without writing 4 GiB.
111 bigPath := work + "/big"
112 if err := os.WriteFile(bigPath, nil, 0o644); err != nil {
113 t.Fatal(err)
114 }
115 if err := os.Truncate(bigPath, MaxRunArtifactBytes+1); err != nil {
116 t.Skipf("this filesystem does not support a sparse file: %v", err)
117 }
118 big := []artifactFile{{Path: "big", Name: "big"}}
119 if err := checkArtifactLimits(work, big); err == nil {
120 t.Fatal("an oversized artifact set was accepted")
121 }
122}
123
124// A secret is removed before the bytes reach the file, so the streamer and the
125// instance's copy are masked by construction rather than by a later pass.
126func TestSpoolMasksSecretValues(t *testing.T) {
127 t.Parallel()
128 s, err := newSpool(1024)
129 if err != nil {
130 t.Fatal(err)
131 }
132 defer s.Close()
133 s.SetMask(redact.New([]string{"s3cret-token"}))
134
135 // Two writes, because a pipe splits wherever it likes.
136 if _, err := s.Write([]byte("token=s3cret-")); err != nil {
137 t.Fatal(err)
138 }
139 if _, err := s.Write([]byte("token\n")); err != nil {
140 t.Fatal(err)
141 }
142 if err := s.seal(); err != nil {
143 t.Fatal(err)
144 }
145 raw, err := os.ReadFile(s.Path())
146 if err != nil {
147 t.Fatal(err)
148 }
149 if string(raw) != "token="+redact.Placeholder+"\n" {
150 t.Fatalf("log = %q", raw)
151 }
152 if s.Len() != int64(len(raw)) {
153 t.Fatalf("Len = %d, file = %d bytes; step offsets must match the file", s.Len(), len(raw))
154 }
155}
156
157// The held-back tail is flushed when the log is sealed, so a value at the very end
158// is masked rather than missing.
159func TestSpoolFlushesTheMaskedTail(t *testing.T) {
160 t.Parallel()
161 s, err := newSpool(1024)
162 if err != nil {
163 t.Fatal(err)
164 }
165 defer s.Close()
166 s.SetMask(redact.New([]string{"s3cret-token"}))
167 if _, err := s.Write([]byte("done s3cret-token")); err != nil {
168 t.Fatal(err)
169 }
170 if err := s.seal(); err != nil {
171 t.Fatal(err)
172 }
173 raw, err := os.ReadFile(s.Path())
174 if err != nil {
175 t.Fatal(err)
176 }
177 if string(raw) != "done "+redact.Placeholder {
178 t.Fatalf("log = %q", raw)
179 }
180}
181
182// The runner flushes at the end of each step, so a few held-back bytes do not make
183// the next step's range start early.
184func TestSpoolFlushEndsAStepRange(t *testing.T) {
185 t.Parallel()
186 s, err := newSpool(1024)
187 if err != nil {
188 t.Fatal(err)
189 }
190 defer s.Close()
191 s.SetMask(redact.New([]string{"s3cret-token"}))
192 start := s.Len()
193 if _, err := s.Write([]byte("step one s3cret-token")); err != nil {
194 t.Fatal(err)
195 }
196 s.Flush()
197 want := int64(len("step one ") + len(redact.Placeholder))
198 if end := s.Len(); end != start+want {
199 t.Fatalf("step range = %d bytes, want %d", end-start, want)
200 }
201}
202