Skip to content

Commit 75f8169

Browse files
authored
Merge branch 'main' into CN-941-consolidate-archive-extraction
2 parents 740c55d + 7936ba5 commit 75f8169

File tree

42 files changed

+8212
-3638
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+8212
-3638
lines changed

.github/CODEOWNERS

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# This is a comment.
22
# Each line is a file pattern followed by one or more owners.
33

4-
* @snyk/infrasec_container
4+
* @snyk/infrasec_container @snyk/container_container
55

.snyk

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,6 @@
22
version: v1.25.0
33
# ignores vulnerabilities until expiry date; change duration by modifying expiry date
44
ignore:
5-
SNYK-JS-TAR-15307072:
6-
- 'snyk-nodejs-lockfile-parser > @yarnpkg/core > tar':
7-
reason: 'Indirect dependency from snyk-nodejs-lockfile-parser, waiting for upstream fix'
8-
expires: 2026-05-06T00:00:00.000Z
9-
SNYK-JS-TAR-15416075:
10-
- 'snyk-nodejs-lockfile-parser > @yarnpkg/core > tar':
11-
reason: 'Indirect dependency from snyk-nodejs-lockfile-parser, waiting for upstream fix'
12-
expires: 2026-05-06T00:00:00.000Z
13-
SNYK-JS-TAR-15456201:
14-
- 'snyk-nodejs-lockfile-parser > @yarnpkg/core > tar':
15-
reason: 'Indirect dependency from snyk-nodejs-lockfile-parser, waiting for upstream fix'
16-
expires: 2026-05-06T00:00:00.000Z
175
SNYK-JS-LODASH-15869625:
186
- '*':
197
reason: 'Indirect dependency, waiting for upstream fix'

components/common.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ schemas:
1717
enum:
1818
- autoDetectedUserInstructions
1919
- binaries
20+
- baseRuntimes
2021
- depGraph
2122
- dockerfileAnalysis
2223
- dockerLayers

lib/analyzer/applications/node-modules-utils.ts

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import * as Debug from "debug";
22
import { mkdir, mkdtemp, rm, stat, writeFile } from "fs/promises";
3+
import * as os from "os";
34
import * as path from "path";
5+
import { getErrorMessage } from "../../error-utils";
46
import { FilePathToContent, FilesByDirMap } from "./types";
57
const debug = Debug("snyk");
68

