Skip to content

[Bug Report] createTokens: resolve() returns inherited prototype members for unknown path segments #400

Description

@johnleider

Summary

createTokens resolve() walks alias path segments with segment in current, which traverses the prototype chain. When a prefix resolves to a plain-object token value, resolving a path whose final segment names an Object.prototype member (constructor, toString, hasOwnProperty, __proto__, …) returns that inherited member instead of undefined + the "Path not found" warning.

Location

  • packages/0/src/composables/createTokens/index.ts:234-243resolve() uses if (!isObject(current) || !(segment in current)).
  • Contrast its twin flatten() (:419-420, :437-438), which already guards with Object.prototype.hasOwnProperty.call(...) + UNSAFE_KEYS. UNSAFE_KEYS is imported (:36) but used only in flatten.
  • Reachable when a whole object is registered as a token value (flat mode, :455-457) or a { $value: {...} } alias is unwrapped (:232 / :242).

Impact

This is a read of an inherited member, not a write — not prototype pollution, no info disclosure beyond global builtins, no attacker surface (token config and the resolve() argument are developer-authored; createTokens backs useTheme / useLocale / useFeatures). The real-world bite is a correctness inconsistency: resolve() can hand back a builtin function instead of undefined. Low severity — consistency / defense-in-depth.

Suggested fix

Mirror flatten's guard in the resolve loop (:235):

if (!isObject(current) || UNSAFE_KEYS.has(segment) || !Object.prototype.hasOwnProperty.call(current, segment))

Add tests asserting resolve('{obj.constructor}') / resolve('{obj.__proto__}') return undefined. Since flatten can never register an unsafe/inherited key, mirroring the guard carries zero false-negative risk. Describe it in the changelog as "resolve() no longer returns inherited prototype members," not as a security fix.

Metadata

Metadata

Assignees

Labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions