Skip to content
quarry Artifact [4db487732f]
Login

Artifact [4db487732f]

Artifact 4db487732f130c6aa80dc9295f8b49011fba80053e82c4132f8c30450005d74f:


package health

import (
	"context"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
	"time"
)

func hostsIn(t *testing.T, page string) []string {
	t.Helper()
	got, err := ForeignResources("https://forge.example/login", strings.NewReader(page))
	if err != nil {
		t.Fatal(err)
	}
	return got
}

// The exact shape Cloudflare appended to every page on zeitforge: a
// module script from another host, after the footer, carrying an
// integrity hash and no nonce.
func TestAnInjectedBeaconIsFound(t *testing.T) {
	got := hostsIn(t, `<html><body><p>hi</p>
<script type="module" src="https://static.cloudflareinsights.com/beacon.min.js/v31edd" integrity="sha512-abc"></script>
</body></html>`)
	if len(got) != 1 || got[0] != "static.cloudflareinsights.com" {
		t.Errorf("FAIL: found %v, want the beacon's host", got)
	}
}

// A page that keeps the promise reports nothing — including the parts
// that LOOK external. The footer links to fossil-scm.org, and an
// anchor is an address a reader may follow, not a resource the page
// pulls in. Calling that a violation would make the check cry wolf on
// every page it ever ran against.
func TestAPageThatKeepsThePromiseIsSilent(t *testing.T) {
	got := hostsIn(t, `<html><head>
<link rel="stylesheet" href="/assets/quarry.abc.css">
<script src="/assets/onerror.js" nonce="x"></script>
<script type="module" src="https://forge.example/assets/datastar.js"></script>
</head><body>
<img src="data:image/png;base64,AAAA" alt="">
<img src="/r/alpha/avatar.png" alt="">
<p>powered by <a href="https://fossil-scm.org/">fossil</a> · <a href="https://example.org/x">docs</a></p>
<svg><use href="#i-file"/></svg>
</body></html>`)
	if len(got) != 0 {
		t.Errorf("FAIL: a clean page reported %v", got)
	}
}

// Every element that makes the browser fetch counts, not just scripts:
// a font, a tracking pixel and an iframe are the same promise broken.
func TestEveryFetchingElementCounts(t *testing.T) {
	got := hostsIn(t, `<html><head>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=X">
</head><body>
<img src="https://pixel.example/track.gif">
<iframe src="https://embed.example/x"></iframe>
<video><source src="https://media.example/v.mp4"></video>
<object data="https://obj.example/o.swf"></object>
<img srcset="https://cdn.example/a.png 1x, /local/b.png 2x">
</body></html>`)
	want := []string{"cdn.example", "embed.example", "fonts.googleapis.com", "media.example", "obj.example", "pixel.example"}
	if len(got) != len(want) {
		t.Fatalf("FAIL: found %v, want %v", got, want)
	}
	for i := range want {
		if got[i] != want[i] {
			t.Fatalf("FAIL: found %v, want %v", got, want)
		}
	}
}

// Reported once per host however many times it appears: the panel
// should say "cloudflareinsights", not say it forty times.
func TestAHostIsReportedOnce(t *testing.T) {
	got := hostsIn(t, `<html><body>
<img src="https://cdn.example/a.png"><img src="https://cdn.example/b.png"><img src="https://CDN.example/c.png">
</body></html>`)
	if len(got) != 1 {
		t.Errorf("FAIL: found %v, want one host", got)
	}
}

// The probe reports a foreign host as an error, and a clean page as nil.
func TestTheProbeAnswersOnWhatItRead(t *testing.T) {
	var page string
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "text/html")
		w.Write([]byte(page))
	}))
	defer srv.Close()
	// No throttle in the test: each call must read the page, because the
	// two halves of this test are two different pages.
	probe := ForeignProbe(srv.Client(), time.Nanosecond)

	page = `<html><body><script src="/assets/x.js"></script></body></html>`
	if err := probe(context.Background(), Dep{URL: srv.URL}); err != nil {
		t.Errorf("FAIL: a clean page probed as %v", err)
	}

	page = `<html><body><script src="https://tracker.example/b.js"></script></body></html>`
	err := probe(context.Background(), Dep{URL: srv.URL})
	if err == nil {
		t.Fatal("FAIL: an injected script probed clean")
	}
	if !strings.Contains(err.Error(), "tracker.example") {
		t.Errorf("FAIL: the fault does not name the host: %v", err)
	}
}

