Skip to content

fix: refuse prototype-polluting mount-path segments in api.add() - #305

Merged
Shinrai merged 4 commits into
nextfrom
fix/proto-pollution-mount-path
Aug 24, 2026
Merged

fix: refuse prototype-polluting mount-path segments in api.add()#305
Shinrai merged 4 commits into
nextfrom
fix/proto-pollution-mount-path

Conversation

@cldmv-bot

@cldmv-bot cldmv-bot Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

🚀 What's Changed

💥 Breaking Changes

No breaking changes

✨ Features

No new features

🐛 Bug Fixes

📦 Dependencies

No dependency updates

🔧 Other Changes

👥 Contributors

A dotted or array mount path passed to api.slothlet.api.add() was split into
segments and written into the api tree without guarding prototype-chain segment
names. A path containing __proto__, constructor, or prototype therefore mutated
Object.prototype / Function.prototype globally instead of the api tree — classic
prototype pollution — with no error raised.

normalizeApiPath now refuses any of these three names in any segment position
(both the dotted-string and array path forms) with INVALID_CONFIG_API_PATH_INVALID
and the new API_PATH_REASON_UNSAFE_SEGMENT reason, mirroring the existing
reserved-name guard. Adds the reason string to all 12 language packs and
coverage for both path forms plus a no-pollution assertion.

This corrects never-intended behavior (a silent global write), so it ships as a
patch — no legitimate consumer mounts at __proto__/constructor/prototype.

Fixes #302
@cldmv-bot cldmv-bot Bot added ! fix → next v4 flow: fix contributor PR targeting the next integration branch area: core Touches core library / runtime source code area: tests Touches test files, fixtures, or test infrastructure labels Aug 24, 2026
@Shinrai
Shinrai requested a lite review from Copilot August 24, 2026 12:53

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR hardens ApiManager.normalizeApiPath() to reject prototype-polluting mount-path segments (__proto__, constructor, prototype) so api.add() can’t mutate the JS prototype chain when constructing the mounted API tree.

Changes:

  • Add a normalization-time guard that refuses unsafe path segments in any position for both string and array apiPath forms.
  • Add Vitest coverage for rejecting unsafe segments and verifying no Object.prototype pollution.
  • Add a new i18n reason string (API_PATH_REASON_UNSAFE_SEGMENT) across all locales.

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/vitests/suites/api-manager/api-manager-reserved-paths.test.vitest.mjs Adds test cases for unsafe segments and prototype-pollution prevention.
src/lib/handlers/api-manager.mjs Introduces UNSAFE_PATH_SEGMENTS and rejects unsafe segments during path normalization.
src/lib/i18n/languages/en-us.json Adds new error reason string for unsafe segments.
src/lib/i18n/languages/en-gb.json Adds new error reason string for unsafe segments.
src/lib/i18n/languages/de-de.json Adds translation for the new unsafe-segment reason.
src/lib/i18n/languages/es-es.json Adds translation for the new unsafe-segment reason.
src/lib/i18n/languages/es-mx.json Adds translation for the new unsafe-segment reason.
src/lib/i18n/languages/fr-fr.json Adds translation for the new unsafe-segment reason.
src/lib/i18n/languages/hi-in.json Adds translation for the new unsafe-segment reason.
src/lib/i18n/languages/ja-jp.json Adds translation for the new unsafe-segment reason.
src/lib/i18n/languages/ko-kr.json Adds translation for the new unsafe-segment reason.
src/lib/i18n/languages/pt-br.json Adds translation for the new unsafe-segment reason.
src/lib/i18n/languages/ru-ru.json Adds translation for the new unsafe-segment reason.
src/lib/i18n/languages/zh-cn.json Adds translation for the new unsafe-segment reason.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread tests/vitests/suites/api-manager/api-manager-reserved-paths.test.vitest.mjs Outdated
Comment thread src/lib/i18n/languages/ru-ru.json Outdated
The prototype-pollution guard added to normalizeApiPath preempted the dedicated
LOOSE_SET_RESERVED_KEY error that setOwnedProperty raises for `self.<key> = …`
assignments: setOwnedProperty calls normalizeApiPath first, so a
`self.prototype = {}` / `self['__proto__'] = obj` now threw the generic
INVALID_CONFIG_API_PATH_INVALID instead, breaking
runtime/self-assign-prototype-pollution.

Run the reserved-segment check (LOOSE_SET_RESERVED_KEY) before normalizeApiPath
so the self-assign surface keeps its own error while the api.add() mount-path
surface keeps the new unsafe-segment error. Both guards stay reachable and tested.
Address Copilot review on #305:
- The "__-prefixed still accepted" test mounted at "plugins", so it never
  exercised a __-prefixed segment. Mount "__config.value" and a
  ["prototypeName","leaf"] substring path instead, proving the guard matches
  whole segments (only __proto__/constructor/prototype), not prefixes/substrings.
- Capitalize the ru-ru API_PATH_REASON_UNSAFE_SEGMENT string to match that
  locale's sentence-cased sibling messages.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated 1 comment.

Comment thread src/lib/handlers/api-manager.mjs Outdated
Shinrai added a commit to CLDMV/slothlet-vine that referenced this pull request Aug 24, 2026
… keepers

The three slothlet bugs found while building vine now have fix PRs
(CLDMV/slothlet#305/#306/#307 for issues #302/#303/#304). Update the code comments
to reference them — but the guards STAY, and the comments now say why:
- frame.mjs UNSAFE_SEGMENTS is security (validating untrusted REMOTE surface paths
  at vine's boundary), not a bug workaround — independent of the slothlet version.
- grow.mjs hyphen moduleID is zero-cost and works on patched + unpatched slothlet.
- serve.mjs Reflect.apply is idiomatic and shadow-proof — better than leaf.apply
  even once #307 lands.
No behavior change; 330 tests green.
…dProperty

Address Copilot review on #305: the loose-set prototype-pollution guard
allocated a fresh Set on every call and coerced via a template literal, which
throws a raw TypeError on a Symbol instead of the intended SlothletError path.
Reuse the module-level UNSAFE_PATH_SEGMENTS and coerce with String() — branchless
and Symbol-safe.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated no new comments.

@Shinrai
Shinrai merged commit 1863235 into next Aug 24, 2026
25 checks passed
@cldmv-bot
cldmv-bot Bot deleted the fix/proto-pollution-mount-path branch August 24, 2026 20:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: core Touches core library / runtime source code area: tests Touches test files, fixtures, or test infrastructure ! fix → next v4 flow: fix contributor PR targeting the next integration branch

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants