Migrate ordinary I/O from confined file helpers

This commit is contained in:
2026-09-01 17:51:37 +00:00
parent 0d57422b61
commit ba858cc997
34 changed files with 96 additions and 547 deletions

View File

@@ -5,11 +5,11 @@ import (
"encoding/hex"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"gitea.maximumdirect.net/eric/narratio/internal/artifactmodel"
"gitea.maximumdirect.net/eric/narratio/internal/fileops"
"gitea.maximumdirect.net/eric/narratio/internal/manifest"
"gitea.maximumdirect.net/eric/narratio/internal/pathsafe"
)
@@ -85,7 +85,7 @@ func InspectAnalyzeEvidence(
return nonCurrent("configured artifact manifest identity is incompatible; regenerate the artifact")
}
file, err := fileops.OpenConfinedRegularFile(paths.Root, configuredPath)
file, err := os.Open(filepath.Join(paths.Root, filepath.FromSlash(configuredPath)))
if err != nil {
return nonCurrent("configured artifact output is missing or unsafe; regenerate the artifact")
}

View File

@@ -104,30 +104,6 @@ func TestInspectAnalyzeEvidenceRejectsMissingAndUnsafeFiles(t *testing.T) {
t.Fatal(err)
}
}},
{name: "symlink leaf", alter: func(t *testing.T, paths SessionPaths, outputPath string) {
if err := os.Remove(outputPath); err != nil {
t.Fatal(err)
}
target := filepath.Join(paths.Root, "target.md")
if err := os.WriteFile(target, []byte("recap\n"), 0o644); err != nil {
t.Fatal(err)
}
if err := os.Symlink(target, outputPath); err != nil {
t.Fatal(err)
}
}},
{name: "symlink ancestor", alter: func(t *testing.T, paths SessionPaths, outputPath string) {
if err := os.RemoveAll(paths.ArtifactsDir); err != nil {
t.Fatal(err)
}
outside := t.TempDir()
if err := os.WriteFile(filepath.Join(outside, "session_recap.md"), []byte("recap\n"), 0o644); err != nil {
t.Fatal(err)
}
if err := os.Symlink(outside, paths.ArtifactsDir); err != nil {
t.Fatal(err)
}
}},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
@@ -136,7 +112,7 @@ func TestInspectAnalyzeEvidenceRejectsMissingAndUnsafeFiles(t *testing.T) {
outputPath := filepath.Join(paths.ArtifactsDir, "session_recap.md")
test.alter(t, paths, outputPath)
got := InspectAnalyzeEvidence(paths, m, "session_recap", ConfiguredArtifactDefinition{OutputPath: "artifacts/session_recap.md"})
if got.State != AnalyzeEvidenceNonCurrent || !strings.Contains(got.Reason, "missing or unsafe") {
if got.State != AnalyzeEvidenceNonCurrent {
t.Fatalf("evidence = %#v", got)
}
})

View File

@@ -497,7 +497,7 @@ func validateNonEmptyContent(path string) error {
}
func readResolvedArtifact(path, category string) ([]byte, error) {
data, err := fileops.ReadRegularFile(path, MaxResolvedArtifactBytes)
data, err := fileops.ReadFileBounded(path, MaxResolvedArtifactBytes)
if err != nil {
return nil, fmt.Errorf("resolved %s exceeds or cannot be read within %d-byte limit: %w", category, MaxResolvedArtifactBytes, err)
}

View File

@@ -144,7 +144,7 @@ func inspectExtractionPayload(root, path, checksum string) (ExtractionEvidenceSt
if actual != checksum {
return ExtractionEvidenceObsolete, "extract output checksum does not match durable bytes", 0
}
body, err := fileops.ReadRegularFile(path, MaxExtractionPayloadBytes)
body, err := fileops.ReadFileBounded(path, MaxExtractionPayloadBytes)
if err != nil || !json.Valid(body) {
return ExtractionEvidenceObsolete, "extract output is not valid JSON", 0
}

View File

@@ -34,7 +34,7 @@ func ResolveExtractionInputIdentity(paths SessionPaths, m *manifest.Manifest) (E
if err != nil {
return ExtractionInputIdentity{}, fmt.Errorf("resolve trimmed transcript path: %w", err)
}
bytes, err := fileops.ReadRegularFile(path, MaxResolvedArtifactBytes)
bytes, err := fileops.ReadFileBounded(path, MaxResolvedArtifactBytes)
if err != nil {
return ExtractionInputIdentity{}, fmt.Errorf("read trimmed transcript: %w", err)
}

View File

@@ -207,7 +207,10 @@ func (s *LocalStore) AcquireSessionLockForContext(ctx context.Context, campaign,
}
func (s *LocalStore) acquireSessionLockForPaths(paths SessionPaths) (*LockHandle, error) {
f, err := fileops.OpenFileConfined(paths.LockPath, os.O_CREATE|os.O_RDWR, fileops.WorkspaceFileMode)
if err := fileops.EnsureWorkspaceDirectory(filepath.Dir(paths.LockPath)); err != nil {
return nil, fmt.Errorf("create lock directory %q: %w", filepath.Dir(paths.LockPath), err)
}
f, err := os.OpenFile(paths.LockPath, os.O_CREATE|os.O_RDWR, fileops.WorkspaceFileMode)
if err != nil {
return nil, fmt.Errorf("acquire lock %q: %w", paths.LockPath, err)
}

View File

@@ -6,11 +6,11 @@ import (
"errors"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"gitea.maximumdirect.net/eric/narratio/internal/artifactpolicy"
"gitea.maximumdirect.net/eric/narratio/internal/fileops"
"gitea.maximumdirect.net/eric/narratio/internal/manifest"
)
@@ -107,11 +107,16 @@ func ResolvePreparedInput(paths SessionPaths, m *manifest.Manifest, sourceID str
return PreparedInputIdentity{}, fmt.Errorf("prepared input source %q manifest checksum is required", descriptor.SourceID)
}
file, err := fileops.OpenConfinedRegularFile(rootPath, relativePath)
file, err := os.Open(filepath.Join(rootPath, filepath.FromSlash(relativePath)))
if err != nil {
return PreparedInputIdentity{}, fmt.Errorf("open prepared input source %q: %w", descriptor.SourceID, err)
}
digest := sha256.New()
info, statErr := file.Stat()
if statErr != nil || !info.Mode().IsRegular() {
_ = file.Close()
return PreparedInputIdentity{}, fmt.Errorf("prepared input source %q is not a regular file", descriptor.SourceID)
}
size, readErr := io.Copy(digest, file)
closeErr := file.Close()
if readErr != nil {

View File

@@ -130,22 +130,6 @@ func TestResolvePreparedInputRejectsInvalidCanonicalFile(t *testing.T) {
},
wantErr: "open prepared input source",
},
{
name: "symlink",
mutate: func(t *testing.T, path string) {
outside := filepath.Join(t.TempDir(), "outside.json")
if err := os.WriteFile(outside, []byte("outside\n"), 0o644); err != nil {
t.Fatalf("WriteFile(outside) error = %v", err)
}
if err := os.Remove(path); err != nil {
t.Fatalf("Remove() error = %v", err)
}
if err := os.Symlink(outside, path); err != nil {
t.Fatalf("Symlink() error = %v", err)
}
},
wantErr: "not a regular file",
},
{
name: "directory",
mutate: func(t *testing.T, path string) {