preship 2.1.1 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/cli.js +103 -51
  2. package/dist/index.js +57 -30
  3. package/package.json +5 -5
package/dist/cli.js CHANGED
@@ -41,6 +41,58 @@ function getCliVersion() {
41
41
  return "0.0.0";
42
42
  }
43
43
  }
44
+ function getOsvEcosystem(projectType) {
45
+ switch (projectType) {
46
+ case "npm":
47
+ case "yarn":
48
+ case "pnpm":
49
+ return "npm";
50
+ case "flutter":
51
+ return "Pub";
52
+ case "maven":
53
+ case "gradle":
54
+ return "Maven";
55
+ case "cocoapods":
56
+ return "CocoaPods";
57
+ case "spm":
58
+ return "SwiftURL";
59
+ default:
60
+ return "npm";
61
+ }
62
+ }
63
+ function parseProjectDeps(project) {
64
+ const manifestMap = {
65
+ npm: "package.json",
66
+ yarn: "package.json",
67
+ pnpm: "package.json",
68
+ flutter: "pubspec.yaml",
69
+ maven: "pom.xml",
70
+ gradle: "build.gradle",
71
+ cocoapods: "Podfile",
72
+ spm: "Package.swift"
73
+ };
74
+ const manifest = path.join(project.path, manifestMap[project.type] || "package.json");
75
+ switch (project.type) {
76
+ case "npm":
77
+ return (0, import_core.parseNpmLockfile)(project.lockFile, manifest);
78
+ case "yarn":
79
+ return (0, import_core.parseYarnLockfile)(project.lockFile, manifest);
80
+ case "pnpm":
81
+ return (0, import_core.parsePnpmLockfile)(project.lockFile, manifest);
82
+ case "flutter":
83
+ return (0, import_core.parsePubspecLockfile)(project.lockFile, manifest);
84
+ case "maven":
85
+ return (0, import_core.parseMavenPom)(project.lockFile, manifest);
86
+ case "gradle":
87
+ return (0, import_core.parseGradleLockfile)(project.lockFile, manifest);
88
+ case "cocoapods":
89
+ return (0, import_core.parsePodfileLock)(project.lockFile, manifest);
90
+ case "spm":
91
+ return (0, import_core.parsePackageResolved)(project.lockFile, manifest);
92
+ default:
93
+ throw new Error(`Unsupported project type: ${project.type}`);
94
+ }
95
+ }
44
96
  async function scan(options) {
45
97
  const startTime = Date.now();
46
98
  const projectPath = path.resolve(options?.projectPath ?? process.cwd());
@@ -50,26 +102,14 @@ async function scan(options) {
50
102
  }
51
103
  const projects = (0, import_core.detectProjects)(projectPath);
52
104
  const project = projects[0];
53
- const packageJsonPath = path.join(project.path, "package.json");
54
105
  let parsedDeps;
55
- switch (project.type) {
56
- case "npm":
57
- parsedDeps = (0, import_core.parseNpmLockfile)(project.lockFile, packageJsonPath);
58
- break;
59
- case "yarn":
60
- parsedDeps = (0, import_core.parseYarnLockfile)(project.lockFile, packageJsonPath);
61
- break;
62
- case "pnpm":
63
- parsedDeps = (0, import_core.parsePnpmLockfile)(project.lockFile, packageJsonPath);
64
- break;
65
- default:
66
- throw new Error(`Unsupported project type: ${project.type}`);
67
- }
106
+ parsedDeps = parseProjectDeps(project);
68
107
  if (!config.scanDevDependencies) {
69
108
  parsedDeps = parsedDeps.filter((dep) => !dep.isDevDependency);
70
109
  }
71
110
  const dependencies = await (0, import_license.resolveLicenses)(parsedDeps, projectPath, {
72
111
  mode: config.mode ?? "auto",
112
+ ecosystem: project.type,
73
113
  networkTimeout: config.networkTimeout ?? 5e3,
74
114
  networkConcurrency: config.networkConcurrency ?? 10,
75
115
  cache: config.cache ?? true,
@@ -125,21 +165,7 @@ async function unifiedScan(options) {
125
165
  const projects = (0, import_core.detectProjects)(projectPath);
126
166
  const project = projects[0];
127
167
  progress(`Parsing ${project.type} lockfile...`);
128
- const packageJsonPath = path.join(project.path, "package.json");
129
- let parsedDeps;
130
- switch (project.type) {
131
- case "npm":
132
- parsedDeps = (0, import_core.parseNpmLockfile)(project.lockFile, packageJsonPath);
133
- break;
134
- case "yarn":
135
- parsedDeps = (0, import_core.parseYarnLockfile)(project.lockFile, packageJsonPath);
136
- break;
137
- case "pnpm":
138
- parsedDeps = (0, import_core.parsePnpmLockfile)(project.lockFile, packageJsonPath);
139
- break;
140
- default:
141
- throw new Error(`Unsupported project type: ${project.type}`);
142
- }
168
+ let parsedDeps = parseProjectDeps(project);
143
169
  if (!config.scanDevDependencies) {
144
170
  parsedDeps = parsedDeps.filter((dep) => !dep.isDevDependency);
145
171
  }
@@ -154,6 +180,7 @@ async function unifiedScan(options) {
154
180
  const licenseStart = Date.now();
155
181
  const dependencies = await (0, import_license.resolveLicenses)(parsedDeps, projectPath, {
156
182
  mode,
183
+ ecosystem: project.type,
157
184
  networkTimeout: config.networkTimeout ?? 5e3,
158
185
  networkConcurrency: config.networkConcurrency ?? 10,
159
186
  cache: config.cache ?? true,
@@ -186,7 +213,7 @@ async function unifiedScan(options) {
186
213
  (async () => {
187
214
  progress("Checking security vulnerabilities...");
188
215
  const securityConfig = (0, import_security.mergeSecurityConfig)(config.security);
189
- securityResult = await (0, import_security.scanSecurity)(parsedDeps, projectPath, securityConfig, mode);
216
+ securityResult = await (0, import_security.scanSecurity)(parsedDeps, projectPath, securityConfig, mode, getOsvEcosystem(project.type));
190
217
  })()
191
218
  );
192
219
  }
@@ -1180,18 +1207,40 @@ var path3 = __toESM(require("path"));
1180
1207
  var crypto = __toESM(require("crypto"));
1181
1208
 
1182
1209
  // src/formatters/purl.ts
1183
- function generatePurl(name, version) {
1210
+ function getPurlType(ecosystem) {
1211
+ switch (ecosystem) {
1212
+ case "flutter":
1213
+ return "pub";
1214
+ case "maven":
1215
+ case "gradle":
1216
+ return "maven";
1217
+ case "cocoapods":
1218
+ return "cocoapods";
1219
+ case "spm":
1220
+ return "swift";
1221
+ case "npm":
1222
+ case "yarn":
1223
+ case "pnpm":
1224
+ default:
1225
+ return "npm";
1226
+ }
1227
+ }
1228
+ function generatePurl(name, version, ecosystem) {
1229
+ const purlType = getPurlType(ecosystem);
1184
1230
  let encodedName;
1185
- if (name.startsWith("@")) {
1231
+ if (purlType === "npm" && name.startsWith("@")) {
1186
1232
  const withoutAt = name.slice(1);
1187
1233
  encodedName = `%40${withoutAt}`;
1234
+ } else if (purlType === "maven" && name.includes(":")) {
1235
+ const [groupId, artifactId] = name.split(":");
1236
+ encodedName = `${groupId}/${artifactId}`;
1188
1237
  } else {
1189
1238
  encodedName = name;
1190
1239
  }
1191
1240
  if (version) {
1192
- return `pkg:npm/${encodedName}@${version}`;
1241
+ return `pkg:${purlType}/${encodedName}@${version}`;
1193
1242
  }
1194
- return `pkg:npm/${encodedName}`;
1243
+ return `pkg:${purlType}/${encodedName}`;
1195
1244
  }
1196
1245
  function sanitizeSpdxId(value) {
1197
1246
  return value.replace(/[^a-zA-Z0-9.-]/g, "-");
@@ -1204,10 +1253,12 @@ function formatCycloneDx(result) {
1204
1253
  }
1205
1254
  function generateCycloneDxBom(result) {
1206
1255
  const projectName = getProjectName2(result.projectPath);
1207
- const rootBomRef = `pkg:npm/${projectName}`;
1256
+ const ecosystem = result.projectType;
1257
+ const purlType = ecosystem === "flutter" ? "pub" : "npm";
1258
+ const rootBomRef = `pkg:${purlType}/${projectName}`;
1208
1259
  const allPolicyResults = getLicenseResults(result);
1209
- const components = allPolicyResults.map((pr) => buildComponent(pr));
1210
- const directDeps = allPolicyResults.filter((pr) => pr.dependency.isDirect).map((pr) => generateBomRef(pr.dependency.name, pr.dependency.version));
1260
+ const components = allPolicyResults.map((pr) => buildComponent(pr, ecosystem));
1261
+ const directDeps = allPolicyResults.filter((pr) => pr.dependency.isDirect).map((pr) => generateBomRef(pr.dependency.name, pr.dependency.version, ecosystem));
1211
1262
  const dependencies = [
1212
1263
  {
1213
1264
  ref: rootBomRef,
@@ -1218,7 +1269,7 @@ function generateCycloneDxBom(result) {
1218
1269
  ref: c["bom-ref"]
1219
1270
  }))
1220
1271
  ];
1221
- const vulnerabilities = getVulnerabilities(result, allPolicyResults);
1272
+ const vulnerabilities = getVulnerabilities(result, allPolicyResults, ecosystem);
1222
1273
  const bom = {
1223
1274
  bomFormat: "CycloneDX",
1224
1275
  specVersion: "1.6",
@@ -1265,17 +1316,17 @@ function getLicenseResults(result) {
1265
1316
  ...license.unknown
1266
1317
  ];
1267
1318
  }
1268
- function generateBomRef(name, version) {
1269
- return generatePurl(name, version);
1319
+ function generateBomRef(name, version, ecosystem) {
1320
+ return generatePurl(name, version, ecosystem);
1270
1321
  }
1271
- function buildComponent(pr) {
1322
+ function buildComponent(pr, ecosystem) {
1272
1323
  const dep = pr.dependency;
1273
1324
  const component = {
1274
1325
  type: "library",
1275
1326
  name: dep.name,
1276
1327
  version: dep.version,
1277
- purl: generatePurl(dep.name, dep.version),
1278
- "bom-ref": generatePurl(dep.name, dep.version)
1328
+ purl: generatePurl(dep.name, dep.version, ecosystem),
1329
+ "bom-ref": generatePurl(dep.name, dep.version, ecosystem)
1279
1330
  };
1280
1331
  if (dep.isDevDependency) {
1281
1332
  component.scope = "optional";
@@ -1293,12 +1344,12 @@ function buildComponent(pr) {
1293
1344
  }
1294
1345
  return component;
1295
1346
  }
1296
- function getVulnerabilities(result, allPolicyResults) {
1347
+ function getVulnerabilities(result, allPolicyResults, ecosystem) {
1297
1348
  if (!result.modules.security) return [];
1298
1349
  const security = result.modules.security;
1299
1350
  const vulnerabilities = [];
1300
1351
  for (const vuln of security.vulnerabilities) {
1301
- const bomRef = findBomRefForPackage(vuln.package, vuln.version, allPolicyResults);
1352
+ const bomRef = findBomRefForPackage(vuln.package, vuln.version, allPolicyResults, ecosystem);
1302
1353
  const cdxVuln = {
1303
1354
  id: vuln.id,
1304
1355
  source: {
@@ -1322,12 +1373,12 @@ function getVulnerabilities(result, allPolicyResults) {
1322
1373
  }
1323
1374
  return vulnerabilities;
1324
1375
  }
1325
- function findBomRefForPackage(packageName, version, allPolicyResults) {
1376
+ function findBomRefForPackage(packageName, version, allPolicyResults, ecosystem) {
1326
1377
  const found = allPolicyResults.find(
1327
1378
  (pr) => pr.dependency.name === packageName && pr.dependency.version === version
1328
1379
  );
1329
1380
  if (found) {
1330
- return generatePurl(found.dependency.name, found.dependency.version);
1381
+ return generatePurl(found.dependency.name, found.dependency.version, ecosystem);
1331
1382
  }
1332
1383
  return void 0;
1333
1384
  }
@@ -1366,7 +1417,8 @@ function generateSpdxDocument(result) {
1366
1417
  supplier: "NOASSERTION",
1367
1418
  externalRefs: []
1368
1419
  };
1369
- const depPackages = allPolicyResults.map((pr) => buildSpdxPackage(pr));
1420
+ const ecosystem = result.projectType;
1421
+ const depPackages = allPolicyResults.map((pr) => buildSpdxPackage(pr, ecosystem));
1370
1422
  const relationships = [];
1371
1423
  relationships.push({
1372
1424
  spdxElementId: "SPDXRef-DOCUMENT",
@@ -1429,12 +1481,12 @@ function generateSpdxPackageId(name, version) {
1429
1481
  const sanitizedVersion = sanitizeSpdxId(version);
1430
1482
  return `SPDXRef-Package-${sanitizedName}-${sanitizedVersion}`;
1431
1483
  }
1432
- function buildSpdxPackage(pr) {
1484
+ function buildSpdxPackage(pr, ecosystem) {
1433
1485
  const dep = pr.dependency;
1434
1486
  const spdxId = generateSpdxPackageId(dep.name, dep.version);
1435
- const purl = generatePurl(dep.name, dep.version);
1487
+ const purl = generatePurl(dep.name, dep.version, ecosystem);
1436
1488
  const license = dep.license === "UNKNOWN" ? "NOASSERTION" : dep.license;
1437
- const downloadLocation = `https://registry.npmjs.org/${dep.name}/-/${getPackageBasename(dep.name)}-${dep.version}.tgz`;
1489
+ const downloadLocation = ecosystem === "flutter" ? `https://pub.dev/packages/${dep.name}/versions/${dep.version}` : `https://registry.npmjs.org/${dep.name}/-/${getPackageBasename(dep.name)}-${dep.version}.tgz`;
1438
1490
  return {
1439
1491
  SPDXID: spdxId,
1440
1492
  name: dep.name,
package/dist/index.js CHANGED
@@ -51,6 +51,58 @@ function getCliVersion() {
51
51
  return "0.0.0";
52
52
  }
53
53
  }
54
+ function getOsvEcosystem(projectType) {
55
+ switch (projectType) {
56
+ case "npm":
57
+ case "yarn":
58
+ case "pnpm":
59
+ return "npm";
60
+ case "flutter":
61
+ return "Pub";
62
+ case "maven":
63
+ case "gradle":
64
+ return "Maven";
65
+ case "cocoapods":
66
+ return "CocoaPods";
67
+ case "spm":
68
+ return "SwiftURL";
69
+ default:
70
+ return "npm";
71
+ }
72
+ }
73
+ function parseProjectDeps(project) {
74
+ const manifestMap = {
75
+ npm: "package.json",
76
+ yarn: "package.json",
77
+ pnpm: "package.json",
78
+ flutter: "pubspec.yaml",
79
+ maven: "pom.xml",
80
+ gradle: "build.gradle",
81
+ cocoapods: "Podfile",
82
+ spm: "Package.swift"
83
+ };
84
+ const manifest = path.join(project.path, manifestMap[project.type] || "package.json");
85
+ switch (project.type) {
86
+ case "npm":
87
+ return (0, import_core.parseNpmLockfile)(project.lockFile, manifest);
88
+ case "yarn":
89
+ return (0, import_core.parseYarnLockfile)(project.lockFile, manifest);
90
+ case "pnpm":
91
+ return (0, import_core.parsePnpmLockfile)(project.lockFile, manifest);
92
+ case "flutter":
93
+ return (0, import_core.parsePubspecLockfile)(project.lockFile, manifest);
94
+ case "maven":
95
+ return (0, import_core.parseMavenPom)(project.lockFile, manifest);
96
+ case "gradle":
97
+ return (0, import_core.parseGradleLockfile)(project.lockFile, manifest);
98
+ case "cocoapods":
99
+ return (0, import_core.parsePodfileLock)(project.lockFile, manifest);
100
+ case "spm":
101
+ return (0, import_core.parsePackageResolved)(project.lockFile, manifest);
102
+ default:
103
+ throw new Error(`Unsupported project type: ${project.type}`);
104
+ }
105
+ }
54
106
  async function scan(options) {
55
107
  const startTime = Date.now();
56
108
  const projectPath = path.resolve(options?.projectPath ?? process.cwd());
@@ -60,26 +112,14 @@ async function scan(options) {
60
112
  }
61
113
  const projects = (0, import_core.detectProjects)(projectPath);
62
114
  const project = projects[0];
63
- const packageJsonPath = path.join(project.path, "package.json");
64
115
  let parsedDeps;
65
- switch (project.type) {
66
- case "npm":
67
- parsedDeps = (0, import_core.parseNpmLockfile)(project.lockFile, packageJsonPath);
68
- break;
69
- case "yarn":
70
- parsedDeps = (0, import_core.parseYarnLockfile)(project.lockFile, packageJsonPath);
71
- break;
72
- case "pnpm":
73
- parsedDeps = (0, import_core.parsePnpmLockfile)(project.lockFile, packageJsonPath);
74
- break;
75
- default:
76
- throw new Error(`Unsupported project type: ${project.type}`);
77
- }
116
+ parsedDeps = parseProjectDeps(project);
78
117
  if (!config.scanDevDependencies) {
79
118
  parsedDeps = parsedDeps.filter((dep) => !dep.isDevDependency);
80
119
  }
81
120
  const dependencies = await (0, import_license.resolveLicenses)(parsedDeps, projectPath, {
82
121
  mode: config.mode ?? "auto",
122
+ ecosystem: project.type,
83
123
  networkTimeout: config.networkTimeout ?? 5e3,
84
124
  networkConcurrency: config.networkConcurrency ?? 10,
85
125
  cache: config.cache ?? true,
@@ -135,21 +175,7 @@ async function unifiedScan(options) {
135
175
  const projects = (0, import_core.detectProjects)(projectPath);
136
176
  const project = projects[0];
137
177
  progress(`Parsing ${project.type} lockfile...`);
138
- const packageJsonPath = path.join(project.path, "package.json");
139
- let parsedDeps;
140
- switch (project.type) {
141
- case "npm":
142
- parsedDeps = (0, import_core.parseNpmLockfile)(project.lockFile, packageJsonPath);
143
- break;
144
- case "yarn":
145
- parsedDeps = (0, import_core.parseYarnLockfile)(project.lockFile, packageJsonPath);
146
- break;
147
- case "pnpm":
148
- parsedDeps = (0, import_core.parsePnpmLockfile)(project.lockFile, packageJsonPath);
149
- break;
150
- default:
151
- throw new Error(`Unsupported project type: ${project.type}`);
152
- }
178
+ let parsedDeps = parseProjectDeps(project);
153
179
  if (!config.scanDevDependencies) {
154
180
  parsedDeps = parsedDeps.filter((dep) => !dep.isDevDependency);
155
181
  }
@@ -164,6 +190,7 @@ async function unifiedScan(options) {
164
190
  const licenseStart = Date.now();
165
191
  const dependencies = await (0, import_license.resolveLicenses)(parsedDeps, projectPath, {
166
192
  mode,
193
+ ecosystem: project.type,
167
194
  networkTimeout: config.networkTimeout ?? 5e3,
168
195
  networkConcurrency: config.networkConcurrency ?? 10,
169
196
  cache: config.cache ?? true,
@@ -196,7 +223,7 @@ async function unifiedScan(options) {
196
223
  (async () => {
197
224
  progress("Checking security vulnerabilities...");
198
225
  const securityConfig = (0, import_security.mergeSecurityConfig)(config.security);
199
- securityResult = await (0, import_security.scanSecurity)(parsedDeps, projectPath, securityConfig, mode);
226
+ securityResult = await (0, import_security.scanSecurity)(parsedDeps, projectPath, securityConfig, mode, getOsvEcosystem(project.type));
200
227
  })()
201
228
  );
202
229
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "preship",
3
- "version": "2.1.1",
3
+ "version": "2.2.0",
4
4
  "description": "Pre-ship verification for modern dev teams. License compliance, security vulnerability scanning, and secret detection — all in one CLI. Zero config. Fully offline. Free forever.",
5
5
  "keywords": [
6
6
  "license",
@@ -59,10 +59,10 @@
59
59
  "lint": "tsc --noEmit"
60
60
  },
61
61
  "dependencies": {
62
- "@preship/core": "2.0.1",
63
- "@preship/license": "2.0.1",
64
- "@preship/security": "1.0.2",
65
- "@preship/secrets": "1.0.4",
62
+ "@preship/core": "2.1.0",
63
+ "@preship/license": "2.1.0",
64
+ "@preship/security": "1.1.0",
65
+ "@preship/secrets": "1.0.6",
66
66
  "chalk": "^4.1.2",
67
67
  "cli-table3": "^0.6.5",
68
68
  "commander": "^12.1.0"