carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1package main
2
3import (
4 "bytes"
5 "strings"
6 "testing"
7 "time"
8)
9
10func scanReplyFixture() apiScanReply {
11 at := time.Date(2026, 9, 1, 10, 0, 0, 0, time.UTC)
12 return apiScanReply{
13 App: "titogo",
14 Backend: "aws",
15 Engine: "clamav",
16 Signature: "ClamAV 1.4.1/27500/" + time.Now().Format(time.ANSIC),
17 MaxSignatureAgeSeconds: int64((168 * time.Hour) / time.Second),
18 Scanning: &apiScanBlock{
19 Requested: &apiScanRequested{At: at, By: "m@example.com"},
20 Enabled: &apiScanEnabled{Engine: "clamav", At: at, By: "op@example.com"},
21 },
22 }
23}
24
25// The audit line names BOTH numbers. A bucket of over-size objects reporting
26// "1000 scanned" and nothing else would be true and misleading — nobody looked
27// at the other two hundred, and the sentence that hides that is the one an
28// auditor is entitled to catch.
29func TestPrintScanStatusNamesUnscannableCount(t *testing.T) {
30 res := scanReplyFixture()
31 res.Coverage = &apiScanCoverage{LastComplete: &apiSweep{
32 At: time.Now(), Objects: 1200, Scanned: 1000, Unscanned: 200, Skipped: 195, Failed: 5,
33 }}
34
35 var b bytes.Buffer
36 printScanStatus(&b, res)
37 out := b.String()
38
39 if !strings.Contains(out, "1000 of 1200 objects scanned") {
40 t.Fatalf("coverage line missing the two headline numbers:\n%s", out)
41 }
42 if !strings.Contains(out, "200 not scanned") {
43 t.Fatalf("the unscanned count is not stated:\n%s", out)
44 }
45 if !strings.Contains(out, "195 too large or refused") {
46 t.Fatalf("the breakdown of what was not scanned is missing:\n%s", out)
47 }
48}
49
50// An encrypted store never gets the coverage sentence — not even a zeroed one.
51// A scanner cannot read ciphertext, so any number here would be a
52// true-looking claim about bytes nobody examined.
53func TestPrintScanStatusRefusesCoverageLineWhenEncrypted(t *testing.T) {
54 res := scanReplyFixture()
55 res.Scanning.Requested.Encrypted = true
56 // Even with a rollup present — which should never happen, but the print
57 // layer must not depend on the server having got it right.
58 res.Coverage = &apiScanCoverage{LastComplete: &apiSweep{Objects: 10, Scanned: 10}}
59
60 var b bytes.Buffer
61 printScanStatus(&b, res)
62 out := b.String()
63
64 if strings.Contains(out, "objects scanned") {
65 t.Fatalf("an encrypted store was given a coverage claim:\n%s", out)
66 }
67 if !strings.Contains(out, "client-encrypted") || !strings.Contains(out, "NO coverage") {
68 t.Fatalf("the output does not say why there is no coverage:\n%s", out)
69 }
70}
71
72// A sweep spanning ticks says so, and says which numbers are stale, rather
73// than implying the partial count is the answer.
74func TestPrintScanStatusShowsProgressMidSweep(t *testing.T) {
75 res := scanReplyFixture()
76 res.Coverage = &apiScanCoverage{
77 Current: &apiSweep{StartedAt: time.Now().Add(-2 * time.Hour), Objects: 300},
78 LastComplete: &apiSweep{At: time.Now().Add(-24 * time.Hour), Objects: 1200, Scanned: 1200},
79 }
80
81 var b bytes.Buffer
82 printScanStatus(&b, res)
83 out := b.String()
84
85 if !strings.Contains(out, "sweep in progress") {
86 t.Fatalf("an in-flight sweep is not mentioned:\n%s", out)
87 }
88 if !strings.Contains(out, "last COMPLETE sweep") {
89 t.Fatalf("the output does not say which numbers are stale:\n%s", out)
90 }
91 if !strings.Contains(out, "1200 of 1200 objects scanned") {
92 t.Fatalf("the completed coverage is not shown:\n%s", out)
93 }
94}
95
96// A first sweep that has not finished claims nothing at all. Partial counts
97// with no complete sweep behind them are not coverage.
98func TestPrintScanStatusClaimsNothingBeforeFirstCompleteSweep(t *testing.T) {
99 res := scanReplyFixture()
100 res.Coverage = &apiScanCoverage{Current: &apiSweep{StartedAt: time.Now(), Objects: 40}}
101
102 var b bytes.Buffer
103 printScanStatus(&b, res)
104 out := b.String()
105
106 if strings.Contains(out, "objects scanned") {
107 t.Fatalf("a partial first sweep was printed as coverage:\n%s", out)
108 }
109 if !strings.Contains(out, "no coverage can be claimed") {
110 t.Fatalf("the output does not refuse the claim outright:\n%s", out)
111 }
112}
113
114// Old signatures are called out. A clean verdict from three-month-old
115// definitions is a weaker claim than a fresh one.
116func TestPrintScanStatusWarnsOnStaleSignatures(t *testing.T) {
117 res := scanReplyFixture()
118 old := time.Now().Add(-90 * 24 * time.Hour).Format(time.ANSIC)
119 res.Signature = "ClamAV 1.4.1/27000/" + old
120 res.Coverage = &apiScanCoverage{LastComplete: &apiSweep{At: time.Now(), Objects: 1, Scanned: 1}}
121
122 var b bytes.Buffer
123 printScanStatus(&b, res)
124 out := b.String()
125
126 if !strings.Contains(out, "days old") {
127 t.Fatalf("stale signatures were not called out:\n%s", out)
128 }
129}
130
131// A signature line we cannot parse produces NO age claim. Inventing one would
132// undermine the very line it was meant to strengthen.
133func TestSignatureAgeRefusesToGuess(t *testing.T) {
134 for _, v := range []string{"ClamAV 1.4.1", "", "something/else", "ClamAV/27000/not a date"} {
135 if _, ok := signatureAge(v, time.Now()); ok {
136 t.Fatalf("signatureAge guessed an age from %q", v)
137 }
138 }
139 good := "ClamAV 1.4.1/27500/" + time.Now().Add(-48*time.Hour).Format(time.ANSIC)
140 age, ok := signatureAge(good, time.Now())
141 if !ok || age < 47*time.Hour || age > 49*time.Hour {
142 t.Fatalf("signatureAge(%q) = %v, %v", good, age, ok)
143 }
144}
145
146// An unreachable engine is reported as such, not silently omitted. "No new
147// verdicts are being reached" is the fact an operator needs.
148func TestPrintScanStatusReportsUnreachableEngine(t *testing.T) {
149 res := scanReplyFixture()
150 res.Signature = ""
151 res.EngineError = "dial unix /run/clamav/clamd.ctl: connection refused"
152
153 var b bytes.Buffer
154 printScanStatus(&b, res)
155 out := b.String()
156
157 if !strings.Contains(out, "not answering") {
158 t.Fatalf("an unreachable engine was not reported:\n%s", out)
159 }
160}
161
162// A store nobody has requested scanning for says exactly that, and names the
163// verb that would start it.
164func TestPrintScanStatusUnrequested(t *testing.T) {
165 res := apiScanReply{App: "titogo", Backend: "aws"}
166
167 var b bytes.Buffer
168 printScanStatus(&b, res)
169 out := b.String()
170
171 if !strings.Contains(out, "not requested") {
172 t.Fatalf("output = %q", out)
173 }
174 if !strings.Contains(out, "carlos store scan request --app titogo") {
175 t.Fatalf("the output does not name the verb that starts it:\n%s", out)
176 }
177}
178
179// Enabled but not enforcing is stated plainly. An operator reading "engine:
180// clamav" and assuming infected objects are blocked would be wrong, and the
181// difference is the whole of what enforcement buys.
182func TestPrintScanStatusDistinguishesEnabledFromEnforcing(t *testing.T) {
183 res := scanReplyFixture()
184 res.Coverage = &apiScanCoverage{LastComplete: &apiSweep{At: time.Now(), Objects: 2, Scanned: 2}}
185
186 var b bytes.Buffer
187 printScanStatus(&b, res)
188 if !strings.Contains(b.String(), "not enforcing") {
189 t.Fatalf("an enabled-but-not-enforcing store did not say so:\n%s", b.String())
190 }
191
192 res.Scanning.Enforce = &apiScanEnforce{Key: "carlos-scan", Value: "infected"}
193 var b2 bytes.Buffer
194 printScanStatus(&b2, res)
195 if !strings.Contains(b2.String(), "cannot read an object tagged carlos-scan=infected") {
196 t.Fatalf("an enforcing store did not say what is blocked:\n%s", b2.String())
197 }
198}
199