Apply deployment permissions to checkpoints

This commit is contained in:
2026-09-11 15:03:39 +00:00
parent ea503acd42
commit 7498fda030
6 changed files with 162 additions and 24 deletions

View File

@@ -179,6 +179,8 @@ passes. This stage is small enough for one implementation prompt.
## Stage 3: Convert Checkpoints And Prove Assembled Cache Behavior
**Completed:** 2026-09-11
### Goal
Move checkpoints to the new shared writer, remove stale private-cache

View File

@@ -57,7 +57,6 @@ func TestRunStateSurfaceMatrix(t *testing.T) {
}
}
assertAnyFile(t, roots.checkpoints)
assertRestrictedTree(t, roots.checkpoints)
if debug {
bundle := onlyChildDir(t, roots.debug)
assertFile(t, filepath.Join(bundle, "summary", "invocation.json"))

View File

@@ -0,0 +1,61 @@
//go:build linux || darwin
package cli
import (
"os"
"path/filepath"
"syscall"
"testing"
)
func TestRunCreatesReusableGroupAccessibleCacheState(t *testing.T) {
previousUmask := syscall.Umask(0o007)
defer syscall.Umask(previousUmask)
roots := newStateTestRoots(t)
harness := newStateTestHarness()
result := runStateTest(t, roots, harness.options(), false, false, "auto")
if result.code != 0 {
t.Fatalf("initial run code=%d stderr=%q", result.code, result.stderr)
}
assertGroupAccessibleCacheTree(t, roots.plans)
assertGroupAccessibleCacheTree(t, roots.checkpoints)
extractCalls := harness.extractCalls
result = runStateTest(t, roots, harness.options(), false, true, "auto")
if result.code != 0 {
t.Fatalf("resume run code=%d stderr=%q", result.code, result.stderr)
}
if harness.extractCalls != extractCalls {
t.Fatalf("extract calls after reuse = %d, want %d", harness.extractCalls, extractCalls)
}
}
func assertGroupAccessibleCacheTree(t *testing.T, root string) {
t.Helper()
if err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
want := os.FileMode(0o660)
if info.IsDir() {
want = 0o770
}
if got := info.Mode().Perm(); got != want {
return &cachePermissionError{path: path, got: got, want: want}
}
return nil
}); err != nil {
t.Fatal(err)
}
}
type cachePermissionError struct {
path string
got, want os.FileMode
}
func (err *cachePermissionError) Error() string {
return err.path + " mode mismatch"
}

View File

@@ -0,0 +1,98 @@
//go:build linux || darwin
package checkpoint
import (
"os"
"path/filepath"
"syscall"
"testing"
"gitea.maximumdirect.net/eric/notarius/internal/core/source"
)
func TestFilesystemCheckpointPermissionsFollowUmask(t *testing.T) {
for _, test := range []struct {
name string
umask int
dirMode os.FileMode
fileMode os.FileMode
}{
{name: "private", umask: 0o077, dirMode: 0o700, fileMode: 0o600},
{name: "group", umask: 0o007, dirMode: 0o770, fileMode: 0o660},
} {
t.Run(test.name, func(t *testing.T) {
previousUmask := syscall.Umask(test.umask)
defer syscall.Umask(previousUmask)
root := filepath.Join(t.TempDir(), "checkpoints")
identity, err := NewIdentity(representativeIdentityInput())
if err != nil {
t.Fatal(err)
}
recorder, err := NewFilesystemRecorder(root, identity)
if err != nil {
t.Fatal(err)
}
if err := recorder.SourceSucceeded("source-module", sourceDocumentForPermissions()); err != nil {
t.Fatal(err)
}
relative, err := identity.RelativePath()
if err != nil {
t.Fatal(err)
}
identityRoot := filepath.Join(root, filepath.FromSlash(relative))
sourceRoot := filepath.Join(identityRoot, "source")
for path, want := range map[string]os.FileMode{
root: test.dirMode,
identityRoot: test.dirMode,
sourceRoot: test.dirMode,
filepath.Join(sourceRoot, "manifest.json"): test.fileMode,
filepath.Join(sourceRoot, "source-document.json"): test.fileMode,
} {
assertCheckpointPermissions(t, path, want)
}
})
}
}
func TestFilesystemCheckpointLeavesExistingRootUntouched(t *testing.T) {
previousUmask := syscall.Umask(0o077)
defer syscall.Umask(previousUmask)
root := filepath.Join(t.TempDir(), "checkpoints")
syscall.Umask(0)
if err := os.Mkdir(root, 0o751); err != nil {
t.Fatal(err)
}
syscall.Umask(0o077)
identity, err := NewIdentity(representativeIdentityInput())
if err != nil {
t.Fatal(err)
}
recorder, err := NewFilesystemRecorder(root, identity)
if err != nil {
t.Fatal(err)
}
if err := recorder.SourceSucceeded("source-module", sourceDocumentForPermissions()); err != nil {
t.Fatal(err)
}
assertCheckpointPermissions(t, root, 0o751)
}
func sourceDocumentForPermissions() *source.SourceDocument {
document := checkpointDocument()
return &document
}
func assertCheckpointPermissions(t *testing.T, path string, want os.FileMode) {
t.Helper()
info, err := os.Stat(path)
if err != nil {
t.Fatal(err)
}
if got := info.Mode().Perm(); got != want {
t.Fatalf("%s mode = %#o, want %#o", path, got, want)
}
}

View File

@@ -5,7 +5,6 @@ import (
"encoding/json"
"os"
"path/filepath"
"runtime"
"strings"
"testing"
"unicode/utf8"
@@ -106,27 +105,6 @@ func TestFilesystemCheckpointRoundTripsAllStages(t *testing.T) {
})
}
if runtime.GOOS != "windows" {
t.Run("restrictive permissions", func(t *testing.T) {
root := filepath.Join(fixture.root, mustRelativePath(t, fixture.identity))
err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
want := os.FileMode(0o600)
if info.IsDir() {
want = 0o700
}
if got := info.Mode().Perm(); got != want {
t.Errorf("%s permissions = %o, want %o", path, got, want)
}
return nil
})
if err != nil {
t.Fatal(err)
}
})
}
}
func TestFilesystemCheckpointRejectsMissingAndCorruptState(t *testing.T) {

View File

@@ -217,7 +217,7 @@ func (r *FilesystemRecorder) writeJSON(name string, payload any) error {
if r == nil || strings.TrimSpace(r.root) == "" {
return nil
}
return fileio.WriteJSON(r.root, name, payload, 0o700, 0o600)
return fileio.WriteJSONWithProcessPermissions(r.root, name, payload)
}
func (r *FilesystemRecorder) timestamp() time.Time {