diff --git a/cmd/syft/internal/test/integration/multiplatform_oci_test.go b/cmd/syft/internal/test/integration/multiplatform_oci_test.go index 94879a190..3373d8f36 100644 --- a/cmd/syft/internal/test/integration/multiplatform_oci_test.go +++ b/cmd/syft/internal/test/integration/multiplatform_oci_test.go @@ -60,6 +60,8 @@ func TestMultiPlatformOCIImageSelection(t *testing.T) { // The raw config of the selected image must match the requested platform... assertConfigPlatform(t, meta.RawConfig, "linux", arch) + assert.Equal(t, "linux", meta.OS) + assert.Equal(t, arch, meta.Architecture) // ...and it must be the exact per-platform image from the multi-platform index. assert.Equal(t, expectedDigest[arch], meta.ID) }) @@ -125,6 +127,8 @@ func TestMultiPlatformOCIImageSelection_DefaultPlatform(t *testing.T) { // The raw config of the selected image must match the current platform assertConfigPlatform(t, meta.RawConfig, "linux", runtime.GOARCH) + assert.Equal(t, "linux", meta.OS) + assert.Equal(t, runtime.GOARCH, meta.Architecture) }) } } diff --git a/cmd/syft/internal/test/integration/single_platform_oci_test.go b/cmd/syft/internal/test/integration/single_platform_oci_test.go new file mode 100644 index 000000000..b028aaaa4 --- /dev/null +++ b/cmd/syft/internal/test/integration/single_platform_oci_test.go @@ -0,0 +1,64 @@ +package integration + +import ( + "context" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/anchore/stereoscope/pkg/image" + "github.com/anchore/stereoscope/pkg/imagetest" + "github.com/anchore/syft/syft" + "github.com/anchore/syft/syft/source" +) + +// TestSinglePlatformOCIImage covers the single-image (non-index) OCI layouts, where syft used to report +// empty os/architecture metadata and silently accept any --platform. Both now come from the image config. +func TestSinglePlatformOCIImage(t *testing.T) { + // a platform this fixture is never built for, so the mismatch is not host dependent + unavailablePlatform, err := image.NewPlatform("linux/ppc64le") + require.NoError(t, err) + + sources := map[string]image.Source{ + "oci-dir": image.OciDirectorySource, + "oci-archive": image.OciTarballSource, + } + + for from, imageSource := range sources { + t.Run(from, func(t *testing.T) { + localPath := strings.TrimPrefix(imagetest.PrepareFixtureImage(t, imageSource, "image-distro-id"), string(imageSource)+":") + + t.Run("reports the platform from the image config", func(t *testing.T) { + src, err := syft.GetSource( + context.Background(), + localPath, + syft.DefaultGetSourceConfig().WithSources(from), + ) + require.NoError(t, err) + t.Cleanup(func() { + require.NoError(t, src.Close()) + }) + + meta, ok := src.Describe().Metadata.(source.ImageMetadata) + require.True(t, ok, "expected image metadata, got %T", src.Describe().Metadata) + + // the fixture is built for whatever the local docker daemon runs, so assert against the + // config rather than a fixed platform + assert.NotEmpty(t, meta.OS) + assert.NotEmpty(t, meta.Architecture) + assertConfigPlatform(t, meta.RawConfig, meta.OS, meta.Architecture) + }) + + t.Run("rejects a platform the image does not match", func(t *testing.T) { + _, err := syft.GetSource( + context.Background(), + localPath, + syft.DefaultGetSourceConfig().WithSources(from).WithPlatform(unavailablePlatform), + ) + require.ErrorContains(t, err, "linux/ppc64le") + }) + }) + } +} diff --git a/go.mod b/go.mod index 7043af03e..e24031c11 100644 --- a/go.mod +++ b/go.mod @@ -19,10 +19,10 @@ require ( github.com/anchore/go-logger v0.1.1 github.com/anchore/go-macholibre v0.1.1 github.com/anchore/go-rpmdb v0.2.0 - github.com/anchore/go-sync v0.1.1 + github.com/anchore/go-sync v0.1.2 github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b github.com/anchore/packageurl-go v0.2.0 - github.com/anchore/stereoscope v0.3.1 + github.com/anchore/stereoscope v0.3.2-0.20260915210014-a22e5ae7d131 github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be github.com/aquasecurity/go-pep440-version v0.0.1 github.com/bitnami/go-version v0.0.0-20250131085805-b1f57a8634ef @@ -181,7 +181,7 @@ require ( github.com/containerd/ttrpc v1.2.8 // indirect github.com/containerd/typeurl/v2 v2.2.3 // indirect github.com/cyphar/filepath-securejoin v0.6.1 // indirect - github.com/docker/cli v29.7.2+incompatible // indirect + github.com/docker/cli v29.8.0+incompatible // indirect github.com/docker/docker-credential-helpers v0.9.5 // indirect github.com/docker/go-connections v0.8.1 // indirect github.com/docker/go-units v0.5.0 // indirect @@ -236,8 +236,8 @@ require ( github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/locker v1.0.1 // indirect - github.com/moby/moby/api v1.55.0 // indirect - github.com/moby/moby/client v0.5.1 // indirect + github.com/moby/moby/api v1.56.0 // indirect + github.com/moby/moby/client v0.6.0 // indirect github.com/moby/sys/sequential v0.6.0 // indirect github.com/moby/sys/signal v0.7.1 // indirect github.com/moby/sys/user v0.4.0 // indirect diff --git a/go.sum b/go.sum index 097201078..dc4fa5af7 100644 --- a/go.sum +++ b/go.sum @@ -140,14 +140,14 @@ github.com/anchore/go-rpmdb v0.2.0 h1:ixcyMg/guhBL9XjBVwhTv9CG48xO07Qkg2uDX2+zx/ github.com/anchore/go-rpmdb v0.2.0/go.mod h1:ATsRlpCXstnoYfzqBfhwGw0U2dolx1BBQ9rAU8jWBAw= github.com/anchore/go-struct-converter v0.2.0-rc2 h1:q+859fW2/jbHJHB2etbNfRlFwYpknyvbqqk1hUdamQ4= github.com/anchore/go-struct-converter v0.2.0-rc2/go.mod h1:cDBA5vhcR62nXWo8QH9/Kk2807o65ISaHPNPX66L+Uw= -github.com/anchore/go-sync v0.1.1 h1:91SZ+YqUIIHmf2jPAYZPuHqM/ZqnK1pVJDWtET6+AJE= -github.com/anchore/go-sync v0.1.1/go.mod h1:3haGsk2BnaoVhsfqae8Kd0FKIAILE1Hw69P4Xo5iVBw= +github.com/anchore/go-sync v0.1.2 h1:RDD5RCanrWqhbtBulqPSZpERadUlgcYjpybYzYd3Uk8= +github.com/anchore/go-sync v0.1.2/go.mod h1:grjZH059bCHM6f6jh+Y7VYvX/q8YcjkDJqG6nplHGkE= github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b h1:e1bmaoJfZVsCYMrIZBpFxwV26CbsuoEh5muXD5I1Ods= github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E= github.com/anchore/packageurl-go v0.2.0 h1:CkrM4RMUwrEGAiE1OVlxaZNzWj0TuHRey7o4T/EAErk= github.com/anchore/packageurl-go v0.2.0/go.mod h1:2JCgOQMIsqZ7TmliXG4PnUthPJAKE3mWQbsW2XHjAOE= -github.com/anchore/stereoscope v0.3.1 h1:SFLeRyi+3L0fVDTcd4sjT6Xalvi9pju7DegeUMmxna8= -github.com/anchore/stereoscope v0.3.1/go.mod h1:KHLeUz03/lpsxM8jvs7gFSWRHCXB9MLnxpusCIPe3ME= +github.com/anchore/stereoscope v0.3.2-0.20260915210014-a22e5ae7d131 h1:cOtVHdWCsMEIRNi6BQ7++HWK3/0CpYfymfZoVDNrse4= +github.com/anchore/stereoscope v0.3.2-0.20260915210014-a22e5ae7d131/go.mod h1:/IUDVDka+4gufc8kDw+JO3gFl9PkpCjJ2r3bd9UWouM= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ= github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY= @@ -337,8 +337,8 @@ github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5Qvfr github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/djherbis/times v1.6.0 h1:w2ctJ92J8fBvWPxugmXIv7Nz7Q3iDMKNx9v5ocVH20c= github.com/djherbis/times v1.6.0/go.mod h1:gOHeRAz2h+VJNZ5Gmc/o7iD9k4wW7NMVqieYCY99oc0= -github.com/docker/cli v29.7.2+incompatible h1:dlkwallR8XqfeVnA2ELEhdwvb4lsSwuB4IgsG8Q9cLY= -github.com/docker/cli v29.7.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v29.8.0+incompatible h1:ih0c2jq/nN7QfES8zIfwSzIhRScjs4ehER+kZ60aeSk= +github.com/docker/cli v29.8.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/docker-credential-helpers v0.9.5 h1:EFNN8DHvaiK8zVqFA2DT6BjXE0GzfLOZ38ggPTKePkY= github.com/docker/docker-credential-helpers v0.9.5/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c= github.com/docker/go-connections v0.8.1 h1:JibmG5hULs5qXSr/cp/w3Pw5fZuStt4MOHMUExb29/M= @@ -721,10 +721,10 @@ github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3N github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= -github.com/moby/moby/api v1.55.0 h1:2/sexvQyqIWS8pRSCFddBfpW2qE7vR7FCL+vN8pxwMc= -github.com/moby/moby/api v1.55.0/go.mod h1:+RQ6wluLwtYaTd1WnPLykIDPekkuyD/ROWQClE83pzs= -github.com/moby/moby/client v0.5.1 h1:tYNaJno4c0HXz12y5BiqEDy0rVTYkWzI26lGvnTMiJw= -github.com/moby/moby/client v0.5.1/go.mod h1:odLstlZ6uSnfvAgVxMpvgmb8SUdd+siH2T0GBuxVAlM= +github.com/moby/moby/api v1.56.0 h1:GQzua3NA599ASSIICx0iFgiJeO9YkdDARvQsm23ZZuQ= +github.com/moby/moby/api v1.56.0/go.mod h1:sZ+THbVWkjOmBPPfbnzdD/G1LuIexWhqlSHHPTDQ1Uk= +github.com/moby/moby/client v0.6.0 h1:AJjEB21QPbXSXjDsZorFBoDZPhMrfbpaPLgSMAW9Bgs= +github.com/moby/moby/client v0.6.0/go.mod h1:OCo00wNRyA3m4lmJ228W3JbyCN4ZNNYjpOXiJydBdcQ= github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg= github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4= github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= diff --git a/syft/format/spdxjson/testdata/snapshot/TestSPDXJSONImageEncoder.golden b/syft/format/spdxjson/testdata/snapshot/TestSPDXJSONImageEncoder.golden index 3e55bda38..9f8da17a8 100644 --- a/syft/format/spdxjson/testdata/snapshot/TestSPDXJSONImageEncoder.golden +++ b/syft/format/spdxjson/testdata/snapshot/TestSPDXJSONImageEncoder.golden @@ -81,7 +81,7 @@ { "referenceCategory": "PACKAGE-MANAGER", "referenceType": "purl", - "referenceLocator": "pkg:oci/user-image-input@sha256%3A2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368?arch=" + "referenceLocator": "pkg:oci/user-image-input@sha256%3A2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368?arch=amd64" } ], "primaryPackagePurpose": "CONTAINER" diff --git a/syft/format/spdxjson/testdata/snapshot/TestSPDXRelationshipOrder.golden b/syft/format/spdxjson/testdata/snapshot/TestSPDXRelationshipOrder.golden index 2edf48c7e..844fc5a42 100644 --- a/syft/format/spdxjson/testdata/snapshot/TestSPDXRelationshipOrder.golden +++ b/syft/format/spdxjson/testdata/snapshot/TestSPDXRelationshipOrder.golden @@ -81,7 +81,7 @@ { "referenceCategory": "PACKAGE-MANAGER", "referenceType": "purl", - "referenceLocator": "pkg:oci/user-image-input@sha256%3A2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368?arch=" + "referenceLocator": "pkg:oci/user-image-input@sha256%3A2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368?arch=amd64" } ], "primaryPackagePurpose": "CONTAINER" diff --git a/syft/format/spdxtagvalue/testdata/snapshot/TestSPDXRelationshipOrder.golden b/syft/format/spdxtagvalue/testdata/snapshot/TestSPDXRelationshipOrder.golden index fb5ffb8e9..41535f1d3 100644 --- a/syft/format/spdxtagvalue/testdata/snapshot/TestSPDXRelationshipOrder.golden +++ b/syft/format/spdxtagvalue/testdata/snapshot/TestSPDXRelationshipOrder.golden @@ -71,7 +71,7 @@ PackageChecksum: SHA256: 2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf4 PackageLicenseConcluded: NOASSERTION PackageLicenseDeclared: NOASSERTION PackageCopyrightText: NOASSERTION -ExternalRef: PACKAGE-MANAGER purl pkg:oci/user-image-input@sha256%3A2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368?arch= +ExternalRef: PACKAGE-MANAGER purl pkg:oci/user-image-input@sha256%3A2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368?arch=amd64 ##### Package: package-2 diff --git a/syft/format/spdxtagvalue/testdata/snapshot/TestSPDXTagValueImageEncoder.golden b/syft/format/spdxtagvalue/testdata/snapshot/TestSPDXTagValueImageEncoder.golden index 54afe3558..cd97d7217 100644 --- a/syft/format/spdxtagvalue/testdata/snapshot/TestSPDXTagValueImageEncoder.golden +++ b/syft/format/spdxtagvalue/testdata/snapshot/TestSPDXTagValueImageEncoder.golden @@ -21,7 +21,7 @@ PackageChecksum: SHA256: 2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf4 PackageLicenseConcluded: NOASSERTION PackageLicenseDeclared: NOASSERTION PackageCopyrightText: NOASSERTION -ExternalRef: PACKAGE-MANAGER purl pkg:oci/user-image-input@sha256%3A2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368?arch= +ExternalRef: PACKAGE-MANAGER purl pkg:oci/user-image-input@sha256%3A2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368?arch=amd64 ##### Package: package-2 diff --git a/syft/format/syftjson/testdata/snapshot/TestImageEncoder.golden b/syft/format/syftjson/testdata/snapshot/TestImageEncoder.golden index 35984b5be..9a8c244bc 100644 --- a/syft/format/syftjson/testdata/snapshot/TestImageEncoder.golden +++ b/syft/format/syftjson/testdata/snapshot/TestImageEncoder.golden @@ -105,8 +105,8 @@ "manifest": "eyJzY2hlbWFWZXJzaW9uIjoyLCJtZWRpYVR5cGUiOiJhcHBsaWNhdGlvbi92bmQuZG9ja2VyLmRpc3RyaWJ1dGlvbi5tYW5pZmVzdC52Mitqc29uIiwiY29uZmlnIjp7Im1lZGlhVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5kb2NrZXIuY29udGFpbmVyLmltYWdlLnYxK2pzb24iLCJzaXplIjo2NTgsImRpZ2VzdCI6InNoYTI1NjozNWE2NjU4ZTI0ZmFiOTJlYWU5ZWM2ZmMyNTJkZWM1ODk4NmM0YzAwNzg5MTc1OGQ0ZDM3YzdlNDNmYmJlMGM1In0sImxheWVycyI6W3sibWVkaWFUeXBlIjoiYXBwbGljYXRpb24vdm5kLmRvY2tlci5pbWFnZS5yb290ZnMuZGlmZi50YXIuZ3ppcCIsInNpemUiOjIwNDgsImRpZ2VzdCI6InNoYTI1NjpkZmVmZTYxOGM4OWIwOGZlZjBmOWM3ZjFhMjY4MjUyMWRkZGJlMDNkNjY3OGY0YTlmYjliMDc4MzgxZDhlYjQ1In0seyJtZWRpYVR5cGUiOiJhcHBsaWNhdGlvbi92bmQuZG9ja2VyLmltYWdlLnJvb3Rmcy5kaWZmLnRhci5nemlwIiwic2l6ZSI6MjA0OCwiZGlnZXN0Ijoic2hhMjU2OjM4ZGRjMjg0N2ZiNmJjYWZkNzQwMWI0YmYyN2MxMDAxNGI1ZDYwZTI0MDBiYzE4ODg5MGM3Y2I3Y2RkN2NkNmMifV19", "config": "eyJhcmNoaXRlY3R1cmUiOiJhbWQ2NCIsImNvbmZpZyI6eyJFbnYiOlsiUEFUSD0vdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4iXSwiV29ya2luZ0RpciI6Ii8ifSwiY3JlYXRlZCI6IjIwMjQtMTEtMDRUMDQ6MTM6NDMuMzQwNTA3MTc1WiIsImhpc3RvcnkiOlt7ImNyZWF0ZWQiOiIyMDI0LTExLTA0VDA0OjEzOjQzLjMyMDg2OTk2OFoiLCJjcmVhdGVkX2J5IjoiQUREIGZpbGUtMS50eHQgL3NvbWVmaWxlLTEudHh0ICMgYnVpbGRraXQiLCJjb21tZW50IjoiYnVpbGRraXQuZG9ja2VyZmlsZS52MCJ9LHsiY3JlYXRlZCI6IjIwMjQtMTEtMDRUMDQ6MTM6NDMuMzQwNTA3MTc1WiIsImNyZWF0ZWRfYnkiOiJBREQgZmlsZS0yLnR4dCAvc29tZWZpbGUtMi50eHQgIyBidWlsZGtpdCIsImNvbW1lbnQiOiJidWlsZGtpdC5kb2NrZXJmaWxlLnYwIn1dLCJvcyI6ImxpbnV4Iiwicm9vdGZzIjp7InR5cGUiOiJsYXllcnMiLCJkaWZmX2lkcyI6WyJzaGEyNTY6ZGZlZmU2MThjODliMDhmZWYwZjljN2YxYTI2ODI1MjFkZGRiZTAzZDY2NzhmNGE5ZmI5YjA3ODM4MWQ4ZWI0NSIsInNoYTI1NjozOGRkYzI4NDdmYjZiY2FmZDc0MDFiNGJmMjdjMTAwMTRiNWQ2MGUyNDAwYmMxODg4OTBjN2NiN2NkZDdjZDZjIl19fQ==", "repoDigests": [], - "architecture": "", - "os": "" + "architecture": "amd64", + "os": "linux" } }, "distro": {