rastrillo / fiesta Public

Leave parsing to the Row Rule check's callers, which are all tests

The gate failed on the previous commit at govulncheck: rowrule.Breaches
called x/net's html.Parse, which has a known quadratic case
(GO-2026-6860, fixed in x/net v0.62.0), and govulncheck fails for any
package of the module that calls it. When the check lived in check's
test file the call sat in test code, which govulncheck does not scan.

Breaches now walks a document its caller has respelled and parsed.
Every caller is a test reading this repository's markup, so the call
goes back out of the scan with no change to what the check reads. The
alternative, raising x/net to v0.62.0, would move a requirement every
app importing Fiesta inherits, in the middle of a release candidate,
to cover input that is never untrusted.

A caller that skips the respell is still told: a class list left
unrespelled is reported, as before.

Red: the hub's govulncheck shard on 0cadc79, naming
internal/rowrule/rowrule.go calling html.Parse. Green: make govulncheck
here finds no vulnerability the code calls, and the Row Rule's unit
tests, TestEveryDropdownSitsInARow and TestTheLandingKeepsTheRowRule
pass.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev 36aa5796748667856cf3bc99f2e862dedd270217 parent 0cadc79
4 files changed, +49 −16
  • check/rows_rule_test.go +10 −1
  • internal/rowrule/rowrule.go +13 −12
  • internal/rowrule/rowrule_test.go +18 −2
  • internal/www/landing_rows_browser_test.go +8 −1

CI passed — run details

CI log
=== gofmt ===
=== no-rastrillo ===
go test -run '^(TestFiestaNeverImportsRastrillo|TestThePublicPackagesLinkNoBrowser)$' -count=1 .
ok  	amadan.net/rastrillo/fiesta	0.255s
=== size ===
go test ./internal/build -run '^TestTheOneLinkIsUnder25KiBGzipped$' -count=1 -v
=== RUN   TestTheOneLinkIsUnder25KiBGzipped
    build_test.go:138: fiesta-plain.css: 158347 bytes, 23104 bytes gzipped (22.6 KiB of the 25.0 KiB limit)
    build_test.go:138: fiesta-signal.css: 158442 bytes, 23217 bytes gzipped (22.7 KiB of the 25.0 KiB limit)
    build_test.go:138: fiesta.css: 161261 bytes, 23823 bytes gzipped (23.3 KiB of the 25.0 KiB limit)
