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
|
## 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
|
**Priority P2.** When a companion binds to the main context with imports
|
||||||
disabled, `acquireStandaloneInput` checks the resolved directories with
|
disabled, `acquireStandaloneInput` checks the resolved directories with
|
||||||
`Stat`. An equivalent explicit context goes through
|
`Stat`. An equivalent explicit context goes through
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package configkit_test
|
package configkit_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -9,6 +10,61 @@ import (
|
|||||||
configkit "go.maximumdirect.net/configkit"
|
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) {
|
func TestMainRelativeDiscoveryAndEvidence(t *testing.T) {
|
||||||
root := t.TempDir()
|
root := t.TempDir()
|
||||||
first, second := filepath.Join(root, "first"), filepath.Join(root, "second")
|
first, second := filepath.Join(root, "first"), filepath.Join(root, "second")
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ func acquireStandaloneInput(input assembledInput, request inputAcquisitionReques
|
|||||||
input.document = processing.payload
|
input.document = processing.payload
|
||||||
}
|
}
|
||||||
if request.resolved != nil {
|
if request.resolved != nil {
|
||||||
if diagnostic := validateResolvedFilesystemDirectories(*request.resolved); diagnostic != nil {
|
if diagnostic := validateResolvedFilesystemContext(*request.resolved); diagnostic != nil {
|
||||||
return input, []Diagnostic{*diagnostic}
|
return input, []Diagnostic{*diagnostic}
|
||||||
}
|
}
|
||||||
input.resolvedFilesystem = request.resolved
|
input.resolvedFilesystem = request.resolved
|
||||||
|
|||||||
@@ -17,23 +17,30 @@ func validateFilesystemContext(source Source, context *FilesystemContext, main p
|
|||||||
}
|
}
|
||||||
return resolvedFilesystemContext{}, diagnostic
|
return resolvedFilesystemContext{}, diagnostic
|
||||||
}
|
}
|
||||||
if diagnostic := validateResolvedFilesystemDirectories(resolved); diagnostic != nil {
|
if diagnostic := validateResolvedFilesystemContext(resolved); diagnostic != nil {
|
||||||
return resolvedFilesystemContext{}, diagnostic
|
return resolvedFilesystemContext{}, diagnostic
|
||||||
}
|
}
|
||||||
|
return resolved, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateResolvedFilesystemContext(resolved resolvedFilesystemContext) *Diagnostic {
|
||||||
|
if diagnostic := validateResolvedFilesystemDirectories(resolved); diagnostic != nil {
|
||||||
|
return diagnostic
|
||||||
|
}
|
||||||
if resolved.native != nil {
|
if resolved.native != nil {
|
||||||
return resolved, nil
|
return nil
|
||||||
}
|
}
|
||||||
root, err := os.OpenRoot(resolved.rootPath)
|
root, err := os.OpenRoot(resolved.rootPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
value := globFilesystemDiagnostic(err, "configuration root could not be opened", Location{})
|
value := globFilesystemDiagnostic(err, "configuration root could not be opened", Location{})
|
||||||
value.Primary = nil
|
value.Primary = nil
|
||||||
return resolvedFilesystemContext{}, &value
|
return &value
|
||||||
}
|
}
|
||||||
if err := root.Close(); err != nil {
|
if err := root.Close(); err != nil {
|
||||||
value := importDiagnostic(CodeImportRead, "configuration root could not be closed", 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 {
|
func filesystemOptionDiagnostics(source Source, context *FilesystemContext) []Diagnostic {
|
||||||
|
|||||||
Reference in New Issue
Block a user