Skip to content
This repository was archived by the owner on Jul 18, 2025. It is now read-only.

Commit d72712d

Browse files
committed
Check compose file don't use relative paths for bind mount
Signed-off-by: Nicolas De Loof <[email protected]>
1 parent b339e0b commit d72712d

File tree

3 files changed

+118
-2
lines changed

3 files changed

+118
-2
lines changed

internal/commands/build/types.go

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package build
22

33
import (
44
"fmt"
5+
"path/filepath"
56
"reflect"
67
"strings"
78

@@ -15,8 +16,14 @@ import (
1516
type ServiceConfig struct {
1617
Name string `yaml:"-" json:"-"`
1718

18-
Build *ImageBuildConfig
19-
Image *string
19+
Build *ImageBuildConfig
20+
Image *string
21+
Volumes []ServiceVolumeConfig `yaml:",omitempty" json:"volumes,omitempty"`
22+
}
23+
24+
type ServiceVolumeConfig struct {
25+
Type string `yaml:",omitempty" json:"type,omitempty"`
26+
Source string `yaml:",omitempty" json:"source,omitempty"`
2027
}
2128

2229
type ImageBuildConfig struct {
@@ -46,6 +53,13 @@ func loadServices(servicesDict map[string]interface{}, buildArgs []string) ([]Se
4653
return nil, err
4754
}
4855
services = append(services, *serviceConfig)
56+
57+
// Sanity check
58+
for _, volume := range serviceConfig.Volumes {
59+
if volume.Type == "bind" && !filepath.IsAbs(volume.Source) {
60+
return nil, fmt.Errorf("invalid service %q: can't use relative path as volume source", name)
61+
}
62+
}
4963
}
5064
return services, nil
5165
}
@@ -57,6 +71,9 @@ func loadService(name string, serviceDict map[string]interface{}, buildArgs []st
5771
if err := loader.Transform(serviceDict, serviceConfig, loader.Transformer{
5872
TypeOf: reflect.TypeOf(ImageBuildConfig{}),
5973
Func: transformBuildConfig,
74+
}, loader.Transformer{
75+
TypeOf: reflect.TypeOf(ServiceVolumeConfig{}),
76+
Func: transformVolumeConfig,
6077
}); err != nil {
6178
return nil, err
6279
}
@@ -77,6 +94,22 @@ func transformBuildConfig(data interface{}) (interface{}, error) {
7794
}
7895
}
7996

97+
func transformVolumeConfig(data interface{}) (interface{}, error) {
98+
switch value := data.(type) {
99+
case string:
100+
spec := data.(string)
101+
volume, err := loader.ParseVolume(spec)
102+
if err != nil {
103+
return nil, err
104+
}
105+
return ServiceVolumeConfig{Type: volume.Type, Source: volume.Source}, nil
106+
case map[string]interface{}:
107+
return data, nil
108+
default:
109+
return data, errors.Errorf("invalid type %T for service volume", value)
110+
}
111+
}
112+
80113
func buildArgsToMap(array []string) map[string]string {
81114
result := make(map[string]string)
82115
for _, value := range array {

internal/packager/init.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,50 @@ func checkEnvFiles(errWriter io.Writer, appName string, cfgMap map[string]interf
125125
return nil
126126
}
127127

128+
func checkRelativePaths(cfgMap map[string]interface{}) error {
129+
services := cfgMap["services"]
130+
servicesMap, ok := services.(map[string]interface{})
131+
if !ok {
132+
return fmt.Errorf("invalid Compose file")
133+
}
134+
for svcName, svc := range servicesMap {
135+
svcContent, ok := svc.(map[string]interface{})
136+
if !ok {
137+
return fmt.Errorf("invalid service %q", svcName)
138+
}
139+
v, ok := svcContent["volumes"]
140+
if !ok {
141+
continue
142+
}
143+
volumes, ok := v.([]interface{})
144+
if !ok {
145+
return fmt.Errorf("invalid Compose file")
146+
}
147+
for _, volume := range volumes {
148+
switch volume.(type) {
149+
case string:
150+
svol := volume.(string)
151+
source := strings.TrimRight(svol, ":")
152+
if !filepath.IsAbs(source) {
153+
return fmt.Errorf("invalid service %q: can't use relative path as volume source", svcName)
154+
}
155+
case map[string]interface{}:
156+
lvol := volume.(map[string]interface{})
157+
src, ok := lvol["source"]
158+
if !ok {
159+
return fmt.Errorf("invalid volume in service %q", svcName)
160+
}
161+
if !filepath.IsAbs(src.(string)) {
162+
return fmt.Errorf("invalid service %q: can't use relative path as volume source", svcName)
163+
}
164+
default:
165+
return fmt.Errorf("invalid Compose file")
166+
}
167+
}
168+
}
169+
return nil
170+
}
171+
128172
func getParamsFromDefaultEnvFile(composeFile string, composeRaw []byte) (map[string]string, bool, error) {
129173
params := make(map[string]string)
130174
envs, err := opts.ParseEnvFile(filepath.Join(filepath.Dir(composeFile), ".env"))
@@ -173,6 +217,9 @@ func initFromComposeFile(errWriter io.Writer, name string, composeFile string) e
173217
if err := checkEnvFiles(errWriter, name, cfgMap); err != nil {
174218
return err
175219
}
220+
if err := checkRelativePaths(cfgMap); err != nil {
221+
return err
222+
}
176223
params, needsFilling, err := getParamsFromDefaultEnvFile(composeFile, composeRaw)
177224
if err != nil {
178225
return err

internal/packager/init_test.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,3 +186,39 @@ maintainers:
186186
)
187187
assert.Assert(t, fs.Equal(tmpdir.Path(), manifest))
188188
}
189+
190+
func TestInitRelativeVolumePath(t *testing.T) {
191+
for _, composeData := range []string{`
192+
version: '3.7'
193+
services:
194+
nginx:
195+
image: nginx
196+
volumes:
197+
- ./foo:/data
198+
`,
199+
`
200+
version: '3.7'
201+
services:
202+
nginx:
203+
image: nginx
204+
volumes:
205+
- type: bind
206+
source: ./foo
207+
target: /data
208+
`,
209+
} {
210+
inputDir := fs.NewDir(t, "app_input_",
211+
fs.WithFile(internal.ComposeFileName, composeData),
212+
)
213+
defer inputDir.Remove()
214+
215+
appName := "my.dockerapp"
216+
dir := fs.NewDir(t, "app_",
217+
fs.WithDir(appName),
218+
)
219+
defer dir.Remove()
220+
221+
err := initFromComposeFile(nil, dir.Join(appName), inputDir.Join(internal.ComposeFileName))
222+
assert.ErrorContains(t, err, "can't use relative path")
223+
}
224+
}

0 commit comments

Comments
 (0)