security: publish hardened v1 initiative snapshot

Publish the reviewed security policy and evidence, exact runtime ABI enforcement, orphan-output and permission safeguards, dead-upstream cleanup, and the evidence-gated v1 launch plan.

This commit is an exact sanitized export from the private development record. Material implementation and review were assisted by OpenAI Codex; Cole Speelman reviewed the changes and accepts human responsibility.

Himesan-Output-Permission: v1.0
Signed-off-by: Cole Speelman <gamertan@noreply.localhost>
This commit is contained in:
2026-08-12 03:54:46 -04:00
parent 4166a66c66
commit 113c95c21e
22 changed files with 1028 additions and 73 deletions
+68
View File
@@ -0,0 +1,68 @@
// SPDX-License-Identifier: AGPL-3.0-only
package compiler
import (
"context"
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
)
func TestGeneratedCodeRequiresVersionedRuntimeABIMarker(t *testing.T) {
if testing.Short() {
t.Skip("skipping temporary-module ABI compilation in short mode")
}
t.Parallel()
directory := resolvedTempDir(t)
templatePath := filepath.Join(directory, "page.sando")
mustWrite(t, templatePath, "<?sando go\npackage generated\nfunc Page()\n?>")
result, err := Generate(context.Background(), []string{templatePath})
if err != nil {
t.Fatalf("Generate: %v (%v)", err, result.Diagnostics)
}
generated := string(mustRead(t, templatePath+".go"))
if !strings.Contains(generated, ".ABISandoV1") {
t.Fatalf("generated code does not require the sando.v1 marker:\n%s", generated)
}
mustWrite(t, filepath.Join(directory, "go.mod"), `module example.test/abi
go 1.25
require gamertan.com/sandwich-hime/sando v0.0.0
replace gamertan.com/sandwich-hime/sando => ./fake-sando
`)
mustWrite(t, filepath.Join(directory, "fake-sando", "go.mod"), `module gamertan.com/sandwich-hime/sando
go 1.25
`)
mustWrite(t, filepath.Join(directory, "fake-sando", "component.go"), `package sando
import (
"context"
"io"
)
const ABI = "sando.incompatible"
type Component interface { Render(context.Context, io.Writer) error }
type ComponentFunc func(context.Context, io.Writer) error
func (f ComponentFunc) Render(ctx context.Context, w io.Writer) error { return f(ctx, w) }
`)
command := exec.Command("go", "test", "./...")
command.Dir = directory
command.Env = append(os.Environ(), "GOWORK=off")
output, buildErr := command.CombinedOutput()
if buildErr == nil {
t.Fatalf("generated code compiled against an incompatible runtime:\n%s", output)
}
if !strings.Contains(string(output), "undefined: __himesan_sando.ABISandoV1") {
t.Fatalf("incompatible runtime failed for an unexpected reason: %v\n%s", buildErr, output)
}
}
+4 -1
View File
@@ -92,7 +92,10 @@ func generateGo(file *sourceFile) ([]byte, []Diagnostic) {
}
}
output.WriteString(")\n\n")
fmt.Fprintf(&output, "var _ = %s.ABI\n\n", imports.Sando)
// A version-specific exported marker makes the generated/runtime ABI a Go
// build-time contract. The descriptive ABI string alone cannot enforce
// compatibility because constant values are not part of symbol resolution.
fmt.Fprintf(&output, "var _ = %s.ABISandoV1\n\n", imports.Sando)
fmt.Fprintf(&output, "func %s%s%s %s.Component {\n", file.Name, file.TypeParams, file.Params, imports.Sando)
fmt.Fprintf(&output, "\treturn %s.ComponentFunc(func(%s %s.Context, %s %s.Writer) error {\n", imports.Sando, contextName, imports.Context, writerName, imports.IO)
fmt.Fprintf(&output, "\t\t_ = %s\n", contextName)
+62
View File
@@ -340,6 +340,68 @@ func TestGenerateRefusesUnownedOutput(t *testing.T) {
}
}
func TestDirectoryOperationsRejectOrphanedOwnedOutputBeforeWrites(t *testing.T) {
t.Parallel()
directory := resolvedTempDir(t)
orphanSource := filepath.Join(directory, "orphan.sando")
mustWrite(t, orphanSource, simpleSource("Orphan", "last good"))
if _, err := Generate(context.Background(), []string{directory}); err != nil {
t.Fatal(err)
}
orphanOutput := orphanSource + ".go"
lastGood := mustRead(t, orphanOutput)
if err := os.Remove(orphanSource); err != nil {
t.Fatal(err)
}
liveSource := filepath.Join(directory, "live.sando")
mustWrite(t, liveSource, simpleSource("Live", "must not be written"))
// A handwritten file whose name merely resembles an output is not owned by
// Hime-san and must not be treated as an orphan.
mustWrite(t, filepath.Join(directory, "handwritten.sando.go"), "package demo\n")
checked, err := Check(context.Background(), []string{directory})
if err == nil {
t.Fatalf("orphaned owned output unexpectedly passed check: %+v", checked)
}
assertDiagnosticCode(t, checked.Diagnostics, "HIM2014")
generated, err := Generate(context.Background(), []string{directory})
if err == nil {
t.Fatalf("orphaned owned output unexpectedly allowed generation: %+v", generated)
}
assertDiagnosticCode(t, generated.Diagnostics, "HIM2014")
if !bytes.Equal(lastGood, mustRead(t, orphanOutput)) {
t.Fatal("orphaned last-good output changed")
}
if _, statErr := os.Stat(liveSource + ".go"); !errors.Is(statErr, os.ErrNotExist) {
t.Fatalf("batch wrote a live output despite the orphan diagnostic: %v", statErr)
}
}
func TestNewGeneratedOutputInheritsRestrictiveSourceMode(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("POSIX file mode test")
}
t.Parallel()
directory := resolvedTempDir(t)
path := filepath.Join(directory, "private.sando")
mustWrite(t, path, simpleSource("Private", "private"))
if err := os.Chmod(path, 0o600); err != nil {
t.Fatal(err)
}
if _, err := Generate(context.Background(), []string{path}); err != nil {
t.Fatal(err)
}
info, err := os.Stat(path + ".go")
if err != nil {
t.Fatal(err)
}
if got := info.Mode().Perm(); got != 0o600 {
t.Fatalf("generated output mode = %04o, want 0600", got)
}
}
func TestDiscoveryBoundariesAndExplicitNestedFile(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("symlink creation commonly requires additional Windows privileges")
+12 -10
View File
@@ -34,7 +34,8 @@ const (
)
type contextAnalyzer struct {
file *sourceFile
file *sourceFile
positions positionTable
state htmlState
currentTag string
@@ -79,7 +80,12 @@ var unsupportedDynamicAttributes = map[string]string{
}
func analyzeContexts(file *sourceFile) []Diagnostic {
analyzer := &contextAnalyzer{file: file, state: htmlData, attrFirstDynamic: -1}
analyzer := &contextAnalyzer{
file: file,
positions: newPositionTable(file.Source),
state: htmlData,
attrFirstDynamic: -1,
}
var diagnostics []Diagnostic
for nodeIndex := range file.Nodes {
node := &file.Nodes[nodeIndex]
@@ -136,20 +142,20 @@ func analyzeContexts(file *sourceFile) []Diagnostic {
}
}
end := analyzer.positions.at(len(file.Source))
if analyzer.state != htmlData {
diagnostics = append(diagnostics, diagnostic(file.Path, endPosition(file.Source), "HIM1310", "template ends in an incomplete or ambiguous HTML parser context"))
diagnostics = append(diagnostics, diagnostic(file.Path, end, "HIM1310", "template ends in an incomplete or ambiguous HTML parser context"))
}
if len(analyzer.stack) != 0 {
diagnostics = append(diagnostics, diagnostic(file.Path, endPosition(file.Source), "HIM1311", fmt.Sprintf("component must finish in its starting HTML context; unclosed <%s>", analyzer.stack[len(analyzer.stack)-1])))
diagnostics = append(diagnostics, diagnostic(file.Path, end, "HIM1311", fmt.Sprintf("component must finish in its starting HTML context; unclosed <%s>", analyzer.stack[len(analyzer.stack)-1])))
}
return diagnostics
}
func (a *contextAnalyzer) consumeText(text string, start sourcePosition) *Diagnostic {
positionTable := newPositionTable(a.file.Source)
for index := 0; index < len(text); index++ {
b := text[index]
position := positionTable.at(start.Offset + index)
position := a.positions.at(start.Offset + index)
if a.rawTag == "script" {
a.scriptTail += string(b)
if len(a.scriptTail) > len("<!--") {
@@ -614,7 +620,3 @@ func isAttributeNameChar(b byte) bool {
func isHTMLSpace(b byte) bool {
return b == ' ' || b == '\t' || b == '\r' || b == '\n' || b == '\f'
}
func endPosition(source []byte) sourcePosition {
return newPositionTable(source).at(len(source))
}
+64 -1
View File
@@ -4,7 +4,9 @@ package compiler
import (
"context"
"errors"
"fmt"
"io"
"io/fs"
"os"
"path/filepath"
@@ -115,7 +117,24 @@ func discover(ctx context.Context, paths []string) ([]string, []Diagnostic) {
return filepath.SkipDir
}
}
if entry.IsDir() || filepath.Ext(entry.Name()) != ".sando" {
if entry.IsDir() {
return nil
}
if strings.HasSuffix(entry.Name(), ".sando.go") {
entryInfo, statErr := entry.Info()
if statErr != nil {
diagnostics = append(diagnostics, diagnostic(path, sourcePosition{Line: 1, Column: 1}, "HIM2013", "cannot inspect possible generated output: "+statErr.Error()))
return nil
}
if !entryInfo.Mode().IsRegular() {
return nil
}
if orphanDiagnostic := inspectOwnedGeneratedOutput(path); orphanDiagnostic != nil {
diagnostics = append(diagnostics, *orphanDiagnostic)
}
return nil
}
if filepath.Ext(entry.Name()) != ".sando" {
return nil
}
entryInfo, statErr := entry.Info()
@@ -157,6 +176,50 @@ func discover(ctx context.Context, paths []string) ([]string, []Diagnostic) {
return discovered, diagnostics
}
func inspectOwnedGeneratedOutput(path string) *Diagnostic {
owned, err := hasGeneratedMarker(path)
if err != nil {
item := diagnostic(path, sourcePosition{Line: 1, Column: 1}, "HIM2013", "cannot inspect possible generated output: "+err.Error())
return &item
}
if !owned {
return nil
}
sourcePath := strings.TrimSuffix(path, ".go")
info, err := os.Lstat(sourcePath)
if err == nil && info.Mode().IsRegular() && info.Mode()&os.ModeSymlink == 0 {
return nil
}
message := "owned generated output is orphaned because its adjacent .sando source is missing; review and remove the output explicitly"
if err == nil {
message = "owned generated output is orphaned because its adjacent .sando source is not a regular file; review and remove the output explicitly"
} else if !os.IsNotExist(err) {
message = "cannot inspect the adjacent .sando source for an owned generated output: " + err.Error()
}
item := diagnostic(path, sourcePosition{Line: 1, Column: 1}, "HIM2014", message)
return &item
}
func hasGeneratedMarker(path string) (bool, error) {
file, err := os.Open(path)
if err != nil {
return false, err
}
defer file.Close()
marker := []byte(generatedPrefix + "\n")
prefix := make([]byte, len(marker))
if _, err := io.ReadFull(file, prefix); err != nil {
if errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) {
return false, nil
}
return false, err
}
return string(prefix) == string(marker), nil
}
func firstSymlinkComponent(path string) (string, error) {
absolute, err := filepath.Abs(path)
if err != nil {
+1
View File
@@ -86,6 +86,7 @@ type CompiledFile struct {
Digest string
Code []byte
source *sourceFile
sourceMode uint32
}
// FileResult describes one source/output pair processed by Generate or Check.
+5 -1
View File
@@ -61,7 +61,10 @@ func Generate(ctx context.Context, paths []string) (Result, error) {
result.Unchanged++
continue
}
mode := os.FileMode(0o644)
// Generated Go can contain every literal present in its source. A new
// output therefore must not be more permissive than the source file.
// Execute bits are never meaningful for Go source and are stripped.
mode := os.FileMode(file.sourceMode) & 0o666
if info, statErr := os.Stat(file.OutputPath); statErr == nil {
mode = info.Mode().Perm()
}
@@ -151,6 +154,7 @@ func compileOperation(ctx context.Context, paths []string) ([]CompiledFile, Resu
output, diagnostics := compileWithMapping(sourcePath, source, moduleRelativeSourcePath(sourcePath))
result.Diagnostics = append(result.Diagnostics, diagnostics...)
if output.Code != nil {
output.sourceMode = uint32(info.Mode().Perm())
compiled = append(compiled, output)
result.Files = append(result.Files, FileResult{SourcePath: output.SourcePath, OutputPath: output.OutputPath})
}
+1 -1
View File
@@ -11,7 +11,7 @@ import (
__himesan_io "io"
)
var _ = __himesan_sando.ABI
var _ = __himesan_sando.ABISandoV1
func Greeting(name string) __himesan_sando.Component {
return __himesan_sando.ComponentFunc(func(__himesan_render_context __himesan_context.Context, __himesan_writer __himesan_io.Writer) error {