Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions internal/flamegraph/assets.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,19 @@ import (
// one line of warning. The legend, the keyboard map, the notes and the label
// table are all one keypress (or one icon) away.

// faviconHRef is the perf-agent mark, inlined as a data: URI so the page
// keeps its one-file contract -- a <link rel="icon" href="brand/..."> would
// render a broken tab icon from file:// and leak a request everywhere else.
//
// It is the 16px mark from docs/brand/perf-agent-favicon.svg, which is drawn
// for this size rather than scaled down from the full one: the full mark
// loses its top rows below about 24px and reads as a smudge in a tab strip.
//
// Base64 rather than percent-encoding because the markup contains #, < and
// " -- the three characters that make a raw SVG data: URI fragile inside an
// HTML attribute.
const faviconHRef = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNiAxNiI+PHJlY3QgeD0iMSIgeT0iMTEiIHdpZHRoPSIxNCIgaGVpZ2h0PSI0IiByeD0iMS4zIiBmaWxsPSIjZDE0OTdmIi8+PHJlY3QgeD0iMSIgeT0iNiIgd2lkdGg9IjYiIGhlaWdodD0iNCIgcng9IjEuMyIgZmlsbD0iI2U4ODczYSIvPjxyZWN0IHg9IjgiIHk9IjYiIHdpZHRoPSI3IiBoZWlnaHQ9IjQiIHJ4PSIxLjMiIGZpbGw9IiMzZmEwYWIiLz48cmVjdCB4PSI5IiB5PSIxIiB3aWR0aD0iNiIgaGVpZ2h0PSI0IiByeD0iMS4zIiBmaWxsPSIjNDRhODVlIi8+PC9zdmc+"

// darkChrome is the page chrome's dark theme, written once and emitted
// twice: once behind prefers-color-scheme for readers who never touch the
// D key, once behind [data-theme="dark"] for readers who do. Two copies of
Expand Down
3 changes: 2 additions & 1 deletion internal/flamegraph/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,8 @@ func RenderHTML(w io.Writer, res *foldedstacks.Result, opts Options) error {
degenerate := res.Degenerate()

ew.s("<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n")
ew.s("<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>")
ew.s("<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n")
ew.s("<link rel=\"icon\" href=\"" + faviconHRef + "\">\n<title>")
ew.esc(opts.Title)
ew.s("</title>\n<style>\n")
ew.s(styleSheet)
Expand Down
16 changes: 15 additions & 1 deletion internal/flamegraph/render_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,26 @@ func TestRenderHTMLFetchesNothing(t *testing.T) {
), Options{})

for _, forbidden := range []string{
"<script src", "<link ", "@import", "url(http", "url('http", "url(\"http",
"<script src", "@import", "url(http", "url('http", "url(\"http",
"fonts.googleapis.com", "fonts.gstatic.com", "cdn.", "fetch(", "XMLHttpRequest",
"<img", "<iframe",
} {
assert.NotContains(t, got, forbidden, "the page must load nothing from the network")
}

// <link> is allowed only when it fetches nothing. The favicon is one, and
// it is inlined as a data: URI; a stylesheet or an icon by path would
// both be a network request from anywhere but the directory it was
// written in, which is the whole point of the one-file contract. So the
// rule is not "no <link>" but "no <link> that resolves off-page".
links := regexp.MustCompile(`<link\b[^>]*>`).FindAllString(got, -1)
for _, link := range links {
href := regexp.MustCompile(`href="([^"]*)"`).FindStringSubmatch(link)
if assert.Len(t, href, 2, "every <link> must carry an href: %s", link) {
assert.True(t, strings.HasPrefix(href[1], "data:"),
"a <link> must resolve to a data: URI, got %q", href[1])
}
}
// Not one URL of any kind is left in the document. There used to be
// exactly one — the SVG XML namespace, an identifier rather than a fetch
// — and the geometry that needed it is gone.
Expand Down
Loading