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
9 changes: 9 additions & 0 deletions tools/refgen/security.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import (
"strings"
)

// limitsHeading is the README's section on what the model does not protect, beside its open items.
const limitsHeading = "What this does not protect"

// securityPages writes the security model's rules and its open items from spin's own
// docs/security/README.md, where code cites each rule at the line that keeps it and a gate fails
// the build when one is broken. A page that restated them went stale within a week of being
Expand Down Expand Up @@ -53,6 +56,12 @@ func securityPages(spin, commit string) (map[string][]byte, error) {
"[its rules](/reference/security/rules/).\n", source, commit[:9])
gaps.Write(bytes.TrimRight(open, "\n"))
gaps.WriteString("\n")
// Older tags have no such section: the page is built from whichever tag the site documents.
if limits := section(raw, "## "+limitsHeading); limits != nil {
gaps.WriteString("\n## " + limitsHeading + "\n")
gaps.Write(bytes.TrimRight(limits, "\n"))
gaps.WriteString("\n")
}

return map[string][]byte{
filepath.Join("security", "rules.md"): []byte(rules.String()),
Expand Down
51 changes: 51 additions & 0 deletions tools/refgen/security_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package main

import (
"os"
"path/filepath"
"strings"
"testing"
)

// The known gaps are the README's open items and, where its tag has the section, what the model does
// not protect: a limit moved between the two stays on the page.
func TestKnownGapsCarryWhatTheModelDoesNotProtect(t *testing.T) {
const rules = "| I1 | a rule | keeps | gate |\n"
for name, tc := range map[string]struct {
readme string
want []string
}{
"with the section": {
readme: rules + "\n## Open\n\n- an open item\n\n## What this does not protect\n\n- a limit\n\n## Next\n\nelse\n",
want: []string{"- an open item", "## What this does not protect", "- a limit"},
},
"without it": {
readme: rules + "\n## Open\n\n- an open item\n",
want: []string{"- an open item"},
},
} {
t.Run(name, func(t *testing.T) {
spin := t.TempDir()
path := filepath.Join(spin, "docs", "security", "README.md")
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(path, []byte(tc.readme), 0o644); err != nil {
t.Fatal(err)
}
pages, err := securityPages(spin, "0123456789abcdef")
if err != nil {
t.Fatal(err)
}
page := string(pages[filepath.Join("security", "known-gaps.md")])
for _, want := range tc.want {
if !strings.Contains(page, want) {
t.Errorf("the page does not say %q:\n%s", want, page)
}
}
if strings.Contains(page, "else") {
t.Errorf("the page runs past its sections:\n%s", page)
}
})
}
}
Loading