Skip to content

Commit

Permalink
Merge pull request #724 from Elizafox/striplinter
Browse files Browse the repository at this point in the history
Add stripped file linter
  • Loading branch information
Elizafox authored Sep 28, 2023
2 parents 3d0ba19 + 01477a3 commit d123557
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 28 deletions.
2 changes: 1 addition & 1 deletion pkg/build/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -1130,7 +1130,7 @@ func (b *Build) BuildPackage(ctx context.Context) error {

path := filepath.Join(b.WorkspaceDir, "melange-out", lt.pkgName)
fsys := os.DirFS(path)
lctx := linter.NewLinterContext(lt.pkgName, &b.Configuration, &lt.checks)
lctx := linter.NewLinterContext(lt.pkgName, fsys, &b.Configuration, &lt.checks)
linters := lt.checks.GetLinters()

err = lctx.LintPackageFs(fsys, linters)
Expand Down
1 change: 1 addition & 0 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,7 @@ var defaultLinters = []string{
"opt",
"srv",
"setuidgid",
"strip",
"tempdir",
"usrlocal",
"varempty",
Expand Down
80 changes: 72 additions & 8 deletions pkg/linter/linter.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,26 @@
package linter

import (
"debug/elf"
"fmt"
"io"
"io/fs"
"os"
"path/filepath"
"regexp"

"chainguard.dev/melange/pkg/config"
)

type LinterContext struct {
pkgname string
fsys fs.FS
cfg *config.Configuration
chk *config.Checks
}

func NewLinterContext(name string, cfg *config.Configuration, chk *config.Checks) LinterContext {
return LinterContext{name, cfg, chk}
func NewLinterContext(name string, fsys fs.FS, cfg *config.Configuration, chk *config.Checks) LinterContext {
return LinterContext{name, fsys, cfg, chk}
}

type linterFunc func(lctx LinterContext, path string, d fs.DirEntry) error
Expand All @@ -55,6 +60,7 @@ var linterMap = map[string]linter{
"usrlocal": linter{usrLocalLinter, "This package should be a -compat package"},
"varempty": linter{varEmptyLinter, "Remove any offending files in /var/empty in the pipeline"},
"worldwrite": linter{worldWriteableLinter, "Change the permissions of any world-writeable files in the package, disable the linter, or make this a -compat package"},
"strip": linter{strippedLinter, "Properly strip all binaries in the pipeline"},
}

var postLinterMap = map[string]postLinter{
Expand All @@ -67,7 +73,8 @@ var isSrvRegex = regexp.MustCompile("^srv/")
var isTempDirRegex = regexp.MustCompile("^(var/)?(tmp|run)/")
var isUsrLocalRegex = regexp.MustCompile("^usr/local/")
var isVarEmptyRegex = regexp.MustCompile("^var/empty/")
var isCompatPackage = regexp.MustCompile("-compat$")
var isCompatPackageRegex = regexp.MustCompile("-compat$")
var isObjectFileRegex = regexp.MustCompile(`\.(a|so|dylib)(\..*)?`)

func devLinter(_ LinterContext, path string, _ fs.DirEntry) error {
if isDevRegex.MatchString(path) {
Expand Down Expand Up @@ -156,6 +163,64 @@ func worldWriteableLinter(_ LinterContext, path string, d fs.DirEntry) error {
return nil
}

func strippedLinter(lctx LinterContext, path string, d fs.DirEntry) error {
if !d.Type().IsRegular() {
// Don't worry about non-files
return nil
}

info, err := d.Info()
if err != nil {
return err
}

ext := filepath.Ext(path)
mode := info.Mode()
if mode&0111 == 0 && !isObjectFileRegex.MatchString(ext) {
// Not an executable or library
return nil
}

reader, err := lctx.fsys.Open(path)
if err != nil {
return fmt.Errorf("Could not open file for reading: %v", err)
}
defer reader.Close()

// XXX(Elizafox) - fs.Open doesn't support the ReaderAt interface so we copy it to a temp file.
// This sucks but what can you do?
tempfile, err := os.CreateTemp("", "melange.XXXXX")
if err != nil {
return fmt.Errorf("Could not create temporary file: %v", err)
}
defer os.Remove(tempfile.Name())

_, err = io.Copy(tempfile, reader)
if err != nil {
return fmt.Errorf("Could not write to temporary file: %v", err)
}

_, err = tempfile.Seek(0, 0)
if err != nil {
return fmt.Errorf("Could not rewind temporary file: %v", err)
}

file, err := elf.NewFile(tempfile)
if err != nil {
// We don't particularly care if this fails, it means it's probably not an ELF file
fmt.Printf("WARNING: Could not open file %q as executable: %v\n", path, err)
return nil
}
defer file.Close()

// No debug sections allowed
if file.Section(".debug") != nil || file.Section(".zdebug") != nil {
return fmt.Errorf("File is not stripped")
}

return nil
}

func emptyPostLinter(_ LinterContext, fsys fs.FS) error {
foundfile := false
walkCb := func(path string, _ fs.DirEntry, err error) error {
Expand Down Expand Up @@ -187,7 +252,7 @@ func emptyPostLinter(_ LinterContext, fsys fs.FS) error {

func (lctx LinterContext) LintPackageFs(fsys fs.FS, linters []string) error {
// If this is a compat package, do nothing.
if isCompatPackage.MatchString(lctx.pkgname) {
if isCompatPackageRegex.MatchString(lctx.pkgname) {
return nil
}

Expand All @@ -212,22 +277,21 @@ func (lctx LinterContext) LintPackageFs(fsys fs.FS, linters []string) error {

err = linter.LinterFunc(lctx, path, d)
if err != nil {
return fmt.Errorf("Linter %s failed at path \"%s\": %w; suggest: %s", linterName, path, err, linter.Explain)
return fmt.Errorf("Linter %s failed at path %q: %w; suggest: %s", linterName, path, err, linter.Explain)
}
}

return nil
}

err := fs.WalkDir(fsys, ".", walkCb)
if err != nil {
if err := fs.WalkDir(fsys, ".", walkCb); err != nil {
return err
}

// Run post-walking linters
for _, linterName := range postLinters {
linter := postLinterMap[linterName]
err = linter.LinterFunc(lctx, fsys)
err := linter.LinterFunc(lctx, fsys)
if err != nil {
return fmt.Errorf("Linter %s failed; suggest: %s", linterName, linter.Explain)
}
Expand Down
38 changes: 19 additions & 19 deletions pkg/linter/linter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,15 @@ func Test_emptyLinter(t *testing.T) {
Epoch: 0,
Checks: config.Checks{
Enabled: []string{"empty"},
Disabled: []string{"dev", "opt", "setuidgid", "srv", "tempdir", "usrlocal", "varempty", "worldwrite"},
Disabled: []string{"dev", "opt", "setuidgid", "srv", "strip", "tempdir", "usrlocal", "varempty", "worldwrite"},
},
},
}

linters := cfg.Package.Checks.GetLinters()
assert.Equal(t, linters, []string{"empty"})
fsys := os.DirFS(dir)
lctx := NewLinterContext(cfg.Package.Name, &cfg, &cfg.Package.Checks)
lctx := NewLinterContext(cfg.Package.Name, fsys, &cfg, &cfg.Package.Checks)
assert.Error(t, lctx.LintPackageFs(fsys, linters))
}

Expand All @@ -61,7 +61,7 @@ func Test_usrLocalLinter(t *testing.T) {
Epoch: 0,
Checks: config.Checks{
Enabled: []string{"usrlocal"},
Disabled: []string{"dev", "empty", "opt", "setuidgid", "srv", "tempdir", "varempty", "worldwrite"},
Disabled: []string{"dev", "empty", "opt", "setuidgid", "strip", "srv", "tempdir", "varempty", "worldwrite"},
},
},
}
Expand All @@ -74,7 +74,7 @@ func Test_usrLocalLinter(t *testing.T) {
linters := cfg.Package.Checks.GetLinters()
assert.Equal(t, linters, []string{"usrlocal"})
fsys := os.DirFS(dir)
lctx := NewLinterContext(cfg.Package.Name, &cfg, &cfg.Package.Checks)
lctx := NewLinterContext(cfg.Package.Name, fsys, &cfg, &cfg.Package.Checks)
assert.Error(t, lctx.LintPackageFs(fsys, linters))
}

Expand All @@ -90,7 +90,7 @@ func Test_varEmptyLinter(t *testing.T) {
Epoch: 0,
Checks: config.Checks{
Enabled: []string{"varempty"},
Disabled: []string{"dev", "empty", "opt", "setuidgid", "srv", "tempdir", "usrlocal", "worldwrite"},
Disabled: []string{"dev", "empty", "opt", "setuidgid", "strip", "srv", "tempdir", "usrlocal", "worldwrite"},
},
},
}
Expand All @@ -104,7 +104,7 @@ func Test_varEmptyLinter(t *testing.T) {
linters := cfg.Package.Checks.GetLinters()
assert.Equal(t, linters, []string{"varempty"})
fsys := os.DirFS(dir)
lctx := NewLinterContext(cfg.Package.Name, &cfg, &cfg.Package.Checks)
lctx := NewLinterContext(cfg.Package.Name, fsys, &cfg, &cfg.Package.Checks)
assert.Error(t, lctx.LintPackageFs(fsys, linters))
}

Expand All @@ -120,7 +120,7 @@ func Test_devLinter(t *testing.T) {
Epoch: 0,
Checks: config.Checks{
Enabled: []string{"dev"},
Disabled: []string{"empty", "opt", "setuidgid", "srv", "tempdir", "usrlocal", "varempty", "worldwrite"},
Disabled: []string{"empty", "opt", "setuidgid", "strip", "srv", "tempdir", "usrlocal", "varempty", "worldwrite"},
},
},
}
Expand All @@ -134,7 +134,7 @@ func Test_devLinter(t *testing.T) {
linters := cfg.Package.Checks.GetLinters()
assert.Equal(t, linters, []string{"dev"})
fsys := os.DirFS(dir)
lctx := NewLinterContext(cfg.Package.Name, &cfg, &cfg.Package.Checks)
lctx := NewLinterContext(cfg.Package.Name, fsys, &cfg, &cfg.Package.Checks)
assert.Error(t, lctx.LintPackageFs(fsys, linters))
}

Expand All @@ -150,7 +150,7 @@ func Test_optLinter(t *testing.T) {
Epoch: 0,
Checks: config.Checks{
Enabled: []string{"opt"},
Disabled: []string{"dev", "empty", "setuidgid", "srv", "tempdir", "usrlocal", "varempty", "worldwrite"},
Disabled: []string{"dev", "empty", "setuidgid", "strip", "srv", "tempdir", "usrlocal", "varempty", "worldwrite"},
},
},
}
Expand All @@ -164,7 +164,7 @@ func Test_optLinter(t *testing.T) {
linters := cfg.Package.Checks.GetLinters()
assert.Equal(t, linters, []string{"opt"})
fsys := os.DirFS(dir)
lctx := NewLinterContext(cfg.Package.Name, &cfg, &cfg.Package.Checks)
lctx := NewLinterContext(cfg.Package.Name, fsys, &cfg, &cfg.Package.Checks)
assert.Error(t, lctx.LintPackageFs(fsys, linters))
}

Expand All @@ -180,7 +180,7 @@ func Test_srvLinter(t *testing.T) {
Epoch: 0,
Checks: config.Checks{
Enabled: []string{"srv"},
Disabled: []string{"dev", "empty", "opt", "setuidgid", "tempdir", "usrlocal", "varempty", "worldwrite"},
Disabled: []string{"dev", "empty", "opt", "setuidgid", "strip", "tempdir", "usrlocal", "varempty", "worldwrite"},
},
},
}
Expand All @@ -194,7 +194,7 @@ func Test_srvLinter(t *testing.T) {
linters := cfg.Package.Checks.GetLinters()
assert.Equal(t, linters, []string{"srv"})
fsys := os.DirFS(dir)
lctx := NewLinterContext(cfg.Package.Name, &cfg, &cfg.Package.Checks)
lctx := NewLinterContext(cfg.Package.Name, fsys, &cfg, &cfg.Package.Checks)
assert.Error(t, lctx.LintPackageFs(fsys, linters))
}

Expand All @@ -210,7 +210,7 @@ func Test_tempDirLinter(t *testing.T) {
Epoch: 0,
Checks: config.Checks{
Enabled: []string{"tempdir"},
Disabled: []string{"dev", "empty", "opt", "setuidgid", "srv", "usrlocal", "varempty", "worldwrite"},
Disabled: []string{"dev", "empty", "opt", "setuidgid", "strip", "srv", "usrlocal", "varempty", "worldwrite"},
},
},
}
Expand All @@ -236,7 +236,7 @@ func Test_tempDirLinter(t *testing.T) {
assert.NoError(t, err)
_, err = os.Create(filename)
assert.NoError(t, err)
lctx := NewLinterContext(cfg.Package.Name, &cfg, &cfg.Package.Checks)
lctx := NewLinterContext(cfg.Package.Name, fsys, &cfg, &cfg.Package.Checks)
assert.Error(t, lctx.LintPackageFs(fsys, linters))
os.Remove(filename)

Expand Down Expand Up @@ -271,7 +271,7 @@ func Test_setUidGidLinter(t *testing.T) {
Epoch: 0,
Checks: config.Checks{
Enabled: []string{"setuidgid"},
Disabled: []string{"dev", "empty", "opt", "srv", "tempdir", "usrlocal", "varempty", "worldwrite"},
Disabled: []string{"dev", "empty", "opt", "srv", "strip", "tempdir", "usrlocal", "varempty", "worldwrite"},
},
},
}
Expand All @@ -291,7 +291,7 @@ func Test_setUidGidLinter(t *testing.T) {
linters := cfg.Package.Checks.GetLinters()
assert.Equal(t, linters, []string{"setuidgid"})
fsys := os.DirFS(dir)
lctx := NewLinterContext(cfg.Package.Name, &cfg, &cfg.Package.Checks)
lctx := NewLinterContext(cfg.Package.Name, fsys, &cfg, &cfg.Package.Checks)
assert.Error(t, lctx.LintPackageFs(fsys, linters))
}

Expand All @@ -307,7 +307,7 @@ func Test_worldWriteLinter(t *testing.T) {
Epoch: 0,
Checks: config.Checks{
Enabled: []string{"worldwrite"},
Disabled: []string{"dev", "empty", "opt", "srv", "setuidgid", "tempdir", "usrlocal", "varempty"},
Disabled: []string{"dev", "empty", "opt", "setuidgid", "strip", "srv", "tempdir", "usrlocal", "varempty"},
},
},
}
Expand All @@ -320,7 +320,7 @@ func Test_worldWriteLinter(t *testing.T) {
linters := cfg.Package.Checks.GetLinters()
assert.Equal(t, linters, []string{"worldwrite"})
fsys := os.DirFS(dir)
lctx := NewLinterContext(cfg.Package.Name, &cfg, &cfg.Package.Checks)
lctx := NewLinterContext(cfg.Package.Name, fsys, &cfg, &cfg.Package.Checks)
assert.NoError(t, lctx.LintPackageFs(fsys, linters))

// Create test file
Expand Down Expand Up @@ -377,6 +377,6 @@ func Test_disableDefaultLinter(t *testing.T) {

linters := cfg.Package.Checks.GetLinters()
fsys := os.DirFS(dir)
lctx := NewLinterContext(cfg.Package.Name, &cfg, &cfg.Package.Checks)
lctx := NewLinterContext(cfg.Package.Name, fsys, &cfg, &cfg.Package.Checks)
assert.NoError(t, lctx.LintPackageFs(fsys, linters))
}

0 comments on commit d123557

Please sign in to comment.