mirror of
https://github.com/anchore/syft.git
synced 2026-07-05 02:28:25 +02:00
* Vcpkg cataloger for vcpkg "Manifest Mode" Find and parse vcpkg-lock.json to get HEAD commit hash Signed-off-by: Gabriel Rau <gabetrau@gmail.com> Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * just use local vcpkg git repo if it exists, clone it if it doesn't Signed-off-by: Gabriel Rau <gabetrau@gmail.com> Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * Config opt for git remote clones for vcpkg and README update Signed-off-by: Gabriel Rau <gabetrau@gmail.com> * Look in vcpkg cache git repo for custom git repos Signed-off-by: Gabriel Rau <gabetrau@gmail.com> Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * add triplet to metadata and support overlay-ports from config file Signed-off-by: Gabriel Rau <gabetrau@gmail.com> Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * Add PURL to packages (not sure if this is correct) Signed-off-by: Gabriel Rau <gabetrau@gmail.com> Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * flatten structs in pkg module and move vcpkg structs to resolver Signed-off-by: Gabriel Rau <gabetrau@gmail.com> Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * account for overriden versions in toplevel manifest Signed-off-by: Gabriel Rau <gabetrau@gmail.com> Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * generate json schema for vcpkg metadata Signed-off-by: Gabriel Rau <gabetrau@gmail.com> * test for basic vcpkg project dependencies for vcpkg registry to be pulled in add tree hashes and use correct git hash in builtin-baseline for helloworld test vcpkg-registry for testing that uses object hashes from syft repo fix broken tests Signed-off-by: Gabriel Rau <gabetrau@gmail.com> * formatting Signed-off-by: Gabriel Rau <gabetrau@gmail.com> * fix static-analysis violations Signed-off-by: Gabriel Rau <gabetrau@gmail.com> Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * fix integration test failure Signed-off-by: Gabriel Rau <gabetrau@gmail.com> * remove uneeded files from vcpkg test fixture and use custom registry Signed-off-by: Gabriel Rau <gabetrau@gmail.com> * change vcpkg registry to anchore one Signed-off-by: Gabriel Rau <gabetrau@gmail.com> Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * purl spec based on open PR Signed-off-by: Gabriel Rau <gabetrau@gmail.com> Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * generate-json-schema Signed-off-by: Gabriel Rau <gabetrau@gmail.com> Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * rebased and generate json schema 16.0.40 Signed-off-by: Gabriel Rau <gabetrau@gmail.com> * address low hanging fruit Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * handle additional comments Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * migrate to testdata Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * improve docs and testing Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * fix static analysis Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * remove license from pkg metadata Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * fix capabilities claim Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> --------- Signed-off-by: Gabriel Rau <gabetrau@gmail.com> Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> Co-authored-by: Alex Goodman <wagoodman@users.noreply.github.com>
183 lines
5.8 KiB
Go
183 lines
5.8 KiB
Go
package cpp
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
|
|
"github.com/anchore/syft/internal"
|
|
"github.com/anchore/syft/internal/log"
|
|
"github.com/anchore/syft/syft/artifact"
|
|
"github.com/anchore/syft/syft/file"
|
|
"github.com/anchore/syft/syft/pkg"
|
|
"github.com/anchore/syft/syft/pkg/cataloger/cpp/internal/vcpkg"
|
|
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
|
)
|
|
|
|
// vcpkg manifests are small JSON files; cap reads to guard against pathological inputs
|
|
const maxVcpkgManifestSize = 5 * 1024 * 1024
|
|
|
|
type vcpkgCataloger struct {
|
|
allowGitClone bool
|
|
}
|
|
|
|
func newVcpkgCataloger(allowGitClone bool) *vcpkgCataloger {
|
|
return &vcpkgCataloger{
|
|
allowGitClone: allowGitClone,
|
|
}
|
|
}
|
|
|
|
// parser is for vcpkg in "Manifest" mode. This is opposed to "Classic" mode which or is more akin to a system package manager. (https://learn.microsoft.com/en-us/vcpkg/concepts/classic-mode)
|
|
func (v *vcpkgCataloger) parseVcpkgManifest(ctx context.Context, resolver file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
|
conf, err := findVcpkgConfig(resolver)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("something went wrong parsing vcpkg-configuration.json file: %w", err)
|
|
}
|
|
|
|
var toplevelVcpkg vcpkg.Vcpkg
|
|
dec := json.NewDecoder(reader)
|
|
err = dec.Decode(&toplevelVcpkg)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("failed to parse vcpkg.json file: %w", err)
|
|
}
|
|
|
|
var vcpkgs []vcpkg.Vcpkg
|
|
vcpkgs = append(vcpkgs, toplevelVcpkg)
|
|
overlayVcpkgs, err := findOverlayManifests(resolver, conf.OverlayPorts)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("could not get overlay port manifests: %w", err)
|
|
}
|
|
vcpkgs = append(vcpkgs, overlayVcpkgs...)
|
|
var pkgs []pkg.Package
|
|
var relationships []artifact.Relationship
|
|
for _, parentVcpkg := range vcpkgs {
|
|
pv := parentVcpkg
|
|
// the top-level project manifest has no registry (it is the thing being scanned, not a resolved dep)
|
|
parentMan := &vcpkg.ResolvedManifest{Vcpkg: &pv, Registry: nil}
|
|
pPkg := newVcpkgPackage(ctx, parentMan, reader.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation))
|
|
pkgs = append(
|
|
pkgs,
|
|
pPkg)
|
|
|
|
r := vcpkg.NewResolver(
|
|
ctx,
|
|
conf,
|
|
v.allowGitClone,
|
|
)
|
|
for _, dep := range parentVcpkg.Dependencies {
|
|
cMans, fetchErr := r.FindManifests(dep, true, toplevelVcpkg.BuiltinBaseline, toplevelVcpkg.Overrides, parentMan)
|
|
if fetchErr != nil {
|
|
// best-effort: a single unresolvable dependency shouldn't discard packages already found
|
|
log.Debugf("vcpkg: unable to resolve dependency in %q: %v", parentVcpkg.Name, fetchErr)
|
|
continue
|
|
}
|
|
pkgs, relationships = appendPkgsAndRelationships(ctx, cMans, overlayVcpkgs, reader, relationships, pkgs)
|
|
}
|
|
}
|
|
pkg.Sort(pkgs)
|
|
return pkgs, relationships, nil
|
|
}
|
|
|
|
func appendPkgsAndRelationships(ctx context.Context, cMans []vcpkg.ManifestNode, overlayVcpkgs []vcpkg.Vcpkg, reader file.LocationReadCloser, relationships []artifact.Relationship, pkgs []pkg.Package) ([]pkg.Package, []artifact.Relationship) {
|
|
p := pkgs
|
|
r := relationships
|
|
for _, c := range cMans {
|
|
if c.Child != nil && !hasBeenOverlayed(c.Child.Vcpkg.Name, overlayVcpkgs) {
|
|
cPkg := newVcpkgPackage(ctx, c.Child, reader.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation))
|
|
if c.Parent != nil {
|
|
pPkg := newVcpkgPackage(ctx, c.Parent, reader.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation))
|
|
pPkg.FoundBy = "vcpkg-manifest-cataloger"
|
|
cPkg.FoundBy = "vcpkg-manifest-cataloger"
|
|
rship := artifact.Relationship{
|
|
// From is the dependency, To is the dependent (syft convention)
|
|
From: cPkg,
|
|
To: pPkg,
|
|
Type: artifact.DependencyOfRelationship,
|
|
}
|
|
r = append(
|
|
r,
|
|
rship)
|
|
}
|
|
p = append(
|
|
p,
|
|
cPkg)
|
|
}
|
|
}
|
|
return p, r
|
|
}
|
|
|
|
// These are to be used in place of dependencies with the same name
|
|
func findOverlayManifests(resolver file.Resolver, overlayPorts []string) ([]vcpkg.Vcpkg, error) {
|
|
var manifests []vcpkg.Vcpkg
|
|
for _, op := range overlayPorts {
|
|
// overlay port path is relative to location of vcpkg-configuration.json file
|
|
locs, err := resolver.FilesByGlob(op + "/**/vcpkg.json")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for _, loc := range locs {
|
|
man, err := findManAtLoc(loc, resolver)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
manifests = append(manifests, *man)
|
|
}
|
|
}
|
|
return manifests, nil
|
|
}
|
|
|
|
func findManAtLoc(loc file.Location, resolver file.Resolver) (*vcpkg.Vcpkg, error) {
|
|
manCont, err := resolver.FileContentsByLocation(loc)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer internal.CloseAndLogError(manCont, loc.RealPath)
|
|
manBytes, err := io.ReadAll(io.LimitReader(manCont, maxVcpkgManifestSize))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var man vcpkg.Vcpkg
|
|
err = json.Unmarshal(manBytes, &man)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &man, nil
|
|
}
|
|
|
|
// check to see if a package is not going to get pulled in because it's apart of the overlay-ports in vcpkg-configuration.json file
|
|
func hasBeenOverlayed(pkgName string, overlayMans []vcpkg.Vcpkg) bool {
|
|
for _, om := range overlayMans {
|
|
if om.Name == pkgName {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// needed to know what vcpkg registries to use for what packages when looking for manifest files
|
|
func findVcpkgConfig(resolver file.Resolver) (*vcpkg.Config, error) {
|
|
locs, err := resolver.FilesByGlob("**/vcpkg-configuration.json")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(locs) != 0 {
|
|
cfgCont, err := resolver.FileContentsByLocation(locs[0])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer internal.CloseAndLogError(cfgCont, locs[0].RealPath)
|
|
cfgBytes, err := io.ReadAll(io.LimitReader(cfgCont, maxVcpkgManifestSize))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var vcpkgConf vcpkg.Config
|
|
err = json.Unmarshal(cfgBytes, &vcpkgConf)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &vcpkgConf, err
|
|
}
|
|
return &vcpkg.Config{}, nil
|
|
}
|