check-safety/toolbar.go
2026-09-05 21:14:15 +08:00

94 lines
2.3 KiB
Go

package main
import (
"go/ast"
"go/parser"
"go/token"
"os"
"path/filepath"
"strings"
)
type toolbarViolation struct {
file string
line int
funcName string
}
// Functions that render full public pages but legitimately skip toolbar injection.
var toolbarExemptFuncs = map[string]bool{
// Auto-generated SEO listing pages — not block-editable.
"renderListingPage": true,
// Historical version preview — has its own preview banner; toolbar edit/publish
// buttons would be misleading in that context.
"renderVersionPreview": true,
}
// checkToolbarInjection scans handler Go files for public page render functions
// (identified by containing both getSiteSettings and doc["site_settings"]) and
// verifies each one also injects siteSettings["toolbar"] for the admin toolbar.
func checkToolbarInjection(root string) []toolbarViolation {
var violations []toolbarViolation
handlersDir := filepath.Join(root, "internal", "handlers")
if _, err := os.Stat(handlersDir); err != nil {
return nil
}
_ = filepath.Walk(handlersDir, func(path string, info os.FileInfo, err error) error {
if err != nil || info.IsDir() || !strings.HasSuffix(path, ".go") {
return nil
}
if strings.HasSuffix(path, "_test.go") {
return nil
}
src, err := os.ReadFile(path)
if err != nil {
return nil
}
srcText := string(src)
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, path, src, 0)
if err != nil {
return nil
}
relPath, _ := filepath.Rel(root, path)
for _, decl := range f.Decls {
fn, ok := decl.(*ast.FuncDecl)
if !ok || fn.Body == nil {
continue
}
if toolbarExemptFuncs[fn.Name.Name] {
continue
}
bodyStart := fset.Position(fn.Body.Pos()).Offset
bodyEnd := min(fset.Position(fn.Body.End()).Offset, len(srcText))
body := srcText[bodyStart:bodyEnd]
hasSiteSettings := strings.Contains(body, "getSiteSettings")
hasDocAssign := strings.Contains(body, `doc["site_settings"]`)
if hasSiteSettings && hasDocAssign {
hasToolbar := strings.Contains(body, `siteSettings["toolbar"]`)
if !hasToolbar {
violations = append(violations, toolbarViolation{
file: relPath,
line: fset.Position(fn.Pos()).Line,
funcName: fn.Name.Name,
})
}
}
}
return nil
})
return violations
}