Skip to content

fix(core): add package name validation to updateAllPackages#1208

Open
kagura-agent wants to merge 2 commits intoVoltAgent:mainfrom
kagura-agent:fix/update-all-packages-command-injection
Open

fix(core): add package name validation to updateAllPackages#1208
kagura-agent wants to merge 2 commits intoVoltAgent:mainfrom
kagura-agent:fix/update-all-packages-command-injection

Conversation

@kagura-agent
Copy link
Copy Markdown

@kagura-agent kagura-agent commented Apr 16, 2026

Summary

updateAllPackages() concatenates package names from package.json directly into shell commands passed to execSync() without validation. The sibling function updateSinglePackage() already validates names against a regex, but updateAllPackages() does not.

Changes

  • Extract shared constant: VALID_PKG_NAME_RE — the npm package name validation regex previously duplicated between functions
  • Add validation filter in updateAllPackages: packages with invalid names are now filtered out before command construction
  • Add warning log for skipped packages so the rejection is not silent
  • Deduplicate regex in updateSinglePackage to use the shared constant

Testing

Verified the regex correctly:

  • Accepts: lodash, @voltagent/core, my-pkg
  • Rejects: pkg$(curl evil.com), pkg; rm -rf /, pkg && echo pwned

Closes #1205


Summary by cubic

Add npm package name validation to updateAllPackages to block command injection and avoid bare installs when only invalid names are found. Aligns behavior with updateSinglePackage, logs warnings on skipped names, and fixes #1205.

  • Bug Fixes
    • Shared VALID_PKG_NAME_RE used in updateAllPackages and updateSinglePackage.
    • Skip invalid names and warn; return early if packagesToUpdate is empty.
    • Build update commands only from validated packages.

Written for commit 15c4cd8. Summary will update on new commits.

Summary by CodeRabbit

Bug Fixes

  • Patched a security issue in package update operations where invalid package names could be used before execution.
  • Invalid or malformed package names are now filtered out before any update commands run; skipped entries produce warning messages.
  • Package name validation is now consistent between single-package and bulk update flows.

…t#1205)

updateAllPackages concatenated package names from package.json directly
into shell commands passed to execSync() without validation, while the
sibling updateSinglePackage already had this check.

- Extract validation regex into shared VALID_PKG_NAME_RE constant
- Filter invalid package names in updateAllPackages before building command
- Log warning for skipped packages with invalid names
- Reuse constant in updateSinglePackage (dedup)
@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Apr 16, 2026

🦋 Changeset detected

Latest commit: 15c4cd8

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@voltagent/core Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 16, 2026

📝 Walkthrough

Walkthrough

Adds npm package name validation to the updater: extracts a shared VALID_PKG_NAME_RE, filters invalid names in updateAllPackages (logging skipped entries), and refactors updateSinglePackage to use the shared regex. Also adds a Changeset documenting the patch-level security fix.

Changes

