Skip to content

Commit

Permalink
feat: support walking python site-packages inside of images
Browse files Browse the repository at this point in the history
  • Loading branch information
G-Rath committed Sep 14, 2023
1 parent f115400 commit 35ed291
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 1 deletion.
13 changes: 12 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,8 @@ func findAllLockfiles(r *reporter.Reporter, pathsToCheck []string, parseAs strin
return false
}

if strings.HasSuffix(string(path), "/node_modules") {
if strings.HasSuffix(string(path), "/node_modules") ||
strings.HasSuffix(string(path), "/site-packages") {
paths = append(paths, string(path))

return false
Expand Down Expand Up @@ -414,6 +415,16 @@ func parseLockfile(pathToLock string, args []string, img *image.Image) (lockfile
return l, err
}

if isPathTo(pathToLock, "site-packages") {
l, err := lockfile.WalkPythonSitePackagesInImage(*img, pathToLock)

if err != nil {
err = fmt.Errorf("%w", err)
}

return l, err
}

if pathToLock == "/lib/apk/db/installed" {
r, err := img.OpenPathFromSquash(file.Path(pathToLock))
if err != nil {
Expand Down
86 changes: 86 additions & 0 deletions pkg/lockfile/walk-python-site-packages.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ package lockfile
import (
"bufio"
"fmt"
"github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/stereoscope/pkg/filetree"
"github.com/anchore/stereoscope/pkg/filetree/filenode"
"github.com/anchore/stereoscope/pkg/image"
"io"
"io/fs"
"os"
Expand Down Expand Up @@ -49,6 +53,88 @@ func readPythonSitePackageMetadata(r io.Reader) (name, version string, err error
return name, version, err
}

func WalkPythonSitePackagesInImage(img image.Image, pathToPythonSitePackages string) (Lockfile, error) {
var packages []PackageDetails

err := img.SquashedTree().Walk(
func(path file.Path, f filenode.FileNode) error {
metadataFile := ""

if strings.HasSuffix(string(path), ".dist-info") {
metadataFile = "METADATA"
}

if strings.HasSuffix(string(path), ".egg-info") {
metadataFile = "PKG-INFO"
}

if metadataFile != "" {
r, err := img.OpenPathFromSquash(path + file.Path("/"+metadataFile))

if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "%v\n", err)

return nil
}

name, version, err := readPythonSitePackageMetadata(r)

if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "%v\n", err)

return nil
}

packages = append(packages, PackageDetails{
Name: name,
Version: version,
Commit: "",
Ecosystem: PipEcosystem,
CompareAs: PipEcosystem,
})

return nil
}

return nil
},
&filetree.WalkConditions{
ShouldVisit: func(path file.Path, node filenode.FileNode) bool {
// we only want to visit the node if:
// 1. it is a directory
// 2. it is within the given site-packages directory
return node.FileType == file.TypeDirectory &&
strings.HasPrefix(string(path), pathToPythonSitePackages)
},
ShouldContinueBranch: func(path file.Path, node filenode.FileNode) bool {
// We want to avoid any symlinks as they could be cyclical, and they should
// be safe to skip since we should end up walking their targets eventually
if IsSymlink(path, node) {
return false
}

// we don't actually need to explore these directories, as site-packages is a flat module tree,
// and we can access the metadata file within the directory when we visit it
return !strings.HasSuffix(string(path), ".dist-info") && !strings.HasSuffix(string(path), ".egg-info")
},
},
)

sort.Slice(packages, func(i, j int) bool {
if packages[i].Name == packages[j].Name {
return packages[i].Version < packages[j].Version
}

return packages[i].Name < packages[j].Name
})

return Lockfile{
FilePath: pathToPythonSitePackages,
ParsedAs: "site-packages",
Packages: packages,
}, err
}

func WalkPythonSitePackages(pathToPythonSitePackages string) (Lockfile, error) {
var packages []PackageDetails

Expand Down

0 comments on commit 35ed291

Please sign in to comment.