mirror of
https://github.com/anchore/syft
synced 2024-11-10 06:14:16 +00:00
Use values in relationship To/From fields (#2871)
* use pkg values in relationship fields Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * add linter rule for using values in relationships Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * use new cmptest package for comparing relationships Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * create cmptest for common cmp.Diff options in test Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * condense matches for relationship ruleguard Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * remove relationship type from rules Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * restore build tag Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * suggest using values Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * nil check pkgs Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> --------- Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
This commit is contained in:
parent
7ad7627d5d
commit
048df17e3d
9 changed files with 239 additions and 134 deletions
71
internal/cmptest/common_options.go
Normal file
71
internal/cmptest/common_options.go
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
package cmptest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
|
"github.com/google/go-cmp/cmp/cmpopts"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/file"
|
||||||
|
"github.com/anchore/syft/syft/pkg"
|
||||||
|
)
|
||||||
|
|
||||||
|
func DefaultCommonOptions() []cmp.Option {
|
||||||
|
return CommonOptions(nil, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CommonOptions(licenseCmp LicenseComparer, locationCmp LocationComparer) []cmp.Option {
|
||||||
|
if licenseCmp == nil {
|
||||||
|
licenseCmp = DefaultLicenseComparer
|
||||||
|
}
|
||||||
|
|
||||||
|
if locationCmp == nil {
|
||||||
|
locationCmp = DefaultLocationComparer
|
||||||
|
}
|
||||||
|
|
||||||
|
return []cmp.Option{
|
||||||
|
cmpopts.IgnoreFields(pkg.Package{}, "id"), // note: ID is not deterministic for test purposes
|
||||||
|
cmpopts.SortSlices(pkg.Less),
|
||||||
|
cmpopts.SortSlices(DefaultRelationshipComparer),
|
||||||
|
cmp.Comparer(
|
||||||
|
func(x, y file.LocationSet) bool {
|
||||||
|
xs := x.ToSlice()
|
||||||
|
ys := y.ToSlice()
|
||||||
|
|
||||||
|
if len(xs) != len(ys) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i, xe := range xs {
|
||||||
|
ye := ys[i]
|
||||||
|
if !locationCmp(xe, ye) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
),
|
||||||
|
cmp.Comparer(
|
||||||
|
func(x, y pkg.LicenseSet) bool {
|
||||||
|
xs := x.ToSlice()
|
||||||
|
ys := y.ToSlice()
|
||||||
|
|
||||||
|
if len(xs) != len(ys) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i, xe := range xs {
|
||||||
|
ye := ys[i]
|
||||||
|
if !licenseCmp(xe, ye) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
),
|
||||||
|
cmp.Comparer(
|
||||||
|
locationCmp,
|
||||||
|
),
|
||||||
|
cmp.Comparer(
|
||||||
|
licenseCmp,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
37
internal/cmptest/diff_reporter.go
Normal file
37
internal/cmptest/diff_reporter.go
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
package cmptest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DiffReporter is a simple custom reporter that only records differences detected during comparison.
|
||||||
|
type DiffReporter struct {
|
||||||
|
path cmp.Path
|
||||||
|
diffs []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDiffReporter() DiffReporter {
|
||||||
|
return DiffReporter{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *DiffReporter) PushStep(ps cmp.PathStep) {
|
||||||
|
r.path = append(r.path, ps)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *DiffReporter) Report(rs cmp.Result) {
|
||||||
|
if !rs.Equal() {
|
||||||
|
vx, vy := r.path.Last().Values()
|
||||||
|
r.diffs = append(r.diffs, fmt.Sprintf("%#v:\n\t-: %+v\n\t+: %+v\n", r.path, vx, vy))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *DiffReporter) PopStep() {
|
||||||
|
r.path = r.path[:len(r.path)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *DiffReporter) String() string {
|
||||||
|
return strings.Join(r.diffs, "\n")
|
||||||
|
}
|
29
internal/cmptest/license.go
Normal file
29
internal/cmptest/license.go
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
package cmptest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/file"
|
||||||
|
"github.com/anchore/syft/syft/pkg"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LicenseComparer func(x, y pkg.License) bool
|
||||||
|
|
||||||
|
func DefaultLicenseComparer(x, y pkg.License) bool {
|
||||||
|
return cmp.Equal(x, y, cmp.Comparer(DefaultLocationComparer), cmp.Comparer(
|
||||||
|
func(x, y file.LocationSet) bool {
|
||||||
|
xs := x.ToSlice()
|
||||||
|
ys := y.ToSlice()
|
||||||
|
if len(xs) != len(ys) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i, xe := range xs {
|
||||||
|
ye := ys[i]
|
||||||
|
if !DefaultLocationComparer(xe, ye) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
13
internal/cmptest/location.go
Normal file
13
internal/cmptest/location.go
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
package cmptest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/file"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LocationComparer func(x, y file.Location) bool
|
||||||
|
|
||||||
|
func DefaultLocationComparer(x, y file.Location) bool {
|
||||||
|
return cmp.Equal(x.Coordinates, y.Coordinates) && cmp.Equal(x.AccessPath, y.AccessPath)
|
||||||
|
}
|
26
internal/cmptest/relationship.go
Normal file
26
internal/cmptest/relationship.go
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
package cmptest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/sanity-io/litter"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/artifact"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RelationshipComparer func(x, y artifact.Relationship) bool
|
||||||
|
|
||||||
|
var relationshipStringer = litter.Options{
|
||||||
|
Compact: true,
|
||||||
|
StripPackageNames: false,
|
||||||
|
HidePrivateFields: true, // we want to ignore package IDs
|
||||||
|
HideZeroValues: true,
|
||||||
|
StrictGo: true,
|
||||||
|
//FieldExclusions: ... // these can be added for future values that need to be ignored
|
||||||
|
//FieldFilter: ...
|
||||||
|
}
|
||||||
|
|
||||||
|
func DefaultRelationshipComparer(x, y artifact.Relationship) bool {
|
||||||
|
// we just need a stable sort, the ordering does not need to be sensible
|
||||||
|
xStr := relationshipStringer.Sdump(x)
|
||||||
|
yStr := relationshipStringer.Sdump(y)
|
||||||
|
return xStr < yStr
|
||||||
|
}
|
|
@ -58,9 +58,19 @@ func byFileOwnershipOverlap(catalog *pkg.Collection) []artifact.Relationship {
|
||||||
parent := catalog.Package(parentID) // TODO: this is potentially expensive
|
parent := catalog.Package(parentID) // TODO: this is potentially expensive
|
||||||
child := catalog.Package(childID) // TODO: this is potentially expensive
|
child := catalog.Package(childID) // TODO: this is potentially expensive
|
||||||
|
|
||||||
|
if parent == nil {
|
||||||
|
log.Tracef("parent package not found: %v", parentID)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if child == nil {
|
||||||
|
log.Tracef("child package not found: %v", childID)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
edges = append(edges, artifact.Relationship{
|
edges = append(edges, artifact.Relationship{
|
||||||
From: parent,
|
From: *parent,
|
||||||
To: child,
|
To: *child,
|
||||||
Type: artifact.OwnershipByFileOverlapRelationship,
|
Type: artifact.OwnershipByFileOverlapRelationship,
|
||||||
Data: ownershipByFilesMetadata{
|
Data: ownershipByFilesMetadata{
|
||||||
Files: fs,
|
Files: fs,
|
||||||
|
|
|
@ -3,8 +3,10 @@ package relationship
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/internal/cmptest"
|
||||||
"github.com/anchore/syft/syft/artifact"
|
"github.com/anchore/syft/syft/artifact"
|
||||||
"github.com/anchore/syft/syft/file"
|
"github.com/anchore/syft/syft/file"
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
|
@ -143,10 +145,9 @@ func TestOwnershipByFilesRelationship(t *testing.T) {
|
||||||
assert.Len(t, relationships, len(expectedRelations))
|
assert.Len(t, relationships, len(expectedRelations))
|
||||||
for idx, expectedRelationship := range expectedRelations {
|
for idx, expectedRelationship := range expectedRelations {
|
||||||
actualRelationship := relationships[idx]
|
actualRelationship := relationships[idx]
|
||||||
assert.Equal(t, expectedRelationship.From.ID(), actualRelationship.From.ID())
|
if d := cmp.Diff(expectedRelationship, actualRelationship, cmptest.DefaultCommonOptions()...); d != "" {
|
||||||
assert.Equal(t, expectedRelationship.To.ID(), actualRelationship.To.ID())
|
t.Errorf("unexpected relationship (-want, +got): %s", d)
|
||||||
assert.Equal(t, expectedRelationship.Type, actualRelationship.Type)
|
}
|
||||||
assert.Equal(t, expectedRelationship.Data, actualRelationship.Data)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ package pkgtest
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -10,11 +9,11 @@ import (
|
||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
"github.com/google/go-cmp/cmp/cmpopts"
|
"github.com/google/go-cmp/cmp/cmpopts"
|
||||||
"github.com/sanity-io/litter"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/anchore/stereoscope/pkg/imagetest"
|
"github.com/anchore/stereoscope/pkg/imagetest"
|
||||||
|
"github.com/anchore/syft/internal/cmptest"
|
||||||
"github.com/anchore/syft/internal/relationship"
|
"github.com/anchore/syft/internal/relationship"
|
||||||
"github.com/anchore/syft/syft/artifact"
|
"github.com/anchore/syft/syft/artifact"
|
||||||
"github.com/anchore/syft/syft/file"
|
"github.com/anchore/syft/syft/file"
|
||||||
|
@ -26,9 +25,6 @@ import (
|
||||||
"github.com/anchore/syft/syft/source/stereoscopesource"
|
"github.com/anchore/syft/syft/source/stereoscopesource"
|
||||||
)
|
)
|
||||||
|
|
||||||
type locationComparer func(x, y file.Location) bool
|
|
||||||
type licenseComparer func(x, y pkg.License) bool
|
|
||||||
|
|
||||||
type CatalogTester struct {
|
type CatalogTester struct {
|
||||||
expectedPkgs []pkg.Package
|
expectedPkgs []pkg.Package
|
||||||
expectedRelationships []artifact.Relationship
|
expectedRelationships []artifact.Relationship
|
||||||
|
@ -42,16 +38,16 @@ type CatalogTester struct {
|
||||||
resolver file.Resolver
|
resolver file.Resolver
|
||||||
wantErr require.ErrorAssertionFunc
|
wantErr require.ErrorAssertionFunc
|
||||||
compareOptions []cmp.Option
|
compareOptions []cmp.Option
|
||||||
locationComparer locationComparer
|
locationComparer cmptest.LocationComparer
|
||||||
licenseComparer licenseComparer
|
licenseComparer cmptest.LicenseComparer
|
||||||
customAssertions []func(t *testing.T, pkgs []pkg.Package, relationships []artifact.Relationship)
|
customAssertions []func(t *testing.T, pkgs []pkg.Package, relationships []artifact.Relationship)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCatalogTester() *CatalogTester {
|
func NewCatalogTester() *CatalogTester {
|
||||||
return &CatalogTester{
|
return &CatalogTester{
|
||||||
wantErr: require.NoError,
|
wantErr: require.NoError,
|
||||||
locationComparer: DefaultLocationComparer,
|
locationComparer: cmptest.DefaultLocationComparer,
|
||||||
licenseComparer: DefaultLicenseComparer,
|
licenseComparer: cmptest.DefaultLicenseComparer,
|
||||||
ignoreUnfulfilledPathResponses: map[string][]string{
|
ignoreUnfulfilledPathResponses: map[string][]string{
|
||||||
"FilesByPath": {
|
"FilesByPath": {
|
||||||
// most catalogers search for a linux release, which will not be fulfilled in testing
|
// most catalogers search for a linux release, which will not be fulfilled in testing
|
||||||
|
@ -65,29 +61,6 @@ func NewCatalogTester() *CatalogTester {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func DefaultLocationComparer(x, y file.Location) bool {
|
|
||||||
return cmp.Equal(x.Coordinates, y.Coordinates) && cmp.Equal(x.AccessPath, y.AccessPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
func DefaultLicenseComparer(x, y pkg.License) bool {
|
|
||||||
return cmp.Equal(x, y, cmp.Comparer(DefaultLocationComparer), cmp.Comparer(
|
|
||||||
func(x, y file.LocationSet) bool {
|
|
||||||
xs := x.ToSlice()
|
|
||||||
ys := y.ToSlice()
|
|
||||||
if len(xs) != len(ys) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for i, xe := range xs {
|
|
||||||
ye := ys[i]
|
|
||||||
if !DefaultLocationComparer(xe, ye) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
},
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *CatalogTester) FromDirectory(t *testing.T, path string) *CatalogTester {
|
func (p *CatalogTester) FromDirectory(t *testing.T, path string) *CatalogTester {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
|
@ -270,77 +243,14 @@ func (p *CatalogTester) TestCataloger(t *testing.T, cataloger pkg.Cataloger) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var relationshipStringer = litter.Options{
|
|
||||||
Compact: true,
|
|
||||||
StripPackageNames: false,
|
|
||||||
HidePrivateFields: true, // we want to ignore package IDs
|
|
||||||
HideZeroValues: true,
|
|
||||||
StrictGo: true,
|
|
||||||
//FieldExclusions: ... // these can be added for future values that need to be ignored
|
|
||||||
//FieldFilter: ...
|
|
||||||
}
|
|
||||||
|
|
||||||
func relationshipLess(x, y artifact.Relationship) bool {
|
|
||||||
// we just need a stable sort, the ordering does not need to be sensible
|
|
||||||
xStr := relationshipStringer.Sdump(x)
|
|
||||||
yStr := relationshipStringer.Sdump(y)
|
|
||||||
return xStr < yStr
|
|
||||||
}
|
|
||||||
|
|
||||||
// nolint:funlen
|
// nolint:funlen
|
||||||
func (p *CatalogTester) assertPkgs(t *testing.T, pkgs []pkg.Package, relationships []artifact.Relationship) {
|
func (p *CatalogTester) assertPkgs(t *testing.T, pkgs []pkg.Package, relationships []artifact.Relationship) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
p.compareOptions = append(p.compareOptions,
|
p.compareOptions = append(p.compareOptions, cmptest.CommonOptions(p.licenseComparer, p.locationComparer)...)
|
||||||
cmpopts.IgnoreFields(pkg.Package{}, "id"), // note: ID is not deterministic for test purposes
|
|
||||||
cmpopts.SortSlices(pkg.Less),
|
|
||||||
cmpopts.SortSlices(relationshipLess),
|
|
||||||
cmp.Comparer(
|
|
||||||
func(x, y file.LocationSet) bool {
|
|
||||||
xs := x.ToSlice()
|
|
||||||
ys := y.ToSlice()
|
|
||||||
|
|
||||||
if len(xs) != len(ys) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for i, xe := range xs {
|
|
||||||
ye := ys[i]
|
|
||||||
if !p.locationComparer(xe, ye) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
},
|
|
||||||
),
|
|
||||||
cmp.Comparer(
|
|
||||||
func(x, y pkg.LicenseSet) bool {
|
|
||||||
xs := x.ToSlice()
|
|
||||||
ys := y.ToSlice()
|
|
||||||
|
|
||||||
if len(xs) != len(ys) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for i, xe := range xs {
|
|
||||||
ye := ys[i]
|
|
||||||
if !p.licenseComparer(xe, ye) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
},
|
|
||||||
),
|
|
||||||
cmp.Comparer(
|
|
||||||
p.locationComparer,
|
|
||||||
),
|
|
||||||
cmp.Comparer(
|
|
||||||
p.licenseComparer,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
{
|
{
|
||||||
var r diffReporter
|
r := cmptest.NewDiffReporter()
|
||||||
var opts []cmp.Option
|
var opts []cmp.Option
|
||||||
|
|
||||||
opts = append(opts, p.compareOptions...)
|
opts = append(opts, p.compareOptions...)
|
||||||
|
@ -356,7 +266,7 @@ func (p *CatalogTester) assertPkgs(t *testing.T, pkgs []pkg.Package, relationshi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
var r diffReporter
|
r := cmptest.NewDiffReporter()
|
||||||
var opts []cmp.Option
|
var opts []cmp.Option
|
||||||
|
|
||||||
opts = append(opts, p.compareOptions...)
|
opts = append(opts, p.compareOptions...)
|
||||||
|
@ -399,7 +309,7 @@ func AssertPackagesEqual(t *testing.T, a, b pkg.Package) {
|
||||||
}
|
}
|
||||||
for i, xe := range xs {
|
for i, xe := range xs {
|
||||||
ye := ys[i]
|
ye := ys[i]
|
||||||
if !DefaultLocationComparer(xe, ye) {
|
if !cmptest.DefaultLocationComparer(xe, ye) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -417,7 +327,7 @@ func AssertPackagesEqual(t *testing.T, a, b pkg.Package) {
|
||||||
}
|
}
|
||||||
for i, xe := range xs {
|
for i, xe := range xs {
|
||||||
ye := ys[i]
|
ye := ys[i]
|
||||||
if !DefaultLicenseComparer(xe, ye) {
|
if !cmptest.DefaultLicenseComparer(xe, ye) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -426,10 +336,10 @@ func AssertPackagesEqual(t *testing.T, a, b pkg.Package) {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
cmp.Comparer(
|
cmp.Comparer(
|
||||||
DefaultLocationComparer,
|
cmptest.DefaultLocationComparer,
|
||||||
),
|
),
|
||||||
cmp.Comparer(
|
cmp.Comparer(
|
||||||
DefaultLicenseComparer,
|
cmptest.DefaultLicenseComparer,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -437,28 +347,3 @@ func AssertPackagesEqual(t *testing.T, a, b pkg.Package) {
|
||||||
t.Errorf("unexpected packages from parsing (-expected +actual)\n%s", diff)
|
t.Errorf("unexpected packages from parsing (-expected +actual)\n%s", diff)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// diffReporter is a simple custom reporter that only records differences detected during comparison.
|
|
||||||
type diffReporter struct {
|
|
||||||
path cmp.Path
|
|
||||||
diffs []string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *diffReporter) PushStep(ps cmp.PathStep) {
|
|
||||||
r.path = append(r.path, ps)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *diffReporter) Report(rs cmp.Result) {
|
|
||||||
if !rs.Equal() {
|
|
||||||
vx, vy := r.path.Last().Values()
|
|
||||||
r.diffs = append(r.diffs, fmt.Sprintf("%#v:\n\t-: %+v\n\t+: %+v\n", r.path, vx, vy))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *diffReporter) PopStep() {
|
|
||||||
r.path = r.path[:len(r.path)-1]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *diffReporter) String() string {
|
|
||||||
return strings.Join(r.diffs, "\n")
|
|
||||||
}
|
|
||||||
|
|
|
@ -2,10 +2,15 @@
|
||||||
|
|
||||||
package rules
|
package rules
|
||||||
|
|
||||||
import "github.com/quasilyte/go-ruleguard/dsl"
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/quasilyte/go-ruleguard/dsl"
|
||||||
|
)
|
||||||
|
|
||||||
// nolint:unused
|
// nolint:unused
|
||||||
func resourceCleanup(m dsl.Matcher) {
|
func resourceCleanup(m dsl.Matcher) {
|
||||||
|
// this rule defends against use of internal.CloseAndLogError() without a defer statement
|
||||||
m.Match(`$res, $err := $resolver.FileContentsByLocation($loc); if $*_ { $*_ }; $next`).
|
m.Match(`$res, $err := $resolver.FileContentsByLocation($loc); if $*_ { $*_ }; $next`).
|
||||||
Where(m["res"].Type.Implements(`io.Closer`) &&
|
Where(m["res"].Type.Implements(`io.Closer`) &&
|
||||||
m["res"].Type.Implements(`io.Reader`) &&
|
m["res"].Type.Implements(`io.Reader`) &&
|
||||||
|
@ -13,3 +18,31 @@ func resourceCleanup(m dsl.Matcher) {
|
||||||
!m["next"].Text.Matches(`defer internal.CloseAndLogError`)).
|
!m["next"].Text.Matches(`defer internal.CloseAndLogError`)).
|
||||||
Report(`please call "defer internal.CloseAndLogError($res, $loc.RealPath)" right after checking the error returned from $resolver.FileContentsByLocation.`)
|
Report(`please call "defer internal.CloseAndLogError($res, $loc.RealPath)" right after checking the error returned from $resolver.FileContentsByLocation.`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint:unused
|
||||||
|
func isPtr(ctx *dsl.VarFilterContext) bool {
|
||||||
|
return strings.HasPrefix(ctx.Type.String(), "*") || strings.HasPrefix(ctx.Type.Underlying().String(), "*")
|
||||||
|
}
|
||||||
|
|
||||||
|
// nolint:unused
|
||||||
|
func packagesInRelationshipsAsValues(m dsl.Matcher) {
|
||||||
|
m.Import("github.com/anchore/syft/syft/artifact")
|
||||||
|
|
||||||
|
isRelationship := func(m dsl.Matcher) bool {
|
||||||
|
return m["x"].Type.Is("artifact.Relationship")
|
||||||
|
}
|
||||||
|
|
||||||
|
hasPointerType := func(m dsl.Matcher) bool {
|
||||||
|
return m["y"].Filter(isPtr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// this rule defends against using pointers as values in artifact.Relationship
|
||||||
|
m.Match(
|
||||||
|
`$x{$*_, From: $y, $*_}`,
|
||||||
|
`$x{$*_, To: $y, $*_}`,
|
||||||
|
`$x.From = $y`,
|
||||||
|
`$x.To = $y`,
|
||||||
|
).
|
||||||
|
Where(isRelationship(m) && hasPointerType(m)).
|
||||||
|
Report("pointer used as a value for From/To field in artifact.Relationship (use values instead)")
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue