fix: refuse prototype-polluting mount-path segments in api.add() - #305
Merged
Conversation
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
There was a problem hiding this comment.
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.prototypepollution. - 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.
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.
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.
Shinrai
approved these changes
Aug 24, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
🚀 What's Changed
💥 Breaking Changes
No breaking changes
✨ Features
No new features
🐛 Bug Fixes
📦 Dependencies
No dependency updates
🔧 Other Changes
👥 Contributors