From 9ad503c16ba541c2078128fbd72be51905e5125f Mon Sep 17 00:00:00 2001 From: octo-patch Date: Sun, 26 Apr 2026 09:47:39 +0800 Subject: [PATCH 1/2] fix(core): add package name validation to updateAllPackages (fixes #1205) updateAllPackages() was passing package names from package.json directly into execSync() shell commands without validation. updateSinglePackage() already had this protection via a regex guard; this commit applies the same filter in updateAllPackages() so malformed or injected names are silently skipped rather than forwarded to the shell. Co-Authored-By: Octopus --- .changeset/fix-updateallpackages-validation.md | 5 +++++ packages/core/src/utils/update/index.ts | 10 ++++++++++ 2 files changed, 15 insertions(+) create mode 100644 .changeset/fix-updateallpackages-validation.md diff --git a/.changeset/fix-updateallpackages-validation.md b/.changeset/fix-updateallpackages-validation.md new file mode 100644 index 000000000..1ce1329e1 --- /dev/null +++ b/.changeset/fix-updateallpackages-validation.md @@ -0,0 +1,5 @@ +--- +"@voltagent/core": patch +--- + +fix(core): add package name validation to updateAllPackages to prevent command injection diff --git a/packages/core/src/utils/update/index.ts b/packages/core/src/utils/update/index.ts index a600cabf9..5c1e433e8 100644 --- a/packages/core/src/utils/update/index.ts +++ b/packages/core/src/utils/update/index.ts @@ -332,11 +332,21 @@ export const updateAllPackages = async ( const packageManager = detectPackageManager(rootDir); // 3. Prepare the package list for updating + const validPkgName = /^(@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/; const packagesToUpdate = updateCheckResult.updates .filter((pkg) => pkg.type !== "latest") + .filter((pkg) => validPkgName.test(pkg.name)) .map((pkg) => `${pkg.name}@latest`); const logger = new LoggerProxy({ component: "update-checker" }); + + if (packagesToUpdate.length === 0) { + return { + success: true, + message: "No packages to update after filtering invalid package names", + }; + } + logger.info(`Updating ${packagesToUpdate.length} packages in ${rootDir}`); // 4. Run the update command based on package manager From 63a2cc76174a55ae822261246308178e33f9c438 Mon Sep 17 00:00:00 2001 From: Octopus Date: Sun, 26 Apr 2026 13:17:59 +0800 Subject: [PATCH 2/2] fix(core): warn-log invalid package names instead of dropping silently Surface package names rejected by the validation regex via a warn-level log so operators can investigate possible package.json tampering rather than only seeing the generic "No packages to update after filtering" message. Addresses CodeRabbit review feedback on PR #1250. Co-Authored-By: Octopus --- packages/core/src/utils/update/index.ts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/packages/core/src/utils/update/index.ts b/packages/core/src/utils/update/index.ts index 5c1e433e8..08a97e19c 100644 --- a/packages/core/src/utils/update/index.ts +++ b/packages/core/src/utils/update/index.ts @@ -333,13 +333,24 @@ export const updateAllPackages = async ( // 3. Prepare the package list for updating const validPkgName = /^(@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/; - const packagesToUpdate = updateCheckResult.updates - .filter((pkg) => pkg.type !== "latest") + const candidateUpdates = updateCheckResult.updates.filter((pkg) => pkg.type !== "latest"); + const invalidNames = candidateUpdates + .map((pkg) => pkg.name) + .filter((name) => !validPkgName.test(name)); + const packagesToUpdate = candidateUpdates .filter((pkg) => validPkgName.test(pkg.name)) .map((pkg) => `${pkg.name}@latest`); const logger = new LoggerProxy({ component: "update-checker" }); + if (invalidNames.length > 0) { + // Surface filtered names so operators can investigate possible package.json + // tampering rather than just silently dropping them. + logger.warn("Skipping packages with invalid names (possible package.json tampering)", { + invalidNames, + }); + } + if (packagesToUpdate.length === 0) { return { success: true,