Cohort / File(s) Summary
Security Fix Documentation
\.changeset/fix-update-all-packages-injection.md
Adds a Changesets entry describing a patch release for a command-injection fix in updateAllPackages.
Package Name Validation & Updater Logic
packages/core/src/utils/update/index.ts
Extracts VALID_PKG_NAME_RE constant; updateAllPackages now filters out invalid package names, logs warnings for skipped entries, and returns early if nothing valid remains; updateSinglePackage now reuses the shared regex for validation.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I nibble through lines where danger hides,
Snipped names that slipped past careless tides,
With a regex stitch I patch the seam,
Commands now safe — no sneaky scheme,
Hopping off, I guard your build-time dreams!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: adding package name validation to updateAllPackages to fix a security vulnerability.
Description check ✅ Passed The description is comprehensive with clear sections covering the bug, changes made, and testing verification. It includes testing of the regex and links the issue #1205.
Linked Issues check ✅ Passed The PR fulfills all objectives from issue #1205: extracts shared VALID_PKG_NAME_RE constant, adds validation filter in updateAllPackages, logs warnings for skipped packages, and deduplicates regex in updateSinglePackage.
Out of Scope Changes check ✅ Passed All changes are directly related to fixing the command injection vulnerability in updateAllPackages as specified in issue #1205. No out-of-scope modifications detected.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 2 files

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/core/src/utils/update/index.ts`:
- Around line 352-356: The filtered packagesToUpdate array can be empty even
when hasUpdates is true, causing execSync to run an install/add with no package
args; update the logic after computing packagesToUpdate (and before
building/executing the install command) to guard against an empty list: if
packagesToUpdate.length === 0, log a clear message (via logger.info or
logger.debug) and skip the install step (either return early from the function
or set hasUpdates=false) so execSync is never invoked with a bare command;
reference the packagesToUpdate variable and the code path that later invokes
execSync for the install/add.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a14fb864-e36d-4482-b3ae-60f108d5a2ac

📥 Commits

Reviewing files that changed from the base of the PR and between 71c9f84 and 0995019.

📒 Files selected for processing (2)
  • .changeset/fix-update-all-packages-injection.md
  • packages/core/src/utils/update/index.ts

Comment thread packages/core/src/utils/update/index.ts
…mand

Addresses CodeRabbit review: after filtering invalid names,
packagesToUpdate can be empty while hasUpdates is true, which would
run a bare install/add command with no packages.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/core/src/utils/update/index.ts (1)

352-355: Consider a single-pass split for valid vs invalid packages.

This currently re-checks similar predicates in separate chains. A single pass would reduce drift risk and keep this block easier to maintain.

♻️ Suggested refactor
-    const skippedPackages = updateCheckResult.updates.filter(
-      (pkg) => pkg.type !== "latest" && !VALID_PKG_NAME_RE.test(pkg.name),
-    );
+    const nonLatestUpdates = updateCheckResult.updates.filter(
+      (pkg) => pkg.type !== "latest",
+    );
+
+    const { skippedPackages, packagesToUpdate } = nonLatestUpdates.reduce(
+      (acc, pkg) => {
+        if (VALID_PKG_NAME_RE.test(pkg.name)) {
+          acc.packagesToUpdate.push(`${pkg.name}@latest`);
+        } else {
+          acc.skippedPackages.push(pkg);
+        }
+        return acc;
+      },
+      { skippedPackages: [] as PackageUpdateInfo[], packagesToUpdate: [] as string[] },
+    );

-    const packagesToUpdate = updateCheckResult.updates
-      .filter((pkg) => pkg.type !== "latest")
-      .filter((pkg) => VALID_PKG_NAME_RE.test(pkg.name))
-      .map((pkg) => `${pkg.name}@latest`);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/utils/update/index.ts` around lines 352 - 355, The current
creation of packagesToUpdate runs multiple array passes over
updateCheckResult.updates; replace it with a single-pass loop (e.g., use reduce
or a for..of) that checks each pkg once against pkg.type !== "latest" and
VALID_PKG_NAME_RE.test(pkg.name) and pushes valid entries as
`${pkg.name}@latest` into packagesToUpdate (and optionally collects invalid
package names into a separate list) to avoid duplicated predicate evaluation;
update the code around packagesToUpdate and reference updateCheckResult.updates
and VALID_PKG_NAME_RE accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/core/src/utils/update/index.ts`:
- Around line 352-355: The current creation of packagesToUpdate runs multiple
array passes over updateCheckResult.updates; replace it with a single-pass loop
(e.g., use reduce or a for..of) that checks each pkg once against pkg.type !==
"latest" and VALID_PKG_NAME_RE.test(pkg.name) and pushes valid entries as
`${pkg.name}@latest` into packagesToUpdate (and optionally collects invalid
package names into a separate list) to avoid duplicated predicate evaluation;
update the code around packagesToUpdate and reference updateCheckResult.updates
and VALID_PKG_NAME_RE accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0a0e0ee3-ebbf-42ee-8154-818b486903a5

📥 Commits

Reviewing files that changed from the base of the PR and between 0995019 and 15c4cd8.

📒 Files selected for processing (1)
  • packages/core/src/utils/update/index.ts

@kagura-agent
Copy link
Copy Markdown
Author

Thanks @coderabbitai — addressed in 15c4cd8. Added an early return when packagesToUpdate is empty after filtering, with a descriptive message distinguishing between 'skipped invalid names' vs 'no valid packages'. This prevents execSync from running install/add with no package arguments.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 17, 2026

Tip

For best results, initiate chat on the files or code changes.

@kagura-agent: That's a great approach — distinguishing between the "all names were invalid" case and the "nothing needed updating" case makes the log output much more actionable for users. The early return also cleanly prevents a no-argument execSync call. Thanks for the thorough follow-through! 🎉


If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] updateAllPackages missing command injection protection present in updateSinglePackage

1 participant