Skip to content

Commit 574ff1f

Browse files
committed
fix: fallback to tag if digest missing
1 parent defb054 commit 574ff1f

File tree

6 files changed

+56
-25
lines changed

6 files changed

+56
-25
lines changed

src/scanner/images/index.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,15 @@ export async function pullImages(images: IPullableImage[]): Promise<IPullableIma
1010
const pulledImages: IPullableImage[] = [];
1111

1212
for (const image of images) {
13-
const {imageWithDigest, fileSystemPath} = image;
13+
const { imageName, imageWithDigest, fileSystemPath } = image;
1414
if (!fileSystemPath) {
1515
continue;
1616
}
1717

1818
try {
19-
await skopeoCopy(imageWithDigest, fileSystemPath);
19+
// Scan image by digest if exists, other way fallback tag
20+
const scanId = imageWithDigest ?? imageName;
21+
await skopeoCopy(scanId, fileSystemPath);
2022
pulledImages.push(image);
2123
} catch (error) {
2224
logger.error({error, image: imageWithDigest}, 'failed to pull image');
@@ -93,11 +95,12 @@ export async function scanImages(images: IPullableImage[]): Promise<IScanResult[
9395
}
9496

9597
const imageParts = getImageParts(imageName);
98+
const imageDigest = imageWithDigest && getImageParts(imageWithDigest).imageDigest;
9699

97100
result.imageMetadata = {
98101
image: imageParts.imageName,
99102
imageTag: imageParts.imageTag,
100-
imageDigest: getImageParts(imageWithDigest).imageDigest,
103+
imageDigest,
101104
};
102105

103106
scannedImages.push({

src/scanner/images/types.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
export interface IScanImage {
22
imageName: string;
3-
imageWithDigest: string;
3+
imageWithDigest?: string;
44
}
55

66
export interface IPullableImage {
77
imageName: string;
8-
imageWithDigest: string;
8+
imageWithDigest?: string;
99
fileSystemPath: string;
1010
}
1111

src/scanner/index.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ 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 = getUniqueImages(workloadMetadata);
11+
const uniqueImages: IScanImage[] = getUniqueImages(workloadMetadata);
1212

1313
logger.info({ workloadName, imageCount: uniqueImages.length }, 'pulling unique images');
1414
const imagesWithFileSystemPath = getImagesWithFileSystemPath(uniqueImages);
@@ -35,6 +35,11 @@ export async function sendDeleteWorkloadRequest(workloadName: string, localWorkl
3535

3636
export function getUniqueImages(workloadMetadata: IWorkload[]): IScanImage[] {
3737
const uniqueImages: { [key: string]: IScanImage } = workloadMetadata.reduce((accum, meta) => {
38+
logger.info({
39+
workloadName: workloadMetadata[0].name,
40+
name: meta.imageName,
41+
id: meta.imageId
42+
}, 'image metadata');
3843
// example: For DCR "redis:latest"
3944
// example: For GCR "gcr.io/test-dummy/redis:latest"
4045
// example: For ECR "291964488713.dkr.ecr.us-east-2.amazonaws.com/snyk/redis:latest"
@@ -44,11 +49,13 @@ export function getUniqueImages(workloadMetadata: IWorkload[]): IScanImage[] {
4449
// example: For DCR "docker.io/library/redis@sha256:8e9f8546050da8aae393a41d65ad37166b4f0d8131d627a520c0f0451742e9d6"
4550
// example: For GCR "sha256:8e9f8546050da8aae393a41d65ad37166b4f0d8131d627a520c0f0451742e9d6"
4651
// example: For ECR "sha256:8e9f8546050da8aae393a41d65ad37166b4f0d8131d627a520c0f0451742e9d6"
47-
const digest = meta.imageId.substring(meta.imageId.lastIndexOf('@') + 1);
48-
const imageWithDigest = `${imageName}@${digest}`;
52+
let digest: string | undefined = undefined;
53+
if (meta.imageId.lastIndexOf('@') > -1 || meta.imageId.startsWith('sha')) {
54+
digest = meta.imageId.substring(meta.imageId.lastIndexOf('@') + 1);
55+
}
4956

50-
accum[imageWithDigest] = {
51-
imageWithDigest,
57+
accum[meta.imageName] = {
58+
imageWithDigest: digest && `${imageName}@${digest}`,
5259
imageName: meta.imageName, // Image name with tag
5360
};
5461

src/scanner/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
export interface IScanResult {
22
image: string;
3-
imageWithDigest: string;
3+
imageWithDigest?: string;
44
imageWithTag: string;
55
pluginResult: any;
66
}

src/transmitter/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export interface IWorkloadMetadata {
2525

2626
export interface IImageLocator extends IWorkloadLocator {
2727
imageId: string;
28-
imageWithDigest: string;
28+
imageWithDigest?: string;
2929
}
3030

3131
export interface IKubernetesMonitorMetadata {

test/unit/scanner/index.test.ts

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,15 @@ tap.test('getUniqueImages()', async (t) => {
1515
imageName: 'redis:latest',
1616
imageId: 'docker.io/library/redis@sha256:8e9f8546050da8aae393a41d65ad37166b4f0d8131d627a520c0f0451742e9d6'
1717
},
18-
// 3. Duplicate without tag
18+
// 3. With SHA instead of tag
1919
{
20-
imageName: 'redis',
20+
imageName: 'redis@sha256:8e9f8546050da8aae393a41d65ad37166b4f0d8131d627a520c0f0451742e9d6',
2121
imageId: 'docker.io/library/redis@sha256:8e9f8546050da8aae393a41d65ad37166b4f0d8131d627a520c0f0451742e9d6'
2222
},
23-
// 4. Duplicate with SHA instead of tag
23+
// 4. With SHA missing in imageId
2424
{
25-
imageName: 'redis@sha256:8e9f8546050da8aae393a41d65ad37166b4f0d8131d627a520c0f0451742e9d6',
26-
imageId: 'docker.io/library/redis@sha256:8e9f8546050da8aae393a41d65ad37166b4f0d8131d627a520c0f0451742e9d6'
25+
imageName: 'redis:prod',
26+
imageId: 'docker.io/library/redis:eaa6f054e4a140bc3a1696cc7b1e84529e7e9567',
2727
},
2828
// 5. GCR
2929
{
@@ -35,21 +35,42 @@ tap.test('getUniqueImages()', async (t) => {
3535
imageName: '291964488713.dkr.ecr.us-east-2.amazonaws.com/snyk/redis:latest',
3636
imageId: 'sha256:8e9f8546050da8aae393a41d65ad37166b4f0d8131d627a520c0f0451742e9d6'
3737
},
38+
// 7. With docker-pullable as protocol in imageId
39+
{
40+
imageName: 'redis:some-tag',
41+
imageId: 'docker-pullable://name@sha256:8e9f8546050da8aae393a41d65ad37166b4f0d8131d627a520c0f0451742e9d6',
42+
},
43+
// 8. With docker as protocol in imageId
44+
{
45+
imageName: 'redis:another-tag',
46+
imageId: 'docker://name@sha256:8e9f8546050da8aae393a41d65ad37166b4f0d8131d627a520c0f0451742e9d6'
47+
}
3848
];
3949

4050
const result = scanner.getUniqueImages(workload as any);
4151

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');
52+
t.strictEqual(result.length, 7, 'removed duplicate image');
53+
const resultWithDigest = result.filter(({imageWithDigest}) => imageWithDigest);
54+
const resultWithoutDigest = result.filter(({imageWithDigest}) => !imageWithDigest);
55+
56+
t.strictEqual(resultWithDigest.length, 6, 'correct amount with digest');
57+
t.strictEqual(resultWithoutDigest.length, 1, 'correct amount without digest');
58+
59+
resultWithDigest.map((metaData) => {
60+
t.ok(metaData.imageWithDigest!.includes('redis'), 'has name in imageWithDigest');
61+
t.ok(metaData.imageWithDigest!.includes('sha256:8e9f8546050da8aae393a41d65ad37166b4f0d8131d627a520c0f0451742e9d6'), 'has digest');
4662

47-
if (metaData.imageWithDigest.includes('gcr')) {
48-
t.ok(metaData.imageWithDigest.includes('/'), 'contains / in GCR imageWithDigest');
63+
if (metaData.imageWithDigest!.includes('gcr')) {
64+
t.ok(metaData.imageWithDigest!.includes('/'), 'contains / in GCR imageWithDigest');
4965
}
5066

51-
if (metaData.imageWithDigest.includes('ecr')) {
52-
t.ok(metaData.imageWithDigest.includes('/'), 'contains / in ECR imageWithDigest');
67+
if (metaData.imageWithDigest!.includes('ecr')) {
68+
t.ok(metaData.imageWithDigest!.includes('/'), 'contains / in ECR imageWithDigest');
5369
}
5470
});
71+
72+
resultWithoutDigest.map((metadata) => {
73+
t.ok(metadata.imageName.includes('redis'));
74+
t.notOk(metadata.imageWithDigest);
75+
});
5576
});

0 commit comments

Comments
 (0)