/
index.ts
118 lines (104 loc) · 3.66 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
import * as fs from 'fs';
import * as path from 'path';
import { extractPatchMetadata } from './snyk-file';
import { applyPatchToFile } from './patch';
import { findPhysicalModules } from './explore-node-modules';
import {
VulnIdAndPackageName,
VulnPatches,
PatchedModule,
ProtectResultType,
} from './types';
import { getAllPatches } from './fetch-patches';
import { sendAnalytics } from './analytics';
async function protect(projectFolderPath: string) {
// Handle runs with flags
if (process.argv.includes('--help')) {
console.log(getHelp());
return;
}
if (process.argv.includes('--version')) {
console.log(getVersion());
return;
}
const snykFilePath = path.resolve(projectFolderPath, '.snyk');
if (!fs.existsSync(snykFilePath)) {
console.log('No .snyk file found');
sendAnalytics({
type: ProtectResultType.NO_SNYK_FILE,
});
return;
}
const snykFileContents = fs.readFileSync(snykFilePath, 'utf8');
const snykFilePatchMetadata = extractPatchMetadata(snykFileContents);
const vulnIdAndPackageNames: VulnIdAndPackageName[] = snykFilePatchMetadata;
const targetPackageNames = [
...new Set(snykFilePatchMetadata.map((vpn) => vpn.packageName)), // get a list of unique package names by converting to Set and then back to array
];
// find instances of the target packages by spelunking through the node_modules looking for modules with a target packageName
const foundPhysicalPackages = findPhysicalModules(
projectFolderPath,
targetPackageNames,
);
// Map of package name to versions (for the target package names).
// For each package name, we might have found multiple versions and we'll need to fetch patches for each version.
// We will use this to lookup the versions of packages to get patches for.
const packageNameToVersionsMap = new Map<string, string[]>();
foundPhysicalPackages.forEach((p) => {
if (packageNameToVersionsMap.has(p.packageName)) {
const versions = packageNameToVersionsMap.get(p.packageName);
if (!versions?.includes(p.packageVersion)) {
versions?.push(p.packageVersion);
}
} else {
packageNameToVersionsMap.set(p.packageName, [p.packageVersion]);
}
});
const packageAtVersionsToPatches: Map<
string,
VulnPatches[]
> = await getAllPatches(vulnIdAndPackageNames, packageNameToVersionsMap);
if (packageAtVersionsToPatches.size === 0) {
console.log('Nothing to patch');
sendAnalytics({
type: ProtectResultType.NOTHING_TO_PATCH,
});
return;
}
const patchedModules: PatchedModule[] = [];
foundPhysicalPackages.forEach((fpp) => {
const packageNameAtVersion = `${fpp.packageName}@${fpp.packageVersion}`;
const vuldIdAndPatches = packageAtVersionsToPatches.get(
packageNameAtVersion,
);
vuldIdAndPatches?.forEach((vp) => {
vp.patches.forEach((patchDiffs) => {
patchDiffs.patchDiffs.forEach((diff) => {
applyPatchToFile(diff, fpp.path);
});
});
patchedModules.push({
vulnId: vp.vulnId,
packageName: fpp.packageName,
packageVersion: fpp.packageVersion,
});
});
});
console.log('Successfully applied Snyk patches');
sendAnalytics({
type: ProtectResultType.APPLIED_PATCHES,
patchedModules,
});
}
export function getHelp(): string {
const filePath = path.resolve(__dirname, '../../help.txt');
const helpText = fs.readFileSync(filePath, 'utf8');
return helpText;
}
export function getVersion() {
const filePath = path.resolve(__dirname, '../../package.json');
const rawData = fs.readFileSync(filePath, 'utf8');
const packageJSON = JSON.parse(rawData);
return packageJSON.version;
}
export default protect;