main 5041353

main.go RawHistoryFolder

1// Command systemviews-assets generates and checks the content manifest
2// for served systemview assets (Plan 05 P3.1).
3//
4// Generation (default) reads the uncompressed servables from -dir,
5// requires the compressed WASM to decompress to the same bytes under a
6// bounded read, and writes asset-manifest.json. It fails loudly on stale
7// or missing inputs instead of writing a partial manifest.
8//
9// Check mode (-check) validates the generated bundle manifest against the
10// artifacts on disk and exits nonzero on any mismatch, for CI and
11// release verification.
12package main
13
14import (
15	"bytes"
16	"crypto/sha256"
17	"encoding/hex"
18	"flag"
19	"fmt"
20	"io"
21	"io/fs"
22	"os"
23	"path/filepath"
24
25	"github.com/andybalholm/brotli"
26
27	"github.com/bnema/dumber/assets"
28)
29
30// maxManifestWASMBytes bounds manifest-time decompression, mirroring the
31// serving bound so a manifest can never bless what serving would reject.
32const maxManifestWASMBytes = 64 * 1024 * 1024
33
34// manifestFilePerm matches repo asset files.
35const manifestFilePerm = 0o644
36
37func main() {
38	dir := flag.String("dir", "assets/systemviews", "directory holding the built systemview assets")
39	check := flag.Bool("check", false, "verify the generated manifest against disk artifacts instead of regenerating")
40	checkIfPresent := flag.Bool("check-if-present", false, "like -check, but pass with a notice when no built WASM exists (for quick builds)")
41	flag.Parse()
42
43	if err := run(*dir, *check, *checkIfPresent); err != nil {
44		fmt.Fprintf(os.Stderr, "systemviews-assets: %v\n", err)
45		os.Exit(1)
46	}
47}
48
49func run(dir string, check, checkIfPresent bool) error {
50	manifestPath := filepath.Join(dir, assets.ManifestName)
51	if checkIfPresent {
52		// Serving treats the compressed WASM as authoritative, so a
53		// stale .br without raw (or vice versa) is a partial bundle
54		// that must fail validation, never skip it.
55		_, rawErr := os.Stat(filepath.Join(dir, "systemviews.wasm"))
56		_, brErr := os.Stat(filepath.Join(dir, "systemviews.wasm.br"))
57		if os.IsNotExist(rawErr) && os.IsNotExist(brErr) {
58			fmt.Printf("no built systemviews WASM in %s; skipping manifest check\n", dir)
59			return nil
60		}
61		return checkTree(dir, manifestPath)
62	}
63	if check {
64		return checkTree(dir, manifestPath)
65	}
66	m, err := collectManifest(os.DirFS(dir))
67	if err != nil {
68		return err
69	}
70	data, err := m.Bytes()
71	if err != nil {
72		return err
73	}
74	if err := os.WriteFile(manifestPath, data, manifestFilePerm); err != nil {
75		return fmt.Errorf("write %s: %w", manifestPath, err)
76	}
77	fmt.Printf("wrote %s pinning %d assets\n", manifestPath, len(assets.ManifestFiles))
78	return nil
79}
80
81// checkTree validates the generated manifest against disk artifacts:
82// pins, raw bytes, and raw/compressed WASM agreement. Generation checks
83// agreement at write time, but only the check path guards a later
84// compressed-only swap, which serving would treat as authoritative.
85func checkTree(dir, manifestPath string) error {
86	data, err := os.ReadFile(manifestPath)
87	if err != nil {
88		return fmt.Errorf("read %s: run 'make build-systemviews' first: %w", manifestPath, err)
89	}
90	m, err := assets.ParseManifest(data)
91	if err != nil {
92		return err
93	}
94	fsys := os.DirFS(dir)
95	if verr := m.Verify(fsys); verr != nil {
96		return verr
97	}
98	raw, err := readBoundedManifestFile(fsys, "systemviews.wasm", maxManifestWASMBytes)
99	if err != nil {
100		return fmt.Errorf("read servable systemviews.wasm: %w", err)
101	}
102	if err := requireCompressedAgreement(fsys, raw, maxManifestWASMBytes); err != nil {
103		return err
104	}
105	fmt.Printf("manifest %s matches %d pinned assets with agreeing compressed WASM\n", manifestPath, len(assets.ManifestFiles))
106	return nil
107}
108
109// collectManifest pins every manifest file from fsys. The raw and
110// compressed WASM must agree under bounded decompression; any missing,
111// corrupt, or oversized input is a hard error.
112func collectManifest(fsys fs.FS) (assets.Manifest, error) {
113	files := make(map[string]assets.FileEntry, len(assets.ManifestFiles))
114	for _, name := range assets.ManifestFiles {
115		data, err := readBoundedManifestFile(fsys, name, maxManifestWASMBytes)
116		if err != nil {
117			return assets.Manifest{}, fmt.Errorf("read servable asset %s: %w", name, err)
118		}
119		if name == "systemviews.wasm" {
120			if err := requireCompressedAgreement(fsys, data, maxManifestWASMBytes); err != nil {
121				return assets.Manifest{}, err
122			}
123		}
124		sum := sha256.Sum256(data)
125		files[name] = assets.FileEntry{SHA256: hex.EncodeToString(sum[:]), Size: int64(len(data))}
126	}
127	return assets.Manifest{Version: assets.ManifestVersion, Files: files}, nil
128}
129
130// readBoundedManifestFile reads a manifest input with the pre-allocation
131// bound the runtime loader enforces, so generation and checks cannot be
132// pushed into unbounded allocation by a corrupt or hostile artifact.
133func readBoundedManifestFile(fsys fs.FS, name string, maxBytes int) ([]byte, error) {
134	f, err := fsys.Open(name)
135	if err != nil {
136		return nil, err
137	}
138	defer func() { _ = f.Close() }()
139	data, err := io.ReadAll(io.LimitReader(f, int64(maxBytes)+1))
140	if err != nil {
141		return nil, fmt.Errorf("read %s: %w", name, err)
142	}
143	if len(data) > maxBytes {
144		return nil, fmt.Errorf("%s exceeds %d bytes", name, maxBytes)
145	}
146	return data, nil
147}
148
149// requireCompressedAgreement ensures the shipped .br decompresses to
150// exactly the raw bytes being pinned. Both inputs arrive bounded, and the
151// decoded output is rejected past the bound before comparison, mirroring
152// the runtime loader: without the sentinel check a max+1-byte bundle
153// would bless here what serving refuses there.
154func requireCompressedAgreement(fsys fs.FS, raw []byte, maxBytes int) error {
155	compressed, err := readBoundedManifestFile(fsys, "systemviews.wasm.br", maxBytes)
156	if err != nil {
157		return fmt.Errorf("read compressed systemviews.wasm.br: %w", err)
158	}
159	data, err := io.ReadAll(io.LimitReader(brotli.NewReader(bytes.NewReader(compressed)), int64(maxBytes)+1))
160	if err != nil {
161		return fmt.Errorf("decompress systemviews.wasm.br: %w", err)
162	}
163	if len(data) > maxBytes {
164		return fmt.Errorf("systemviews.wasm.br decompresses past %d bytes", maxBytes)
165	}
166	if !bytes.Equal(data, raw) {
167		return fmt.Errorf("systemviews.wasm.br decompresses to %d bytes, raw file has %d", len(data), len(raw))
168	}
169	return nil
170}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 8

Tags 42

↑↓ moveenter openesc close