Skip to content

Commit 8870df6

Browse files
committed
fix/batch-changes: validate files target paths in changeset hooks and executor
1 parent 2a185a5 commit 8870df6

4 files changed

Lines changed: 58 additions & 0 deletions

File tree

internal/batches/executor/run_steps.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,16 @@ func createFilesToMount(tempDir string, step batcheslib.Step, stepContext *templ
540540
// can mount them into the container.
541541
filesToMount := make(map[string]*os.File, len(files))
542542
for name, content := range files {
543+
// Defense-in-depth: the mount target name is concatenated directly
544+
// into a Docker `--mount type=bind,...,target=<name>` argument. A comma
545+
// in the name would let an attacker break out of the mount spec and
546+
// inject arbitrary source=/target= pairs (e.g. /var/run/docker.sock).
547+
// Parse-time validation rejects this, but the executor receives
548+
// pre-parsed steps via JSON and never re-runs that validation, so we
549+
// re-check here at the sink.
550+
if strings.Contains(name, ",") {
551+
return nil, cleanup, errors.Newf("files target path %q contains invalid characters", name)
552+
}
543553
fp, err := os.CreateTemp(tempDir, "")
544554
if err != nil {
545555
return nil, cleanup, errors.Wrap(err, "creating temporary file")

internal/batches/executor/run_steps_test.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,25 @@ import (
55

66
"github.com/stretchr/testify/require"
77

8+
batcheslib "github.com/sourcegraph/sourcegraph/lib/batches"
89
"github.com/sourcegraph/sourcegraph/lib/batches/template"
910
)
1011

12+
func TestCreateFilesToMount_RejectsCommaInTargetPath(t *testing.T) {
13+
step := batcheslib.Step{
14+
Files: map[string]string{
15+
"/tmp/x,source=/var/run/docker.sock,target=/var/run/docker.sock": "IGNORED",
16+
},
17+
}
18+
19+
_, cleanup, err := createFilesToMount(t.TempDir(), step, &template.StepContext{})
20+
if cleanup != nil {
21+
cleanup()
22+
}
23+
require.Error(t, err)
24+
require.Contains(t, err.Error(), "contains invalid characters")
25+
}
26+
1127
func TestRenderStepContainer(t *testing.T) {
1228
t.Run("static image", func(t *testing.T) {
1329
got, err := renderStepContainer("alpine:3", &template.StepContext{})

internal/batches/service/service_test.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -615,6 +615,31 @@ changesetTemplate:
615615
`,
616616
expectedErr: errors.New("parsing batch spec: step 1 files target path contains invalid characters"),
617617
},
618+
{
619+
name: "hook files target path with comma",
620+
batchSpecDir: tempDir,
621+
rawSpec: `
622+
version: 3
623+
name: test-spec
624+
description: A test spec
625+
on:
626+
- repository: github.com/sourcegraph/src-cli
627+
changesetTemplate:
628+
title: Test Hook Files
629+
body: Test hook files target path with comma
630+
branch: test
631+
commit:
632+
message: Test
633+
changesetHooks:
634+
onCIFailure:
635+
steps:
636+
- run: echo test
637+
image: alpine:3
638+
files:
639+
"/tmp/x,source=/var/run/docker.sock,target=/var/run/docker.sock": "IGNORED"
640+
`,
641+
expectedErr: errors.New("parsing batch spec: hooks.onCIFailure step 1 files target path contains invalid characters"),
642+
},
618643
{
619644
name: "mount path dot-dot traversal",
620645
batchSpecDir: tempDir,

lib/batches/batch_spec.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,13 @@ func validateHooks(spec *BatchSpec) error {
299299
)))
300300
}
301301
}
302+
for name := range step.Files {
303+
if strings.Contains(name, invalidMountCharacters) {
304+
errs = errors.Append(errs, NewValidationError(errors.Newf(
305+
"hooks.%s step %d files target path contains invalid characters", event, i+1,
306+
)))
307+
}
308+
}
302309
}
303310
}
304311

0 commit comments

Comments
 (0)