Files
moby/daemon/pkg/plugin/v2/plugin_linux_test.go
T
Paweł Gronowski a8181e8020 daemon/plugins: Confine plugin mount paths to scoped roots
A managed volume plugin could return a path containing dot-dot
elements that passed the propagated-mount string prefix check, then
escaped the host mount root when filepath.Join cleaned it. Sibling
paths sharing the same string prefix were also treated as propagated
paths.

Normalize returned paths in the plugin's rooted namespace. Use
filepath.Rel and filepath.IsLocal so only path-component descendants
are mapped through the propagated mount. Other paths remain confined
beneath the plugin rootfs.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2026-08-26 22:55:46 +02:00

89 lines
2.2 KiB
Go

package v2
import (
"testing"
"github.com/moby/moby/api/types/plugin"
"gotest.tools/v3/assert"
)
func TestScopedPath(t *testing.T) {
t.Parallel()
tests := []struct {
name string
propagatedMount string
path string
expected string
}{
{
name: "ordinary rootfs path",
propagatedMount: "/propagated",
path: "/etc/plugin.json",
expected: "/plugin/rootfs/etc/plugin.json",
},
{
name: "exact propagated root",
propagatedMount: "/propagated",
path: "/propagated",
expected: "/plugin/propagated-mount",
},
{
name: "propagated child",
propagatedMount: "/propagated",
path: "/propagated/child",
expected: "/plugin/propagated-mount/child",
},
{
name: "in-subtree normalization",
propagatedMount: "/propagated",
path: "/propagated/subtree/../child",
expected: "/plugin/propagated-mount/child",
},
{
name: "reported traversal escape",
propagatedMount: "/propagated",
path: "/propagated/../../../volumes/victim/_data",
expected: "/plugin/rootfs/volumes/victim/_data",
},
{
name: "sibling prefix path",
propagatedMount: "/propagated",
path: "/propagated-other/child",
expected: "/plugin/rootfs/propagated-other/child",
},
{
name: "relative dot-dot containment",
propagatedMount: "/propagated",
path: "../../../outside",
expected: "/plugin/rootfs/outside",
},
{
name: "trailing separator",
propagatedMount: "/propagated/",
path: "/propagated/",
expected: "/plugin/propagated-mount",
},
{
name: "propagated root slash",
propagatedMount: "/",
path: "/var/lib",
expected: "/plugin/propagated-mount/var/lib",
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
p := &Plugin{
Rootfs: "/plugin/rootfs",
PluginObj: plugin.Plugin{
Config: plugin.Config{PropagatedMount: tc.propagatedMount},
},
}
assert.Equal(t, p.ScopedPath(tc.path), tc.expected)
})
}
}