Skip to content

Commit 8e517cc

Browse files
authored
Merge pull request from GHSA-w7jw-q4fg-qc4c
* feat(security): adds the umask option closes GHSA-w7jw-q4fg-qc4c Signed-off-by: Carlos Alexandro Becker <[email protected]> * fix: correct bitwise op --------- Signed-off-by: Carlos Alexandro Becker <[email protected]>
1 parent 9ac3288 commit 8e517cc

File tree

5 files changed

+60
-39
lines changed

5 files changed

+60
-39
lines changed

files/files.go

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ func (c Contents) ContainsDestination(dst string) bool {
104104
return false
105105
}
106106

107-
func (c *Content) WithFileInfoDefaults() *Content {
107+
func (c *Content) WithFileInfoDefaults(umask fs.FileMode) *Content {
108108
cc := &Content{
109109
Source: c.Source,
110110
Destination: c.Destination,
@@ -144,7 +144,8 @@ func (c *Content) WithFileInfoDefaults() *Content {
144144
cc.FileInfo.MTime = info.ModTime()
145145
}
146146
if cc.FileInfo.Mode == 0 {
147-
cc.FileInfo.Mode = info.Mode()
147+
fmt.Println("files.go:147", info.Mode().String(), (info.Mode() & ^umask).String())
148+
cc.FileInfo.Mode = info.Mode() &^ umask
148149
}
149150
cc.FileInfo.Size = info.Size()
150151
}
@@ -226,13 +227,14 @@ func (c *Content) String() string {
226227
// - It expands globs (if enabled) and file trees
227228
// - It adds implicit directories (parent directories of files)
228229
// - It adds ownership and other file information if not specified directly
230+
// - It applies the given umask if the file does not have a specific mode
229231
// - It normalizes content source paths to be unix style paths
230232
// - It normalizes content destination paths to be absolute paths with a trailing
231233
// slash if the entry is a directory
232234
//
233235
// If no packager is specified, only the files that are relevant for any
234236
// packager are considered.
235-
func PrepareForPackager(rawContents Contents, packager string, disableGlobbing bool) (Contents, error) {
237+
func PrepareForPackager(rawContents Contents, umask fs.FileMode, packager string, disableGlobbing bool) (Contents, error) {
236238
contentMap := make(map[string]*Content)
237239

238240
for _, content := range rawContents {
@@ -253,13 +255,13 @@ func PrepareForPackager(rawContents Contents, packager string, disableGlobbing b
253255
return nil, err
254256
}
255257

256-
cc := content.WithFileInfoDefaults()
258+
cc := content.WithFileInfoDefaults(umask)
257259
cc.Source = ToNixPath(cc.Source)
258260
cc.Destination = NormalizeAbsoluteDirPath(cc.Destination)
259261
contentMap[cc.Destination] = cc
260262
case TypeImplicitDir:
261-
// if theres an implicit directory, the contents probably already
262-
// have been expanend so we can just ignore it, it will be created
263+
// if there's an implicit directory, the contents probably already
264+
// have been expanded so we can just ignore it, it will be created
263265
// by another content element again anyway
264266
case TypeRPMGhost, TypeSymlink, TypeRPMDoc, TypeRPMLicence, TypeRPMLicense, TypeRPMReadme, TypeDebChangelog:
265267
presentContent, destinationOccupied := contentMap[NormalizeAbsoluteFilePath(content.Destination)]
@@ -272,12 +274,12 @@ func PrepareForPackager(rawContents Contents, packager string, disableGlobbing b
272274
return nil, err
273275
}
274276

275-
cc := content.WithFileInfoDefaults()
277+
cc := content.WithFileInfoDefaults(umask)
276278
cc.Source = ToNixPath(cc.Source)
277279
cc.Destination = NormalizeAbsoluteFilePath(cc.Destination)
278280
contentMap[cc.Destination] = cc
279281
case TypeTree:
280-
err := addTree(contentMap, content)
282+
err := addTree(contentMap, content, umask)
281283
if err != nil {
282284
return nil, fmt.Errorf("add tree: %w", err)
283285
}
@@ -287,7 +289,7 @@ func PrepareForPackager(rawContents Contents, packager string, disableGlobbing b
287289
return nil, err
288290
}
289291

290-
err = addGlobbedFiles(contentMap, globbed, content)
292+
err = addGlobbedFiles(contentMap, globbed, content, umask)
291293
if err != nil {
292294
return nil, fmt.Errorf("add globbed files from %q: %w", content.Source, err)
293295
}
@@ -384,7 +386,7 @@ func sortedParents(dst string) []string {
384386
return paths
385387
}
386388

387-
func addGlobbedFiles(all map[string]*Content, globbed map[string]string, origFile *Content) error {
389+
func addGlobbedFiles(all map[string]*Content, globbed map[string]string, origFile *Content, umask fs.FileMode) error {
388390
for src, dst := range globbed {
389391
dst = NormalizeAbsoluteFilePath(dst)
390392
presentContent, destinationOccupied := all[dst]
@@ -413,7 +415,7 @@ func addGlobbedFiles(all map[string]*Content, globbed map[string]string, origFil
413415
Type: origFile.Type,
414416
FileInfo: newFileInfo,
415417
Packager: origFile.Packager,
416-
}).WithFileInfoDefaults()
418+
}).WithFileInfoDefaults(umask)
417419
if dst, err := os.Readlink(src); err == nil {
418420
newFile.Source = dst
419421
newFile.Type = TypeSymlink
@@ -425,7 +427,7 @@ func addGlobbedFiles(all map[string]*Content, globbed map[string]string, origFil
425427
return nil
426428
}
427429

428-
func addTree(all map[string]*Content, tree *Content) error {
430+
func addTree(all map[string]*Content, tree *Content, umask os.FileMode) error {
429431
if tree.Destination != "/" && tree.Destination != "" {
430432
presentContent, destinationOccupied := all[NormalizeAbsoluteDirPath(tree.Destination)]
431433
if destinationOccupied && presentContent.Type != TypeImplicitDir {
@@ -461,10 +463,11 @@ func addTree(all map[string]*Content, tree *Content) error {
461463

462464
c.Type = TypeDir
463465
c.Destination = NormalizeAbsoluteDirPath(destination)
466+
fmt.Println("files.go:446", info.Mode().String(), (info.Mode() &^ umask).String())
464467
c.FileInfo = &ContentFileInfo{
465468
Owner: "root",
466469
Group: "root",
467-
Mode: info.Mode(),
470+
Mode: info.Mode() &^ umask,
468471
MTime: info.ModTime(),
469472
}
470473
case d.Type()&os.ModeSymlink != 0:
@@ -480,12 +483,13 @@ func addTree(all map[string]*Content, tree *Content) error {
480483
c.Source = path
481484
c.Destination = NormalizeAbsoluteFilePath(destination)
482485
c.Type = TypeFile
486+
fmt.Println("files.go:486", d.Type().String(), (d.Type() &^ umask).String())
483487
c.FileInfo = &ContentFileInfo{
484-
Mode: d.Type(),
488+
Mode: d.Type() &^ umask,
485489
}
486490
}
487491

488-
all[c.Destination] = c.WithFileInfoDefaults()
492+
all[c.Destination] = c.WithFileInfoDefaults(umask)
489493

490494
return nil
491495
})

files/files_test.go

Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ contents:
4343
}
4444
}
4545

46-
func TestDeepPathsWithGlob(t *testing.T) {
46+
func TestDeepPathsWithGlobAndUmask(t *testing.T) {
4747
var config testStruct
4848
dec := yaml.NewDecoder(strings.NewReader(`---
4949
contents:
@@ -52,19 +52,26 @@ contents:
5252
file_info:
5353
mode: 0644
5454
mtime: 2008-01-02T15:04:05Z
55+
- src: testdata/deep-paths/
56+
dst: /bla
5557
`))
5658
dec.KnownFields(true)
5759
err := dec.Decode(&config)
5860
require.NoError(t, err)
59-
require.Len(t, config.Contents, 1)
60-
parsedContents, err := files.PrepareForPackager(config.Contents, "", false)
61+
require.Len(t, config.Contents, 2)
62+
parsedContents, err := files.PrepareForPackager(config.Contents, 0o113, "", false)
6163
require.NoError(t, err)
6264
for _, c := range parsedContents {
6365
switch c.Source {
6466
case "testdata/globtest/nested/b.txt":
6567
require.Equal(t, "/bla/nested/b.txt", c.Destination)
68+
require.Equal(t, "-rw-r--r--", c.Mode().String())
6669
case "testdata/globtest/multi-nested/subdir/c.txt":
6770
require.Equal(t, "/bla/multi-nested/subdir/c.txt", c.Destination)
71+
require.Equal(t, "-rw-r--r--", c.Mode().String())
72+
case "testdata/deep-paths/nested1/nested2/a.txt":
73+
require.Equal(t, "/bla/nested1/nested2/a.txt", c.Destination)
74+
require.Equal(t, "-rw-rw-r--", c.Mode().String())
6875
}
6976
}
7077
}
@@ -80,7 +87,7 @@ contents:
8087
err := dec.Decode(&config)
8188
require.NoError(t, err)
8289
require.Len(t, config.Contents, 1)
83-
parsedContents, err := files.PrepareForPackager(config.Contents, "", true)
90+
parsedContents, err := files.PrepareForPackager(config.Contents, 0, "", true)
8491
require.NoError(t, err)
8592
present := false
8693

@@ -110,7 +117,7 @@ contents:
110117
err := dec.Decode(&config)
111118
require.NoError(t, err)
112119

113-
config.Contents, err = files.PrepareForPackager(config.Contents, "", true)
120+
config.Contents, err = files.PrepareForPackager(config.Contents, 0, "", true)
114121
require.NoError(t, err)
115122
require.Len(t, config.Contents, 1)
116123

@@ -140,7 +147,7 @@ contents:
140147
err := dec.Decode(&config)
141148
require.NoError(t, err)
142149

143-
config.Contents, err = files.PrepareForPackager(config.Contents, "rpm", true)
150+
config.Contents, err = files.PrepareForPackager(config.Contents, 0, "rpm", true)
144151
require.NoError(t, err)
145152
require.Len(t, config.Contents, 1)
146153

@@ -172,7 +179,7 @@ contents:
172179
err := dec.Decode(&config)
173180
require.NoError(t, err)
174181

175-
config.Contents, err = files.PrepareForPackager(config.Contents, "", true)
182+
config.Contents, err = files.PrepareForPackager(config.Contents, 0, "", true)
176183
require.NoError(t, err)
177184
config.Contents = withoutFileInfo(config.Contents)
178185

@@ -232,7 +239,7 @@ contents:
232239
wg.Add(1)
233240
go func() {
234241
defer wg.Done()
235-
_, err := files.PrepareForPackager(config.Contents, "", false)
242+
_, err := files.PrepareForPackager(config.Contents, 0, "", false)
236243
require.NoError(t, err)
237244
}()
238245
}
@@ -246,7 +253,7 @@ func TestCollision(t *testing.T) {
246253
{Source: "../testdata/whatever2.conf", Destination: "/samedestination"},
247254
}
248255

249-
_, err := files.PrepareForPackager(configuredFiles, "", true)
256+
_, err := files.PrepareForPackager(configuredFiles, 0, "", true)
250257
require.ErrorIs(t, err, files.ErrContentCollision)
251258
})
252259

@@ -256,7 +263,7 @@ func TestCollision(t *testing.T) {
256263
{Source: "../testdata/whatever2.conf", Destination: "/samedestination", Packager: "bar"},
257264
}
258265

259-
_, err := files.PrepareForPackager(configuredFiles, "foo", true)
266+
_, err := files.PrepareForPackager(configuredFiles, 0, "foo", true)
260267
require.NoError(t, err)
261268
})
262269

@@ -266,7 +273,7 @@ func TestCollision(t *testing.T) {
266273
{Source: "../testdata/whatever2.conf", Destination: "/samedestination", Packager: ""},
267274
}
268275

269-
_, err := files.PrepareForPackager(configuredFiles, "foo", true)
276+
_, err := files.PrepareForPackager(configuredFiles, 0, "foo", true)
270277
require.ErrorIs(t, err, files.ErrContentCollision)
271278
})
272279
}
@@ -297,7 +304,7 @@ func TestDisableGlobbing(t *testing.T) {
297304
content := testCase
298305

299306
t.Run(strconv.Itoa(i), func(t *testing.T) {
300-
result, err := files.PrepareForPackager(files.Contents{&content}, "", disableGlobbing)
307+
result, err := files.PrepareForPackager(files.Contents{&content}, 0, "", disableGlobbing)
301308
if err != nil {
302309
t.Fatalf("expand content globs: %v", err)
303310
}
@@ -341,7 +348,7 @@ func TestGlobbingWhenFilesHaveBrackets(t *testing.T) {
341348
Source: "./testdata/\\{test\\}/",
342349
Destination: ".",
343350
},
344-
}, "", false)
351+
}, 0, "", false)
345352
if err != nil {
346353
t.Fatalf("expand content globs: %v", err)
347354
}
@@ -382,7 +389,7 @@ func TestGlobbingFilesWithDifferentSizesWithFileInfo(t *testing.T) {
382389
Mode: 0o777,
383390
},
384391
},
385-
}, "", false)
392+
}, 0, "", false)
386393
if err != nil {
387394
t.Fatalf("expand content globs: %v", err)
388395
}
@@ -404,7 +411,7 @@ func TestDestEndsWithSlash(t *testing.T) {
404411
Source: "./testdata/globtest/a.txt",
405412
Destination: "./foo/",
406413
},
407-
}, "", false)
414+
}, 0, "", false)
408415
result = withoutImplicitDirs(result)
409416
require.NoError(t, err)
410417
require.Len(t, result, 1)
@@ -421,7 +428,7 @@ contents:
421428
`))
422429
dec.KnownFields(true)
423430
require.NoError(t, dec.Decode(&config))
424-
_, err := files.PrepareForPackager(config.Contents, "", false)
431+
_, err := files.PrepareForPackager(config.Contents, 0, "", false)
425432
require.EqualError(t, err, "invalid content type: filr")
426433
}
427434

@@ -452,7 +459,7 @@ contents:
452459
`))
453460
dec.KnownFields(true)
454461
require.NoError(t, dec.Decode(&config))
455-
_, err := files.PrepareForPackager(config.Contents, "", false)
462+
_, err := files.PrepareForPackager(config.Contents, 0, "", false)
456463
require.NoError(t, err)
457464
}
458465

