mirror of
https://github.com/anchore/syft.git
synced 2026-08-19 16:48:27 +02:00
Fix mix.lock git/path deps mislabeled as hex.pm with bogus PURL (#5041)
parseMixLock read the version from a hex-only token position and hardcoded a pkg:hex PURL, so git/path deps got a URL-fragment version and a false hex.pm PURL (false CVE matches). Branch on the source atom: git uses the commit SHA, path an empty version, and pkg:hex PURLs are emitted only for hex deps. Signed-off-by: Synvoya <16019863+Synvoya@users.noreply.github.com> Co-authored-by: Synvoya <16019863+Synvoya@users.noreply.github.com>
This commit is contained in:
parent
f1457f2554
commit
aebdf4bff4
@ -6,13 +6,18 @@ import (
|
|||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newPackage(d pkg.ElixirMixLockEntry, locations ...file.Location) pkg.Package {
|
// newPackage builds a package from a mix.lock entry. source is the entry's
|
||||||
|
// source atom (`hex`, `git`, or `path`). Only hex entries are backed by the
|
||||||
|
// hex.pm registry, so only they receive a pkg:hex/ PURL; git/path entries get
|
||||||
|
// an empty PURL to avoid being asserted as hex.pm packages (which would produce
|
||||||
|
// false hex.pm vulnerability matches).
|
||||||
|
func newPackage(source string, d pkg.ElixirMixLockEntry, locations ...file.Location) pkg.Package {
|
||||||
p := pkg.Package{
|
p := pkg.Package{
|
||||||
Name: d.Name,
|
Name: d.Name,
|
||||||
Version: d.Version,
|
Version: d.Version,
|
||||||
Language: pkg.Elixir,
|
Language: pkg.Elixir,
|
||||||
Locations: file.NewLocationSet(locations...),
|
Locations: file.NewLocationSet(locations...),
|
||||||
PURL: packageURL(d),
|
PURL: packageURL(source, d),
|
||||||
Type: pkg.HexPkg,
|
Type: pkg.HexPkg,
|
||||||
Metadata: d,
|
Metadata: d,
|
||||||
}
|
}
|
||||||
@ -22,7 +27,14 @@ func newPackage(d pkg.ElixirMixLockEntry, locations ...file.Location) pkg.Packag
|
|||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
func packageURL(m pkg.ElixirMixLockEntry) string {
|
func packageURL(source string, m pkg.ElixirMixLockEntry) string {
|
||||||
|
// Non-hex sources (git, path) are not published to the hex.pm registry, so a
|
||||||
|
// pkg:hex/ PURL would be incorrect and would drive false hex.pm vulnerability
|
||||||
|
// matches. Emit no PURL for them.
|
||||||
|
if source != "hex" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
var qualifiers packageurl.Qualifiers
|
var qualifiers packageurl.Qualifiers
|
||||||
|
|
||||||
return packageurl.NewPackageURL(
|
return packageurl.NewPackageURL(
|
||||||
|
|||||||
@ -53,7 +53,34 @@ func parseMixLock(_ context.Context, _ file.Resolver, _ *generic.Environment, re
|
|||||||
errs = unknown.Appendf(errs, reader, "unable to read mix lock line %d: %s", lineNum, line)
|
errs = unknown.Appendf(errs, reader, "unable to read mix lock line %d: %s", lineNum, line)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
name, version, hash, hashExt := tokens[1], tokens[4], tokens[5], tokens[len(tokens)-2]
|
|
||||||
|
// tokens[2] is the source atom of the entry's tuple: `hex`, `git`, or
|
||||||
|
// `path`. The layout of the remaining tokens differs per source, so the
|
||||||
|
// version/hash positions must be read accordingly. Only hex entries are
|
||||||
|
// backed by the hex.pm registry; git/path entries must not be emitted as
|
||||||
|
// hex packages (see newPackage), otherwise a bogus pkg:hex/ PURL produces
|
||||||
|
// false hex.pm vulnerability matches.
|
||||||
|
source := tokens[2]
|
||||||
|
|
||||||
|
var name, version, hash, hashExt string
|
||||||
|
switch source {
|
||||||
|
case "git":
|
||||||
|
// e.g. `"dep": {:git, "https://host/dep.git", "<sha-or-ref>", [ref: "..."]}`
|
||||||
|
// tokens: ["", name, "git", "<url-scheme>", "//host/dep.git", "<sha-or-ref>", ...]
|
||||||
|
// The version is the commit SHA/ref immediately after the URL; there
|
||||||
|
// is no hex checksum for a git-sourced dependency.
|
||||||
|
name, version = tokens[1], tokens[5]
|
||||||
|
case "path":
|
||||||
|
// e.g. `"dep": {:path, "../local", []}`
|
||||||
|
// tokens: ["", name, "path", "../local", "[]", ""]
|
||||||
|
// A path dependency has no version or checksum; tokens[4] is the empty
|
||||||
|
// dependency list `[]`, not a version.
|
||||||
|
name = tokens[1]
|
||||||
|
default:
|
||||||
|
// hex (and any registry-style tuple): keep the original behavior.
|
||||||
|
// tokens: ["", name, "hex", name, version, hash, ..., hashExt, ""]
|
||||||
|
name, version, hash, hashExt = tokens[1], tokens[4], tokens[5], tokens[len(tokens)-2]
|
||||||
|
}
|
||||||
|
|
||||||
if name == "" {
|
if name == "" {
|
||||||
log.WithFields("path", reader.RealPath).Debug("skipping empty package name from mix.lock file")
|
log.WithFields("path", reader.RealPath).Debug("skipping empty package name from mix.lock file")
|
||||||
@ -63,6 +90,7 @@ func parseMixLock(_ context.Context, _ file.Resolver, _ *generic.Environment, re
|
|||||||
|
|
||||||
packages = append(packages,
|
packages = append(packages,
|
||||||
newPackage(
|
newPackage(
|
||||||
|
source,
|
||||||
pkg.ElixirMixLockEntry{
|
pkg.ElixirMixLockEntry{
|
||||||
Name: name,
|
Name: name,
|
||||||
Version: version,
|
Version: version,
|
||||||
|
|||||||
@ -230,6 +230,34 @@ func TestParseMixLock(t *testing.T) {
|
|||||||
Dependencies: []string{"decimal"},
|
Dependencies: []string{"decimal"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// git-sourced dependency: version is the commit SHA (not a URL
|
||||||
|
// fragment) and no pkg:hex/ PURL is emitted, so it cannot be
|
||||||
|
// matched as a hex.pm package.
|
||||||
|
Name: "enacl",
|
||||||
|
Version: "2f50ba6289f2f2d9fef05d22a396c0bab4f64149",
|
||||||
|
Language: pkg.Elixir,
|
||||||
|
Type: pkg.HexPkg,
|
||||||
|
Locations: locations,
|
||||||
|
PURL: "",
|
||||||
|
Metadata: pkg.ElixirMixLockEntry{
|
||||||
|
Name: "enacl",
|
||||||
|
Version: "2f50ba6289f2f2d9fef05d22a396c0bab4f64149",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// path-sourced dependency: no version (tokens[4] is the empty
|
||||||
|
// dependency list `[]`, not a version) and no pkg:hex/ PURL.
|
||||||
|
Name: "local_dep",
|
||||||
|
Version: "",
|
||||||
|
Language: pkg.Elixir,
|
||||||
|
Type: pkg.HexPkg,
|
||||||
|
Locations: locations,
|
||||||
|
PURL: "",
|
||||||
|
Metadata: pkg.ElixirMixLockEntry{
|
||||||
|
Name: "local_dep",
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
fixture := "testdata/mix.lock"
|
fixture := "testdata/mix.lock"
|
||||||
|
|||||||
2
syft/pkg/cataloger/elixir/testdata/mix.lock
vendored
2
syft/pkg/cataloger/elixir/testdata/mix.lock
vendored
@ -14,4 +14,6 @@
|
|||||||
"gettext": {:hex, :gettext, "0.19.1", "564953fd21f29358e68b91634799d9d26989f8d039d7512622efb3c3b1c97892", [:mix], [], "hexpm", "10c656c0912b8299adba9b061c06947511e3f109ab0d18b44a866a4498e77222"},
|
"gettext": {:hex, :gettext, "0.19.1", "564953fd21f29358e68b91634799d9d26989f8d039d7512622efb3c3b1c97892", [:mix], [], "hexpm", "10c656c0912b8299adba9b061c06947511e3f109ab0d18b44a866a4498e77222"},
|
||||||
"hpax": {:hex, :hpax, "0.1.1", "2396c313683ada39e98c20a75a82911592b47e5c24391363343bde74f82396ca", [:mix], [], "hexpm", "0ae7d5a0b04a8a60caf7a39fcf3ec476f35cc2cc16c05abea730d3ce6ac6c826"},
|
"hpax": {:hex, :hpax, "0.1.1", "2396c313683ada39e98c20a75a82911592b47e5c24391363343bde74f82396ca", [:mix], [], "hexpm", "0ae7d5a0b04a8a60caf7a39fcf3ec476f35cc2cc16c05abea730d3ce6ac6c826"},
|
||||||
"jason": {:hex, :jason, "1.3.0", "fa6b82a934feb176263ad2df0dbd91bf633d4a46ebfdffea0c8ae82953714946", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "53fc1f51255390e0ec7e50f9cb41e751c260d065dcba2bf0d08dc51a4002c2ac"},
|
"jason": {:hex, :jason, "1.3.0", "fa6b82a934feb176263ad2df0dbd91bf633d4a46ebfdffea0c8ae82953714946", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "53fc1f51255390e0ec7e50f9cb41e751c260d065dcba2bf0d08dc51a4002c2ac"},
|
||||||
|
"enacl": {:git, "https://github.com/aeternity/enacl.git", "2f50ba6289f2f2d9fef05d22a396c0bab4f64149", [ref: "2f50ba6"]},
|
||||||
|
"local_dep": {:path, "../local_dep", []},
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user