From 7f7520bde8ed8d9983cc3ed887c3d721f771cb13 Mon Sep 17 00:00:00 2001 From: "datadog-datadog-prod-us1[bot]" <88084959+datadog-datadog-prod-us1[bot]@users.noreply.github.com> Date: Mon, 9 Mar 2026 19:18:08 +0000 Subject: [PATCH 1/2] Use allowlist (O_RDONLY only) instead of denylist Co-authored-by: AlexandreYang <49917914+AlexandreYang@users.noreply.github.com> --- interp/allowed_paths.go | 7 +++++- interp/allowed_paths_internal_test.go | 31 +++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/interp/allowed_paths.go b/interp/allowed_paths.go index 6386e510..e3501d04 100644 --- a/interp/allowed_paths.go +++ b/interp/allowed_paths.go @@ -97,8 +97,13 @@ func toAbs(path, cwd string) string { } // open implements the restricted file-open policy. The file is opened through -// os.Root for atomic path validation. +// os.Root for atomic path validation. Only read-only access is permitted; +// any write flags are rejected as a defense-in-depth measure. func (s *pathSandbox) open(ctx context.Context, path string, flag int, perm os.FileMode) (io.ReadWriteCloser, error) { + if flag != os.O_RDONLY { + return nil, &os.PathError{Op: "open", Path: path, Err: os.ErrPermission} + } + absPath := toAbs(path, HandlerCtx(ctx).Dir) root, relPath, ok := s.resolve(absPath) diff --git a/interp/allowed_paths_internal_test.go b/interp/allowed_paths_internal_test.go index ef2a7fd9..b0b54b27 100644 --- a/interp/allowed_paths_internal_test.go +++ b/interp/allowed_paths_internal_test.go @@ -167,3 +167,34 @@ func TestAllowedPathsExecDefaultBlocksAll(t *testing.T) { assert.Equal(t, 127, exitCode) assert.Contains(t, stderr, "command not found") } + +func TestPathSandboxOpenRejectsWriteFlags(t *testing.T) { + dir := t.TempDir() + require.NoError(t, os.WriteFile(filepath.Join(dir, "test.txt"), []byte("data"), 0644)) + + sb, err := newPathSandbox([]string{dir}) + require.NoError(t, err) + require.NoError(t, sb.openRoots()) + defer sb.Close() + + ctx := context.WithValue(context.Background(), handlerCtxKey{}, HandlerContext{Dir: dir}) + + writeFlags := []int{ + os.O_WRONLY, + os.O_RDWR, + os.O_APPEND, + os.O_CREATE, + os.O_TRUNC, + os.O_WRONLY | os.O_CREATE | os.O_TRUNC, + } + for _, flag := range writeFlags { + f, err := sb.open(ctx, "test.txt", flag, 0644) + assert.Nil(t, f, "open with flag %d should return nil", flag) + assert.ErrorIs(t, err, os.ErrPermission, "open with flag %d should be denied", flag) + } + + // Read-only should still work. + f, err := sb.open(ctx, "test.txt", os.O_RDONLY, 0) + require.NoError(t, err) + f.Close() +} From f5708f0697295af83375875ccdf904927fb12a0b Mon Sep 17 00:00:00 2001 From: Alexandre Yang Date: Mon, 9 Mar 2026 20:29:41 +0100 Subject: [PATCH 2/2] Fix build: remove call to non-existent openRoots method in test Co-Authored-By: Claude Opus 4.6 --- interp/allowed_paths_internal_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/interp/allowed_paths_internal_test.go b/interp/allowed_paths_internal_test.go index b0b54b27..c268f107 100644 --- a/interp/allowed_paths_internal_test.go +++ b/interp/allowed_paths_internal_test.go @@ -174,7 +174,6 @@ func TestPathSandboxOpenRejectsWriteFlags(t *testing.T) { sb, err := newPathSandbox([]string{dir}) require.NoError(t, err) - require.NoError(t, sb.openRoots()) defer sb.Close() ctx := context.WithValue(context.Background(), handlerCtxKey{}, HandlerContext{Dir: dir})