Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion interp/allowed_paths.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,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)
Expand Down
30 changes: 30 additions & 0 deletions interp/allowed_paths_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,3 +167,33 @@ 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)
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()
}