Skip to content

Commit cb3ba22

Browse files
authored
fix: add -D flag to fix commands for dev dependencies (#690)
* fix: add -D flag to fix commands for dev dependencies SuggestedFixTarget now carries isDev, propagated from pkg.dev for direct findings and looked up via devLookup for parent upgrades. buildCommandForTargets splits dev/prod into separate install commands. buildWorkspaceInstallCommands and findSuggestedCommandForFinding both emit the correct flag per package manager (-D for npm/pnpm/yarn, --dev for bun). Closes #689 * fix(action): use --package flag to explicitly resolve cve-lite binary npx derives the command name from the package name (cve-lite-cli) but the bin field is named cve-lite, causing exit code 127 on npm 10.x. Using --package separates the install target from the binary name.
1 parent c16e6d0 commit cb3ba22

3 files changed

Lines changed: 272 additions & 26 deletions

File tree

action.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ runs:
135135
if [[ -n "${INPUT_CA_CERT}" ]]; then
136136
sync_args+=("--ca-cert" "${INPUT_CA_CERT}")
137137
fi
138-
npx --yes "cve-lite-cli@${INPUT_VERSION}" "${sync_args[@]}"
138+
npx --yes --package "cve-lite-cli@${INPUT_VERSION}" cve-lite "${sync_args[@]}"
139139
140140
- name: Run CVE Lite CLI scan
141141
shell: bash
@@ -202,4 +202,4 @@ runs:
202202
args+=("--ca-cert" "${INPUT_CA_CERT}")
203203
fi
204204
205-
npx --yes "cve-lite-cli@${INPUT_VERSION}" "${args[@]}"
205+
npx --yes --package "cve-lite-cli@${INPUT_VERSION}" cve-lite "${args[@]}"

src/remediation/fix-commands.ts

Lines changed: 51 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export type SuggestedFixTarget = {
3030
coveredPaths?: string[][];
3131
remainingPaths?: string[][];
3232
usage?: { imported: boolean; files: string[] } | null;
33+
isDev?: boolean;
3334
};
3435

3536
export type SuggestedFixSkip = {
@@ -83,6 +84,11 @@ export function buildSuggestedFixCommandPlan(
8384
}
8485
}
8586

87+
const devLookup = new Map<string, boolean>();
88+
for (const pkg of scanInput.packages) {
89+
devLookup.set(`${pkg.name}@${pkg.version}`, pkg.dev ?? false);
90+
}
91+
8692
const prioritizedFindings = [...findings]
8793
.filter(f => f.severity === "critical" || f.severity === "high")
8894
.sort((a, b) => {
@@ -176,6 +182,7 @@ export function buildSuggestedFixCommandPlan(
176182
reason: `Direct upgrade target for ${finding.pkg.name}@${finding.pkg.version}`,
177183
usage: finding.usage ?? null,
178184
workspaces: pkgWorkspaces.length > 0 ? pkgWorkspaces : undefined,
185+
isDev: finding.pkg.dev ?? false,
179186
});
180187
} else if (finding.fixVersionValidationNote) {
181188
skippedByKey.set(`${finding.relationship}:${finding.pkg.name}@${finding.pkg.version}`, {
@@ -234,6 +241,7 @@ export function buildSuggestedFixCommandPlan(
234241
reason: finding.recommendedNpmTransitiveRemediation.reason,
235242
command: buildNpmUpdateCommand(finding.recommendedNpmTransitiveRemediation.package, finding.recommendedNpmTransitiveRemediation.workspaces),
236243
usage: finding.usage ?? null,
244+
isDev: devLookup.get(`${finding.recommendedNpmTransitiveRemediation.package}@${finding.recommendedNpmTransitiveRemediation.currentVersion}`) ?? false,
237245
});
238246
continue;
239247
}
@@ -268,6 +276,7 @@ export function buildSuggestedFixCommandPlan(
268276
coveredPaths: coverage.coveredPaths,
269277
remainingPaths: coverage.remainingPaths,
270278
usage: finding.usage ?? null,
279+
isDev: devLookup.get(`${finding.recommendedNpmTransitiveRemediation.package}@${finding.recommendedNpmTransitiveRemediation.currentVersion}`) ?? false,
271280
});
272281
continue;
273282
}
@@ -300,6 +309,7 @@ export function buildSuggestedFixCommandPlan(
300309
coveredPaths: coverage.coveredPaths,
301310
remainingPaths: coverage.remainingPaths,
302311
usage: finding.usage ?? null,
312+
isDev: devLookup.get(`${finding.recommendedParentUpgrade.package}@${finding.recommendedParentUpgrade.currentVersion}`) ?? false,
303313
});
304314
continue;
305315
}
@@ -448,6 +458,10 @@ function commandPrefix(packageManager: SuggestedFixPackageManager): string {
448458
return "yarn add";
449459
}
450460

461+
function devFlag(packageManager: SuggestedFixPackageManager): string {
462+
return packageManager === "bun" ? "--dev" : "-D";
463+
}
464+
451465
export function findSuggestedCommandForFinding(
452466
plan: SuggestedFixCommandPlan,
453467
finding: Finding,
@@ -482,7 +496,8 @@ export function findSuggestedCommandForFinding(
482496
const commands = buildWorkspaceInstallCommands([target], plan.packageManager);
483497
if (commands.length > 0) return commands.join(" && ");
484498
}
485-
return `${commandPrefix(plan.packageManager)} ${target.package}@${target.targetVersion}`;
499+
const flag = target.isDev ? ` ${devFlag(plan.packageManager)}` : "";
500+
return `${commandPrefix(plan.packageManager)}${flag} ${target.package}@${target.targetVersion}`;
486501
}
487502

