Skip to content

Commit 9abf986

Browse files
committed
fix: use central parsing in extractimagedetails
1 parent 7d9bf73 commit 9abf986

File tree

3 files changed

+15
-95
lines changed

3 files changed

+15
-95
lines changed

lib/analyzer/image-inspector.ts

Lines changed: 9 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import * as path from "path";
55

66
import { Docker, DockerOptions } from "../docker";
77
import { ImageName } from "../extractor/image";
8+
import { parseImageReference } from "../image-reference";
89

910
import type { DockerPullResult } from "@snyk/snyk-docker-pull";
1011
import type {
@@ -265,99 +266,15 @@ async function getImageArchive(
265266
}
266267
}
267268

268-
function isImagePartOfURL(targetImage): boolean {
269-
// Based on the Docker spec, if the image contains a hostname, then the hostname should contain
270-
// a `.` or `:` before the first instance of a `/`. ref: https://stackoverflow.com/a/37867949
271-
if (!targetImage.includes("/")) {
272-
return false;
273-
}
274-
275-
const partBeforeFirstForwardSlash = targetImage.split("/")[0];
276-
277-
return (
278-
partBeforeFirstForwardSlash.includes(".") ||
279-
partBeforeFirstForwardSlash.includes(":") ||
280-
partBeforeFirstForwardSlash === "localhost"
281-
);
282-
}
283-
284-
function extractHostnameFromTargetImage(targetImage: string): {
285-
hostname: string;
286-
remainder: string;
287-
} {
288-
// We need to detect if the `targetImage` is part of a URL. If not, the default hostname will be
289-
// used (registry-1.docker.io). ref: https://stackoverflow.com/a/37867949
290-
const defaultHostname = "registry-1.docker.io";
291-
292-
if (!isImagePartOfURL(targetImage)) {
293-
return { hostname: defaultHostname, remainder: targetImage };
294-
}
295-
296-
const dockerFriendlyRegistryHostname = "docker.io/";
297-
if (targetImage.startsWith(dockerFriendlyRegistryHostname)) {
298-
return {
299-
hostname: defaultHostname,
300-
remainder: targetImage.substring(dockerFriendlyRegistryHostname.length),
301-
};
302-
}
303-
304-
const i = targetImage.indexOf("/");
305-
return {
306-
hostname: targetImage.substring(0, i),
307-
remainder: targetImage.substring(i + 1),
308-
};
309-
}
310-
311-
function extractImageNameAndTag(
312-
remainder: string,
313-
targetImage: string,
314-
): { imageName: string; tag: string } {
315-
const defaultTag = "latest";
316-
317-
if (!remainder.includes("@")) {
318-
const [imageName, tag] = remainder.split(":");
319-
320-
return {
321-
imageName: appendDefaultRepoPrefixIfRequired(imageName, targetImage),
322-
tag: tag || defaultTag,
323-
};
324-
}
325-
326-
const [imageName, tag] = remainder.split("@");
327-
328-
return {
329-
imageName: appendDefaultRepoPrefixIfRequired(
330-
dropTagIfSHAIsPresent(imageName),
331-
targetImage,
332-
),
333-
tag: tag || defaultTag,
334-
};
335-
}
336-
337-
function appendDefaultRepoPrefixIfRequired(
338-
imageName: string,
339-
targetImage: string,
340-
): string {
341-
const defaultRepoPrefix = "library/";
342-
343-
if (isImagePartOfURL(targetImage) || imageName.includes("/")) {
344-
return imageName;
345-
}
346-
347-
return defaultRepoPrefix + imageName;
348-
}
349-
350-
function dropTagIfSHAIsPresent(imageName: string): string {
351-
if (!imageName.includes(":")) {
352-
return imageName;
353-
}
354-
355-
return imageName.split(":")[0];
356-
}
357-
358269
function extractImageDetails(targetImage: string): ImageDetails {
359-
const { hostname, remainder } = extractHostnameFromTargetImage(targetImage);
360-
const { imageName, tag } = extractImageNameAndTag(remainder, targetImage);
270+
const parsed = parseImageReference(targetImage);
271+
const hostname = parsed.registryForPull;
272+
const isDockerHub = hostname === "registry-1.docker.io";
273+
const imageName =
274+
isDockerHub && !parsed.repository.includes("/")
275+
? "library/" + parsed.repository
276+
: parsed.repository;
277+
const tag = parsed.tailReferenceForPull;
361278
return { hostname, imageName, tag };
362279
}
363280

lib/image-reference.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,10 @@ export class ParsedImageReference {
5959
* If the registry is not set, use Docker Hub.
6060
*/
6161
get registryForPull(): string {
62-
return this.registry ?? "registry-1.docker.io";
62+
if (this.registry === "docker.io" || this.registry === undefined) {
63+
return "registry-1.docker.io";
64+
}
65+
return this.registry;
6366
}
6467

6568
/**

lib/scan.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { getArchivePath, getImageType } from "./image-type";
1212
import { isNumber, isTrue } from "./option-utils";
1313
import * as staticModule from "./static";
1414
import { ImageType, PluginOptions, PluginResponse } from "./types";
15-
import { isValidDockerImageReference } from "./utils";
15+
import { isValidImageReference } from "./image-reference";
1616

1717
// Registry credentials may also be provided by env vars. When both are set, flags take precedence.
1818
export function mergeEnvVarsIntoCredentials(
@@ -178,7 +178,7 @@ async function imageIdentifierAnalysis(
178178
// Validate Docker image reference format to catch malformed references early. We implement initial validation here
179179
// in lieu of simply sending to the docker daemon since some invalid references can result in unknown or invalid API
180180
// paths to the Docker daemon, sometimes producing confusing error results (like redirects) instead of the not found response.
181-
if (!isValidDockerImageReference(targetImage)) {
181+
if (!isValidImageReference(targetImage)) {
182182
throw new Error(`invalid image reference format: ${targetImage}`);
183183
}
184184

0 commit comments

Comments
 (0)