@@ -462,7 +469,7 @@ func TestImplicitDirectories(t *testing.T) {
462469
Source: "./testdata/globtest/a.txt",
463470
Destination: "./foo/bar/baz",
464471
},
465-
}, "", false)
472+
}, 0, "", false)
466473
require.NoError(t, err)
467474

468475
expected := files.Contents{
@@ -535,7 +542,7 @@ func TestRelevantFiles(t *testing.T) {
535542
}
536543

537544
t.Run("deb", func(t *testing.T) {
538-
results, err := files.PrepareForPackager(contents, "deb", false)
545+
results, err := files.PrepareForPackager(contents, 0, "deb", false)
539546
require.NoError(t, err)
540547
require.Equal(t, files.Contents{
541548
{
@@ -558,7 +565,7 @@ func TestRelevantFiles(t *testing.T) {
558565
})
559566

560567
t.Run("rpm", func(t *testing.T) {
561-
results, err := files.PrepareForPackager(contents, "rpm", false)
568+
results, err := files.PrepareForPackager(contents, 0, "rpm", false)
562569
require.NoError(t, err)
563570
require.Equal(t, files.Contents{
564571
{
@@ -601,7 +608,7 @@ func TestRelevantFiles(t *testing.T) {
601608
})
602609

603610
t.Run("apk", func(t *testing.T) {
604-
results, err := files.PrepareForPackager(contents, "apk", false)
611+
results, err := files.PrepareForPackager(contents, 0, "apk", false)
605612
require.NoError(t, err)
606613
require.Equal(t, files.Contents{
607614
{
@@ -620,7 +627,7 @@ func TestTree(t *testing.T) {
620627
Destination: "/base",
621628
Type: files.TypeTree,
622629
},
623-
}, "", false)
630+
}, 0, "", false)
624631
require.NoError(t, err)
625632

626633
require.Equal(t, files.Contents{

files/testdata/deep-paths/nested1/nested2/a.txt

100644100755
File mode changed.

nfpm.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,7 @@ type Overridables struct {
304304
Suggests []string `yaml:"suggests,omitempty" json:"suggests,omitempty" jsonschema:"title=suggests directive,example=nfpm"`
305305
Conflicts []string `yaml:"conflicts,omitempty" json:"conflicts,omitempty" jsonschema:"title=conflicts directive,example=nfpm"`
306306
Contents files.Contents `yaml:"contents,omitempty" json:"contents,omitempty" jsonschema:"title=files to add to the package"`
307+
Umask os.FileMode `yaml:"umask,omitempty" json:"umask,omitempty" jsonschema:"title=umask for file contents,example=002"`
307308
Scripts Scripts `yaml:"scripts,omitempty" json:"scripts,omitempty" jsonschema:"title=scripts to execute"`
308309
RPM RPM `yaml:"rpm,omitempty" json:"rpm,omitempty" jsonschema:"title=rpm-specific settings"`
309310
Deb Deb `yaml:"deb,omitempty" json:"deb,omitempty" jsonschema:"title=deb-specific settings"`
@@ -441,7 +442,7 @@ func PrepareForPackager(info *Info, packager string) (err error) {
441442
return ErrFieldEmpty{"version"}
442443
}
443444

444-
info.Contents, err = files.PrepareForPackager(info.Contents, packager, info.DisableGlobbing)
445+
info.Contents, err = files.PrepareForPackager(info.Contents, info.Umask, packager, info.DisableGlobbing)
445446

446447
return err
447448
}
@@ -460,7 +461,7 @@ func Validate(info *Info) (err error) {
460461
}
461462

462463
for packager := range packagers {
463-
_, err := files.PrepareForPackager(info.Contents, packager, info.DisableGlobbing)
464+
_, err := files.PrepareForPackager(info.Contents, info.Umask, packager, info.DisableGlobbing)
464465
if err != nil {
465466
return err
466467
}

www/docs/configuration.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,15 @@ contents:
195195
dst: /etc/bar.conf
196196
type: config|noreplace
197197

198+
# Umask to be used on files without explicit mode set.
199+
#
200+
# By default, nFPM will use the mode of the original file in the file system.
201+
# This may lead to issues if these files are checkout out in Git, for example,
202+
# as it won't keep all the permissions on fresh checkouts.
203+
#
204+
# 0o002 would remove the world-writable permission, for example.
205+
umask: 0o002
206+
198207
# These files are not actually present in the package, but the file names
199208
# are added to the package header. From the RPM directives documentation:
200209
#

0 commit comments

Comments
 (0)