Skip to content

Commit

Permalink
Clean up displayed paths when encountering archives (#217)
Browse files Browse the repository at this point in the history
* Clean up displayed paths when encountering archives

* Use separate functions; add tests

* Remove print

* Use the convenient archive root when cleaning archive paths

Signed-off-by: egibs <[email protected]>

* Clean up paths inside archive

* Clean up test

Signed-off-by: egibs <[email protected]>

---------

Signed-off-by: egibs <[email protected]>
Co-authored-by: egibs <[email protected]>
  • Loading branch information
egibs and egibs authored May 22, 2024
1 parent cfb8f04 commit a2e2769
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 13 deletions.
5 changes: 0 additions & 5 deletions pkg/action/archive_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,11 +242,6 @@ func TestScanArchive(t *testing.T) {
t.Fatalf("full: %v", err)
}

// Remove the header since it is not deterministic
// due to the usage of temporary directories
idx := bytes.IndexByte(out.Bytes(), '\n')
out.Next(idx + 1)

outBytes := out.Bytes()

// Sort the output to ensure consistent ordering
Expand Down
43 changes: 35 additions & 8 deletions pkg/action/scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,20 @@ func findFilesRecursively(ctx context.Context, root string, c Config) ([]string,
return files, err
}

func scanSinglePath(ctx context.Context, c Config, yrs *yara.Rules, path string) (*bincapz.FileReport, error) {
// cleanPath removes the temporary directory prefix from the path.
func cleanPath(path string, prefix string) string {
return strings.TrimPrefix(path, prefix)
}

// formatPath formats the path for display.
func formatPath(path string) string {
if strings.Contains(path, "\\") {
path = strings.ReplaceAll(path, "\\", "/")
}
return strings.TrimPrefix(path, "/")
}

func scanSinglePath(ctx context.Context, c Config, yrs *yara.Rules, path string, absPath string, root string) (*bincapz.FileReport, error) {
logger := clog.FromContext(ctx)
var mrs yara.MatchRules
logger = logger.With("path", path)
Expand All @@ -78,6 +91,12 @@ func scanSinglePath(ctx context.Context, c Config, yrs *yara.Rules, path string)
return nil, err
}

// If absPath is provided, use it instead of the path if they are different.
// This is useful when scanning images and archives.
if absPath != "" && absPath != path {
fr.Path = fmt.Sprintf("%s ∴ %s", absPath, formatPath(cleanPath(path, root)))
}

if len(fr.Behaviors) == 0 && c.OmitEmpty {
return nil, nil
}
Expand All @@ -103,8 +122,11 @@ func recursiveScan(ctx context.Context, c Config) (*bincapz.Report, error) {
logger.Infof("%d rules loaded", len(yrs.GetRules()))

for _, sp := range c.ScanPaths {
var ip string
if c.OCI {
var err error
// store the image URI for later use
ip = sp
sp, err = oci(ctx, sp)
if err != nil {
return nil, fmt.Errorf("failed to prepare OCI image for scanning: %w", err)
Expand All @@ -128,7 +150,10 @@ func recursiveScan(ctx context.Context, c Config) (*bincapz.Report, error) {
logger.Errorf("unable to process %s: %v", p, err)
}
} else {
err = processFile(ctx, c, yrs, r, p, logger)
if c.OCI {
sp = ip
}
err = processFile(ctx, c, yrs, r, p, sp, "", logger)
if err != nil {
logger.Errorf("unable to process %s: %v", p, err)
}
Expand All @@ -147,24 +172,24 @@ func recursiveScan(ctx context.Context, c Config) (*bincapz.Report, error) {
func processArchive(ctx context.Context, c Config, yrs *yara.Rules, r *bincapz.Report, path string, logger *clog.Logger) error {
var err error

extractedRoot, err := extractArchiveToTempDir(ctx, path)
er, err := extractArchiveToTempDir(ctx, path)
if err != nil {
return fmt.Errorf("extract to temp: %w", err)
}

aps, err := findFilesRecursively(ctx, extractedRoot, c)
aps, err := findFilesRecursively(ctx, er, c)
if err != nil {
return fmt.Errorf("find files: %w", err)
}

for _, ap := range aps {
err = processFile(ctx, c, yrs, r, ap, logger)
err = processFile(ctx, c, yrs, r, ap, path, er, logger)
if err != nil {
return err
}
}
if err := os.RemoveAll(extractedRoot); err != nil {
logger.Errorf("remove %s: %v", extractedRoot, err)
if err := os.RemoveAll(er); err != nil {
logger.Errorf("remove %s: %v", er, err)
}
return nil
}
Expand All @@ -174,9 +199,11 @@ func processFile(
c Config, yrs *yara.Rules,
r *bincapz.Report,
path string,
absPath string,
ar string,
logger *clog.Logger,
) error {
fr, err := scanSinglePath(ctx, c, yrs, path)
fr, err := scanSinglePath(ctx, c, yrs, path, absPath, ar)
if err != nil {
logger.Errorf("scan path: %v", err)
return nil
Expand Down
69 changes: 69 additions & 0 deletions pkg/action/scan_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package action

import "testing"

func TestCleanPath(t *testing.T) {
tests := []struct {
name string
path string
prefix string
want string
}{
{
name: "linux",
path: "/tmp/static3980366648/usr/share/zoneinfo/zone1970",
prefix: "/tmp/static3980366648/",
want: "usr/share/zoneinfo/zone1970",
},
{
name: "macOS",
path: "/var/folders/3g/88131l9j11x995ppjbxsvhbh0000gn/T/apko_0.13.2_linux_arm64.tar.gz2526862474/apko_0.13.2_linux_arm64/apko",
prefix: "/var/folders/3g/88131l9j11x995ppjbxsvhbh0000gn/T/apko_0.13.2_linux_arm64.tar.gz2526862474/",
want: "apko_0.13.2_linux_arm64/apko",
},
{
name: "windows",
path: "C:\\Users\\abc\\AppData\\Local\\Temp\\static3980366648\\usr\\share\\zoneinfo\\zone1970",
prefix: "C:\\Users\\abc\\AppData\\Local\\Temp\\static3980366648\\",
want: "usr\\share\\zoneinfo\\zone1970",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := cleanPath(tt.path, tt.prefix); got != tt.want {
t.Errorf("CleanPath() = %v, want %v", got, tt.want)
}
})
}
}

func TestFormatPath(t *testing.T) {
tests := []struct {
name string
path string
want string
}{
{
name: "single separator",
path: "/apko_0.13.2_linux_arm64/apko",
want: "apko_0.13.2_linux_arm64/apko",
},
{
name: "multiple separators",
path: "/usr/share/zoneinfo/zone1970",
want: "usr/share/zoneinfo/zone1970",
},
{
name: "multiple windows separators",
path: "\\usr\\share\\zoneinfo\\zone1970",
want: "usr/share/zoneinfo/zone1970",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := formatPath(tt.path); got != tt.want {
t.Errorf("FormatPath() = %v, want %v", got, tt.want)
}
})
}
}
1 change: 1 addition & 0 deletions pkg/action/testdata/scan_archive
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@

# testdata/apko_nested.tar.gz ∴ apko_0.13.2_linux_arm64/apko
archives/zip
combo/dropper/shell
combo/stealer/ssh
Expand Down

0 comments on commit a2e2769

Please sign in to comment.