Skip to content

Commit 3556f64

Browse files
Merge pull request #508 from snyk/fix/image-name-chopped-for-local-registries
Fix/image name chopped for local registries
2 parents e7f6631 + 7703933 commit 3556f64

File tree

3 files changed

+54
-34
lines changed

3 files changed

+54
-34
lines changed

src/scanner/images/index.ts

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -41,18 +41,31 @@ export async function removePulledImages(images: IPullableImage[]): Promise<void
4141
}
4242

4343
// Exported for testing
44-
export function removeTagFromImage(imageWithTag: string): string {
45-
return imageWithTag.split('@')[0].split(':')[0];
44+
export function getImageParts(imageWithTag: string) : {imageName: string, imageTag: string} {
45+
// we're matching pattern: <registry:port_number>(optional)/<image_name>(mandatory)@<tag_identifier>(optional):<image_tag>(optional)
46+
const regex = /((?:.*(:\d{4})?\/)?(?:[a-z0-9]+))([@|:].+)?/ig;
47+
const groups = regex.exec(imageWithTag);
48+
49+
if(!groups){
50+
logger.error({image: imageWithTag}, 'Image with tag is malformed, cannot extract valid parts');
51+
return {imageName: imageWithTag, imageTag: ''};
52+
}
53+
54+
return {
55+
imageName: groups[1],
56+
imageTag: validateAndExtractImageTag(groups[3])
57+
};
4658
}
4759

48-
// Exported for testing
49-
export function getImageTag(imageWithTag: string): string {
50-
const imageParts: string[] = imageWithTag.split(':');
51-
if (imageParts.length === 2) { // image@sha256:hash or image:tag
52-
return imageParts[1];
60+
function validateAndExtractImageTag(imageTagGroup: string | undefined): string {
61+
if(imageTagGroup === undefined){
62+
return '';
5363
}
5464

55-
return '';
65+
const imageTagParts: string[]= imageTagGroup.split(':');
66+
67+
//valid formats: image@sha256:hash or image:tag
68+
return imageTagParts.length === 2 ? imageTagParts[1] : '';
5669
}
5770

5871
// Exported for testing
@@ -84,13 +97,15 @@ export async function scanImages(images: IPullableImage[]): Promise<IScanResult[
8497
throw Error('Unexpected empty result from docker-plugin');
8598
}
8699

100+
const imageParts: {imageName: string, imageTag: string} = getImageParts(imageName);
101+
87102
result.imageMetadata = {
88-
image: removeTagFromImage(imageName),
89-
imageTag: getImageTag(imageName),
103+
image: imageParts.imageName,
104+
imageTag: imageParts.imageTag,
90105
};
91106

92107
scannedImages.push({
93-
image: removeTagFromImage(imageName),
108+
image: imageParts.imageName,
94109
imageWithTag: imageName,
95110
pluginResult: result,
96111
});

test/integration/kubernetes.test.ts

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -258,13 +258,13 @@ tap.test('snyk-monitor pulls images from a local registry and sends data to kube
258258
t.pass('Not testing local container registry because we\'re not running in KinD');
259259
return;
260260
}
261-
t.plan(4);
261+
t.plan(3);
262262

263263
const deploymentName = 'python-local';
264264
const namespace = 'services';
265265
const clusterName = 'Default cluster';
266266
const deploymentType = WorkloadKind.Deployment;
267-
const imageName = 'kind-registry:5000/python:rc-buster';
267+
const imageName = 'kind-registry:5000/python';
268268

269269
await kubectl.waitForJob('push-to-local-registry', 'default');
270270

@@ -288,13 +288,8 @@ tap.test('snyk-monitor pulls images from a local registry and sends data to kube
288288

289289
t.ok('dependencyGraphResults' in depGraphResult,
290290
'expected dependencyGraphResults field to exist in /dependency-graphs response');
291-
292-
/* Because of a bug in removeTagFromImage() func in src/scanner/images/index.ts,
293-
which chops off everything after ':' from the image name, we store a wrong image name
294-
and the result does not exist in the object referred below */
295-
t.same(depGraphResult.dependencyGraphResults[imageName], null,
296-
'expected result for image kind-registry:5000/python:rc-buster does not exist');
297-
t.ok('kind-registry' in depGraphResult.dependencyGraphResults, 'BUG: the full image name is not stored in kubernetes-upstream');
291+
t.ok('imageMetadata' in JSON.parse(depGraphResult.dependencyGraphResults[imageName]),
292+
'snyk-monitor sent expected data to upstream in the expected time frame');
298293
});
299294

300295
tap.test('snyk-monitor sends deleted workload to kubernetes-upstream', async (t) => {

test/unit/scanner/images.test.ts

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -56,29 +56,39 @@ tap.test('constructStaticAnalysisOptions() tests', async (t) => {
5656
t.deepEqual(options, expectedResult, 'returned options match expectations');
5757
});
5858

59-
tap.test('getImageTag() tests', async (t) => {
60-
t.plan(4);
59+
tap.test('extracted image tag tests', async (t) => {
60+
t.plan(6);
6161

6262
const imageWithSha = 'nginx@sha256:1234567890abcdef';
63-
const imageWithShaResult = scannerImages.getImageTag(imageWithSha);
64-
t.same(imageWithShaResult, '1234567890abcdef', 'image sha is returned');
63+
const imageWithShaResult = scannerImages.getImageParts(imageWithSha);
64+
t.same(imageWithShaResult.imageTag, '1234567890abcdef', 'image sha is returned');
6565

6666
const imageWithTag = 'nginx:latest';
67-
const imageWithTagResult = scannerImages.getImageTag(imageWithTag);
68-
t.same(imageWithTagResult, 'latest', 'image tag is returned');
67+
const imageWithTagResult = scannerImages.getImageParts(imageWithTag);
68+
t.same(imageWithTagResult.imageTag, 'latest', 'image tag is returned');
69+
70+
const imageWithFullRepository = 'kind-registry:5000/nginx:latest';
71+
const imageWithFullRepositoryResult = scannerImages.getImageParts(imageWithFullRepository);
72+
t.same(imageWithFullRepositoryResult.imageTag, 'latest', 'image tag is returned when full repo specified');
6973

7074
const imageWithoutTag = 'nginx';
71-
const imageWithoutTagResult = scannerImages.getImageTag(imageWithoutTag);
72-
t.same(imageWithoutTagResult, '', 'empty tag returned when no tag is specified');
75+
const imageWithoutTagResult = scannerImages.getImageParts(imageWithoutTag);
76+
t.same(imageWithoutTagResult.imageTag, '', 'empty tag returned when no tag is specified');
7377

7478
const imageWithManySeparators = 'nginx@abc:tag@bad:reallybad';
75-
const imageWithManySeparatorsResult = scannerImages.getImageTag(imageWithManySeparators);
76-
t.same(imageWithManySeparatorsResult, '', 'empty tag is returned on malformed image name and tag');
79+
const imageWithManySeparatorsResult = scannerImages.getImageParts(imageWithManySeparators);
80+
t.same(imageWithManySeparatorsResult.imageTag, '', 'empty tag is returned on malformed image name and tag');
81+
82+
const imageWithFullRepoAndManySeparators = 'kind-registry:5000/nginx@abc:tag@bad:reallybad';
83+
const imageWithFullRepoAndManySeparatorsResult = scannerImages.getImageParts(imageWithFullRepoAndManySeparators);
84+
t.same(imageWithFullRepoAndManySeparatorsResult.imageTag, '', 'empty tag is returned on malformed image name and tag with full repo');
7785
});
7886

79-
tap.test('removeTagFromImage() tests', async (t) => {
80-
t.plan(2);
87+
tap.test('extracted image name tests', async (t) => {
88+
t.plan(4);
8189

82-
t.same(scannerImages.removeTagFromImage('nginx:latest'), 'nginx', 'removed image:tag');
83-
t.same(scannerImages.removeTagFromImage('nginx:@sha256:1234567890abcdef'), 'nginx', 'removed image@sha:hex');
90+
t.same(scannerImages.getImageParts('nginx:latest').imageName, 'nginx', 'removed image:tag');
91+
t.same(scannerImages.getImageParts('nginx:@sha256:1234567890abcdef').imageName, 'nginx', 'removed malformed image:@sha:hex');
92+
t.same(scannerImages.getImageParts('node@sha256:215a9fbef4df2c1ceb7c79481d3cfd94ad8f1f0105bade39f3be907bf386c5e1').imageName, 'node', 'removed image@sha:hex');
93+
t.same(scannerImages.getImageParts('kind-registry:5000/python:rc-buster').imageName, 'kind-registry:5000/python', 'removed repository/image:tag');
8494
});

0 commit comments

Comments
 (0)