diff --git a/syft/pkg/cataloger/python/parse_requirements.go b/syft/pkg/cataloger/python/parse_requirements.go index bdc532554d7..2a06033493a 100644 --- a/syft/pkg/cataloger/python/parse_requirements.go +++ b/syft/pkg/cataloger/python/parse_requirements.go @@ -4,6 +4,7 @@ import ( "bufio" "fmt" "strings" + "unicode" "github.com/anchore/syft/internal/log" "github.com/anchore/syft/syft/artifact" @@ -47,8 +48,14 @@ func parseRequirementsTxt(_ source.FileResolver, _ *generic.Environment, reader log.WithFields("path", reader.RealPath).Warnf("unable to parse requirements.txt line: %q", line) continue } + + // check if the version contains hash declarations on the same line + version, _ := parseVersionAndHashes(parts[1]) + name := strings.TrimSpace(parts[0]) - version := strings.TrimSpace(parts[1]) + version = strings.TrimFunc(version, func(r rune) bool { + return !unicode.IsLetter(r) && !unicode.IsNumber(r) + }) packages = append(packages, newPackageForIndex(name, version, reader.Location)) } @@ -59,6 +66,15 @@ func parseRequirementsTxt(_ source.FileResolver, _ *generic.Environment, reader return packages, nil, nil } +func parseVersionAndHashes(version string) (string, []string) { + parts := strings.Split(version, "--hash=") + if len(parts) < 2 { + return version, nil + } + + return parts[0], parts[1:] +} + // trimRequirementsTxtLine removes content from the given requirements.txt line // that should not be considered for parsing. func trimRequirementsTxtLine(line string) string { diff --git a/syft/pkg/cataloger/python/parse_requirements_test.go b/syft/pkg/cataloger/python/parse_requirements_test.go index 1224ebf9c48..9131fcf96af 100644 --- a/syft/pkg/cataloger/python/parse_requirements_test.go +++ b/syft/pkg/cataloger/python/parse_requirements_test.go @@ -37,6 +37,22 @@ func TestParseRequirementsTxt(t *testing.T) { Language: pkg.Python, Type: pkg.PythonPkg, }, + { + Name: "argh", + Version: "0.26.2", + PURL: "pkg:pypi/argh@0.26.2", + Locations: locations, + Language: pkg.Python, + Type: pkg.PythonPkg, + }, + { + Name: "argh", + Version: "0.26.3", + PURL: "pkg:pypi/argh@0.26.3", + Locations: locations, + Language: pkg.Python, + Type: pkg.PythonPkg, + }, } var expectedRelationships []artifact.Relationship diff --git a/syft/pkg/cataloger/python/test-fixtures/requires/requirements.txt b/syft/pkg/cataloger/python/test-fixtures/requires/requirements.txt index d6603639863..b83e864224a 100644 --- a/syft/pkg/cataloger/python/test-fixtures/requires/requirements.txt +++ b/syft/pkg/cataloger/python/test-fixtures/requires/requirements.txt @@ -10,3 +10,7 @@ coverage != 3.5 # Version Exclusion. Anything except version 3.5 numpyNew; sys_platform == 'win32' numpy >= 3.4.1; sys_platform == 'win32' Mopidy-Dirble ~= 1.1 # Compatible release. Same as >= 1.1, == 1.* +argh==0.26.2 \ + --hash=sha256:a9b3aaa1904eeb78e32394cd46c6f37ac0fb4af6dc488daa58971bdc7d7fcaf3 \ + --hash=sha256:e9535b8c84dc9571a48999094fda7f33e63c3f1b74f3e5f3ac0105a58405bb65 +argh==0.26.3 --hash=sha256:a9b3aaa1904eeb78e32394cd46c6f37ac0fb4af6dc488daa58971bdc7d7fcaf3 --hash=sha256:e9535b8c84dc9571a48999094fda7f33e63c3f1b74f3e5f3ac0105a58405bb65