// A probe that could not read the page says so. Treating an unanswered
// question as a good answer is how a check becomes decoration — and
// this one has a real way of failing, since a front that filters robots
// can refuse the probe while serving readers normally.
func TestAnUnreadablePageIsNotCleanAndNotSilent(t *testing.T) {
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		http.Error(w, "blocked", http.StatusForbidden)
	}))
	defer srv.Close()
	err := ForeignProbe(srv.Client(), time.Nanosecond)(context.Background(), Dep{URL: srv.URL})
	if err == nil {
		t.Fatal("FAIL: a page the probe could not read reported clean")
	}
	if !strings.Contains(err.Error(), "could not run") {
		t.Errorf("FAIL: the fault reads as a violation rather than an unanswered question: %v", err)
	}
	// An unreachable host is the same kind of answer.
	err = ForeignProbe(&http.Client{}, time.Nanosecond)(context.Background(), Dep{URL: "http://127.0.0.1:1/nope"})
	if err == nil || !strings.Contains(err.Error(), "could not read") {
		t.Errorf("FAIL: an unreachable page gave %v", err)
	}
}

// A custom probe replaces the default "did it answer" test, and the
// watcher reports its verdict on transition like any other.
func TestACustomProbeDrivesTheWatcher(t *testing.T) {
	var reported []string
	failing := true
	w := New([]Dep{{
		Name: "origin", URL: "https://forge.example/login", Why: "readers may be served a page this forge did not send",
		Probe: func(context.Context, Dep) error {
			if failing {
				return context.DeadlineExceeded
			}
			return nil
		},
	}}, func(d Dep, up bool, err error) {
		reported = append(reported, d.Name+":"+map[bool]string{true: "up", false: "down"}[up])
	})
	w.Check(context.Background())
	failing = false
	w.Check(context.Background())
	w.Check(context.Background())
	if len(reported) != 2 || reported[0] != "origin:down" || reported[1] != "origin:up" {
		t.Errorf("FAIL: reported %v, want one down then one up — transitions, not states", reported)
	}
}

// The page is read on a slower beat than the watcher ticks. An injected
// script is a configuration, not a process that can die between two
// ticks, and pulling the forge's own page through its own front every
// thirty seconds would be two thousand requests a day to learn nothing.
func TestThePageIsNotRefetchedOnEveryTick(t *testing.T) {
	reads := 0
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		reads++
		w.Write([]byte(`<html><body><script src="/x.js"></script></body></html>`))
	}))
	defer srv.Close()
	probe := ForeignProbe(srv.Client(), time.Hour)
	for i := 0; i < 5; i++ {
		if err := probe(context.Background(), Dep{URL: srv.URL}); err != nil {
			t.Fatalf("FAIL: %v", err)
		}
	}
	if reads != 1 {
		t.Errorf("FAIL: read the page %d times across five ticks, want 1", reads)
	}
	// And the held verdict is the real one, not a default: a violation
	// keeps reporting between fetches rather than going quiet.
	bad := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte(`<html><body><script src="https://tracker.example/b.js"></script></body></html>`))
	}))
	defer bad.Close()
	held := ForeignProbe(bad.Client(), time.Hour)
	first := held(context.Background(), Dep{URL: bad.URL})
	if first == nil {
		t.Fatal("FAIL: setup — the bad page probed clean")
	}
	if second := held(context.Background(), Dep{URL: bad.URL}); second == nil {
		t.Error("FAIL: a held verdict went quiet between fetches")
	}
}