@@ -36,6 +36,7 @@ import (
3636 _ "github.com/distribution/distribution/v3/registry/auth/htpasswd"
3737 _ "github.com/distribution/distribution/v3/registry/storage/driver/inmemory"
3838 "github.com/foxcpp/go-mockdns"
39+ ocispec "github.com/opencontainers/image-spec/specs-go/v1"
3940 "github.com/phayes/freeport"
4041 "github.com/stretchr/testify/suite"
4142 "golang.org/x/crypto/bcrypt"
6162type TestSuite struct {
6263 suite.Suite
6364 Out io.Writer
65+ FakeRegistryHost string
6466 DockerRegistryHost string
6567 CompromisedRegistryHost string
6668 WorkspaceDir string
@@ -168,6 +170,7 @@ func setup(suite *TestSuite, tlsEnabled, insecure bool) *registry.Registry {
168170 dockerRegistry , err := registry .NewRegistry (context .Background (), config )
169171 suite .Nil (err , "no error creating test registry" )
170172
173+ suite .FakeRegistryHost = initFakeRegistryTestServer ()
171174 suite .CompromisedRegistryHost = initCompromisedRegistryTestServer ()
172175 return dockerRegistry
173176}
@@ -218,6 +221,173 @@ func initCompromisedRegistryTestServer() string {
218221 return fmt .Sprintf ("localhost:%s" , u .Port ())
219222}
220223
224+ func initFakeRegistryTestServer () string {
225+ s := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
226+ switch r .URL .Path {
227+ case "/v2/testrepo/image-index/manifests/0.1.0" :
228+ w .Header ().Set ("Content-Type" , ocispec .MediaTypeImageIndex )
229+ w .Write ([]byte (`{
230+ "schemaVersion": 2,
231+ "mediaType": "application/vnd.oci.image.index.v1+json",
232+ "manifests": [
233+ {
234+ "mediaType": "application/vnd.oci.image.manifest.v1+json",
235+ "digest": "sha256:2771e37a12b7bcb2902456ecf3f29bf9ee11ec348e66e8eb322d9780ad7fc2df",
236+ "size": 1035,
237+ "platform": {
238+ "architecture": "amd64",
239+ "os": "linux"
240+ },
241+ "annotations": {
242+ "com.docker.official-images.bashbrew.arch": "amd64",
243+ "org.opencontainers.image.base.name": "scratch",
244+ "org.opencontainers.image.created": "2025-08-13T22:16:57Z",
245+ "org.opencontainers.image.revision": "6930d60e10e81283a57be3ee3a2b5ca328a40304",
246+ "org.opencontainers.image.source": "https://github.com/docker-library/hello-world.git#6930d60e10e81283a57be3ee3a2b5ca328a40304:amd64/hello-world",
247+ "org.opencontainers.image.url": "https://hub.docker.com/_/hello-world",
248+ "org.opencontainers.image.version": "linux"
249+ }
250+ },
251+ {
252+ "mediaType": "application/vnd.oci.image.manifest.v1+json",
253+ "digest": "sha256:6b75187531c5e9b6a85c8946d5d82e4ef3801e051fbff338f382f3edfa60e3d2",
254+ "size": 566,
255+ "platform": {
256+ "architecture": "unknown",
257+ "os": "unknown"
258+ },
259+ "annotations": {
260+ "com.docker.official-images.bashbrew.arch": "amd64",
261+ "vnd.docker.reference.digest": "sha256:2771e37a12b7bcb2902456ecf3f29bf9ee11ec348e66e8eb322d9780ad7fc2df",
262+ "vnd.docker.reference.type": "attestation-manifest"
263+ }
264+ },
265+ {
266+ "mediaType": "application/vnd.oci.image.manifest.v1+json",
267+ "digest": "sha256:7fbdc47de56b45d092f8f419e8b6183adf0159d00e05574c01787231b54fe28f",
268+ "size": 815
269+ }
270+ ]
271+ }` ))
272+
273+ case "/v2/testrepo/image-index/manifests/sha256:2771e37a12b7bcb2902456ecf3f29bf9ee11ec348e66e8eb322d9780ad7fc2df" :
274+ w .Header ().Set ("Content-Type" , ocispec .MediaTypeImageManifest )
275+ w .Write ([]byte (`{
276+ "schemaVersion": 2,
277+ "mediaType": "application/vnd.oci.image.manifest.v1+json",
278+ "config": {
279+ "mediaType": "application/vnd.oci.image.config.v1+json",
280+ "digest": "sha256:1b44b5a3e06a9aae883e7bf25e45c100be0bb81a0e01b32de604f3ac44711634",
281+ "size": 547
282+ },
283+ "layers": [
284+ {
285+ "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
286+ "digest": "sha256:17eec7bbc9d79fa397ac95c7283ecd04d1fe6978516932a3db110c6206430809",
287+ "size": 2380
288+ }
289+ ],
290+ "annotations": {
291+ "com.docker.official-images.bashbrew.arch": "amd64",
292+ "org.opencontainers.image.base.name": "scratch",
293+ "org.opencontainers.image.created": "2025-08-08T19:05:17Z",
294+ "org.opencontainers.image.revision": "6930d60e10e81283a57be3ee3a2b5ca328a40304",
295+ "org.opencontainers.image.source": "https://github.com/docker-library/hello-world.git#6930d60e10e81283a57be3ee3a2b5ca328a40304:amd64/hello-world",
296+ "org.opencontainers.image.url": "https://hub.docker.com/_/hello-world",
297+ "org.opencontainers.image.version": "linux"
298+ }
299+ }` ))
300+
301+ case "/v2/testrepo/image-index/manifests/sha256:6b75187531c5e9b6a85c8946d5d82e4ef3801e051fbff338f382f3edfa60e3d2" :
302+ w .Header ().Set ("Content-Type" , ocispec .MediaTypeImageManifest )
303+ w .Write ([]byte (`{
304+ "schemaVersion": 2,
305+ "mediaType": "application/vnd.oci.image.manifest.v1+json",
306+ "config": {
307+ "mediaType": "application/vnd.oci.image.config.v1+json",
308+ "digest": "sha256:ec4b6233950725be4c816667d1eb2782ad59dc65b12f7ac53f1ffa0ad5b95b5b",
309+ "size": 167
310+ },
311+ "layers": [
312+ {
313+ "mediaType": "application/vnd.in-toto+json",
314+ "digest": "sha256:ea52d2000f90ad63267302cba134025ee586b07a63c47aa9467471a395aee6c2",
315+ "size": 4822,
316+ "annotations": {
317+ "in-toto.io/predicate-type": "https://slsa.dev/provenance/v0.2"
318+ }
319+ }
320+ ]
321+ }` ))
322+
323+ case "/v2/testrepo/image-index/manifests/sha256:7fbdc47de56b45d092f8f419e8b6183adf0159d00e05574c01787231b54fe28f" :
324+ w .Header ().Set ("Content-Type" , ocispec .MediaTypeImageManifest )
325+ w .Write ([]byte (`{
326+ "schemaVersion": 2,
327+ "config": {
328+ "mediaType": "application/vnd.cncf.helm.config.v1+json",
329+ "digest": "sha256:24de43e4a9f5ed9427479f27dd7bab9d158227abe593302a6f54d1e13a903ac3",
330+ "size": 112
331+ },
332+ "layers": [
333+ {
334+ "mediaType": "application/vnd.cncf.helm.chart.provenance.v1.prov",
335+ "digest": "sha256:b0a02b7412f78ae93324d48df8fcc316d8482e5ad7827b5b238657a29a22f256",
336+ "size": 695
337+ },
338+ {
339+ "mediaType": "application/vnd.cncf.helm.chart.content.v1.tar+gzip",
340+ "digest": "sha256:e5ef611620fb97704d8751c16bab17fedb68883bfb0edc76f78a70e9173f9b55",
341+ "size": 973
342+ }
343+ ],
344+ "annotations": {
345+ "org.opencontainers.image.description": "A Helm chart for Kubernetes",
346+ "org.opencontainers.image.title": "signtest",
347+ "org.opencontainers.image.version": "0.1.0"
348+ }
349+ }` ))
350+
351+ case "/v2/testrepo/image-index/blobs/sha256:24de43e4a9f5ed9427479f27dd7bab9d158227abe593302a6f54d1e13a903ac3" :
352+ w .Header ().Set ("Content-Type" , ConfigMediaType )
353+ w .Write ([]byte (`{
354+ "name":"signtest",
355+ "version":"0.1.0",
356+ "description":"A Helm chart for Kubernetes",
357+ "apiVersion":"v1"
358+ }` ))
359+
360+ case "/v2/testrepo/image-index/blobs/sha256:b0a02b7412f78ae93324d48df8fcc316d8482e5ad7827b5b238657a29a22f256" :
361+ data , err := os .ReadFile ("../downloader/testdata/signtest-0.1.0.tgz.prov" )
362+ if err != nil {
363+ w .WriteHeader (http .StatusInternalServerError )
364+ w .Write ([]byte (err .Error ()))
365+ return
366+ }
367+
368+ w .Header ().Set ("Content-Type" , ProvLayerMediaType )
369+ w .Write (data )
370+
371+ case "/v2/testrepo/image-index/blobs/sha256:e5ef611620fb97704d8751c16bab17fedb68883bfb0edc76f78a70e9173f9b55" :
372+ data , err := os .ReadFile ("../downloader/testdata/signtest-0.1.0.tgz" )
373+ if err != nil {
374+ w .WriteHeader (http .StatusInternalServerError )
375+ w .Write ([]byte (err .Error ()))
376+ return
377+ }
378+
379+ w .Header ().Set ("Content-Type" , ChartLayerMediaType )
380+ w .Write (data )
381+
382+ default :
383+ w .WriteHeader (http .StatusNotFound )
384+ }
385+ }))
386+
387+ u , _ := url .Parse (s .URL )
388+ return fmt .Sprintf ("localhost:%s" , u .Port ())
389+ }
390+
221391func testPush (suite * TestSuite ) {
222392
223393 testingChartCreationTime := "1977-09-02T22:04:05Z"
0 commit comments