Validate access to derived host roots
This commit is contained in:
@@ -133,6 +133,8 @@ and downstream consumers. Findings 1–4 remain open.
|
||||
|
||||
## Finding 5 — Derived host roots skip standalone access validation
|
||||
|
||||
**Status** — Complete. Derived and explicit standalone contexts share the host root open and close checks. The permission regression passed for both paths.
|
||||
|
||||
**Priority P2.** When a companion binds to the main context with imports
|
||||
disabled, `acquireStandaloneInput` checks the resolved directories with
|
||||
`Stat`. An equivalent explicit context goes through
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package configkit_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -9,6 +10,61 @@ import (
|
||||
configkit "go.maximumdirect.net/configkit"
|
||||
)
|
||||
|
||||
func TestMainRelativeStandaloneRejectsInaccessibleHostRoot(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
mainPath := filepath.Join(root, "main.yml")
|
||||
if err := os.WriteFile(mainPath, []byte("count: 1\n"), 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.Chmod(root, 0100); err != nil {
|
||||
t.Skipf("cannot set search-only permissions: %v", err)
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
if err := os.Chmod(root, 0700); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
})
|
||||
handle, err := os.OpenRoot(root)
|
||||
if err == nil {
|
||||
_ = handle.Close()
|
||||
t.Skip("host allows opening this search-only directory")
|
||||
}
|
||||
if !errors.Is(err, os.ErrPermission) {
|
||||
t.Fatalf("open root = %v, want permission denied", err)
|
||||
}
|
||||
if _, err := os.ReadFile(mainPath); err != nil {
|
||||
t.Skipf("main must remain readable by name: %v", err)
|
||||
}
|
||||
|
||||
main := configkit.Input{Source: configkit.File(mainPath)}
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
filesystem *configkit.FilesystemContext
|
||||
}{
|
||||
{name: "derived"},
|
||||
{name: "explicit", filesystem: &configkit.FilesystemContext{Root: root, BaseDir: root}},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
underlay := configkit.Input{
|
||||
Source: configkit.Bytes("companion", []byte("{}\n")),
|
||||
MainRelative: &configkit.MainRelativeContext{RequireBase: true},
|
||||
Filesystem: tc.filesystem,
|
||||
}
|
||||
_, err := configkit.LoadLayered(main, configkit.LayeredOptions[layeredFacadeConfig]{
|
||||
Underlays: []configkit.Underlay{{Inputs: []configkit.Input{underlay}}},
|
||||
})
|
||||
var failure *configkit.LoadError
|
||||
if !errors.As(err, &failure) {
|
||||
t.Fatalf("error = %v, want a load error", err)
|
||||
}
|
||||
diagnostics := failure.Diagnostics()
|
||||
if len(diagnostics) != 1 || diagnostics[0].Code != configkit.CodeImportPermission {
|
||||
t.Fatalf("diagnostics = %#v, want import.permission", diagnostics)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMainRelativeDiscoveryAndEvidence(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
first, second := filepath.Join(root, "first"), filepath.Join(root, "second")
|
||||
|
||||
@@ -137,7 +137,7 @@ func acquireStandaloneInput(input assembledInput, request inputAcquisitionReques
|
||||
input.document = processing.payload
|
||||
}
|
||||
if request.resolved != nil {
|
||||
if diagnostic := validateResolvedFilesystemDirectories(*request.resolved); diagnostic != nil {
|
||||
if diagnostic := validateResolvedFilesystemContext(*request.resolved); diagnostic != nil {
|
||||
return input, []Diagnostic{*diagnostic}
|
||||
}
|
||||
input.resolvedFilesystem = request.resolved
|
||||
|
||||
@@ -17,23 +17,30 @@ func validateFilesystemContext(source Source, context *FilesystemContext, main p
|
||||
}
|
||||
return resolvedFilesystemContext{}, diagnostic
|
||||
}
|
||||
if diagnostic := validateResolvedFilesystemDirectories(resolved); diagnostic != nil {
|
||||
if diagnostic := validateResolvedFilesystemContext(resolved); diagnostic != nil {
|
||||
return resolvedFilesystemContext{}, diagnostic
|
||||
}
|
||||
return resolved, nil
|
||||
}
|
||||
|
||||
func validateResolvedFilesystemContext(resolved resolvedFilesystemContext) *Diagnostic {
|
||||
if diagnostic := validateResolvedFilesystemDirectories(resolved); diagnostic != nil {
|
||||
return diagnostic
|
||||
}
|
||||
if resolved.native != nil {
|
||||
return resolved, nil
|
||||
return nil
|
||||
}
|
||||
root, err := os.OpenRoot(resolved.rootPath)
|
||||
if err != nil {
|
||||
value := globFilesystemDiagnostic(err, "configuration root could not be opened", Location{})
|
||||
value.Primary = nil
|
||||
return resolvedFilesystemContext{}, &value
|
||||
return &value
|
||||
}
|
||||
if err := root.Close(); err != nil {
|
||||
value := importDiagnostic(CodeImportRead, "configuration root could not be closed", nil)
|
||||
return resolvedFilesystemContext{}, &value
|
||||
return &value
|
||||
}
|
||||
return resolved, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func filesystemOptionDiagnostics(source Source, context *FilesystemContext) []Diagnostic {
|
||||
|
||||
Reference in New Issue
Block a user