@@ -22,7 +24,7 @@ interface ScanPaths {
2224
async function createTempProjectDir(
2325
projectDir: string,
2426
): Promise<{ tmpDir: string; tempProjectRoot: string }> {
25-
const tmpDir = await mkdtemp("snyk");
27+
const tmpDir = await mkdtemp(path.join(os.tmpdir(), "snyk-"));
2628

2729
const tempProjectRoot = path.join(tmpDir, projectDir);
2830

@@ -51,7 +53,9 @@ async function createSyntheticManifest(
5153
await writeFile(tempRootManifestPath, "{}", "utf-8");
5254
} catch (error) {
5355
debug(
54-
`Error while writing file ${tempRootManifestPath} : ${error.message}`,
56+
`Error while writing file ${tempRootManifestPath} : ${getErrorMessage(
57+
error,
58+
)}`,
5559
);
5660
}
5761
}
@@ -76,20 +80,23 @@ async function persistNodeModules(
7680
fileNamesGroupedByDirectory: FilesByDirMap,
7781
): Promise<ScanPaths> {
7882
const modules = fileNamesGroupedByDirectory.get(project);
79-
const tmpDir: string = "";
80-
const tempProjectRoot: string = "";
8183

8284
if (!modules || modules.size === 0) {
8385
debug(`Empty application directory tree.`);
84-
85-
return {
86-
tempDir: tmpDir,
87-
tempProjectPath: tempProjectRoot,
88-
};
86+
return { tempDir: "", tempProjectPath: "" };
8987
}
9088

89+
// Create the temp directory first so we can return it in the catch block
90+
// for cleanup. Previously, the outer tmpDir/tempProjectRoot were always
91+
// empty strings, meaning any temp directory created before a failure in
92+
// saveOnDisk or later steps would be leaked (caller couldn't clean it up).
93+
let tmpDir = "";
94+
let tempProjectRoot = "";
95+
9196
try {
92-
const { tmpDir, tempProjectRoot } = await createTempProjectDir(project);
97+
const created = await createTempProjectDir(project);
98+
tmpDir = created.tmpDir;
99+
tempProjectRoot = created.tempProjectRoot;
93100

94101
await saveOnDisk(tmpDir, modules, filePathToContent);
95102

@@ -113,7 +120,9 @@ async function persistNodeModules(
113120
return result;
114121
} catch (error) {
115122
debug(
116-
`Failed to copy the application manifest files locally: ${error.message}`,
123+
`Failed to copy the application manifest files locally: ${getErrorMessage(
124+
error,
125+
)}`,
117126
);
118127
return {
119128
tempDir: tmpDir,
@@ -122,12 +131,15 @@ async function persistNodeModules(
122131
}
123132
}
124133

125-
async function createFile(filePath, fileContent): Promise<void> {
134+
async function createFile(
135+
filePath: string,
136+
fileContent: string,
137+
): Promise<void> {
126138
try {
127139
await mkdir(path.dirname(filePath), { recursive: true });
128140
await writeFile(filePath, fileContent, "utf-8");
129141
} catch (error) {
130-
debug(`Error while creating file ${filePath} : ${error.message}`);
142+
debug(`Error while creating file ${filePath} : ${getErrorMessage(error)}`);
131143
}
132144
}
133145

@@ -241,6 +253,6 @@ async function cleanupAppNodeModules(appRootDir: string): Promise<void> {
241253
try {
242254
await rm(appRootDir, { recursive: true });
243255
} catch (error) {
244-
debug(`Error while removing ${appRootDir} : ${error.message}`);
256+
debug(`Error while removing ${appRootDir} : ${getErrorMessage(error)}`);
245257
}
246258
}

lib/analyzer/applications/node.ts

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import * as Debug from "debug";
33
import * as path from "path";
44
import * as lockFileParser from "snyk-nodejs-lockfile-parser";
55
import * as resolveDeps from "snyk-resolve-deps";
6+
import { getErrorMessage } from "../../error-utils";
67
import { DepGraphFact, TestedFilesFact } from "../../facts";
78

89
const debug = Debug("snyk");
@@ -136,7 +137,7 @@ async function depGraphFromNodeModules(
136137
}
137138

138139
const depGraph = await legacy.depTreeToGraph(
139-
pkgTree,
140+
pkgTree as any,
140141
pkgTree.type || "npm",
141142
);
142143

@@ -162,7 +163,9 @@ async function depGraphFromNodeModules(
162163
});
163164
} catch (error) {
164165
debug(
165-
`An error occurred while analysing node_modules dir: ${error.message}`,
166+
`An error occurred while analysing node_modules dir: ${getErrorMessage(
167+
error,
168+
)}`,
166169
);
167170
} finally {
168171
await cleanupAppNodeModules(tempDir);
@@ -300,7 +303,9 @@ async function depGraphFromManifestFiles(
300303
);
301304
} catch (err) {
302305
debug(
303-
`An error occurred while analysing a pair of manifest and lock files: ${err.message}`,
306+
`An error occurred while analysing a pair of manifest and lock files: ${getErrorMessage(
307+
err,
308+
)}`,
304309
);
305310
continue;
306311
}
@@ -417,7 +422,7 @@ function stripUndefinedLabels(
417422
parserResult: lockFileParser.PkgTree,
418423
): lockFileParser.PkgTree {
419424
const optionalLabels = parserResult.labels;
420-
const mandatoryLabels: Record<string, string> = {};
425+
const mandatoryLabels: Record<string, any> = {};
421426
if (optionalLabels) {
422427
for (const currentLabelName of Object.keys(optionalLabels)) {
423428
if (optionalLabels[currentLabelName] !== undefined) {
@@ -428,7 +433,7 @@ function stripUndefinedLabels(
428433
const parserResultWithProperLabels = Object.assign({}, parserResult, {
429434
labels: mandatoryLabels,
430435
});
431-
return parserResultWithProperLabels;
436+
return parserResultWithProperLabels as lockFileParser.PkgTree;
432437
}
433438

434439
async function buildDepGraph(
@@ -513,7 +518,10 @@ async function buildDepGraphFromDepTree(
513518
// Don't provide a default manifest file name, prefer the parser to infer it.
514519
);
515520
const strippedLabelsParserResult = stripUndefinedLabels(parserResult);
516-
return await legacy.depTreeToGraph(strippedLabelsParserResult, lockfileType);
521+
return await legacy.depTreeToGraph(
522+
strippedLabelsParserResult as any,
523+
lockfileType,
524+
);
517525
}
518526

519527
export function getLockFileVersion(

lib/analyzer/applications/python/pip.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import * as Debug from "debug";
33
import { eventLoopSpinner } from "event-loop-spinner";
44
import * as path from "path";
55
import * as semver from "semver";
6+
import { getErrorMessage } from "../../../error-utils";
67
import { DepGraphFact } from "../../../facts";
78
import { compareVersions } from "../../../python-parser/common";
89
import { getPackageInfo } from "../../../python-parser/metadata-parser";
@@ -133,7 +134,7 @@ export async function pipFilesToScannedProjects(
133134
}
134135
metadataItems[packageInfo.name.toLowerCase()].push(packageInfo);
135136
} catch (err) {
136-
debug(err.message);
137+
debug(getErrorMessage(err));
137138
}
138139
}
139140
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { ExtractedLayers } from "../../extractor/types";
2+
import { BaseRuntime } from "../../facts";
3+
import { getJavaRuntimeReleaseContent } from "../../inputs/base-runtimes/static";
4+
import { parseJavaRuntimeRelease } from "./parser";
5+
6+
export function detectJavaRuntime(
7+
extractedLayers: ExtractedLayers,
8+
): BaseRuntime | null {
9+
const releaseContent = getJavaRuntimeReleaseContent(extractedLayers);
10+
return releaseContent ? parseJavaRuntimeRelease(releaseContent) : null;
11+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { BaseRuntime } from "../../facts";
2+
3+
const VALID_VERSION_PATTERN =
4+
/^(?!.*\.\.)[0-9]+(?:[._+a-zA-Z0-9-]*[a-zA-Z0-9])?$/;
5+
6+
const regex = /^\s*JAVA_VERSION\s*=\s*(?:(["'])(.*?)\1|([^#\r\n]+))/gm;
7+
8+
function isValidJavaVersion(version: string): boolean {
9+
if (!version || version.length === 0) {
10+
return false;
11+
}
12+
return VALID_VERSION_PATTERN.test(version);
13+
}
14+
15+
export function parseJavaRuntimeRelease(content: string): BaseRuntime | null {
16+
if (!content || content.trim().length === 0) {
17+
return null;
18+
}
19+
try {
20+
const matches = [...content.matchAll(regex)];
21+
22+
if (matches.length !== 1) {
23+
return null;
24+
}
25+
const version = (matches[0][2] || matches[0][3] || "").trim();
26+
27+
if (!isValidJavaVersion(version)) {
28+
return null;
29+
}
30+
return { type: "java", version };
31+
} catch (error) {
32+
return null;
33+
}
34+
}

lib/analyzer/image-inspector.ts

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as Debug from "debug";
22
import * as fs from "fs";
3-
import * as mkdirp from "mkdirp";
43
import * as path from "path";
4+
import { getErrorMessage } from "../error-utils";
55

66
import { Docker, DockerOptions } from "../docker";
77
import { ImageName } from "../extractor/image";
@@ -35,7 +35,11 @@ function cleanupCallback(imageFolderPath: string, imageName: string) {
3535
try {
3636
fs.rmdirSync(imageFolderPath);
3737
} catch (err) {
38-
debug(`Can't remove folder ${imageFolderPath}, got error ${err.message}`);
38+
debug(
39+
`Can't remove folder ${imageFolderPath}, got error ${getErrorMessage(
40+
err,
41+
)}`,
42+
);
3943
}
4044
};
4145
}
@@ -58,8 +62,19 @@ async function pullWithDockerBinary(
5862
await docker.save(targetImage, saveLocation);
5963
return true;
6064
} catch (err) {
61-
debug(`couldn't pull ${targetImage} using docker binary: ${err.message}`);
62-
const errorMessage = err.stderr || err.message || err.toString();
65+
debug(
66+
`couldn't pull ${targetImage} using docker binary: ${getErrorMessage(
67+
err,
68+
)}`,
69+
);
70+
const stderrValue =
71+
err !== null &&
72+
typeof err === "object" &&
73+
"stderr" in (err as object) &&
74+
(err as { stderr: unknown }).stderr;
75+
const errorMessage = stderrValue
76+
? String(stderrValue)
77+
: getErrorMessage(err);
6378
handleDockerPullError(errorMessage, platform);
6479

6580
return false;
@@ -126,7 +141,7 @@ async function pullFromContainerRegistry(
126141
platform,
127142
);
128143
} catch (err) {
129-
handleDockerPullError(err.message);
144+
handleDockerPullError(getErrorMessage(err));
130145
throw err;
131146
}
132147
}
@@ -200,7 +215,7 @@ async function getImageArchive(
200215
platform?: string,
201216
): Promise<ArchiveResult> {
202217
const docker = new Docker();
203-
mkdirp.sync(imageSavePath);
218+
fs.mkdirSync(imageSavePath, { recursive: true });
204219
const destination: DestinationDir = {
205220
name: imageSavePath,
206221
removeCallback: cleanupCallback(imageSavePath, "image.tar"),
@@ -213,7 +228,7 @@ async function getImageArchive(
213228
} catch (error) {
214229
debug(
215230
`${targetImage} does not exist locally, proceeding to pull image.`,
216-
error.stack || error,
231+
getErrorMessage(error),
217232
);
218233
}
219234

@@ -370,7 +385,7 @@ function isLocalImageSameArchitecture(
370385
// Note: this is using the same flag/input pattern as the new Docker buildx: eg. linux/arm64/v8
371386
platformArchitecture = platformOption.split("/")[1];
372387
} catch (error) {
373-
debug(`Error parsing platform flag: '${error.message}'`);
388+
debug(`Error parsing platform flag: '${getErrorMessage(error)}'`);
374389
return false;
375390
}
376391

lib/analyzer/os-release/static.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as Debug from "debug";
22
import { normalize as normalizePath } from "path";
3+
import { getErrorMessage } from "../../error-utils";
34

45
import { DockerFileAnalysis } from "../../dockerfile/types";
56
import { ExtractedLayers } from "../../extractor/types";
@@ -74,7 +75,7 @@ export async function detect(
7475
try {
7576
osRelease = await handler(osReleaseFile);
7677
} catch (err) {
77-
debug(`Malformed OS release file: ${err.message}`);
78+
debug(`Malformed OS release file: ${getErrorMessage(err)}`);
7879
}
7980
if (osRelease) {
8081
break;

0 commit comments

Comments
 (0)