github.com/lineaje-labs/syft@v0.98.1-0.20231227153149-9e393f60ff1b/syft/format/syftjson/model/source_test.go (about)

     1  package model
     2  
     3  import (
     4  	"encoding/json"
     5  	"testing"
     6  
     7  	"github.com/google/go-cmp/cmp"
     8  	"github.com/stretchr/testify/assert"
     9  	"github.com/stretchr/testify/require"
    10  
    11  	"github.com/anchore/syft/syft/file"
    12  	"github.com/anchore/syft/syft/source"
    13  	"github.com/lineaje-labs/syft/syft/internal/sourcemetadata"
    14  )
    15  
    16  func TestSource_UnmarshalJSON(t *testing.T) {
    17  	tracker := sourcemetadata.NewCompletionTester(t)
    18  
    19  	cases := []struct {
    20  		name     string
    21  		input    []byte
    22  		expected *Source
    23  		wantErr  require.ErrorAssertionFunc
    24  	}{
    25  		{
    26  			name: "directory",
    27  			input: []byte(`{
    28  				"id": "foobar",
    29  				"type": "directory",
    30  				"metadata": {"path": "/var/lib/foo", "base":"/nope"}
    31  			}`),
    32  			expected: &Source{
    33  				ID:   "foobar",
    34  				Type: "directory",
    35  				Metadata: source.DirectorySourceMetadata{
    36  					Path: "/var/lib/foo",
    37  					// Base: "/nope", // note: should be ignored entirely
    38  				},
    39  			},
    40  		},
    41  		{
    42  			name: "image",
    43  			input: []byte(`{
    44  				"id": "foobar",
    45  				"type": "image",
    46  				"metadata": {
    47  					"userInput": "alpine:3.10",
    48  					"imageID": "sha256:e7b300aee9f9bf3433d32bc9305bfdd22183beb59d933b48d77ab56ba53a197a",
    49  					"manifestDigest": "sha256:e515aad2ed234a5072c4d2ef86a1cb77d5bfe4b11aa865d9214875734c4eeb3c",
    50  					"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    51  					"tags": [],
    52  					"imageSize": 5576169,
    53  					"layers": [
    54  						{
    55  							"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
    56  							"digest": "sha256:9fb3aa2f8b8023a4bebbf92aa567caf88e38e969ada9f0ac12643b2847391635",
    57  							"size": 5576169
    58  						}
    59  					],
    60  					"manifest": "ewogICAic2NoZW1hVmVyc2lvbiI6IDIsCiAgICJtZWRpYVR5cGUiOiAiYXBwbGljYXRpb24vdm5kLmRvY2tlci5kaXN0cmlidXRpb24ubWFuaWZlc3QudjIranNvbiIsCiAgICJjb25maWciOiB7CiAgICAgICJtZWRpYVR5cGUiOiAiYXBwbGljYXRpb24vdm5kLmRvY2tlci5jb250YWluZXIuaW1hZ2UudjEranNvbiIsCiAgICAgICJzaXplIjogMTQ3MiwKICAgICAgImRpZ2VzdCI6ICJzaGEyNTY6ZTdiMzAwYWVlOWY5YmYzNDMzZDMyYmM5MzA1YmZkZDIyMTgzYmViNTlkOTMzYjQ4ZDc3YWI1NmJhNTNhMTk3YSIKICAgfSwKICAgImxheWVycyI6IFsKICAgICAgewogICAgICAgICAibWVkaWFUeXBlIjogImFwcGxpY2F0aW9uL3ZuZC5kb2NrZXIuaW1hZ2Uucm9vdGZzLmRpZmYudGFyLmd6aXAiLAogICAgICAgICAic2l6ZSI6IDI3OTgzMzgsCiAgICAgICAgICJkaWdlc3QiOiAic2hhMjU2OjM5NmMzMTgzNzExNmFjMjkwNDU4YWZjYjkyOGY2OGI2Y2MxYzdiZGQ2OTYzZmM3MmY1MmYzNjVhMmE4OWMxYjUiCiAgICAgIH0KICAgXQp9",
    61  					"config": "eyJhcmNoaXRlY3R1cmUiOiJhbWQ2NCIsImNvbmZpZyI6eyJIb3N0bmFtZSI6IiIsIkRvbWFpbm5hbWUiOiIiLCJVc2VyIjoiIiwiQXR0YWNoU3RkaW4iOmZhbHNlLCJBdHRhY2hTdGRvdXQiOmZhbHNlLCJBdHRhY2hTdGRlcnIiOmZhbHNlLCJUdHkiOmZhbHNlLCJPcGVuU3RkaW4iOmZhbHNlLCJTdGRpbk9uY2UiOmZhbHNlLCJFbnYiOlsiUEFUSD0vdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4iXSwiQ21kIjpbIi9iaW4vc2giXSwiSW1hZ2UiOiJzaGEyNTY6ZWIyMDgwYzQ1NWU5NGMyMmFlMzViM2FlZjllMDc4YzQ5MmEwMDc5NTQxMmUwMjZlNGQ2YjQxZWY2NGJjN2RkOCIsIlZvbHVtZXMiOm51bGwsIldvcmtpbmdEaXIiOiIiLCJFbnRyeXBvaW50IjpudWxsLCJPbkJ1aWxkIjpudWxsLCJMYWJlbHMiOm51bGx9LCJjb250YWluZXIiOiJmZGI3ZTgwZTMzMzllOGQwNTk5MjgyZTYwNmM5MDdhYTU4ODFlZTRjNjY4YTY4MTM2MTE5ZTZkZmFjNmNlM2E0IiwiY29udGFpbmVyX2NvbmZpZyI6eyJIb3N0bmFtZSI6ImZkYjdlODBlMzMzOSIsIkRvbWFpbm5hbWUiOiIiLCJVc2VyIjoiIiwiQXR0YWNoU3RkaW4iOmZhbHNlLCJBdHRhY2hTdGRvdXQiOmZhbHNlLCJBdHRhY2hTdGRlcnIiOmZhbHNlLCJUdHkiOmZhbHNlLCJPcGVuU3RkaW4iOmZhbHNlLCJTdGRpbk9uY2UiOmZhbHNlLCJFbnYiOlsiUEFUSD0vdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4iXSwiQ21kIjpbIi9iaW4vc2giLCItYyIsIiMobm9wKSAiLCJDTUQgW1wiL2Jpbi9zaFwiXSJdLCJJbWFnZSI6InNoYTI1NjplYjIwODBjNDU1ZTk0YzIyYWUzNWIzYWVmOWUwNzhjNDkyYTAwNzk1NDEyZTAyNmU0ZDZiNDFlZjY0YmM3ZGQ4IiwiVm9sdW1lcyI6bnVsbCwiV29ya2luZ0RpciI6IiIsIkVudHJ5cG9pbnQiOm51bGwsIk9uQnVpbGQiOm51bGwsIkxhYmVscyI6e319LCJjcmVhdGVkIjoiMjAyMS0wNC0xNFQxOToyMDowNS4zMzgzOTc3NjFaIiwiZG9ja2VyX3ZlcnNpb24iOiIxOS4wMy4xMiIsImhpc3RvcnkiOlt7ImNyZWF0ZWQiOiIyMDIxLTA0LTE0VDE5OjIwOjA0Ljk4NzIxOTEyNFoiLCJjcmVhdGVkX2J5IjoiL2Jpbi9zaCAtYyAjKG5vcCkgQUREIGZpbGU6YzUzNzdlYWE5MjZiZjQxMmRkOGQ0YTA4YjBhMWYyMzk5Y2ZkNzA4NzQzNTMzYjBhYTAzYjUzZDE0Y2I0YmI0ZSBpbiAvICJ9LHsiY3JlYXRlZCI6IjIwMjEtMDQtMTRUMTk6MjA6MDUuMzM4Mzk3NzYxWiIsImNyZWF0ZWRfYnkiOiIvYmluL3NoIC1jICMobm9wKSAgQ01EIFtcIi9iaW4vc2hcIl0iLCJlbXB0eV9sYXllciI6dHJ1ZX1dLCJvcyI6ImxpbnV4Iiwicm9vdGZzIjp7InR5cGUiOiJsYXllcnMiLCJkaWZmX2lkcyI6WyJzaGEyNTY6OWZiM2FhMmY4YjgwMjNhNGJlYmJmOTJhYTU2N2NhZjg4ZTM4ZTk2OWFkYTlmMGFjMTI2NDNiMjg0NzM5MTYzNSJdfX0=",
    62  					"repoDigests": [
    63  						"index.docker.io/library/alpine@sha256:451eee8bedcb2f029756dc3e9d73bab0e7943c1ac55cff3a4861c52a0fdd3e98"
    64  					]
    65  				}
    66  			}`),
    67  			expected: &Source{
    68  				ID:   "foobar",
    69  				Type: "image",
    70  				Metadata: source.StereoscopeImageSourceMetadata{
    71  					UserInput:      "alpine:3.10",
    72  					ID:             "sha256:e7b300aee9f9bf3433d32bc9305bfdd22183beb59d933b48d77ab56ba53a197a",
    73  					ManifestDigest: "sha256:e515aad2ed234a5072c4d2ef86a1cb77d5bfe4b11aa865d9214875734c4eeb3c",
    74  					MediaType:      "application/vnd.docker.distribution.manifest.v2+json",
    75  					Tags:           []string{},
    76  					Size:           5576169,
    77  					Layers: []source.StereoscopeLayerMetadata{
    78  						{
    79  							MediaType: "application/vnd.docker.image.rootfs.diff.tar.gzip",
    80  							Digest:    "sha256:9fb3aa2f8b8023a4bebbf92aa567caf88e38e969ada9f0ac12643b2847391635",
    81  							Size:      5576169,
    82  						},
    83  					},
    84  					RawManifest: []byte(`{
    85     "schemaVersion": 2,
    86     "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    87     "config": {
    88        "mediaType": "application/vnd.docker.container.image.v1+json",
    89        "size": 1472,
    90        "digest": "sha256:e7b300aee9f9bf3433d32bc9305bfdd22183beb59d933b48d77ab56ba53a197a"
    91     },
    92     "layers": [
    93        {
    94           "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
    95           "size": 2798338,
    96           "digest": "sha256:396c31837116ac290458afcb928f68b6cc1c7bdd6963fc72f52f365a2a89c1b5"
    97        }
    98     ]
    99  }`),
   100  					RawConfig: []byte(`{"architecture":"amd64","config":{"Hostname":"","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":["/bin/sh"],"Image":"sha256:eb2080c455e94c22ae35b3aef9e078c492a00795412e026e4d6b41ef64bc7dd8","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":null},"container":"fdb7e80e3339e8d0599282e606c907aa5881ee4c668a68136119e6dfac6ce3a4","container_config":{"Hostname":"fdb7e80e3339","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":["/bin/sh","-c","#(nop) ","CMD [\"/bin/sh\"]"],"Image":"sha256:eb2080c455e94c22ae35b3aef9e078c492a00795412e026e4d6b41ef64bc7dd8","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":{}},"created":"2021-04-14T19:20:05.338397761Z","docker_version":"19.03.12","history":[{"created":"2021-04-14T19:20:04.987219124Z","created_by":"/bin/sh -c #(nop) ADD file:c5377eaa926bf412dd8d4a08b0a1f2399cfd708743533b0aa03b53d14cb4bb4e in / "},{"created":"2021-04-14T19:20:05.338397761Z","created_by":"/bin/sh -c #(nop)  CMD [\"/bin/sh\"]","empty_layer":true}],"os":"linux","rootfs":{"type":"layers","diff_ids":["sha256:9fb3aa2f8b8023a4bebbf92aa567caf88e38e969ada9f0ac12643b2847391635"]}}`),
   101  					RepoDigests: []string{
   102  						"index.docker." +
   103  							"io/library/alpine@sha256:451eee8bedcb2f029756dc3e9d73bab0e7943c1ac55cff3a4861c52a0fdd3e98",
   104  					},
   105  				},
   106  			},
   107  		},
   108  		{
   109  			name: "file",
   110  			input: []byte(`{
   111  				"id": "foobar",
   112  				"type": "file",
   113  				"metadata": {
   114  					"path": "/var/lib/foo/go.mod",
   115  					"mimeType": "text/plain",
   116  					"digests": [
   117  						{
   118  							"algorithm": "sha256",
   119  							"value": 	 "e7b300aee9f9bf3433d32bc9305bfdd22183beb59d933b48d77ab56ba53a197a"
   120  						}
   121  					]
   122  				}
   123  			}`),
   124  			expected: &Source{
   125  				ID:   "foobar",
   126  				Type: "file",
   127  				Metadata: source.FileSourceMetadata{
   128  					Path: "/var/lib/foo/go.mod",
   129  					Digests: []file.Digest{
   130  						{
   131  							Algorithm: "sha256",
   132  							Value:     "e7b300aee9f9bf3433d32bc9305bfdd22183beb59d933b48d77ab56ba53a197a",
   133  						},
   134  					},
   135  					MIMEType: "text/plain",
   136  				},
   137  			},
   138  		},
   139  		{
   140  			name: "unknown source type",
   141  			input: []byte(`{
   142  				"id": "foobar",
   143  				"type": "unknown-thing",
   144  				"target":"/var/lib/foo"
   145  			}`),
   146  			expected: &Source{
   147  				ID:   "foobar",
   148  				Type: "unknown-thing",
   149  			},
   150  			wantErr: require.Error,
   151  		},
   152  	}
   153  
   154  	for _, tt := range cases {
   155  		t.Run(tt.name, func(t *testing.T) {
   156  			if tt.wantErr == nil {
   157  				tt.wantErr = require.NoError
   158  			}
   159  
   160  			var src Source
   161  			err := json.Unmarshal(tt.input, &src)
   162  
   163  			tt.wantErr(t, err)
   164  
   165  			if diff := cmp.Diff(tt.expected, &src); diff != "" {
   166  				t.Errorf("unexpected result from Source unmarshaling (-want +got)\n%s", diff)
   167  			}
   168  
   169  			tracker.Tested(t, tt.expected.Metadata)
   170  		})
   171  	}
   172  }
   173  
   174  func TestSource_UnmarshalJSON_PreSchemaV9(t *testing.T) {
   175  	cases := []struct {
   176  		name           string
   177  		input          []byte
   178  		expectedSource *Source
   179  		errAssertion   assert.ErrorAssertionFunc
   180  	}{
   181  		{
   182  			name: "abbreviated directory",
   183  			input: []byte(`{
   184  				"id": "foobar",
   185  				"type": "dir",
   186  				"target":"/var/lib/foo"
   187  			}`),
   188  			expectedSource: &Source{
   189  				ID:   "foobar",
   190  				Type: "directory",
   191  				Metadata: source.DirectorySourceMetadata{
   192  					Path: "/var/lib/foo",
   193  				},
   194  			},
   195  			errAssertion: assert.NoError,
   196  		},
   197  		{
   198  			name: "directory",
   199  			input: []byte(`{
   200  				"id": "foobar",
   201  				"type": "directory",
   202  				"target":"/var/lib/foo"
   203  			}`),
   204  			expectedSource: &Source{
   205  				ID:   "foobar",
   206  				Type: "directory",
   207  				Metadata: source.DirectorySourceMetadata{
   208  					Path: "/var/lib/foo",
   209  				},
   210  			},
   211  			errAssertion: assert.NoError,
   212  		},
   213  		{
   214  			name: "image",
   215  			input: []byte(`{
   216  				"id": "foobar",
   217  				"type": "image",
   218  				"target": {
   219  					"userInput": "alpine:3.10",
   220  					"imageID": "sha256:e7b300aee9f9bf3433d32bc9305bfdd22183beb59d933b48d77ab56ba53a197a",
   221  					"manifestDigest": "sha256:e515aad2ed234a5072c4d2ef86a1cb77d5bfe4b11aa865d9214875734c4eeb3c",
   222  					"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
   223  					"tags": [],
   224  					"imageSize": 5576169,
   225  					"layers": [
   226  						{
   227  							"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
   228  							"digest": "sha256:9fb3aa2f8b8023a4bebbf92aa567caf88e38e969ada9f0ac12643b2847391635",
   229  							"size": 5576169
   230  						}
   231  					],
   232  					"manifest": "ewogICAic2NoZW1hVmVyc2lvbiI6IDIsCiAgICJtZWRpYVR5cGUiOiAiYXBwbGljYXRpb24vdm5kLmRvY2tlci5kaXN0cmlidXRpb24ubWFuaWZlc3QudjIranNvbiIsCiAgICJjb25maWciOiB7CiAgICAgICJtZWRpYVR5cGUiOiAiYXBwbGljYXRpb24vdm5kLmRvY2tlci5jb250YWluZXIuaW1hZ2UudjEranNvbiIsCiAgICAgICJzaXplIjogMTQ3MiwKICAgICAgImRpZ2VzdCI6ICJzaGEyNTY6ZTdiMzAwYWVlOWY5YmYzNDMzZDMyYmM5MzA1YmZkZDIyMTgzYmViNTlkOTMzYjQ4ZDc3YWI1NmJhNTNhMTk3YSIKICAgfSwKICAgImxheWVycyI6IFsKICAgICAgewogICAgICAgICAibWVkaWFUeXBlIjogImFwcGxpY2F0aW9uL3ZuZC5kb2NrZXIuaW1hZ2Uucm9vdGZzLmRpZmYudGFyLmd6aXAiLAogICAgICAgICAic2l6ZSI6IDI3OTgzMzgsCiAgICAgICAgICJkaWdlc3QiOiAic2hhMjU2OjM5NmMzMTgzNzExNmFjMjkwNDU4YWZjYjkyOGY2OGI2Y2MxYzdiZGQ2OTYzZmM3MmY1MmYzNjVhMmE4OWMxYjUiCiAgICAgIH0KICAgXQp9",
   233  					"config": "eyJhcmNoaXRlY3R1cmUiOiJhbWQ2NCIsImNvbmZpZyI6eyJIb3N0bmFtZSI6IiIsIkRvbWFpbm5hbWUiOiIiLCJVc2VyIjoiIiwiQXR0YWNoU3RkaW4iOmZhbHNlLCJBdHRhY2hTdGRvdXQiOmZhbHNlLCJBdHRhY2hTdGRlcnIiOmZhbHNlLCJUdHkiOmZhbHNlLCJPcGVuU3RkaW4iOmZhbHNlLCJTdGRpbk9uY2UiOmZhbHNlLCJFbnYiOlsiUEFUSD0vdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4iXSwiQ21kIjpbIi9iaW4vc2giXSwiSW1hZ2UiOiJzaGEyNTY6ZWIyMDgwYzQ1NWU5NGMyMmFlMzViM2FlZjllMDc4YzQ5MmEwMDc5NTQxMmUwMjZlNGQ2YjQxZWY2NGJjN2RkOCIsIlZvbHVtZXMiOm51bGwsIldvcmtpbmdEaXIiOiIiLCJFbnRyeXBvaW50IjpudWxsLCJPbkJ1aWxkIjpudWxsLCJMYWJlbHMiOm51bGx9LCJjb250YWluZXIiOiJmZGI3ZTgwZTMzMzllOGQwNTk5MjgyZTYwNmM5MDdhYTU4ODFlZTRjNjY4YTY4MTM2MTE5ZTZkZmFjNmNlM2E0IiwiY29udGFpbmVyX2NvbmZpZyI6eyJIb3N0bmFtZSI6ImZkYjdlODBlMzMzOSIsIkRvbWFpbm5hbWUiOiIiLCJVc2VyIjoiIiwiQXR0YWNoU3RkaW4iOmZhbHNlLCJBdHRhY2hTdGRvdXQiOmZhbHNlLCJBdHRhY2hTdGRlcnIiOmZhbHNlLCJUdHkiOmZhbHNlLCJPcGVuU3RkaW4iOmZhbHNlLCJTdGRpbk9uY2UiOmZhbHNlLCJFbnYiOlsiUEFUSD0vdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4iXSwiQ21kIjpbIi9iaW4vc2giLCItYyIsIiMobm9wKSAiLCJDTUQgW1wiL2Jpbi9zaFwiXSJdLCJJbWFnZSI6InNoYTI1NjplYjIwODBjNDU1ZTk0YzIyYWUzNWIzYWVmOWUwNzhjNDkyYTAwNzk1NDEyZTAyNmU0ZDZiNDFlZjY0YmM3ZGQ4IiwiVm9sdW1lcyI6bnVsbCwiV29ya2luZ0RpciI6IiIsIkVudHJ5cG9pbnQiOm51bGwsIk9uQnVpbGQiOm51bGwsIkxhYmVscyI6e319LCJjcmVhdGVkIjoiMjAyMS0wNC0xNFQxOToyMDowNS4zMzgzOTc3NjFaIiwiZG9ja2VyX3ZlcnNpb24iOiIxOS4wMy4xMiIsImhpc3RvcnkiOlt7ImNyZWF0ZWQiOiIyMDIxLTA0LTE0VDE5OjIwOjA0Ljk4NzIxOTEyNFoiLCJjcmVhdGVkX2J5IjoiL2Jpbi9zaCAtYyAjKG5vcCkgQUREIGZpbGU6YzUzNzdlYWE5MjZiZjQxMmRkOGQ0YTA4YjBhMWYyMzk5Y2ZkNzA4NzQzNTMzYjBhYTAzYjUzZDE0Y2I0YmI0ZSBpbiAvICJ9LHsiY3JlYXRlZCI6IjIwMjEtMDQtMTRUMTk6MjA6MDUuMzM4Mzk3NzYxWiIsImNyZWF0ZWRfYnkiOiIvYmluL3NoIC1jICMobm9wKSAgQ01EIFtcIi9iaW4vc2hcIl0iLCJlbXB0eV9sYXllciI6dHJ1ZX1dLCJvcyI6ImxpbnV4Iiwicm9vdGZzIjp7InR5cGUiOiJsYXllcnMiLCJkaWZmX2lkcyI6WyJzaGEyNTY6OWZiM2FhMmY4YjgwMjNhNGJlYmJmOTJhYTU2N2NhZjg4ZTM4ZTk2OWFkYTlmMGFjMTI2NDNiMjg0NzM5MTYzNSJdfX0=",
   234  					"repoDigests": [
   235  						"index.docker.io/library/alpine@sha256:451eee8bedcb2f029756dc3e9d73bab0e7943c1ac55cff3a4861c52a0fdd3e98"
   236  					]
   237  				}
   238  			}`),
   239  			expectedSource: &Source{
   240  				ID:   "foobar",
   241  				Type: "image",
   242  				Metadata: source.StereoscopeImageSourceMetadata{
   243  					UserInput:      "alpine:3.10",
   244  					ID:             "sha256:e7b300aee9f9bf3433d32bc9305bfdd22183beb59d933b48d77ab56ba53a197a",
   245  					ManifestDigest: "sha256:e515aad2ed234a5072c4d2ef86a1cb77d5bfe4b11aa865d9214875734c4eeb3c",
   246  					MediaType:      "application/vnd.docker.distribution.manifest.v2+json",
   247  					Tags:           []string{},
   248  					Size:           5576169,
   249  					Layers: []source.StereoscopeLayerMetadata{
   250  						{
   251  							MediaType: "application/vnd.docker.image.rootfs.diff.tar.gzip",
   252  							Digest:    "sha256:9fb3aa2f8b8023a4bebbf92aa567caf88e38e969ada9f0ac12643b2847391635",
   253  							Size:      5576169,
   254  						},
   255  					},
   256  					RawManifest: []byte(`{
   257     "schemaVersion": 2,
   258     "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
   259     "config": {
   260        "mediaType": "application/vnd.docker.container.image.v1+json",
   261        "size": 1472,
   262        "digest": "sha256:e7b300aee9f9bf3433d32bc9305bfdd22183beb59d933b48d77ab56ba53a197a"
   263     },
   264     "layers": [
   265        {
   266           "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
   267           "size": 2798338,
   268           "digest": "sha256:396c31837116ac290458afcb928f68b6cc1c7bdd6963fc72f52f365a2a89c1b5"
   269        }
   270     ]
   271  }`),
   272  					RawConfig: []byte(`{"architecture":"amd64","config":{"Hostname":"","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":["/bin/sh"],"Image":"sha256:eb2080c455e94c22ae35b3aef9e078c492a00795412e026e4d6b41ef64bc7dd8","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":null},"container":"fdb7e80e3339e8d0599282e606c907aa5881ee4c668a68136119e6dfac6ce3a4","container_config":{"Hostname":"fdb7e80e3339","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":["/bin/sh","-c","#(nop) ","CMD [\"/bin/sh\"]"],"Image":"sha256:eb2080c455e94c22ae35b3aef9e078c492a00795412e026e4d6b41ef64bc7dd8","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":{}},"created":"2021-04-14T19:20:05.338397761Z","docker_version":"19.03.12","history":[{"created":"2021-04-14T19:20:04.987219124Z","created_by":"/bin/sh -c #(nop) ADD file:c5377eaa926bf412dd8d4a08b0a1f2399cfd708743533b0aa03b53d14cb4bb4e in / "},{"created":"2021-04-14T19:20:05.338397761Z","created_by":"/bin/sh -c #(nop)  CMD [\"/bin/sh\"]","empty_layer":true}],"os":"linux","rootfs":{"type":"layers","diff_ids":["sha256:9fb3aa2f8b8023a4bebbf92aa567caf88e38e969ada9f0ac12643b2847391635"]}}`),
   273  					RepoDigests: []string{
   274  						"index.docker." +
   275  							"io/library/alpine@sha256:451eee8bedcb2f029756dc3e9d73bab0e7943c1ac55cff3a4861c52a0fdd3e98",
   276  					},
   277  				},
   278  			},
   279  			errAssertion: assert.NoError,
   280  		},
   281  		{
   282  			name: "file",
   283  			input: []byte(`{
   284  				"id": "foobar",
   285  				"type": "file",
   286  				"target":"/var/lib/foo/go.mod"
   287  			}`),
   288  			expectedSource: &Source{
   289  				ID:   "foobar",
   290  				Type: "file",
   291  				Metadata: source.FileSourceMetadata{
   292  					Path: "/var/lib/foo/go.mod",
   293  				},
   294  			},
   295  			errAssertion: assert.NoError,
   296  		},
   297  		{
   298  			name: "unknown source type",
   299  			input: []byte(`{
   300  				"id": "foobar",
   301  				"type": "unknown-thing",
   302  				"target":"/var/lib/foo"
   303  			}`),
   304  			expectedSource: &Source{
   305  				ID:   "foobar",
   306  				Type: "unknown-thing",
   307  			},
   308  			errAssertion: assert.Error,
   309  		},
   310  	}
   311  
   312  	for _, testCase := range cases {
   313  		t.Run(testCase.name, func(t *testing.T) {
   314  			src := new(Source)
   315  
   316  			err := json.Unmarshal(testCase.input, src)
   317  
   318  			testCase.errAssertion(t, err)
   319  			if diff := cmp.Diff(testCase.expectedSource, src); diff != "" {
   320  				t.Errorf("unexpected result from Source unmarshaling (-want +got)\n%s", diff)
   321  			}
   322  		})
   323  	}
   324  }