@@ -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+ }
0 commit comments