Fix underlay provenance and resource path handling

This commit is contained in:
2026-09-29 20:03:39 +00:00
parent 15f5083282
commit b604aa167b
6 changed files with 217 additions and 8 deletions

View File

@@ -92,14 +92,35 @@ func composeDocuments(inputs ...*document.Node) (*composedDocument, []Diagnostic
for _, input := range inputs {
applications = append(applications, historyApplication{layer: LayerBase, value: input})
}
origins := compositionOrigins(collected.ordered)
completeCompositionOrigins(root, Path{}, origins)
return &composedDocument{
root: root,
claims: cloneCompositionClaims(collected.ordered),
origins: compositionOrigins(collected.ordered),
origins: origins,
applications: applications,
}, nil
}
// Conflict claims treat sequences atomically, but inspection needs origins for
// every descendant. Preserve composite mapping claims while filling those gaps.
func completeCompositionOrigins(node *document.Node, path Path, origins map[string]Origin) {
pointer := path.String()
if _, present := origins[pointer]; !present {
origins[pointer] = Origin{Layer: LayerBase, Locations: []Location{publicLocation(node.Location)}}
}
switch node.Kind {
case document.Mapping:
for _, entry := range node.Mapping {
completeCompositionOrigins(entry.Value, path.Key(entry.Key), origins)
}
case document.Sequence:
for index, child := range node.Sequence {
completeCompositionOrigins(child, path.Index(index), origins)
}
}
}
func (c *compositionClaims) append(node *document.Node, path Path) {
claim := compositionClaim{
path: path.clone(),

View File

@@ -125,9 +125,6 @@ func applyOverlayMetadata(metadata *Metadata, records []overlayRecord) {
if err != nil {
continue
}
if history := events[pointer]; len(history) != 0 && history[len(history)-1].Origin.Layer == info.Origin.Layer && history[len(history)-1].Origin.Operation == info.Origin.Operation {
continue
}
appendInputOriginEvent(events, baseline, pointer, path, info.Origin)
}
metadata.history = make(map[string][]Change, len(baseline)+len(events))
@@ -144,12 +141,37 @@ func appendInputOriginEvent(events map[string][]Change, baseline map[string][]Ch
return
}
history := events[pointer]
if len(history) != 0 && originContainedBy(history[len(history)-1].Origin, origin) {
if len(history) != 0 && originRepresentedByChange(path, origin, history[len(history)-1]) {
return
}
appendInputChangeEvent(events, baseline, pointer, Change{Path: path.clone(), Origin: cloneOrigin(origin)})
}
func originRepresentedByChange(path Path, origin Origin, change Change) bool {
if change.Path.String() == path.String() {
return originContainedBy(origin, change.Origin)
}
if !pathContains(change.Path, path) || origin.Layer != change.Origin.Layer || origin.Operation != change.Origin.Operation {
return false
}
// A subtree replacement already records its descendants' contributions.
// Their coordinates differ from the container's, but their sources must be
// covered by the operation; matching only the precedence role is insufficient.
for _, location := range origin.Locations {
found := false
for _, recorded := range change.Origin.Locations {
if location.SourceID == recorded.SourceID {
found = true
break
}
}
if !found {
return false
}
}
return true
}
func appendInputChangeEvent(events map[string][]Change, baseline map[string][]Change, pointer string, value Change) {
change := cloneChange(value)
history := events[pointer]

View File

@@ -36,7 +36,7 @@ type rootedNativeFS struct {
}
func (r rootedNativeFS) name(name string) (string, error) {
if !fs.ValidPath(name) || strings.Contains(name, "\\") {
if !fs.ValidPath(name) {
return "", &fs.PathError{Op: "open", Path: name, Err: fs.ErrInvalid}
}
joined := path.Join(r.root, name)
@@ -75,7 +75,8 @@ type rootedHostFS struct {
}
func (r rootedHostFS) Open(name string) (fs.File, error) {
if !fs.ValidPath(name) || strings.Contains(name, "\\") {
localName, err := filepath.Localize(name)
if err != nil {
return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrInvalid}
}
opener := r.openRoot
@@ -86,7 +87,7 @@ func (r rootedHostFS) Open(name string) (fs.File, error) {
if err != nil {
return nil, &fs.PathError{Op: "open", Path: name, Err: err}
}
canonical, err := canonicalExistingPath(filepath.Join(r.root, filepath.FromSlash(name)))
canonical, err := canonicalExistingPath(filepath.Join(r.root, localName))
if err != nil {
_ = root.Close()
return nil, &fs.PathError{Op: "open", Path: name, Err: err}

View File

@@ -5,10 +5,46 @@ import (
"io/fs"
"os"
"path/filepath"
"runtime"
"testing"
"testing/fstest"
)
func TestResourceContextsPreserveLiteralBackslashes(t *testing.T) {
const directory = `assets\literal`
const name = directory + `/text\file`
check := func(t *testing.T, filesystem fs.FS) {
t.Helper()
data, err := fs.ReadFile(filesystem, name)
if err != nil || string(data) != "content" {
t.Fatalf("read = %q, %v", data, err)
}
entries, err := fs.ReadDir(filesystem, directory)
if err != nil || len(entries) != 1 || entries[0].Name() != `text\file` {
t.Fatalf("entries = %v, %v", entries, err)
}
if _, err := filesystem.Open("../outside"); !errors.Is(err, fs.ErrInvalid) {
t.Fatalf("escape = %v", err)
}
}
t.Run("native", func(t *testing.T) {
check(t, rootedNativeFS{filesystem: fstest.MapFS{"bundle/" + name: &fstest.MapFile{Data: []byte("content")}}, root: "bundle"})
})
t.Run("host", func(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("backslash is a host separator on Windows; native case remains covered")
}
root := t.TempDir()
if err := os.Mkdir(filepath.Join(root, directory), 0o700); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(root, filepath.FromSlash(name)), []byte("content"), 0o600); err != nil {
t.Fatal(err)
}
check(t, rootedHostFS{root: root})
})
}
type trackedHostRoot struct {
root *os.Root
closed *int