--- PASS: TestTheOneLinkIsUnder25KiBGzipped (0.03s)
PASS
ok  	amadan.net/rastrillo/fiesta/internal/build	0.029s
=== history ===
history: 41 commits; release tags: v1.0.0
=== suite ===
--- shard 0 of 48 ---
==> shard 0 of 48: make root
go build ./...
go vet ./...
FIESTA_TEST_REQUIRE_NODE=1 FIESTA_TEST_REQUIRE_GIT=1 FIESTA_TEST_REQUIRE_TAGS=1 ./hack/gotest.sh root 'internal/gallery@3 cmd/fiesta@2 internal/www@2'
ok  	amadan.net/rastrillo/fiesta	3.333s
ok  	amadan.net/rastrillo/fiesta/check	10.391s
ok  	amadan.net/rastrillo/fiesta/example	0.124s
?   	amadan.net/rastrillo/fiesta/hack/seed/english	[no test files]
ok  	amadan.net/rastrillo/fiesta/internal/axe	0.100s
ok  	amadan.net/rastrillo/fiesta/internal/build	1.143s
ok  	amadan.net/rastrillo/fiesta/internal/codeview	0.006s
?   	amadan.net/rastrillo/fiesta/internal/harness	[no test files]
ok  	amadan.net/rastrillo/fiesta/internal/markup	0.015s
ok  	amadan.net/rastrillo/fiesta/internal/nodetest	0.339s
ok  	amadan.net/rastrillo/fiesta/internal/rowrule	0.007s
ok  	amadan.net/rastrillo/fiesta/internal/site	3.520s
ok  	amadan.net/rastrillo/fiesta/internal/tokens	0.010s
suite: root took 23s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m12.220000s 0m2.370000s
--- shard 1 of 48 ---
==> shard 1 of 48: make test.internal.gallery~1of3
FIESTA_TEST_REQUIRE_NODE=1 FIESTA_TEST_REQUIRE_GIT=1 FIESTA_TEST_REQUIRE_TAGS=1 ./hack/gotest.sh test 'internal.gallery~1of3'
gotest: slice 1 of 3 of ./internal/gallery/ runs 37 of 109 test tests
ok  	amadan.net/rastrillo/fiesta/internal/gallery	101.897s
suite: test.internal.gallery~1of3 took 111s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m50.380000s 0m1.470000s
--- shard 2 of 48 ---
==> shard 2 of 48: make test.internal.gallery~2of3
FIESTA_TEST_REQUIRE_NODE=1 FIESTA_TEST_REQUIRE_GIT=1 FIESTA_TEST_REQUIRE_TAGS=1 ./hack/gotest.sh test 'internal.gallery~2of3'
gotest: slice 2 of 3 of ./internal/gallery/ runs 36 of 109 test tests
ok  	amadan.net/rastrillo/fiesta/internal/gallery	107.170s
suite: test.internal.gallery~2of3 took 117s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m54.470000s 0m1.520000s
--- shard 3 of 48 ---
==> shard 3 of 48: make test.internal.gallery~3of3
FIESTA_TEST_REQUIRE_NODE=1 FIESTA_TEST_REQUIRE_GIT=1 FIESTA_TEST_REQUIRE_TAGS=1 ./hack/gotest.sh test 'internal.gallery~3of3'
gotest: slice 3 of 3 of ./internal/gallery/ runs 36 of 109 test tests
ok  	amadan.net/rastrillo/fiesta/internal/gallery	93.092s
suite: test.internal.gallery~3of3 took 103s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m48.220000s 0m1.320000s
--- shard 4 of 48 ---
==> shard 4 of 48: make test.cmd.fiesta~1of2
FIESTA_TEST_REQUIRE_NODE=1 FIESTA_TEST_REQUIRE_GIT=1 FIESTA_TEST_REQUIRE_TAGS=1 ./hack/gotest.sh test 'cmd.fiesta~1of2'
gotest: slice 1 of 2 of ./cmd/fiesta/ runs 7 of 12 test tests
ok  	amadan.net/rastrillo/fiesta/cmd/fiesta	94.586s
suite: test.cmd.fiesta~1of2 took 100s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m35.380000s 0m2.470000s
--- shard 5 of 48 ---
==> shard 5 of 48: make test.cmd.fiesta~2of2
FIESTA_TEST_REQUIRE_NODE=1 FIESTA_TEST_REQUIRE_GIT=1 FIESTA_TEST_REQUIRE_TAGS=1 ./hack/gotest.sh test 'cmd.fiesta~2of2'
gotest: slice 2 of 2 of ./cmd/fiesta/ runs 5 of 12 test tests
ok  	amadan.net/rastrillo/fiesta/cmd/fiesta	91.558s
suite: test.cmd.fiesta~2of2 took 96s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m37.260000s 0m2.680000s
--- shard 6 of 48 ---
==> shard 6 of 48: make test.internal.www~1of2
FIESTA_TEST_REQUIRE_NODE=1 FIESTA_TEST_REQUIRE_GIT=1 FIESTA_TEST_REQUIRE_TAGS=1 ./hack/gotest.sh test 'internal.www~1of2'
gotest: slice 1 of 2 of ./internal/www/ runs 11 of 25 test tests
ok  	amadan.net/rastrillo/fiesta/internal/www	111.850s
suite: test.internal.www~1of2 took 118s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m49.240000s 0m1.940000s
--- shard 7 of 48 ---
==> shard 7 of 48: make test.internal.www~2of2
FIESTA_TEST_REQUIRE_NODE=1 FIESTA_TEST_REQUIRE_GIT=1 FIESTA_TEST_REQUIRE_TAGS=1 ./hack/gotest.sh test 'internal.www~2of2'
gotest: slice 2 of 2 of ./internal/www/ runs 14 of 25 test tests
ok  	amadan.net/rastrillo/fiesta/internal/www	107.650s
suite: test.internal.www~2of2 took 115s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m47.400000s 0m1.800000s
--- shard 8 of 48 ---
==> shard 8 of 48: make staticcheck
go run honnef.co/go/tools/cmd/staticcheck@v0.8.2 -tags browser ./...
suite: staticcheck took 19s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m16.590000s 0m1.120000s
--- shard 9 of 48 ---
==> shard 9 of 48: make govulncheck
go run golang.org/x/vuln/cmd/govulncheck@v1.8.0 ./...
=== Symbol Results ===

