diff --git a/cmd/image/cdn_test.go b/cmd/image/cdn_test.go index 0c74f89..d9aaee7 100644 --- a/cmd/image/cdn_test.go +++ b/cmd/image/cdn_test.go @@ -76,4 +76,19 @@ func TestUpdateCDN(t *testing.T) { if got := get("/testing/construct_20261004.2.efi"); got != "kernel 20261004.2" || c.redirects.Load() != 1 { t.Errorf("the kernel image through the redirect: %q, %d redirects", got, c.redirects.Load()) } + // Only what an index lists is a file: a version no release has, a file in another release's + // slot, a release directory's other files, and a path out of it are all nothing. + for _, p := range []string{"/testing/construct_20261004.3.efi", "/assets/v1/construct_20261004.2.efi", "/assets/v2/SHA256SUMS", "/assets/v2/../release.pem"} { + resp, err := http.Get(base + p) + if err != nil { + t.Fatal(err) + } + resp.Body.Close() + if resp.StatusCode != http.StatusNotFound { + t.Errorf("%s: %s, not 404", p, resp.Status) + } + } + if c.redirects.Load() != 1 { + t.Errorf("%d redirects after the refused requests, not 1", c.redirects.Load()) + } } diff --git a/cmd/image/lifecycle.go b/cmd/image/lifecycle.go index a6048aa..4174317 100644 --- a/cmd/image/lifecycle.go +++ b/cmd/image/lifecycle.go @@ -495,7 +495,9 @@ func newUpdateCDN(dir string, versions map[string]string, keyRef, pubring string if err := os.RemoveAll(root); err != nil { return nil, err } - byVersion := map[string]string{} + // What is served is what the indexes list and nothing else, each file by the name a request + // gives, to a path made here from the index's line: a request's text never reaches the disk. + slots, files := map[string]string{}, map[string]string{} for ch, vs := range map[string][]string{"stable": {"v1"}, "testing": {"v1", "v2"}} { idx := index{} for _, v := range vs { @@ -508,7 +510,11 @@ func newUpdateCDN(dir string, versions map[string]string, keyRef, pubring string return nil, err } idx[versions[v]] = rel[versions[v]] - byVersion[versions[v]] = v + for _, line := range rel[versions[v]] { + name := strings.Fields(line)[1] + slots[name] = v + files[name] = filepath.Join(dir, v+".release", name) + } } sums := filepath.Join(root, ch, "SHA256SUMS") if err := os.MkdirAll(filepath.Dir(sums), 0o755); err != nil { @@ -522,23 +528,21 @@ func newUpdateCDN(dir string, versions map[string]string, keyRef, pubring string } } c := &updateCDN{} - files := http.FileServer(http.Dir(root)) + indexes := http.FileServer(http.Dir(root)) handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { parts := strings.Split(strings.TrimPrefix(r.URL.Path, "/"), "/") - if len(parts) == 2 { - if m := releaseFile.FindStringSubmatch(parts[1]); m != nil { - if v, ok := byVersion[m[1]]; ok && parts[0] != "assets" { - c.redirects.Add(1) - http.Redirect(w, r, "/assets/"+v+"/"+parts[1], http.StatusFound) - return - } + if len(parts) == 2 && parts[0] != "assets" { + if slot, ok := slots[parts[1]]; ok { + c.redirects.Add(1) + http.Redirect(w, r, "/assets/"+slot+"/"+parts[1], http.StatusFound) + return } } - if len(parts) == 3 && parts[0] == "assets" && releaseFile.MatchString(parts[2]) && (parts[1] == "v1" || parts[1] == "v2") { - http.ServeFile(w, r, filepath.Join(dir, parts[1]+".release", parts[2])) + if len(parts) == 3 && parts[0] == "assets" && slots[parts[2]] == parts[1] { + http.ServeFile(w, r, files[parts[2]]) return } - files.ServeHTTP(w, r) + indexes.ServeHTTP(w, r) }) l, err := net.Listen("tcp", "127.0.0.1:0") if err != nil {