Skip to content

Commit 3689384

Browse files
committed
fix(all): codebase hardening to improve protocol and code safety
1 parent 77e8079 commit 3689384

44 files changed

Lines changed: 1087 additions & 43 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
2020
useradd -r -g bee --uid 999 --no-log-init -m bee;
2121

2222
# make sure mounted volumes have correct permissions
23-
RUN mkdir -p /home/bee/.bee && chown 999:999 /home/bee/.bee
23+
RUN mkdir -p /home/bee/.bee && chown 999:999 /home/bee/.bee && chmod 700 /home/bee/.bee
2424

2525
COPY --from=build /src/dist/bee /usr/local/bin/bee
2626

Dockerfile.ci

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ USER root
44

55
RUN addgroup --system bee --gid 998; \
66
adduser -S -G bee -u 998 -D -h /home/bee bee; \
7-
mkdir -p /home/bee/.bee && chown 998:998 /home/bee/.bee
7+
mkdir -p /home/bee/.bee && chown 998:998 /home/bee/.bee && chmod 700 /home/bee/.bee
88

99
COPY bee /bee
1010

Dockerfile.dev

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
3131
groupadd -r bee --gid 999; \
3232
useradd -r -g bee --uid 999 --no-log-init -m bee;
3333

34-
RUN mkdir -p /home/bee/.bee && chown 999:999 /home/bee/.bee
34+
RUN mkdir -p /home/bee/.bee && chown 999:999 /home/bee/.bee && chmod 700 /home/bee/.bee
3535

3636
COPY --from=build /src/dist/bee /usr/local/bin/bee
3737

Dockerfile.goreleaser

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
1010
useradd -r -g bee --uid 999 --no-log-init -m bee;
1111

1212
# make sure mounted volumes have correct permissions
13-
RUN mkdir -p /home/bee/.bee && chown 999:999 /home/bee/.bee
13+
RUN mkdir -p /home/bee/.bee && chown 999:999 /home/bee/.bee && chmod 700 /home/bee/.bee
1414

1515
COPY bee /usr/local/bin/bee
1616

Dockerfile.scratch

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
88
useradd -r -g bee --uid 999 --no-log-init -m bee;
99

1010
# make sure mounted volumes have correct permissions
11-
RUN mkdir -p /home/bee/.bee && chown 999:999 /home/bee/.bee
11+
RUN mkdir -p /home/bee/.bee && chown 999:999 /home/bee/.bee && chmod 700 /home/bee/.bee
1212

1313
FROM scratch
1414

cmd/bee/cmd/split.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ func splitRefs(cmd *cobra.Command) {
104104
refs = append(refs, ch.Address().String())
105105
return nil
106106
})
107-
writer, err := os.OpenFile(outputFileName, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0o644)
107+
writer, err := os.OpenFile(outputFileName, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0o600)
108108
if err != nil {
109109
return fmt.Errorf("open output file: %w", err)
110110
}
@@ -192,7 +192,7 @@ func splitChunks(cmd *cobra.Command) {
192192
var chunksCount atomic.Int64
193193
store := newPutter(func(chunk swarm.Chunk) error {
194194
filePath := filepath.Join(outputDir, chunk.Address().String())
195-
err := os.WriteFile(filePath, chunk.Data(), 0o644)
195+
err := os.WriteFile(filePath, chunk.Data(), 0o600)
196196
if err != nil {
197197
return err
198198
}

cmd/bee/cmd/split_test.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"os"
1515
"path"
1616
"path/filepath"
17+
"runtime"
1718
"sync"
1819
"testing"
1920

@@ -172,3 +173,47 @@ func requestPipelineFn(s storage.Putter, encrypt bool, rLevel redundancy.Level)
172173
return builder.FeedPipeline(ctx, pipe, r)
173174
}
174175
}
176+
177+
// TestDBSplitChunksFilePermissions is a regression test for PERM-01: the
178+
// "split chunks" command wrote each chunk payload file world-readable (0o644),
179+
// disclosing chunk contents to other local users on a shared host. Files must
180+
// not be group- or world-accessible.
181+
func TestDBSplitChunksFilePermissions(t *testing.T) {
182+
t.Parallel()
183+
if runtime.GOOS == "windows" {
184+
t.Skip("permission bits are not meaningful on windows")
185+
}
186+
187+
buf := make([]byte, 16*1024)
188+
if _, err := crand.Read(buf); err != nil {
189+
t.Fatal(err)
190+
}
191+
192+
inputFileName := path.Join(t.TempDir(), "input")
193+
if err := os.WriteFile(inputFileName, buf, 0o600); err != nil {
194+
t.Fatal(err)
195+
}
196+
outputDir := t.TempDir()
197+
198+
err := newCommand(t, cmd.WithArgs("split", "chunks", "--input-file", inputFileName, "--output-dir", outputDir)).Execute()
199+
if err != nil {
200+
t.Fatal(err)
201+
}
202+
203+
entries, err := os.ReadDir(outputDir)
204+
if err != nil {
205+
t.Fatal(err)
206+
}
207+
if len(entries) == 0 {
208+
t.Fatal("no chunk files were written")
209+
}
210+
for _, e := range entries {
211+
info, err := e.Info()
212+
if err != nil {
213+
t.Fatal(err)
214+
}
215+
if perm := info.Mode().Perm(); perm&0o077 != 0 {
216+
t.Fatalf("chunk file %s is group/other-accessible: %#o", e.Name(), perm)
217+
}
218+
}
219+
}

pkg/api/api.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,9 @@ func (s *Service) SetProbe(probe *Probe) {
391391
}
392392

393393
func (s *Service) SetIsWarmingUp(v bool) {
394-
s.isWarmingUp = v
394+
if s != nil {
395+
s.isWarmingUp = v
396+
}
395397
}
396398

397399
// Close hangs up running websockets on shutdown.

pkg/api/warmup_test.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright 2026 The Swarm Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package api_test
6+
7+
import (
8+
"testing"
9+
10+
"github.com/ethersphere/bee/v2/pkg/api"
11+
)
12+
13+
// TestSetIsWarmingUpNilReceiver is a regression test for NIL-01. When the API
14+
// is disabled (empty --api-addr) the apiService is nil, and the warmup
15+
// completion goroutine calls SetIsWarmingUp on it. Like its sibling setters,
16+
// SetIsWarmingUp must be a no-op on a nil receiver rather than panicking.
17+
func TestSetIsWarmingUpNilReceiver(t *testing.T) {
18+
t.Parallel()
19+
20+
var s *api.Service // nil, as when the API is disabled
21+
22+
// Must not panic.
23+
s.SetIsWarmingUp(false)
24+
s.SetIsWarmingUp(true)
25+
}

pkg/file/buffer.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,13 @@ func (c *ChunkPipe) Write(b []byte) (int, error) {
6868
return nw, nil
6969
}
7070

71+
// CloseRead closes the read side of the underlying pipe. A pending or
72+
// subsequent Write then fails with io.ErrClosedPipe, which lets a writer
73+
// blocked on the pipe (e.g. the copier in SplitWriteAll) unblock and exit.
74+
func (c *ChunkPipe) CloseRead() error {
75+
return c.ReadCloser.Close()
76+
}
77+
7178
// Close implements io.Closer
7279
func (c *ChunkPipe) Close() error {
7380
if c.cursor > 0 {

0 commit comments

Comments
 (0)