Files
Privatae 8822d5df85
ci / personal-data (push) Successful in 5s
ci / test (push) Successful in 3m51s
ci / fuzz (push) Successful in 2m55s
bastion 0.55.3
2026-10-25 23:16:45 +00:00

316 lines
9.9 KiB
Go

package main
import (
"bytes"
"os"
"os/exec"
"path/filepath"
"regexp"
"strconv"
"strings"
"testing"
)
// Every file in this repository is published as the member repository's
// source, so the platform's former internal name must not appear in any of
// them. The only exceptions are identifiers that installed bastions, the
// harness images, or the host still read or write under that name. Each is
// listed in keptOldNames with what holds it in place; removing one means
// changing the other side first. Prose, comments and the changelog are never
// exceptions: a kept identifier is set aside only in Go code, outside its
// comments (goCodeAndComments). In a comment, a Markdown file, a script or
// any other file it is a hit like the bare word, and is described instead
// (the device-fingerprint prefix, fpSANPrefix in pair.go).
//
// The forbidden word is assembled from bytes so that this file does not
// contain it outside the exception list.
var oldName = string([]byte{0x73, 0x6b, 0x65, 0x6d, 0x61})
var keptOldNames = []string{
// Delegation tag the host mints for an agent's environment; agents
// already launched with it carry this name (env.go agentTokenEnvNames).
"SKEMA_AGENT_TOKEN",
// Variables the published harness image reads; it reads no other
// spelling yet (harness.go, lab.go).
"SKEMA_SIDECAR_BIND",
"SKEMA_APP_ID",
"SKEMA_WORKSPACE",
// The harness image's workspace root, a path inside that image (lab.go).
"/var/lib/skema/projects",
// The device-fingerprint URI SAN in the operator CSR. The host and the
// platform accept only this prefix at redeem (pair.go fpSANPrefix).
"urn:skema:hwfp:",
}
// keptOldNameRe matches any kept identifier as a whole token: not preceded or
// followed by a character that would make it part of a longer name.
var keptOldNameRe = func() *regexp.Regexp {
alts := make([]string, len(keptOldNames))
for i, k := range keptOldNames {
alts[i] = regexp.QuoteMeta(k)
}
return regexp.MustCompile(`(?i)(^|[^A-Za-z0-9_])(` + strings.Join(alts, "|") + `)`)
}()
// lineHasOldName reports whether line still carries the old name once every
// kept identifier is set aside. A kept identifier glued to a longer name (a
// trailing letter, digit or underscore) is not set aside, so the check still
// sees the old name in it.
func lineHasOldName(line string) bool {
locs := keptOldNameRe.FindAllStringSubmatchIndex(line, -1)
var b strings.Builder
prev := 0
for _, l := range locs {
start, end := l[4], l[5] // the identifier itself
if end < len(line) && isNameByte(line[end]) {
continue // part of a longer name: not a kept identifier
}
b.WriteString(line[prev:start])
b.WriteByte(' ')
prev = end
}
b.WriteString(line[prev:])
return strings.Contains(strings.ToLower(b.String()), oldName)
}
func isNameByte(c byte) bool {
return c == '_' || c >= '0' && c <= '9' || c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z'
}
// hasOldName reports whether text carries the old name at all, kept
// identifiers included.
func hasOldName(text string) bool {
return strings.Contains(strings.ToLower(text), oldName)
}
// goCodeAndComments splits Go source into its lines twice over: code[i] is
// line i with its comment text blanked, comments[i] the same line with
// everything but comment text blanked. String, raw string and rune literals
// are code, so a "//" inside one does not start a comment, and a raw string
// spanning lines stays code on every line it covers.
func goCodeAndComments(src string) (code, comments []string) {
const (
inCode = iota
inLineComment
inBlockComment
inString
inRawString
inRune
)
codeB := []byte(src)
commB := []byte(src)
state := inCode
for i := 0; i < len(src); i++ {
c := src[i]
isComment := false
switch state {
case inCode:
switch {
case c == '/' && i+1 < len(src) && src[i+1] == '/':
state, isComment = inLineComment, true
case c == '/' && i+1 < len(src) && src[i+1] == '*':
// Both opener bytes are taken here: the opener's '*' cannot
// also start the closer, so "/*/" leaves the comment open.
codeB[i], codeB[i+1] = ' ', ' '
i++
state = inBlockComment
continue
case c == '"':
state = inString
case c == '`':
state = inRawString
case c == '\'':
state = inRune
}
case inLineComment:
if c == '\n' {
state = inCode
} else {
isComment = true
}
case inBlockComment:
isComment = true
if c == '*' && i+1 < len(src) && src[i+1] == '/' {
codeB[i], codeB[i+1] = ' ', ' '
i++
state = inCode
continue
}
case inString, inRune:
end := byte('"')
if state == inRune {
end = '\''
}
switch {
case c == '\\' && i+1 < len(src) && src[i+1] != '\n':
commB[i], commB[i+1] = ' ', ' '
i++
continue
case c == end, c == '\n':
state = inCode
}
case inRawString:
if c == '`' {
state = inCode
}
}
if c == '\n' {
continue
}
if isComment {
codeB[i] = ' '
} else {
commB[i] = ' '
}
}
return strings.Split(string(codeB), "\n"), strings.Split(string(commB), "\n")
}
// oldNameHits lists, as "line: text", every line of the published file rel
// that carries the old name. Kept identifiers are set aside only in the code
// of a Go file; in its comments and in every other file they count.
func oldNameHits(rel, src string) []string {
lines := strings.Split(src, "\n")
var hits []string
if !strings.HasSuffix(rel, ".go") {
for i, l := range lines {
if hasOldName(l) {
hits = append(hits, strconv.Itoa(i+1)+": "+strings.TrimSpace(l))
}
}
return hits
}
code, comments := goCodeAndComments(src)
for i, l := range lines {
if lineHasOldName(code[i]) || hasOldName(comments[i]) {
hits = append(hits, strconv.Itoa(i+1)+": "+strings.TrimSpace(l))
}
}
return hits
}
// publishedFiles lists the files the member repository receives: what git
// tracks at the repository root (the publisher stages `git archive` of the
// release commit). Outside a git checkout it walks the tree instead.
func publishedFiles(t *testing.T, root string) []string {
t.Helper()
if out, err := exec.Command("git", "-C", root, "ls-files", "-z").Output(); err == nil && len(out) > 0 {
var files []string
for _, f := range bytes.Split(out, []byte{0}) {
if len(f) > 0 {
files = append(files, string(f))
}
}
return files
}
var files []string
err := filepath.Walk(root, func(p string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() && info.Name() == ".git" {
return filepath.SkipDir
}
if info.Mode().IsRegular() {
rel, _ := filepath.Rel(root, p)
files = append(files, rel)
}
return nil
})
if err != nil {
t.Fatalf("walking %s: %v", root, err)
}
return files
}
func TestPublishedSourceCarriesNoOldInternalName(t *testing.T) {
root, err := filepath.Abs("..")
if err != nil {
t.Fatal(err)
}
files := publishedFiles(t, root)
if len(files) < 50 {
t.Fatalf("found only %d files under %s; the scan is not looking at the repository", len(files), root)
}
var hits []string
for _, rel := range files {
if strings.Contains(strings.ToLower(rel), oldName) {
hits = append(hits, rel+": (in the file name)")
}
data, err := os.ReadFile(filepath.Join(root, rel))
if err != nil {
continue // a tracked file deleted in the working tree
}
for _, h := range oldNameHits(rel, string(data)) {
hits = append(hits, rel+":"+h)
}
}
if len(hits) > 0 {
t.Errorf("the old internal name appears in %d published lines. Reword prose and comments; an "+
"identifier may be added to keptOldNames only when something outside this repository still "+
"reads it, and is set aside only in Go code:\n %s",
len(hits), strings.Join(hits, "\n "))
}
}
// The check itself: it must catch the word in prose and in a longer name, and
// pass the kept identifiers.
func TestOldNameCheckCatchesProseAndPassesKeptIdentifiers(t *testing.T) {
up := strings.ToUpper(oldName)
title := strings.ToUpper(oldName[:1]) + oldName[1:]
for _, line := range []string{
"// see " + oldName + " for the rest",
"// the " + title + " server",
up + "_NEW_VAR",
keptOldNames[0] + "X", // a kept name with a suffix is a different name
keptOldNames[len(keptOldNames)-1] + "x " + oldName,
} {
if !lineHasOldName(line) {
t.Errorf("not caught: %q", line)
}
}
for _, line := range []string{
`var agentTokenEnvNames = []string{"PRIVATAE_AGENT_TOKEN", "SKEMA_AGENT_TOKEN"}`,
`const fpSANPrefix = "urn:skema:hwfp:"`,
`labWorkspace = "/var/lib/skema/projects"`,
`- {name: SKEMA_WORKSPACE, value: "%s"}`,
`"-e", "SKEMA_APP_ID=" + spec.HarnessID}`,
"nothing to see here",
} {
if lineHasOldName(line) {
t.Errorf("flagged a kept identifier: %q", line)
}
}
}
// Where a kept identifier is set aside: in Go code only. In a Go comment, the
// changelog, any other Markdown file or a script it is a hit like the bare
// word. A "//" inside a string, a raw string spanning lines and an escaped
// quote do not move the line between code and comment.
func TestKeptIdentifiersAreSetAsideOnlyInGoCode(t *testing.T) {
prefix := keptOldNames[len(keptOldNames)-1] // the fingerprint prefix
env := keptOldNames[0]
for _, c := range []struct {
rel, src string
hit bool
}{
{"CHANGELOG.md", "- the fingerprint goes under `" + prefix + "<fp>`, as before\n", true},
{"docs/claims.md", "the agent token is read from " + env + "\n", true},
{"deploy/run.sh", "export " + env + "=x\n", true},
{"a.go", "package a\n\n// names it " + prefix + "<fp>\n", true},
{"a.go", "x := 1 /* " + env + " */\n", true},
{"a.go", "x := 1 /*/ " + env + " */\n", true},
{"a.go", "s := \"a\\\"b\" // " + env + "\n", true},
{"a.go", "const p = \"" + prefix + "\"\n", false},
{"a.go", "v := []string{\"" + env + "\"} // the host mints this tag\n", false},
{"a.go", "u := \"https://example.com/" + env + "\"\n", false},
{"a.go", "y := `\nname: x // " + env + "\n`\n", false},
{"a.go", "r := '\"' + 1 // " + env + "\n", true},
} {
got := oldNameHits(c.rel, c.src)
if (len(got) > 0) != c.hit {
t.Errorf("%s %q: hits %q, want a hit: %v", c.rel, c.src, got, c.hit)
}
}
}