github.com/anchore/syft@v1.4.2-0.20240516191711-1bec1fc5d397/syft/source/stereoscopesource/image_source_test.go (about) 1 package stereoscopesource 2 3 import ( 4 "context" 5 "crypto/sha256" 6 "fmt" 7 "strings" 8 "testing" 9 10 "github.com/stretchr/testify/assert" 11 "github.com/stretchr/testify/require" 12 13 "github.com/anchore/stereoscope" 14 "github.com/anchore/stereoscope/pkg/imagetest" 15 "github.com/anchore/syft/syft/artifact" 16 "github.com/anchore/syft/syft/internal/testutil" 17 "github.com/anchore/syft/syft/source" 18 ) 19 20 func Test_StereoscopeImage_Exclusions(t *testing.T) { 21 testutil.Chdir(t, "..") // run with source/test-fixtures 22 23 testCases := []struct { 24 desc string 25 input string 26 glob string 27 expected int 28 exclusions []string 29 }{ 30 // NOTE: in the Dockerfile, /target is moved to /, which makes /really a top-level dir 31 { 32 input: "image-simple", 33 desc: "a single path excluded", 34 glob: "**", 35 expected: 2, 36 exclusions: []string{"/really/**"}, 37 }, 38 { 39 input: "image-simple", 40 desc: "a directly referenced directory is excluded", 41 glob: "**", 42 expected: 2, 43 exclusions: []string{"/really"}, 44 }, 45 { 46 input: "image-simple", 47 desc: "a partial directory is not excluded", 48 glob: "**", 49 expected: 3, 50 exclusions: []string{"/reall"}, 51 }, 52 { 53 input: "image-simple", 54 desc: "exclude files deeper", 55 glob: "**", 56 expected: 2, 57 exclusions: []string{"**/nested/**"}, 58 }, 59 { 60 input: "image-simple", 61 desc: "files excluded with extension", 62 glob: "**", 63 expected: 2, 64 exclusions: []string{"**/*1.txt"}, 65 }, 66 { 67 input: "image-simple", 68 desc: "keep files with different extensions", 69 glob: "**", 70 expected: 3, 71 exclusions: []string{"**/target/**/*.jar"}, 72 }, 73 { 74 input: "image-simple", 75 desc: "file directly excluded", 76 glob: "**", 77 expected: 2, 78 exclusions: []string{"**/somefile-1.txt"}, // file-1 renamed to somefile-1 in Dockerfile 79 }, 80 } 81 82 for _, test := range testCases { 83 t.Run(test.desc, func(t *testing.T) { 84 imageName := strings.SplitN(imagetest.PrepareFixtureImage(t, "docker-archive", test.input), ":", 2)[1] 85 86 img, err := stereoscope.GetImage(context.TODO(), imageName) 87 require.NoError(t, err) 88 require.NotNil(t, img) 89 90 src := New( 91 img, 92 ImageConfig{ 93 Reference: imageName, 94 Exclude: source.ExcludeConfig{ 95 Paths: test.exclusions, 96 }, 97 }, 98 ) 99 100 t.Cleanup(func() { 101 require.NoError(t, src.Close()) 102 }) 103 104 res, err := src.FileResolver(source.SquashedScope) 105 require.NoError(t, err) 106 107 contents, err := res.FilesByGlob(test.glob) 108 require.NoError(t, err) 109 110 assert.Len(t, contents, test.expected) 111 }) 112 } 113 } 114 115 func Test_StereoscopeImageSource_ID(t *testing.T) { 116 tests := []struct { 117 name string 118 alias source.Alias 119 metadata source.ImageMetadata 120 want artifact.ID 121 }{ 122 { 123 name: "use raw manifest over chain ID or user input", 124 metadata: source.ImageMetadata{ 125 UserInput: "user-input", 126 Layers: []source.LayerMetadata{ 127 { 128 Digest: "a", 129 }, 130 { 131 Digest: "b", 132 }, 133 { 134 Digest: "c", 135 }, 136 }, 137 RawManifest: []byte("raw-manifest"), 138 }, 139 want: func() artifact.ID { 140 hasher := sha256.New() 141 hasher.Write([]byte("raw-manifest")) 142 return artifact.ID(fmt.Sprintf("%x", hasher.Sum(nil))) 143 }(), 144 }, 145 { 146 name: "use chain ID over user input", 147 metadata: source.ImageMetadata{ 148 //UserInput: "user-input", 149 Layers: []source.LayerMetadata{ 150 { 151 Digest: "a", 152 }, 153 { 154 Digest: "b", 155 }, 156 { 157 Digest: "c", 158 }, 159 }, 160 }, 161 want: func() artifact.ID { 162 metadata := []source.LayerMetadata{ 163 { 164 Digest: "a", 165 }, 166 { 167 Digest: "b", 168 }, 169 { 170 Digest: "c", 171 }, 172 } 173 return artifact.ID(strings.TrimPrefix(calculateChainID(metadata), "sha256:")) 174 }(), 175 }, 176 { 177 name: "use user input last", 178 metadata: source.ImageMetadata{ 179 UserInput: "user-input", 180 }, 181 want: func() artifact.ID { 182 hasher := sha256.New() 183 hasher.Write([]byte("user-input")) 184 return artifact.ID(fmt.Sprintf("%x", hasher.Sum(nil))) 185 }(), 186 }, 187 { 188 name: "without alias (first)", 189 metadata: source.ImageMetadata{ 190 UserInput: "user-input", 191 Layers: []source.LayerMetadata{ 192 { 193 Digest: "a", 194 }, 195 { 196 Digest: "b", 197 }, 198 { 199 Digest: "c", 200 }, 201 }, 202 RawManifest: []byte("raw-manifest"), 203 }, 204 want: "85298926ecd92ed57688f13039017160cd728f04dd0d2d10a10629007106f107", 205 }, 206 { 207 name: "always consider alias (first)", 208 alias: source.Alias{ 209 Name: "alias", 210 Version: "version", 211 }, 212 metadata: source.ImageMetadata{ 213 UserInput: "user-input", 214 Layers: []source.LayerMetadata{ 215 { 216 Digest: "a", 217 }, 218 { 219 Digest: "b", 220 }, 221 { 222 Digest: "c", 223 }, 224 }, 225 RawManifest: []byte("raw-manifest"), 226 }, 227 want: "a8717e42449960c1dd4963f2f22bd69c7c105e7e82445be0a65aa1825d62ff0d", 228 }, 229 { 230 name: "without alias (last)", 231 metadata: source.ImageMetadata{ 232 UserInput: "user-input", 233 }, 234 want: "ab0dff627d80b9753193d7280bec8f45e8ec6b4cb0912c6fffcf7cd782d9739e", 235 }, 236 { 237 name: "always consider alias (last)", 238 alias: source.Alias{ 239 Name: "alias", 240 Version: "version", 241 }, 242 metadata: source.ImageMetadata{ 243 UserInput: "user-input", 244 }, 245 want: "fe86c0eecd5654d3c0c0b2176aa394aef6440347c241aa8d9b628dfdde4287cf", 246 }, 247 } 248 for _, tt := range tests { 249 t.Run(tt.name, func(t *testing.T) { 250 assert.Equal(t, tt.want, deriveIDFromStereoscopeImage(tt.alias, tt.metadata)) 251 }) 252 } 253 } 254 255 func Test_Describe(t *testing.T) { 256 tests := []struct { 257 name string 258 source stereoscopeImageSource 259 expected source.Description 260 }{ 261 { 262 name: "name from user input", 263 source: stereoscopeImageSource{ 264 id: "some-id", 265 metadata: source.ImageMetadata{ 266 UserInput: "user input", 267 }, 268 }, 269 expected: source.Description{ 270 ID: "some-id", 271 Name: "user input", 272 }, 273 }, 274 } 275 276 for _, test := range tests { 277 got := test.source.Describe() 278 got.Metadata = nil // might want to test this, but do not to determine if the user input is userd 279 require.Equal(t, test.expected, got) 280 } 281 }