488503
function packageManagerSourceLabel(scanInput: ScanInput): string {
@@ -520,6 +535,7 @@ function upsertTarget(
520535
: "parent-upgrade",
521536
displayTargetVersion: existing.displayTargetVersion ?? next.displayTargetVersion,
522537
command: existing.command ?? next.command,
538+
isDev: (existing.isDev ?? false) && (next.isDev ?? false),
523539
workspaces: mergeStringArrays(existing.workspaces, next.workspaces),
524540
coverage: existing.coverage === "partial" || next.coverage === "partial"
525541
? "partial"
@@ -700,28 +716,35 @@ function buildWorkspaceInstallCommands(
700716
const commands: string[] = [];
701717

702718
for (const [wsKey, groupTargets] of groups) {
703-
const pkgArgs = groupTargets.map(t => `${t.package}@${t.targetVersion}`).join(" ");
704719
const workspaces = wsKey ? wsKey.split("\0") : [];
705-
706-
if (packageManager === "npm") {
707-
const wsFlags = workspaces.map(ws => `-w ${ws}`).join(" ");
708-
commands.push(`npm install${wsFlags ? " " + wsFlags : ""} ${pkgArgs}`);
709-
} else if (packageManager === "pnpm") {
710-
const wsFlags = workspaces.map(ws => `--filter ./${ws}`).join(" ");
711-
commands.push(`pnpm add${wsFlags ? " " + wsFlags : ""} ${pkgArgs}`);
712-
} else if (packageManager === "yarn") {
713-
if (workspaces.length === 0) {
714-
commands.push(`yarn add ${pkgArgs}`);
715-
} else {
716-
for (const ws of workspaces) {
717-
commands.push(`yarn workspace ${ws} add ${pkgArgs}`);
720+
const devTargets = groupTargets.filter(t => t.isDev);
721+
const prodTargets = groupTargets.filter(t => !t.isDev);
722+
723+
for (const [targets, isDev] of [[prodTargets, false], [devTargets, true]] as const) {
724+
if (targets.length === 0) continue;
725+
const pkgArgs = targets.map(t => `${t.package}@${t.targetVersion}`).join(" ");
726+
const flag = isDev ? ` ${devFlag(packageManager)}` : "";
727+
728+
if (packageManager === "npm") {
729+
const wsFlags = workspaces.map(ws => `-w ${ws}`).join(" ");
730+
commands.push(`npm install${flag}${wsFlags ? " " + wsFlags : ""} ${pkgArgs}`);
731+
} else if (packageManager === "pnpm") {
732+
const wsFlags = workspaces.map(ws => `--filter ./${ws}`).join(" ");
733+
commands.push(`pnpm add${flag}${wsFlags ? " " + wsFlags : ""} ${pkgArgs}`);
734+
} else if (packageManager === "yarn") {
735+
if (workspaces.length === 0) {
736+
commands.push(`yarn add${flag} ${pkgArgs}`);
737+
} else {
738+
for (const ws of workspaces) {
739+
commands.push(`yarn workspace ${ws} add${flag} ${pkgArgs}`);
740+
}
718741
}
742+
} else if (packageManager === "bun") {
743+
const wsFlags = workspaces.map(ws => `--filter ${ws}`).join(" ");
744+
commands.push(`bun add${flag}${wsFlags ? " " + wsFlags : ""} ${pkgArgs}`);
745+
} else {
746+
commands.push(`${commandPrefix(packageManager)}${flag} ${pkgArgs}`);
719747
}
720-
} else if (packageManager === "bun") {
721-
const wsFlags = workspaces.map(ws => `--filter ${ws}`).join(" ");
722-
commands.push(`bun add${wsFlags ? " " + wsFlags : ""} ${pkgArgs}`);
723-
} else {
724-
commands.push(`${commandPrefix(packageManager)} ${pkgArgs}`);
725748
}
726749
}
727750

@@ -754,9 +777,14 @@ function buildCommandForTargets(
754777
if (hasWorkspaces) {
755778
commandParts.push(...buildWorkspaceInstallCommands(installTargets, packageManager));
756779
} else {
757-
commandParts.push(
758-
`${commandPrefix(packageManager)} ${installTargets.map(target => `${target.package}@${target.targetVersion}`).join(" ")}`,
759-
);
780+
const prodInstall = installTargets.filter(t => !t.isDev);
781+
const devInstall = installTargets.filter(t => t.isDev);
782+
if (prodInstall.length > 0) {
783+
commandParts.push(`${commandPrefix(packageManager)} ${prodInstall.map(t => `${t.package}@${t.targetVersion}`).join(" ")}`);
784+
}
785+
if (devInstall.length > 0) {
786+
commandParts.push(`${commandPrefix(packageManager)} ${devFlag(packageManager)} ${devInstall.map(t => `${t.package}@${t.targetVersion}`).join(" ")}`);
787+
}
760788
}
761789
}
762790

0 commit comments

Comments
 (0)