@@ -3,31 +3,33 @@ import * as plugin from 'snyk-docker-plugin';
33
44import logger = require( '../../common/logger' ) ;
55import { pull as skopeoCopy , getDestinationForImage } from './skopeo' ;
6- import { IPullableImage } from './types' ;
6+ import { IPullableImage , IScanImage } from './types' ;
77import { IStaticAnalysisOptions , StaticAnalysisImageType , IScanResult , IPluginOptions } from '../types' ;
88
99export async function pullImages ( images : IPullableImage [ ] ) : Promise < IPullableImage [ ] > {
1010 const pulledImages : IPullableImage [ ] = [ ] ;
1111
1212 for ( const image of images ) {
13- const { imageName, fileSystemPath} = image ;
13+ const { imageName, imageWithDigest , fileSystemPath } = image ;
1414 if ( ! fileSystemPath ) {
1515 continue ;
1616 }
1717
1818 try {
19- await skopeoCopy ( imageName , 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 ) {
22- logger . error ( { error, image : imageName } , 'failed to pull image' ) ;
24+ logger . error ( { error, image : imageWithDigest } , 'failed to pull image' ) ;
2325 }
2426 }
2527
2628 return pulledImages ;
2729}
2830
29- export function getImagesWithFileSystemPath ( images : string [ ] ) : IPullableImage [ ] {
30- return images . map ( ( image ) => ( { imageName : image , fileSystemPath : getDestinationForImage ( image ) } ) ) ;
31+ export function getImagesWithFileSystemPath ( images : IScanImage [ ] ) : IPullableImage [ ] {
32+ return images . map ( ( image ) => ( { ... image , fileSystemPath : getDestinationForImage ( image . imageName ) } ) ) ;
3133}
3234
3335export async function removePulledImages ( images : IPullableImage [ ] ) : Promise < void > {
@@ -41,15 +43,15 @@ export async function removePulledImages(images: IPullableImage[]): Promise<void
4143}
4244
4345// Exported for testing
44- export function getImageParts ( imageWithTag : string ) : { imageName : string , imageTag : string } {
46+ export function getImageParts ( imageWithTag : string ) : { imageName : string , imageTag : string , imageDigest : string } {
4547 // we're matching pattern: <registry:port_number>(optional)/<image_name>(mandatory):<image_tag>(optional)@<tag_identifier>(optional)
4648 // extracted from https://github.com/docker/distribution/blob/master/reference/regexp.go
4749 const regex = / ^ ( (?: (?: [ a - z A - Z 0 - 9 ] | [ a - z A - Z 0 - 9 ] [ a - z A - Z 0 - 9 - ] * [ a - z A - Z 0 - 9 ] ) (?: (?: \. (?: [ a - z A - Z 0 - 9 ] | [ a - z A - Z 0 - 9 ] [ a - z A - Z 0 - 9 - ] * [ a - z A - Z 0 - 9 ] ) ) + ) ? (?: : [ 0 - 9 ] + ) ? \/ ) ? [ a - z 0 - 9 ] + (?: (?: (?: [ . _ ] | _ _ | [ - ] * ) [ a - z 0 - 9 ] + ) + ) ? (?: (?: \/ [ a - z 0 - 9 ] + (?: (?: (?: [ . _ ] | _ _ | [ - ] * ) [ a - z 0 - 9 ] + ) + ) ? ) + ) ? ) (?: : ( [ \w ] [ \w . - ] { 0 , 127 } ) ) ? (?: @ ( [ A - Z a - z ] [ A - Z a - z 0 - 9 ] * (?: [ - _ + . ] [ A - Z a - z ] [ A - Z a - z 0 - 9 ] * ) * [: ] [ A - F a - f 0 - 9 ] { 32 , } ) ) ? $ / ig;
4850 const groups = regex . exec ( imageWithTag ) ;
49-
51+
5052 if ( ! groups ) {
5153 logger . error ( { image : imageWithTag } , 'Image with tag is malformed, cannot extract valid parts' ) ;
52- return { imageName : imageWithTag , imageTag : '' } ;
54+ return { imageName : imageWithTag , imageTag : '' , imageDigest : '' } ;
5355 }
5456
5557 const IMAGE_NAME_GROUP = 1 ;
@@ -58,8 +60,8 @@ export function getImageParts(imageWithTag: string) : {imageName: string, imageT
5860
5961 return {
6062 imageName : groups [ IMAGE_NAME_GROUP ] ,
61- // prefer tag over digest
62- imageTag : groups [ IMAGE_TAG_GROUP ] || groups [ IMAGE_DIGEST_GROUP ] || '' ,
63+ imageTag : groups [ IMAGE_TAG_GROUP ] || '' ,
64+ imageDigest : groups [ IMAGE_DIGEST_GROUP ] || '' ,
6365 } ;
6466}
6567
@@ -78,7 +80,7 @@ export async function scanImages(images: IPullableImage[]): Promise<IScanResult[
7880
7981 const dockerfile = undefined ;
8082
81- for ( const { imageName, fileSystemPath} of images ) {
83+ for ( const { imageName, fileSystemPath, imageWithDigest } of images ) {
8284 try {
8385 const staticAnalysisOptions = constructStaticAnalysisOptions ( fileSystemPath ) ;
8486 const options : IPluginOptions = {
@@ -92,16 +94,19 @@ export async function scanImages(images: IPullableImage[]): Promise<IScanResult[
9294 throw Error ( 'Unexpected empty result from docker-plugin' ) ;
9395 }
9496
95- const imageParts : { imageName : string , imageTag : string } = getImageParts ( imageName ) ;
97+ const imageParts = getImageParts ( imageName ) ;
98+ const imageDigest = imageWithDigest && getImageParts ( imageWithDigest ) . imageDigest ;
9699
97100 result . imageMetadata = {
98101 image : imageParts . imageName ,
99102 imageTag : imageParts . imageTag ,
103+ imageDigest,
100104 } ;
101105
102106 scannedImages . push ( {
103107 image : imageParts . imageName ,
104108 imageWithTag : imageName ,
109+ imageWithDigest : imageWithDigest ,
105110 pluginResult : result ,
106111 } ) ;
107112 } catch ( error ) {
0 commit comments