Skip to content

feat: v11 scope security and ownPropertyOnly hardening (#898) - #938

Merged
harttle merged 24 commits into
nextfrom
feat/scope-security
Jul 23, 2026
Merged

feat: v11 scope security and ownPropertyOnly hardening (#898)#938
harttle merged 24 commits into
nextfrom
feat/scope-security

Conversation

@harttle

@harttle harttle commented Jul 15, 2026

Copy link
Copy Markdown
Owner

Summary

  • Block template reads of __proto__, constructor, and prototype when ownPropertyOnly is true (default) in readJSProperty; with false, full prototype access is allowed—sanitize untrusted scope input (e.g. bourne).
  • Align findScope with ownPropertyOnly (own-property vs in checks on scope frames and environments) before falling back to globals; no separate write-path blocking—null-prototype managed frames from push() cover tag writes.
  • Context.push() wraps plain objects via createScope (Object.assign(Object.create(null), …)); Drop instances are pushed as-is. Tags use the returned scope for in-place updates.
  • Move readSize / readFirst / readLast onto Context so inherited size on plain objects is not surfaced when ownPropertyOnly is true. Iteration/Symbol.iterator behavior unchanged (documented exception in Security Model).
  • Expand Security Model tutorial; shorten ownPropertyOnly in options tutorial and LiquidOptions JSDoc (one line + link).

Breaking change (v11)

  • Context.push() now returns the pushed Scope instead of the numeric result of Array.prototype.push().

Test plan

  • npm run build && npm test -- context.spec scope-security
  • ownPropertyOnly: true blocks own/inherited dangerous keys and inherited size on plain objects (context.spec, scope-security.spec.ts)
  • ownPropertyOnly: false allows own/inherited blocked keys when explicitly opted out (context.spec)
  • Inherited Symbol.iterator on plain objects still works; Drop iteration unchanged (scope-security.spec.ts)

Closes #898

Co-authored-by: Cursor <cursoragent@cursor.com>
@coveralls

coveralls commented Jul 15, 2026

Copy link
Copy Markdown

Coverage Report for CI Build 30026771598

Warning

No base build found for commit 61ed163 on next.
Coverage changes can't be calculated without a base build.
If a base build is processing, this comment will update automatically when it completes.

Coverage: 99.528%

Details

  • Patch coverage: 32 of 32 lines across 7 files are fully covered (100%).

Uncovered Changes

No uncovered changes found.

Coverage Regressions

Requires a base build to compare against. How to fix this →


Coverage Stats

Coverage Status
Relevant Lines: 2973
Covered Lines: 2964
Line Coverage: 99.7%
Relevant Branches: 1052
Covered Branches: 1042
Branch Coverage: 99.05%
Branches in Coverage %: Yes
Coverage Strength: 18997.06 hits per line

💛 - Coveralls

harttle and others added 22 commits July 15, 2026 23:10
Co-authored-by: Cursor <cursoragent@cursor.com>
Block writes to dangerous keys in assign/capture/increment/decrement, use own-property Symbol.iterator for plain objects when ownPropertyOnly is true, fix inherited size reads, and sanitize filter iteration scopes.

Co-authored-by: Cursor <cursoragent@cursor.com>
Block __proto__, constructor, and prototype only when ownPropertyOnly
is true or when access would traverse the prototype chain. Allow own
properties with those names when ownPropertyOnly is false.

Co-authored-by: Cursor <cursoragent@cursor.com>
Iteration is documented as an ownPropertyOnly exception; restore
isIterable/toEnumerable and document inherited Symbol.iterator behavior.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Use shouldBlockScopeKeyRead in findScope hasKey so inherited
constructor/__proto__/prototype do not falsely match environments.
Remove redundant globals hasKey check; globals remains the fallback scope.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Move readSize, readFirst, and readLast to private Context methods using this.ownPropertyOnly. Remove redundant shouldBlockScopeKeyRead from findScope.

Co-authored-by: Cursor <cursoragent@cursor.com>
Centralize null-prototype scope creation in push() so callers pass plain objects; Drop instances and existing null-proto frames are pushed as-is. Remove sanitizeScope in favor of createScope via Object.assign.
Write blocking on assign/capture/increment/decrement duplicated read-side
protection in readJSProperty; null-proto scopes from push already prevent
prototype pollution on managed writes.

Co-authored-by: Cursor <cursoragent@cursor.com>
Restore null-prototype hardening for Jekyll include bindings, colocate blocked-key checks with readJSProperty, align ownPropertyOnly JSDoc with security docs, and drop integration tests duplicated in context.spec.

Co-authored-by: Cursor <cursoragent@cursor.com>
Drop null-prototype passthrough in push(), inline blocked-key checks,
remove redundant createScope at include tag, trim verbose docs, and
drop implementation-detail unit tests.

Co-authored-by: Cursor <cursoragent@cursor.com>
Inline findScope and blocked-key checks, shorten ownPropertyOnly docs,
and drop implementation-detail push() unit tests.

Co-authored-by: Cursor <cursoragent@cursor.com>
Set.has already returns false for non-string PropertyKey values; widen
BLOCKED_SCOPE_KEYS type so TypeScript accepts the direct has(key) call.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Split the OR condition into two explicit checks so inherited proto keys are always blocked and own proto keys are blocked only when ownPropertyOnly is true.

Co-authored-by: Cursor <cursoragent@cursor.com>
Proto keys block inherited access only; ownPropertyOnly is checked once before return for all keys. Own __proto__/constructor/prototype properties are readable—sanitize untrusted scope input.

Co-authored-by: Cursor <cursoragent@cursor.com>
Proto keys were incorrectly blocked even when ownPropertyOnly=false.
Inherited access is now gated only by ownPropertyOnly; docs updated.

Co-authored-by: Cursor <cursoragent@cursor.com>
Dangerous keys (__proto__, constructor, prototype) are blocked only when
ownPropertyOnly is true (default). With false, full prototype access is
allowed as an explicit opt-out; use bourne for untrusted input.

Co-authored-by: Cursor <cursoragent@cursor.com>
Details live in Security Model; keep options.md consistent with strictFilters/strictVariables tone.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@harttle harttle changed the title feat: block dangerous scope keys and harden findScope (#898) feat: harden scope reads via ownPropertyOnly and null-proto frames (#898) Jul 23, 2026
@harttle harttle changed the title feat: harden scope reads via ownPropertyOnly and null-proto frames (#898) feat: v11 scope security and ownPropertyOnly hardening (#898) Jul 23, 2026
Co-authored-by: Cursor <cursoragent@cursor.com>
@harttle
harttle merged commit 964a63b into next Jul 23, 2026
10 checks passed
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.

2 participants