Skip to content

Commit 5d60a3b

Browse files
committed
fix: pulling by digest from private registries
1 parent 31c7e4e commit 5d60a3b

File tree

2 files changed

+85
-15
lines changed

2 files changed

+85
-15
lines changed

src/scanner/index.ts

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import logger = require('../common/logger');
2-
import { pullImages, removePulledImages, getImagesWithFileSystemPath, scanImages } from './images';
2+
import { pullImages, removePulledImages, getImagesWithFileSystemPath, scanImages, getImageParts } from './images';
33
import { deleteWorkload, sendDepGraph } from '../transmitter';
44
import { constructDeleteWorkload, constructDepGraph } from '../transmitter/payload';
55
import { IWorkload, ILocalWorkloadLocator } from '../transmitter/types';
@@ -8,23 +8,13 @@ import { IPullableImage, IScanImage } from './images/types';
88
export async function processWorkload(workloadMetadata: IWorkload[]): Promise<void> {
99
// every workload metadata references the same workload name, grab it from the first one
1010
const workloadName = workloadMetadata[0].name;
11-
const uniqueImages: { [key: string]: IScanImage } = workloadMetadata.reduce((accum, meta) => {
12-
// example: "docker.io/library/redis@sha256:33ca074e6019b451235735772a9c3e7216f014aae8eb0580d7e94834fe23efb3"
13-
const imageWithDigest = meta.imageId.substring(meta.imageId.lastIndexOf('/') + 1);
11+
const uniqueImages = getUniqueImages(workloadMetadata);
1412

15-
accum[meta.imageName] = {
16-
imageName: meta.imageName,
17-
imageWithDigest,
18-
};
19-
20-
return accum;
21-
}, {});
22-
23-
logger.info({workloadName, imageCount: Object.values(uniqueImages).length}, 'pulling unique images');
24-
const imagesWithFileSystemPath = getImagesWithFileSystemPath(Object.values(uniqueImages));
13+
logger.info({ workloadName, imageCount: uniqueImages.length }, 'pulling unique images');
14+
const imagesWithFileSystemPath = getImagesWithFileSystemPath(uniqueImages);
2515
const pulledImages = await pullImages(imagesWithFileSystemPath);
2616
if (pulledImages.length === 0) {
27-
logger.info({workloadName}, 'no images were pulled, halting scanner process.');
17+
logger.info({ workloadName }, 'no images were pulled, halting scanner process.');
2818
return;
2919
}
3020

@@ -43,6 +33,31 @@ export async function sendDeleteWorkloadRequest(workloadName: string, localWorkl
4333
await deleteWorkload(deletePayload);
4434
}
4535

36+
export function getUniqueImages(workloadMetadata: IWorkload[]): IScanImage[] {
37+
const uniqueImages: { [key: string]: IScanImage } = workloadMetadata.reduce((accum, meta) => {
38+
// example: For DCR "redis:latest"
39+
// example: For GCR "gcr.io/test-dummy/redis:latest"
40+
// example: For ECR "291964488713.dkr.ecr.us-east-2.amazonaws.com/snyk/redis:latest"
41+
// meta.imageName can be different depends on CR
42+
const { imageName } = getImageParts(meta.imageName);
43+
// meta.imageId can be different depends on CR
44+
// example: For DCR "docker.io/library/redis@sha256:8e9f8546050da8aae393a41d65ad37166b4f0d8131d627a520c0f0451742e9d6"
45+
// example: For GCR "sha256:8e9f8546050da8aae393a41d65ad37166b4f0d8131d627a520c0f0451742e9d6"
46+
// example: For ECR "sha256:8e9f8546050da8aae393a41d65ad37166b4f0d8131d627a520c0f0451742e9d6"
47+
const digest = meta.imageId.substring(meta.imageId.lastIndexOf('@') + 1);
48+
const imageWithDigest = `${imageName}@${digest}`;
49+
50+
accum[imageWithDigest] = {
51+
imageWithDigest,
52+
imageName: meta.imageName, // Image name with tag
53+
};
54+
55+
return accum;
56+
}, {});
57+
58+
return Object.values(uniqueImages);
59+
}
60+
4661
async function scanImagesAndSendResults(
4762
workloadName: string,
4863
pulledImages: IPullableImage[],

test/unit/scanner/index.test.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import * as tap from 'tap';
2+
3+
import * as scanner from '../../../src/scanner';
4+
import { IWorkload } from '../../../src/transmitter/types';
5+
6+
tap.test('getUniqueImages()', async (t) => {
7+
const workload: Partial<IWorkload>[] = [
8+
// 1.DCR
9+
{
10+
imageName: 'redis:latest',
11+
imageId: 'docker.io/library/redis@sha256:8e9f8546050da8aae393a41d65ad37166b4f0d8131d627a520c0f0451742e9d6'
12+
},
13+
// 2.Duplicate to verify uniqueness
14+
{
15+
imageName: 'redis:latest',
16+
imageId: 'docker.io/library/redis@sha256:8e9f8546050da8aae393a41d65ad37166b4f0d8131d627a520c0f0451742e9d6'
17+
},
18+
// 3. Duplicate without tag
19+
{
20+
imageName: 'redis',
21+
imageId: 'docker.io/library/redis@sha256:8e9f8546050da8aae393a41d65ad37166b4f0d8131d627a520c0f0451742e9d6'
22+
},
23+
// 4. Duplicate with SHA instead of tag
24+
{
25+
imageName: 'redis@sha256:8e9f8546050da8aae393a41d65ad37166b4f0d8131d627a520c0f0451742e9d6',
26+
imageId: 'docker.io/library/redis@sha256:8e9f8546050da8aae393a41d65ad37166b4f0d8131d627a520c0f0451742e9d6'
27+
},
28+
// 5. GCR
29+
{
30+
imageName: 'gcr.io/test-dummy/redis:latest',
31+
imageId: 'sha256:8e9f8546050da8aae393a41d65ad37166b4f0d8131d627a520c0f0451742e9d6'
32+
},
33+
// 6. ECR
34+
{
35+
imageName: '291964488713.dkr.ecr.us-east-2.amazonaws.com/snyk/redis:latest',
36+
imageId: 'sha256:8e9f8546050da8aae393a41d65ad37166b4f0d8131d627a520c0f0451742e9d6'
37+
},
38+
];
39+
40+
const result = scanner.getUniqueImages(workload as any);
41+
42+
t.strictEqual(result.length, 3, 'removed duplicate image');
43+
result.map((metaData) => {
44+
t.ok(metaData.imageWithDigest.includes('redis'), 'has name in imageWithDigest');
45+
t.ok(metaData.imageWithDigest.includes('sha256:8e9f8546050da8aae393a41d65ad37166b4f0d8131d627a520c0f0451742e9d6'), 'has digest');
46+
47+
if (metaData.imageWithDigest.includes('gcr')) {
48+
t.ok(metaData.imageWithDigest.includes('/'), 'contains / in GCR imageWithDigest');
49+
}
50+
51+
if (metaData.imageWithDigest.includes('ecr')) {
52+
t.ok(metaData.imageWithDigest.includes('/'), 'contains / in ECR imageWithDigest');
53+
}
54+
});
55+
});

0 commit comments

Comments
 (0)