No vulnerabilities found.

Your code is affected by 0 vulnerabilities.
This scan also found 2 vulnerabilities in packages you import and 5
vulnerabilities in modules you require, but your code doesn't appear to call
these vulnerabilities.
Use '-show verbose' for more details.
suite: govulncheck took 13s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m3.450000s 0m0.420000s
--- shard 10 of 48 ---
==> shard 10 of 48: make gitleaks
go run github.com/zricethezav/gitleaks/v8@v8.30.1 git --no-banner --redact --log-opts='--full-history --diff-filter=tuxdb HEAD' .
10:15AM INF Unknown SCM platform. Use --platform to include links in findings. host=amadan.net
10:15AM INF 41 commits scanned.
10:15AM INF scanned ~9304147 bytes (9.30 MB) in 9.43s
10:15AM INF no leaks found
suite: gitleaks took 12s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m8.270000s 0m0.260000s
--- shard 11 of 48 ---
==> shard 11 of 48: make browser.internal.harness
./hack/gotest.sh browser 'internal.harness'
gotest: on ip-172-31-19-192, driving the harness's own choice (/usr/local/bin/chromium)
gotest: ./internal/harness/ runs its 10 browser tests
ok  	amadan.net/rastrillo/fiesta/internal/harness	7.467s
suite: browser.internal.harness took 15s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m1.800000s 0m1.410000s
--- shard 12 of 48 ---
==> shard 12 of 48: make browser.check~1of13
./hack/gotest.sh browser 'check~1of13'
gotest: on ip-172-31-19-192, driving /opt/amadan-browser/chromium-1234/chrome-linux/chrome
gotest: slice 1 of 13 of ./check/ runs 17 of 228 browser tests
ok  	amadan.net/rastrillo/fiesta/check	135.710s
suite: browser.check~1of13 took 152s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m50.820000s 0m14.410000s
--- shard 13 of 48 ---
==> shard 13 of 48: make browser.check~2of13
./hack/gotest.sh browser 'check~2of13'
gotest: on ip-172-31-19-192, driving /opt/amadan-browser/chromium-1234/chrome-linux/chrome
gotest: slice 2 of 13 of ./check/ runs 17 of 228 browser tests
ok  	amadan.net/rastrillo/fiesta/check	148.198s
suite: browser.check~2of13 took 163s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m57.140000s 0m16.170000s
--- shard 14 of 48 ---
==> shard 14 of 48: make browser.check~3of13
./hack/gotest.sh browser 'check~3of13'
gotest: on ip-172-31-19-192, driving /opt/amadan-browser/chromium-1234/chrome-linux/chrome
gotest: slice 3 of 13 of ./check/ runs 16 of 228 browser tests
ok  	amadan.net/rastrillo/fiesta/check	129.864s
suite: browser.check~3of13 took 146s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m45.390000s 0m12.810000s
--- shard 15 of 48 ---
==> shard 15 of 48: make browser.check~4of13
./hack/gotest.sh browser 'check~4of13'
gotest: on ip-172-31-19-192, driving /opt/amadan-browser/chromium-1234/chrome-linux/chrome
gotest: slice 4 of 13 of ./check/ runs 16 of 228 browser tests
ok  	amadan.net/rastrillo/fiesta/check	140.054s
suite: browser.check~4of13 took 154s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m56.770000s 0m17.310000s
--- shard 16 of 48 ---
==> shard 16 of 48: make browser.check~5of13
./hack/gotest.sh browser 'check~5of13'
gotest: on ip-172-31-19-192, driving /opt/amadan-browser/chromium-1234/chrome-linux/chrome
gotest: slice 5 of 13 of ./check/ runs 18 of 228 browser tests
ok  	amadan.net/rastrillo/fiesta/check	124.106s
suite: browser.check~5of13 took 140s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m43.000000s 0m14.680000s
--- shard 17 of 48 ---
==> shard 17 of 48: make browser.check~6of13
./hack/gotest.sh browser 'check~6of13'
gotest: on ip-172-31-19-192, driving /opt/amadan-browser/chromium-1234/chrome-linux/chrome
gotest: slice 6 of 13 of ./check/ runs 18 of 228 browser tests
ok  	amadan.net/rastrillo/fiesta/check	131.636s
suite: browser.check~6of13 took 147s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m50.180000s 0m16.760000s
--- shard 18 of 48 ---
==> shard 18 of 48: make browser.check~7of13
./hack/gotest.sh browser 'check~7of13'
gotest: on ip-172-31-19-192, driving /opt/amadan-browser/chromium-1234/chrome-linux/chrome
gotest: slice 7 of 13 of ./check/ runs 18 of 228 browser tests
ok  	amadan.net/rastrillo/fiesta/check	130.375s
suite: browser.check~7of13 took 146s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m48.720000s 0m14.800000s
--- shard 19 of 48 ---
==> shard 19 of 48: make browser.check~8of13
./hack/gotest.sh browser 'check~8of13'
gotest: on ip-172-31-19-192, driving /opt/amadan-browser/chromium-1234/chrome-linux/chrome
gotest: slice 8 of 13 of ./check/ runs 18 of 228 browser tests
ok  	amadan.net/rastrillo/fiesta/check	136.378s
suite: browser.check~8of13 took 152s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m47.080000s 0m15.370000s
--- shard 20 of 48 ---
==> shard 20 of 48: make browser.check~9of13
./hack/gotest.sh browser 'check~9of13'
gotest: on ip-172-31-19-192, driving /opt/amadan-browser/chromium-1234/chrome-linux/chrome
gotest: slice 9 of 13 of ./check/ runs 18 of 228 browser tests
ok  	amadan.net/rastrillo/fiesta/check	115.347s
suite: browser.check~9of13 took 130s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m35.170000s 0m10.530000s
--- shard 21 of 48 ---
==> shard 21 of 48: make browser.check~10of13
./hack/gotest.sh browser 'check~10of13'
gotest: on ip-172-31-19-192, driving /opt/amadan-browser/chromium-1234/chrome-linux/chrome
gotest: slice 10 of 13 of ./check/ runs 18 of 228 browser tests
ok  	amadan.net/rastrillo/fiesta/check	124.852s
suite: browser.check~10of13 took 141s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m42.080000s 0m12.900000s
--- shard 22 of 48 ---
==> shard 22 of 48: make browser.check~11of13
./hack/gotest.sh browser 'check~11of13'
gotest: on ip-172-31-19-192, driving /opt/amadan-browser/chromium-1234/chrome-linux/chrome
gotest: slice 11 of 13 of ./check/ runs 18 of 228 browser tests
ok  	amadan.net/rastrillo/fiesta/check	127.575s
suite: browser.check~11of13 took 143s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m43.990000s 0m13.580000s
--- shard 23 of 48 ---
==> shard 23 of 48: make browser.check~12of13
./hack/gotest.sh browser 'check~12of13'
gotest: on ip-172-31-19-192, driving /opt/amadan-browser/chromium-1234/chrome-linux/chrome
gotest: slice 12 of 13 of ./check/ runs 18 of 228 browser tests
ok  	amadan.net/rastrillo/fiesta/check	134.303s
suite: browser.check~12of13 took 150s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m50.530000s 0m15.640000s
--- shard 24 of 48 ---
==> shard 24 of 48: make browser.check~13of13
./hack/gotest.sh browser 'check~13of13'
gotest: on ip-172-31-19-192, driving /opt/amadan-browser/chromium-1234/chrome-linux/chrome
gotest: slice 13 of 13 of ./check/ runs 18 of 228 browser tests
ok  	amadan.net/rastrillo/fiesta/check	130.544s
suite: browser.check~13of13 took 146s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m42.060000s 0m12.850000s
--- shard 25 of 48 ---
==> shard 25 of 48: make browser.internal.gallery~1of18
./hack/gotest.sh browser 'internal.gallery~1of18'
gotest: on ip-172-31-19-192, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 1 of 18 of ./internal/gallery/ runs 6 of 84 browser tests
ok  	amadan.net/rastrillo/fiesta/internal/gallery	69.490s
suite: browser.internal.gallery~1of18 took 84s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m16.560000s 0m3.910000s
--- shard 26 of 48 ---
==> shard 26 of 48: make browser.internal.gallery~2of18
./hack/gotest.sh browser 'internal.gallery~2of18'
gotest: on ip-172-31-19-192, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 2 of 18 of ./internal/gallery/ runs 5 of 84 browser tests
ok  	amadan.net/rastrillo/fiesta/internal/gallery	77.549s
suite: browser.internal.gallery~2of18 took 92s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m15.850000s 0m3.260000s
--- shard 27 of 48 ---
==> shard 27 of 48: make browser.internal.gallery~3of18
./hack/gotest.sh browser 'internal.gallery~3of18'
gotest: on ip-172-31-19-192, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 3 of 18 of ./internal/gallery/ runs 5 of 84 browser tests
ok  	amadan.net/rastrillo/fiesta/internal/gallery	89.174s
suite: browser.internal.gallery~3of18 took 103s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m16.950000s 0m4.000000s
--- shard 28 of 48 ---
==> shard 28 of 48: make browser.internal.gallery~4of18
./hack/gotest.sh browser 'internal.gallery~4of18'
gotest: on ip-172-31-19-192, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 4 of 18 of ./internal/gallery/ runs 5 of 84 browser tests
ok  	amadan.net/rastrillo/fiesta/internal/gallery	70.744s
suite: browser.internal.gallery~4of18 took 84s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m15.460000s 0m3.200000s
--- shard 29 of 48 ---
==> shard 29 of 48: make browser.internal.gallery~5of18
./hack/gotest.sh browser 'internal.gallery~5of18'
gotest: on ip-172-31-19-192, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 5 of 18 of ./internal/gallery/ runs 6 of 84 browser tests
ok  	amadan.net/rastrillo/fiesta/internal/gallery	100.047s
suite: browser.internal.gallery~5of18 took 113s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m21.160000s 0m5.780000s
--- shard 30 of 48 ---
==> shard 30 of 48: make browser.internal.gallery~6of18
./hack/gotest.sh browser 'internal.gallery~6of18'
gotest: on ip-172-31-19-192, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 6 of 18 of ./internal/gallery/ runs 5 of 84 browser tests
ok  	amadan.net/rastrillo/fiesta/internal/gallery	86.549s
suite: browser.internal.gallery~6of18 took 100s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m17.240000s 0m4.220000s
--- shard 31 of 48 ---
==> shard 31 of 48: make browser.internal.gallery~7of18
./hack/gotest.sh browser 'internal.gallery~7of18'
gotest: on ip-172-31-19-192, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 7 of 18 of ./internal/gallery/ runs 5 of 84 browser tests
ok  	amadan.net/rastrillo/fiesta/internal/gallery	81.696s
suite: browser.internal.gallery~7of18 took 95s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m14.980000s 0m2.880000s
--- shard 32 of 48 ---
==> shard 32 of 48: make browser.internal.gallery~8of18
./hack/gotest.sh browser 'internal.gallery~8of18'
gotest: on ip-172-31-19-192, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 8 of 18 of ./internal/gallery/ runs 5 of 84 browser tests
ok  	amadan.net/rastrillo/fiesta/internal/gallery	85.949s
suite: browser.internal.gallery~8of18 took 99s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m19.900000s 0m5.120000s
--- shard 33 of 48 ---
==> shard 33 of 48: make browser.internal.gallery~9of18
./hack/gotest.sh browser 'internal.gallery~9of18'
gotest: on ip-172-31-19-192, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 9 of 18 of ./internal/gallery/ runs 6 of 84 browser tests
ok  	amadan.net/rastrillo/fiesta/internal/gallery	115.086s
suite: browser.internal.gallery~9of18 took 128s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m25.960000s 0m7.980000s
--- shard 34 of 48 ---
==> shard 34 of 48: make browser.internal.gallery~10of18
./hack/gotest.sh browser 'internal.gallery~10of18'
gotest: on ip-172-31-19-192, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 10 of 18 of ./internal/gallery/ runs 2 of 84 browser tests
ok  	amadan.net/rastrillo/fiesta/internal/gallery	81.576s
suite: browser.internal.gallery~10of18 took 95s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m15.200000s 0m2.840000s
--- shard 35 of 48 ---
==> shard 35 of 48: make browser.internal.gallery~11of18
./hack/gotest.sh browser 'internal.gallery~11of18'
gotest: on ip-172-31-19-192, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 11 of 18 of ./internal/gallery/ runs 2 of 84 browser tests
ok  	amadan.net/rastrillo/fiesta/internal/gallery	85.453s
suite: browser.internal.gallery~11of18 took 98s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m15.820000s 0m3.160000s
--- shard 36 of 48 ---
==> shard 36 of 48: make browser.internal.gallery~12of18
./hack/gotest.sh browser 'internal.gallery~12of18'
gotest: on ip-172-31-19-192, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 12 of 18 of ./internal/gallery/ runs 2 of 84 browser tests
ok  	amadan.net/rastrillo/fiesta/internal/gallery	110.909s
suite: browser.internal.gallery~12of18 took 124s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m20.570000s 0m5.350000s
--- shard 37 of 48 ---
==> shard 37 of 48: make browser.internal.gallery~13of18
./hack/gotest.sh browser 'internal.gallery~13of18'
gotest: on ip-172-31-19-192, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 13 of 18 of ./internal/gallery/ runs 4 of 84 browser tests
ok  	amadan.net/rastrillo/fiesta/internal/gallery	107.493s
suite: browser.internal.gallery~13of18 took 121s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m21.080000s 0m5.720000s
--- shard 38 of 48 ---
==> shard 38 of 48: make browser.internal.gallery~14of18
./hack/gotest.sh browser 'internal.gallery~14of18'
gotest: on ip-172-31-19-192, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 14 of 18 of ./internal/gallery/ runs 5 of 84 browser tests
ok  	amadan.net/rastrillo/fiesta/internal/gallery	110.300s
suite: browser.internal.gallery~14of18 took 124s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m20.530000s 0m5.610000s
--- shard 39 of 48 ---
==> shard 39 of 48: make browser.internal.gallery~15of18
./hack/gotest.sh browser 'internal.gallery~15of18'
gotest: on ip-172-31-19-192, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 15 of 18 of ./internal/gallery/ runs 5 of 84 browser tests
ok  	amadan.net/rastrillo/fiesta/internal/gallery	111.332s
suite: browser.internal.gallery~15of18 took 125s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m21.360000s 0m5.940000s
--- shard 40 of 48 ---
==> shard 40 of 48: make browser.internal.gallery~16of18
./hack/gotest.sh browser 'internal.gallery~16of18'
gotest: on ip-172-31-19-192, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 16 of 18 of ./internal/gallery/ runs 5 of 84 browser tests
ok  	amadan.net/rastrillo/fiesta/internal/gallery	105.388s
suite: browser.internal.gallery~16of18 took 119s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m18.400000s 0m4.710000s
--- shard 41 of 48 ---
==> shard 41 of 48: make browser.internal.gallery~17of18
./hack/gotest.sh browser 'internal.gallery~17of18'
gotest: on ip-172-31-19-192, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 17 of 18 of ./internal/gallery/ runs 5 of 84 browser tests
ok  	amadan.net/rastrillo/fiesta/internal/gallery	111.030s
suite: browser.internal.gallery~17of18 took 126s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m21.220000s 0m6.060000s
--- shard 42 of 48 ---
==> shard 42 of 48: make browser.internal.gallery~18of18
./hack/gotest.sh browser 'internal.gallery~18of18'
gotest: on ip-172-31-19-192, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 18 of 18 of ./internal/gallery/ runs 6 of 84 browser tests
ok  	amadan.net/rastrillo/fiesta/internal/gallery	113.760s
suite: browser.internal.gallery~18of18 took 127s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m22.860000s 0m6.810000s
--- shard 43 of 48 ---
==> shard 43 of 48: make browser.internal.gallery.history
./hack/gotest.sh browser 'internal.gallery.history'
gotest: on ip-172-31-19-192, driving /opt/amadan-browser/chromium-1234/chrome-linux/chrome
gotest: ./internal/gallery/history/ runs its 3 browser tests
ok  	amadan.net/rastrillo/fiesta/internal/gallery/history	68.287s
suite: browser.internal.gallery.history took 79s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m22.950000s 0m2.260000s
--- shard 44 of 48 ---
==> shard 44 of 48: make browser.internal.www~1of4
./hack/gotest.sh browser 'internal.www~1of4'
gotest: on ip-172-31-19-192, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 1 of 4 of ./internal/www/ runs 5 of 24 browser tests
ok  	amadan.net/rastrillo/fiesta/internal/www	116.110s
suite: browser.internal.www~1of4 took 128s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m40.700000s 0m2.540000s
--- shard 45 of 48 ---
==> shard 45 of 48: make browser.internal.www~2of4
./hack/gotest.sh browser 'internal.www~2of4'
gotest: on ip-172-31-19-192, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 2 of 4 of ./internal/www/ runs 7 of 24 browser tests
ok  	amadan.net/rastrillo/fiesta/internal/www	124.039s
suite: browser.internal.www~2of4 took 137s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m56.570000s 0m3.570000s
--- shard 46 of 48 ---
==> shard 46 of 48: make browser.internal.www~3of4
./hack/gotest.sh browser 'internal.www~3of4'
gotest: on ip-172-31-19-192, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 3 of 4 of ./internal/www/ runs 6 of 24 browser tests
ok  	amadan.net/rastrillo/fiesta/internal/www	122.264s
suite: browser.internal.www~3of4 took 134s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m55.570000s 0m3.440000s
--- shard 47 of 48 ---
==> shard 47 of 48: make browser.internal.www~4of4
./hack/gotest.sh browser 'internal.www~4of4'
gotest: on ip-172-31-19-192, driving the harness's own choice (/usr/local/bin/chromium)
gotest: slice 4 of 4 of ./internal/www/ runs 6 of 24 browser tests
ok  	amadan.net/rastrillo/fiesta/internal/www	120.176s
suite: browser.internal.www~4of4 took 131s; CPU, user then system, of this shell and then of the role:
0m0.000000s 0m0.000000s
0m48.730000s 0m3.370000s
diff --git a/check/rows_rule_test.go b/check/rows_rule_test.go
index b814068..1bf85ec 100644
--- a/check/rows_rule_test.go
+++ b/check/rows_rule_test.go
@@ -5,7 +5,10 @@ import (
"strings"
"testing"
+ xhtml "golang.org/x/net/html"
+
"amadan.net/rastrillo/fiesta"
+ "amadan.net/rastrillo/fiesta/internal/markup"
"amadan.net/rastrillo/fiesta/internal/rowrule"
)
@@ -28,7 +31,13 @@ func TestEveryDropdownSitsInARow(t *testing.T) {
if strings.Contains(s.Markup, "rst-dropdown") {
menus++
}
- for _, msg := range rowrule.Breaches(s.Markup, nil) {
+ respelled, _ := markup.Respell([]byte(s.Markup))
+ doc, err := xhtml.Parse(strings.NewReader(string(respelled)))
+ if err != nil {
+ t.Errorf("%s/%s: the markup does not parse: %v", s.Name, s.State, err)
+ continue
+ }
+ for _, msg := range rowrule.Breaches(doc, nil) {
t.Errorf("%s/%s: %s", s.Name, s.State, msg)
}
}
diff --git a/internal/rowrule/rowrule.go b/internal/rowrule/rowrule.go
index c9df061..a1e4019 100644
--- a/internal/rowrule/rowrule.go
+++ b/internal/rowrule/rowrule.go
@@ -38,7 +38,7 @@ var Rows = map[string]string{
// header in document order; anything else that does is above the title.
var shellChrome = []string{"rst-shell-tail", "rst-shell-rail-foot"}
-// Breaches reads one piece of markup and returns one message per
+// Breaches reads one parsed document and returns one message per
// breach of the Row Rule, in document order. It is a pure function so
// its own test can hold each rule to an exact message.
//
@@ -49,18 +49,19 @@ var shellChrome = []string{"rst-shell-tail", "rst-shell-rail-foot"}
// keeps the menu on a line with something else is a question of layout,
// which a browser has to answer at each width.
//
-// The markup is respelled first, so a class-spelled dropdown is visited
-// and a class-spelled row counts as its attribute twin. The respeller's
-// notes are not read: most are about escaped markup in a code sample,
-// which is text and holds no dropdown. What matters is an element whose
-// class list it left alone, and that is found on the parsed element.
-func Breaches(src string, pageRows map[string]string) []string {
- respelled, _ := markup.Respell([]byte(src))
+// The caller respells the markup with markup.Respell before parsing
+// it, so a class-spelled dropdown is visited and a class-spelled row
+// counts as its attribute twin; a class list the respeller left alone
+// is reported here, so a caller that skips the respell is told. The
+// respeller's notes are not read: most are about escaped markup in a
+// code sample, which is text and holds no dropdown.
+//
+// Parsing is the caller's because every caller is a test. x/net's
+// html.Parse has a known quadratic case (GO-2026-6860), and govulncheck
+// fails the gate for any package of the module that calls it; tests are
+// outside its scan, and they parse only this repository's markup.
+func Breaches(doc *xhtml.Node, pageRows map[string]string) []string {
var out []string
- doc, err := xhtml.Parse(strings.NewReader(string(respelled)))
- if err != nil {
- return append(out, "the markup does not parse: "+err.Error())
- }
var order []*xhtml.Node
var walk func(*xhtml.Node)
walk = func(n *xhtml.Node) {
diff --git a/internal/rowrule/rowrule_test.go b/internal/rowrule/rowrule_test.go
index 95c7b6c..e965af4 100644
--- a/internal/rowrule/rowrule_test.go
+++ b/internal/rowrule/rowrule_test.go
@@ -2,9 +2,25 @@ package rowrule
import (
"slices"
+ "strings"
"testing"
+
+ xhtml "golang.org/x/net/html"
+
+ "amadan.net/rastrillo/fiesta/internal/markup"
)
+// read is markup as Breaches wants it: respelled, then parsed.
+func read(t *testing.T, src string) *xhtml.Node {
+ t.Helper()
+ respelled, _ := markup.Respell([]byte(src))
+ doc, err := xhtml.Parse(strings.NewReader(string(respelled)))
+ if err != nil {
+ t.Fatalf("parsing %q: %v", src, err)
+ }
+ return doc
+}
+
// TestTheRowCheckNamesEachBreach: each way a dropdown can break the
// rule is named by its own message, in both spellings, and each shape
// the rule allows passes. A checker that only counted breaches would
@@ -81,7 +97,7 @@ func TestTheRowCheckNamesEachBreach(t *testing.T) {
`<main rst-page>` + header + `<footer rst-page-foot><details rst-dropdown></details></footer></main>`, nil},
}
for _, c := range cases {
- if got := Breaches(c.markup, nil); !slices.Equal(got, c.want) {
+ if got := Breaches(read(t, c.markup), nil); !slices.Equal(got, c.want) {
t.Errorf("%s:\n got %q\n want %q", c.name, got, c.want)
}
}
@@ -116,7 +132,7 @@ func TestAPageNamesItsOwnRows(t *testing.T) {
`<div rst-lbar><details rst-dropdown></details></div>`, bar, nil},
}
for _, c := range cases {
- if got := Breaches(c.markup, c.rows); !slices.Equal(got, c.want) {
+ if got := Breaches(read(t, c.markup), c.rows); !slices.Equal(got, c.want) {
t.Errorf("%s:\n got %q\n want %q", c.name, got, c.want)
}
}
diff --git a/internal/www/landing_rows_browser_test.go b/internal/www/landing_rows_browser_test.go
index 3db96c4..0f8630d 100644
--- a/internal/www/landing_rows_browser_test.go
+++ b/internal/www/landing_rows_browser_test.go
@@ -14,7 +14,9 @@ import (
"github.com/chromedp/cdproto/cdp"
"github.com/chromedp/cdproto/emulation"
"github.com/chromedp/chromedp"
+ xhtml "golang.org/x/net/html"
+ "amadan.net/rastrillo/fiesta/internal/markup"
"amadan.net/rastrillo/fiesta/internal/rowrule"
"amadan.net/rastrillo/fiesta/internal/www"
)
@@ -142,7 +144,12 @@ func TestTheLandingKeepsTheRowRule(t *testing.T) {
if got != lang || dir != www.Dir(lang) {
t.Fatalf("%s: the page is in %q, written %q; want %s, %s", pageURL(lang), got, dir, lang, www.Dir(lang))
}
- for _, msg := range rowrule.Breaches(page, landingRows) {
+ respelled, _ := markup.Respell([]byte(page))
+ doc, err := xhtml.Parse(strings.NewReader(string(respelled)))
+ if err != nil {
+ t.Fatalf("%s: the page does not parse: %v", lang, err)
+ }
+ for _, msg := range rowrule.Breaches(doc, landingRows) {
t.Errorf("%s: %s", lang, msg)
}
words, err := www.Strings(siteSource(t).FS(), lang)