diff --git a/lib/extractor/index.ts b/lib/extractor/index.ts index ff01fdce8..804645951 100644 --- a/lib/extractor/index.ts +++ b/lib/extractor/index.ts @@ -232,15 +232,24 @@ function layersWithLatestFileModifications( ): ExtractedLayers { const extractedLayers: ExtractedLayers = {}; const removedFilesToIgnore: Set = new Set(); + const opaqueWhiteoutDirs: Set = new Set(); // TODO: This removes the information about the layer name, maybe we would need it in the future? for (const layer of layers) { + // Collect opaque whiteout dirs from this layer, but don't apply yet — + // they only affect older layers, not the current one. + const layerOpaqueDirs: Set = new Set(); + // go over extracted files products found in this layer for (const filename of Object.keys(layer)) { // if finding a deleted file - trimming to its original file name for excluding it from extractedLayers // + not adding this file + if (isOpaqueWhiteout(filename)) { + layerOpaqueDirs.add(path.dirname(filename)); + continue; + } if (isWhitedOutFile(filename)) { - removedFilesToIgnore.add(filename.replace(/.wh./, "")); + removedFilesToIgnore.add(removeWhiteoutPrefix(filename)); continue; } // not adding previously found to be whited out files to extractedLayers @@ -251,17 +260,69 @@ function layersWithLatestFileModifications( if (isFileInARemovedFolder(filename, removedFilesToIgnore)) { continue; } + // not adding files in directories covered by an opaque whiteout from a newer layer + if (isFileInOpaqueDir(filename, opaqueWhiteoutDirs)) { + continue; + } // file not already in extractedLayers if (!Reflect.has(extractedLayers, filename)) { extractedLayers[filename] = layer[filename]; } } + + // Apply this layer's opaque whiteouts to older layers + for (const dir of layerOpaqueDirs) { + opaqueWhiteoutDirs.add(dir); + } } return extractedLayers; } -export function isWhitedOutFile(filename: string) { - return filename.match(/.wh./gm); +/** + * check if a file is 'whited out' (filename starts with .wh.) + * https://www.madebymikal.com/interpreting-whiteout-files-in-docker-image-layers + * https://github.com/opencontainers/image-spec/blob/main/layer.md#whiteouts + */ +export function isWhitedOutFile(filename: string): boolean { + return getBasename(filename).startsWith(".wh."); +} + +/** + * Check if a file is an opaque whiteout (.wh..wh..opq). + * Opaque whiteouts mean "delete everything in this directory from lower layers." + * https://github.com/opencontainers/image-spec/blob/main/layer.md#opaque-whiteout + */ +export function isOpaqueWhiteout(filename: string): boolean { + return getBasename(filename) === ".wh..wh..opq"; +} + +/** + * Extract the basename from a path, handling both / and \ separators cross-platform. + */ +function getBasename(filename: string): string { + const lastSlash = Math.max( + filename.lastIndexOf("/"), + filename.lastIndexOf("\\"), + ); + return lastSlash === -1 ? filename : filename.substring(lastSlash + 1); +} + +function isFileInOpaqueDir( + filename: string, + opaqueWhiteoutDirs: Set, +): boolean { + return Array.from(opaqueWhiteoutDirs).some((opaqueDir) => + isFileInFolder(filename, opaqueDir), + ); +} + +/** + * Remove the .wh. prefix from a whiteout file to get the original filename + */ +export function removeWhiteoutPrefix(filename: string): string { + // Replace .wh. at the start or after the last slash/backslash. + // Don't match if there are slashes after .wh. + return filename.replace(/^(.*[\/\\])?\.wh\.([^\/\\]*)$/, "$1$2"); } function isBufferType(type: FileContent): type is Buffer { diff --git a/lib/extractor/layer.ts b/lib/extractor/layer.ts index ca4b72081..12ad96dec 100644 --- a/lib/extractor/layer.ts +++ b/lib/extractor/layer.ts @@ -2,7 +2,7 @@ import * as Debug from "debug"; import * as path from "path"; import { Readable } from "stream"; import { extract, Extract } from "tar-stream"; -import { isWhitedOutFile } from "."; +import { isOpaqueWhiteout, isWhitedOutFile } from "."; import { applyCallbacks, isResultEmpty } from "./callbacks"; import { decompressMaybe } from "./decompress-maybe"; import { ExtractAction, ExtractedLayers } from "./types"; @@ -32,7 +32,10 @@ export async function extractImageLayer( const matchedActions = extractActions.filter((action) => action.filePathMatches(absoluteFileName), ); - if (matchedActions.length > 0) { + if (isOpaqueWhiteout(absoluteFileName)) { + // Opaque whiteout: record it so layersWithLatestFileModifications can process it + result[absoluteFileName] = {}; + } else if (matchedActions.length > 0) { try { const callbackResult = await applyCallbacks( matchedActions, diff --git a/test/lib/extractor/index.spec.ts b/test/lib/extractor/index.spec.ts index ad9ed1464..6703c3c21 100644 --- a/test/lib/extractor/index.spec.ts +++ b/test/lib/extractor/index.spec.ts @@ -1,4 +1,9 @@ -import { getContentAsString } from "../../../lib/extractor"; +import { + getContentAsString, + isOpaqueWhiteout, + isWhitedOutFile, + removeWhiteoutPrefix, +} from "../../../lib/extractor"; import { ExtractAction, ExtractedLayers } from "../../../lib/extractor/types"; describe("index", () => { @@ -18,3 +23,115 @@ describe("index", () => { expect(result).toEqual("Hello, world!"); }); }); + +describe("isWhitedOutFile", () => { + test("should return true for files containing .wh. in their path", () => { + expect(isWhitedOutFile("/etc/.wh.hosts")).toBe(true); + expect(isWhitedOutFile("/var/lib/.wh.data")).toBe(true); + expect(isWhitedOutFile("/.wh.config")).toBe(true); + }); + + test("should return false for files not containing .wh.", () => { + expect(isWhitedOutFile("/etc/hosts")).toBe(false); + expect(isWhitedOutFile("")).toBe(false); + expect(isWhitedOutFile("/")).toBe(false); + }); + + test("should return false for similar but different patterns", () => { + // make sure the dots are literal and not match all + expect(isWhitedOutFile("/etc/wh.hosts")).toBe(false); + expect(isWhitedOutFile("/etc/.whosts")).toBe(false); + expect(isWhitedOutFile("/etc/whhosts")).toBe(false); + + // dots in wrong places + expect(isWhitedOutFile("/etc/.w.h.hosts")).toBe(false); + expect(isWhitedOutFile("/etc/..wh..hosts")).toBe(false); + + // case sensitive + expect(isWhitedOutFile("/etc/.WH.hosts")).toBe(false); + expect(isWhitedOutFile("/etc/.Wh.hosts")).toBe(false); + }); + + test("should handle .wh. at different positions", () => { + expect(isWhitedOutFile(".wh.start")).toBe(true); + expect(isWhitedOutFile("middle.wh.file")).toBe(false); + expect(isWhitedOutFile("end.wh.")).toBe(false); + expect(isWhitedOutFile("/deeply/nested/path/.wh.present")).toBe(true); + expect(isWhitedOutFile("/the/.wh./in/path/present")).toBe(false); + }); +}); + +describe("isOpaqueWhiteout", () => { + test("should return true for opaque whiteout files", () => { + expect(isOpaqueWhiteout("/etc/.wh..wh..opq")).toBe(true); + expect(isOpaqueWhiteout("/.wh..wh..opq")).toBe(true); + expect(isOpaqueWhiteout(".wh..wh..opq")).toBe(true); + expect(isOpaqueWhiteout("/deeply/nested/path/.wh..wh..opq")).toBe(true); + }); + + test("should return false for regular whiteout files", () => { + expect(isOpaqueWhiteout("/etc/.wh.hosts")).toBe(false); + expect(isOpaqueWhiteout("/.wh.config")).toBe(false); + }); + + test("should return false for non-whiteout files", () => { + expect(isOpaqueWhiteout("/etc/hosts")).toBe(false); + expect(isOpaqueWhiteout("")).toBe(false); + expect(isOpaqueWhiteout("/")).toBe(false); + }); + + test("should return false for similar but incorrect patterns", () => { + expect(isOpaqueWhiteout("/etc/.wh..wh..opq.extra")).toBe(false); + expect(isOpaqueWhiteout("/etc/.wh..opq")).toBe(false); + expect(isOpaqueWhiteout("/etc/.WH..WH..OPQ")).toBe(false); + }); +}); + +describe("removeWhiteoutPrefix", () => { + test("should remove .wh. prefix from filenames without slashes", () => { + expect(removeWhiteoutPrefix(".wh.hosts")).toBe("hosts"); + expect(removeWhiteoutPrefix(".wh.data")).toBe("data"); + expect(removeWhiteoutPrefix(".wh.config")).toBe("config"); + expect(removeWhiteoutPrefix(".wh.")).toBe(""); + expect(removeWhiteoutPrefix(".wh.file.txt")).toBe("file.txt"); + }); + + test("should remove .wh. prefix after the last slash in paths", () => { + expect(removeWhiteoutPrefix("/etc/.wh.hosts")).toBe("/etc/hosts"); + expect(removeWhiteoutPrefix("/var/lib/.wh.data")).toBe("/var/lib/data"); + expect(removeWhiteoutPrefix("/.wh.config")).toBe("/config"); + expect(removeWhiteoutPrefix("/deeply/nested/path/.wh.present")).toBe( + "/deeply/nested/path/present", + ); + expect(removeWhiteoutPrefix("/path/to/.wh.")).toBe("/path/to/"); + }); + + test("should not modify files that don't have .wh. prefix in the correct position", () => { + expect(removeWhiteoutPrefix("normal.file")).toBe("normal.file"); + expect(removeWhiteoutPrefix("/etc/hosts")).toBe("/etc/hosts"); + expect(removeWhiteoutPrefix("middle.wh.file")).toBe("middle.wh.file"); + expect(removeWhiteoutPrefix("/path/middle.wh.file")).toBe( + "/path/middle.wh.file", + ); + expect(removeWhiteoutPrefix(".whfile")).toBe(".whfile"); + expect(removeWhiteoutPrefix("/path/.whfile")).toBe("/path/.whfile"); + expect(removeWhiteoutPrefix("/xwh.txt")).toBe("/xwh.txt"); + }); + + test("should handle edge cases", () => { + expect(removeWhiteoutPrefix("")).toBe(""); + expect(removeWhiteoutPrefix("/")).toBe("/"); + expect(removeWhiteoutPrefix("//")).toBe("//"); + expect(removeWhiteoutPrefix("/.wh.")).toBe("/"); + expect(removeWhiteoutPrefix("//.wh.test")).toBe("//test"); + }); + + test("should not remove .wh. that appears in the middle of paths", () => { + expect(removeWhiteoutPrefix("/the/.wh./in/path/file")).toBe( + "/the/.wh./in/path/file", + ); + expect(removeWhiteoutPrefix("/path/.wh.dir/.wh.file")).toBe( + "/path/.wh.dir/file", + ); + }); +}); diff --git a/test/system/application-scans/__snapshots__/node.spec.ts.snap b/test/system/application-scans/__snapshots__/node.spec.ts.snap index 0e47b7d17..48daed326 100644 --- a/test/system/application-scans/__snapshots__/node.spec.ts.snap +++ b/test/system/application-scans/__snapshots__/node.spec.ts.snap @@ -57647,11 +57647,6 @@ Array [ "nodeId": "is-cidr@3.0.0", "pkgId": "is-cidr@3.0.0", }, - Object { - "deps": Array [], - "nodeId": "isexe@2.0.0", - "pkgId": "isexe@2.0.0", - }, Object { "deps": Array [], "nodeId": "lazy-property@1.0.0", @@ -58285,6 +58280,20 @@ Array [ "nodeId": "tar@4.4.13", "pkgId": "tar@4.4.13", }, + Object { + "deps": Array [], + "nodeId": "isexe@2.0.0", + "pkgId": "isexe@2.0.0", + }, + Object { + "deps": Array [ + Object { + "nodeId": "isexe@2.0.0", + }, + ], + "nodeId": "which@1.3.1", + "pkgId": "which@1.3.1", + }, Object { "deps": Array [ Object { @@ -58317,6 +58326,9 @@ Array [ Object { "nodeId": "tar@4.4.13", }, + Object { + "nodeId": "which@1.3.1", + }, ], "nodeId": "node-gyp@5.1.0", "pkgId": "node-gyp@5.1.0", @@ -58359,6 +58371,9 @@ Array [ Object { "nodeId": "umask@1.1.0", }, + Object { + "nodeId": "which@1.3.1", + }, ], "nodeId": "npm-lifecycle@3.1.4", "pkgId": "npm-lifecycle@3.1.4", @@ -58786,6 +58801,9 @@ Array [ Object { "nodeId": "unique-filename@1.1.1", }, + Object { + "nodeId": "which@1.3.1", + }, ], "nodeId": "pacote@9.5.12", "pkgId": "pacote@9.5.12", @@ -59264,6 +59282,9 @@ Array [ Object { "nodeId": "shebang-command@1.2.0", }, + Object { + "nodeId": "which@1.3.1", + }, ], "nodeId": "cross-spawn@5.1.0", "pkgId": "cross-spawn@5.1.0", @@ -59817,6 +59838,9 @@ Array [ Object { "nodeId": "shebang-command@1.2.0", }, + Object { + "nodeId": "which@1.3.1", + }, ], "nodeId": "cross-spawn@6.0.5", "pkgId": "cross-spawn@6.0.5", @@ -59926,6 +59950,11 @@ Array [ "nodeId": "require-main-filename@1.0.1", "pkgId": "require-main-filename@1.0.1", }, + Object { + "deps": Array [], + "nodeId": "which-module@2.0.0", + "pkgId": "which-module@2.0.0", + }, Object { "deps": Array [], "nodeId": "y18n@3.2.1", @@ -59969,6 +59998,9 @@ Array [ Object { "nodeId": "string-width@2.1.1", }, + Object { + "nodeId": "which-module@2.0.0", + }, Object { "nodeId": "y18n@3.2.1", }, @@ -59996,6 +60028,9 @@ Array [ Object { "nodeId": "update-notifier@2.5.0", }, + Object { + "nodeId": "which@1.3.1", + }, Object { "nodeId": "y18n@4.0.0", }, @@ -60593,9 +60628,6 @@ Array [ Object { "nodeId": "is-cidr@3.0.0", }, - Object { - "nodeId": "isexe@2.0.0", - }, Object { "nodeId": "json-parse-better-errors@1.0.2", }, @@ -60833,6 +60865,9 @@ Array [ Object { "nodeId": "validate-npm-package-name@3.0.0", }, + Object { + "nodeId": "which@1.3.1", + }, Object { "nodeId": "worker-farm@1.7.0", }, @@ -61760,13 +61795,6 @@ Array [ "version": "3.0.0", }, }, - Object { - "id": "isexe@2.0.0", - "info": Object { - "name": "isexe", - "version": "2.0.0", - }, - }, Object { "id": "lazy-property@1.0.0", "info": Object { @@ -62222,6 +62250,20 @@ Array [ "version": "4.4.13", }, }, + Object { + "id": "isexe@2.0.0", + "info": Object { + "name": "isexe", + "version": "2.0.0", + }, + }, + Object { + "id": "which@1.3.1", + "info": Object { + "name": "which", + "version": "1.3.1", + }, + }, Object { "id": "node-gyp@5.1.0", "info": Object { @@ -63230,6 +63272,13 @@ Array [ "version": "1.0.1", }, }, + Object { + "id": "which-module@2.0.0", + "info": Object { + "name": "which-module", + "version": "2.0.0", + }, + }, Object { "id": "y18n@3.2.1", "info": Object { diff --git a/test/system/application-scans/node.spec.ts b/test/system/application-scans/node.spec.ts index 4edde4e34..761f5d77a 100644 --- a/test/system/application-scans/node.spec.ts +++ b/test/system/application-scans/node.spec.ts @@ -273,7 +273,7 @@ describe("node application scans", () => { const result = await extractContent([getNodeAppFileContentAction], { path: imageNameAndTag, }); - expect(Object.keys(result.extractedLayers).length).toEqual(608); + expect(Object.keys(result.extractedLayers).length).toEqual(610); Object.keys(result.extractedLayers).forEach((fileName) => { expect( fileName.endsWith("/package.json") || diff --git a/test/system/operating-systems/__snapshots__/ubi8.spec.ts.snap b/test/system/operating-systems/__snapshots__/ubi8.spec.ts.snap index c4fb7421d..ac6989e05 100644 --- a/test/system/operating-systems/__snapshots__/ubi8.spec.ts.snap +++ b/test/system/operating-systems/__snapshots__/ubi8.spec.ts.snap @@ -7441,11 +7441,6 @@ Object { "nodeId": "ignore-by-default@1.0.1", "pkgId": "ignore-by-default@1.0.1", }, - Object { - "deps": Array [], - "nodeId": "isexe@2.0.0", - "pkgId": "isexe@2.0.0", - }, Object { "deps": Array [], "nodeId": "duplexer@0.1.1", @@ -7732,6 +7727,20 @@ Object { "nodeId": "shebang-command@1.2.0", "pkgId": "shebang-command@1.2.0", }, + Object { + "deps": Array [], + "nodeId": "isexe@2.0.0", + "pkgId": "isexe@2.0.0", + }, + Object { + "deps": Array [ + Object { + "nodeId": "isexe@2.0.0", + }, + ], + "nodeId": "which@1.3.0", + "pkgId": "which@1.3.0", + }, Object { "deps": Array [ Object { @@ -7740,6 +7749,9 @@ Object { Object { "nodeId": "shebang-command@1.2.0", }, + Object { + "nodeId": "which@1.3.0", + }, ], "nodeId": "cross-spawn@5.1.0|1", "pkgId": "cross-spawn@5.1.0", @@ -7752,6 +7764,9 @@ Object { Object { "nodeId": "shebang-command@1.2.0", }, + Object { + "nodeId": "which@1.3.1", + }, ], "nodeId": "cross-spawn@5.1.0|2", "pkgId": "cross-spawn@5.1.0", @@ -8386,9 +8401,6 @@ Object { Object { "nodeId": "ignore-by-default@1.0.1", }, - Object { - "nodeId": "isexe@2.0.0", - }, Object { "nodeId": "minimatch@3.0.4|1", }, @@ -10178,6 +10190,15 @@ Object { "nodeId": "tar@4.4.13", "pkgId": "tar@4.4.13", }, + Object { + "deps": Array [ + Object { + "nodeId": "isexe@2.0.0", + }, + ], + "nodeId": "which@1.3.1", + "pkgId": "which@1.3.1", + }, Object { "deps": Array [ Object { @@ -10210,6 +10231,9 @@ Object { Object { "nodeId": "tar@4.4.13", }, + Object { + "nodeId": "which@1.3.1", + }, ], "nodeId": "node-gyp@5.1.0", "pkgId": "node-gyp@5.1.0", @@ -10252,6 +10276,9 @@ Object { Object { "nodeId": "umask@1.1.0", }, + Object { + "nodeId": "which@1.3.1", + }, ], "nodeId": "npm-lifecycle@3.1.4", "pkgId": "npm-lifecycle@3.1.4", @@ -10665,6 +10692,9 @@ Object { Object { "nodeId": "unique-filename@1.1.1", }, + Object { + "nodeId": "which@1.3.1", + }, ], "nodeId": "pacote@9.5.12", "pkgId": "pacote@9.5.12", @@ -11305,6 +11335,9 @@ Object { Object { "nodeId": "shebang-command@1.2.0", }, + Object { + "nodeId": "which@1.3.1", + }, ], "nodeId": "cross-spawn@6.0.5", "pkgId": "cross-spawn@6.0.5", @@ -11414,6 +11447,11 @@ Object { "nodeId": "require-main-filename@1.0.1", "pkgId": "require-main-filename@1.0.1", }, + Object { + "deps": Array [], + "nodeId": "which-module@2.0.0", + "pkgId": "which-module@2.0.0", + }, Object { "deps": Array [], "nodeId": "y18n@3.2.1", @@ -11457,6 +11495,9 @@ Object { Object { "nodeId": "string-width@2.1.1", }, + Object { + "nodeId": "which-module@2.0.0", + }, Object { "nodeId": "y18n@3.2.1", }, @@ -11484,6 +11525,9 @@ Object { Object { "nodeId": "update-notifier@2.5.0", }, + Object { + "nodeId": "which@1.3.1", + }, Object { "nodeId": "y18n@4.0.0", }, @@ -12076,9 +12120,6 @@ Object { Object { "nodeId": "is-cidr@3.0.0", }, - Object { - "nodeId": "isexe@2.0.0", - }, Object { "nodeId": "json-parse-better-errors@1.0.2", }, @@ -12316,6 +12357,9 @@ Object { Object { "nodeId": "validate-npm-package-name@3.0.0", }, + Object { + "nodeId": "which@1.3.1", + }, Object { "nodeId": "worker-farm@1.7.0", }, @@ -13145,13 +13189,6 @@ Object { "version": "1.0.1", }, }, - Object { - "id": "isexe@2.0.0", - "info": Object { - "name": "isexe", - "version": "2.0.0", - }, - }, Object { "id": "duplexer@0.1.1", "info": Object { @@ -13404,6 +13441,20 @@ Object { "version": "1.2.0", }, }, + Object { + "id": "isexe@2.0.0", + "info": Object { + "name": "isexe", + "version": "2.0.0", + }, + }, + Object { + "id": "which@1.3.0", + "info": Object { + "name": "which", + "version": "1.3.0", + }, + }, Object { "id": "cross-spawn@5.1.0", "info": Object { @@ -15000,6 +15051,13 @@ Object { "version": "4.4.13", }, }, + Object { + "id": "which@1.3.1", + "info": Object { + "name": "which", + "version": "1.3.1", + }, + }, Object { "id": "node-gyp@5.1.0", "info": Object { @@ -15672,6 +15730,13 @@ Object { "version": "1.0.1", }, }, + Object { + "id": "which-module@2.0.0", + "info": Object { + "name": "which-module", + "version": "2.0.0", + }, + }, Object { "id": "y18n@3.2.1", "info": Object { diff --git a/test/system/registry-authentication/__snapshots__/username-password.spec.ts.snap b/test/system/registry-authentication/__snapshots__/username-password.spec.ts.snap index b01d52c98..e163be961 100644 --- a/test/system/registry-authentication/__snapshots__/username-password.spec.ts.snap +++ b/test/system/registry-authentication/__snapshots__/username-password.spec.ts.snap @@ -1803,11 +1803,6 @@ Object { "nodeId": "is-cidr@2.0.6", "pkgId": "is-cidr@2.0.6", }, - Object { - "deps": Array [], - "nodeId": "isexe@2.0.0", - "pkgId": "isexe@2.0.0", - }, Object { "deps": Array [], "nodeId": "lazy-property@1.0.0", @@ -2423,6 +2418,20 @@ Object { "nodeId": "tar@2.2.1", "pkgId": "tar@2.2.1", }, + Object { + "deps": Array [], + "nodeId": "isexe@2.0.0", + "pkgId": "isexe@2.0.0", + }, + Object { + "deps": Array [ + Object { + "nodeId": "isexe@2.0.0", + }, + ], + "nodeId": "which@1.3.1", + "pkgId": "which@1.3.1", + }, Object { "deps": Array [ Object { @@ -2458,6 +2467,9 @@ Object { Object { "nodeId": "tar@2.2.1", }, + Object { + "nodeId": "which@1.3.1", + }, ], "nodeId": "node-gyp@3.8.0", "pkgId": "node-gyp@3.8.0", @@ -2500,6 +2512,9 @@ Object { Object { "nodeId": "umask@1.1.0", }, + Object { + "nodeId": "which@1.3.1", + }, ], "nodeId": "npm-lifecycle@2.1.0", "pkgId": "npm-lifecycle@2.1.0", @@ -2941,6 +2956,9 @@ Object { Object { "nodeId": "unique-filename@1.1.0", }, + Object { + "nodeId": "which@1.3.1", + }, ], "nodeId": "pacote@8.1.6", "pkgId": "pacote@8.1.6", @@ -3152,6 +3170,9 @@ Object { Object { "nodeId": "shebang-command@1.2.0", }, + Object { + "nodeId": "which@1.3.1", + }, ], "nodeId": "cross-spawn@5.1.0", "pkgId": "cross-spawn@5.1.0", @@ -3739,6 +3760,11 @@ Object { "nodeId": "require-main-filename@1.0.1", "pkgId": "require-main-filename@1.0.1", }, + Object { + "deps": Array [], + "nodeId": "which-module@2.0.0", + "pkgId": "which-module@2.0.0", + }, Object { "deps": Array [], "nodeId": "y18n@3.2.1", @@ -3782,6 +3808,9 @@ Object { Object { "nodeId": "string-width@2.1.1", }, + Object { + "nodeId": "which-module@2.0.0", + }, Object { "nodeId": "y18n@3.2.1", }, @@ -3809,6 +3838,9 @@ Object { Object { "nodeId": "update-notifier@2.5.0", }, + Object { + "nodeId": "which@1.3.1", + }, Object { "nodeId": "y18n@4.0.0", }, @@ -4525,9 +4557,6 @@ Object { Object { "nodeId": "is-cidr@2.0.6", }, - Object { - "nodeId": "isexe@2.0.0", - }, Object { "nodeId": "json-parse-better-errors@1.0.2", }, @@ -4750,6 +4779,9 @@ Object { Object { "nodeId": "validate-npm-package-name@3.0.0", }, + Object { + "nodeId": "which@1.3.1", + }, Object { "nodeId": "worker-farm@1.6.0", }, @@ -5670,13 +5702,6 @@ Object { "version": "2.0.6", }, }, - Object { - "id": "isexe@2.0.0", - "info": Object { - "name": "isexe", - "version": "2.0.0", - }, - }, Object { "id": "lazy-property@1.0.0", "info": Object { @@ -6125,6 +6150,20 @@ Object { "version": "2.2.1", }, }, + Object { + "id": "isexe@2.0.0", + "info": Object { + "name": "isexe", + "version": "2.0.0", + }, + }, + Object { + "id": "which@1.3.1", + "info": Object { + "name": "which", + "version": "1.3.1", + }, + }, Object { "id": "node-gyp@3.8.0", "info": Object { @@ -7000,6 +7039,13 @@ Object { "version": "1.0.1", }, }, + Object { + "id": "which-module@2.0.0", + "info": Object { + "name": "which-module", + "version": "2.0.0", + }, + }, Object { "id": "y18n@3.2.1", "info": Object { @@ -9262,11 +9308,6 @@ Object { "nodeId": "is-cidr@2.0.6", "pkgId": "is-cidr@2.0.6", }, - Object { - "deps": Array [], - "nodeId": "isexe@2.0.0", - "pkgId": "isexe@2.0.0", - }, Object { "deps": Array [], "nodeId": "lazy-property@1.0.0", @@ -9882,6 +9923,20 @@ Object { "nodeId": "tar@2.2.1", "pkgId": "tar@2.2.1", }, + Object { + "deps": Array [], + "nodeId": "isexe@2.0.0", + "pkgId": "isexe@2.0.0", + }, + Object { + "deps": Array [ + Object { + "nodeId": "isexe@2.0.0", + }, + ], + "nodeId": "which@1.3.1", + "pkgId": "which@1.3.1", + }, Object { "deps": Array [ Object { @@ -9917,6 +9972,9 @@ Object { Object { "nodeId": "tar@2.2.1", }, + Object { + "nodeId": "which@1.3.1", + }, ], "nodeId": "node-gyp@3.8.0", "pkgId": "node-gyp@3.8.0", @@ -9959,6 +10017,9 @@ Object { Object { "nodeId": "umask@1.1.0", }, + Object { + "nodeId": "which@1.3.1", + }, ], "nodeId": "npm-lifecycle@2.1.0", "pkgId": "npm-lifecycle@2.1.0", @@ -10400,6 +10461,9 @@ Object { Object { "nodeId": "unique-filename@1.1.0", }, + Object { + "nodeId": "which@1.3.1", + }, ], "nodeId": "pacote@8.1.6", "pkgId": "pacote@8.1.6", @@ -10611,6 +10675,9 @@ Object { Object { "nodeId": "shebang-command@1.2.0", }, + Object { + "nodeId": "which@1.3.1", + }, ], "nodeId": "cross-spawn@5.1.0", "pkgId": "cross-spawn@5.1.0", @@ -11198,6 +11265,11 @@ Object { "nodeId": "require-main-filename@1.0.1", "pkgId": "require-main-filename@1.0.1", }, + Object { + "deps": Array [], + "nodeId": "which-module@2.0.0", + "pkgId": "which-module@2.0.0", + }, Object { "deps": Array [], "nodeId": "y18n@3.2.1", @@ -11241,6 +11313,9 @@ Object { Object { "nodeId": "string-width@2.1.1", }, + Object { + "nodeId": "which-module@2.0.0", + }, Object { "nodeId": "y18n@3.2.1", }, @@ -11268,6 +11343,9 @@ Object { Object { "nodeId": "update-notifier@2.5.0", }, + Object { + "nodeId": "which@1.3.1", + }, Object { "nodeId": "y18n@4.0.0", }, @@ -11984,9 +12062,6 @@ Object { Object { "nodeId": "is-cidr@2.0.6", }, - Object { - "nodeId": "isexe@2.0.0", - }, Object { "nodeId": "json-parse-better-errors@1.0.2", }, @@ -12209,6 +12284,9 @@ Object { Object { "nodeId": "validate-npm-package-name@3.0.0", }, + Object { + "nodeId": "which@1.3.1", + }, Object { "nodeId": "worker-farm@1.6.0", }, @@ -13129,13 +13207,6 @@ Object { "version": "2.0.6", }, }, - Object { - "id": "isexe@2.0.0", - "info": Object { - "name": "isexe", - "version": "2.0.0", - }, - }, Object { "id": "lazy-property@1.0.0", "info": Object { @@ -13584,6 +13655,20 @@ Object { "version": "2.2.1", }, }, + Object { + "id": "isexe@2.0.0", + "info": Object { + "name": "isexe", + "version": "2.0.0", + }, + }, + Object { + "id": "which@1.3.1", + "info": Object { + "name": "which", + "version": "1.3.1", + }, + }, Object { "id": "node-gyp@3.8.0", "info": Object { @@ -14459,6 +14544,13 @@ Object { "version": "1.0.1", }, }, + Object { + "id": "which-module@2.0.0", + "info": Object { + "name": "which-module", + "version": "2.0.0", + }, + }, Object { "id": "y18n@3.2.1", "info": Object { diff --git a/test/system/registry-authentication/username-password.spec.ts b/test/system/registry-authentication/username-password.spec.ts index 348b58eae..5abd991f4 100644 --- a/test/system/registry-authentication/username-password.spec.ts +++ b/test/system/registry-authentication/username-password.spec.ts @@ -10,7 +10,7 @@ describe("username and password authentication", () => { process.env.SNYK_REGISTRY_USERNAME = oldSnykRegistryUsernameEnvVar; } if (oldSnykRegistryPasswordEnvVar !== undefined) { - process.env.SNYK_REGISTRY_PASSWPRD = oldSnykRegistryPasswordEnvVar; + process.env.SNYK_REGISTRY_PASSWORD = oldSnykRegistryPasswordEnvVar; } });