Skip to content

fix(view): shadow dangerous globals in with-scoped evaluator - #188

Merged
JosunLP merged 2 commits into
devfrom
fix/168-view-evaluator-has-trap
Jul 6, 2026
Merged

fix(view): shadow dangerous globals in with-scoped evaluator#188
JosunLP merged 2 commits into
devfrom
fix/168-view-evaluator-has-trap

Conversation

@JosunLP

@JosunLP JosunLP commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator

Summary

Fixes #168 (Medium hardening).

evaluate/evaluateRaw run new Function('$ctx', 'with($ctx){ return (expr); }'). The proxy has trap returned prop in target, so inherited Object.prototype members resolved from the context and constructor.constructor('…')() reached the Function constructor — arbitrary code execution wherever bq-* attributes carry untrusted content.

Fix

The issue's suggested own-keys-only has trap is necessary but not sufficient: once the proxy declines constructor, with resolution falls through to the enclosing global scope, where the global object inherits constructor from Object.prototype and exposes Function/eval/globalThis. A bare Function('…')() escapes the same way.

So the evaluator now shadows a denylist (constructor, __proto__, prototype, Function, eval, globalThis, global, window, self, top, parent): has reports them present (so they never fall through to the global scope) and get resolves them to undefined unless the context legitimately owns that property. Member access on the resulting undefined throws → undefined. Both the lazy (evaluate) and raw (evaluateRaw) paths are covered.

Legitimate templates are unaffected: own context props (including ones shadowing a dangerous name), arithmetic, and method calls on context values (e.g. name.toUpperCase()) all still work.

Verification

  • Tests: constructor.constructor(…)() on both evaluate and evaluateRaw, bare Function(…)() and eval(…), plus positive cases (own props, method calls, own-prop shadowing a global name). The exploit tests fail on the pre-fix code.
  • Full suite: 2993 pass / 0 fail. tsc --noEmit + eslint clean.

🤖 Generated with Claude Code

The evaluate/evaluateRaw proxies compiled new Function('$ctx',
'with($ctx){ return (expr); }'). The has trap returned 'prop in target',
so inherited Object.prototype members resolved from the context and
constructor.constructor('...')() reached the Function constructor (RCE
where bq-* attributes carry untrusted content).

Hardening the has trap to own-keys-only is necessary but not sufficient:
once the proxy declines 'constructor', with resolution falls through to
the global scope, where the global object inherits 'constructor' from
Object.prototype and exposes Function/eval/globalThis. So the evaluator
now *shadows* a denylist (constructor, __proto__, prototype, Function,
eval, globalThis, global, window, self, top, parent): has reports them
present and get resolves them to undefined unless the context owns that
property. Member access on undefined then throws and yields undefined.
Both the lazy (evaluate) and raw (evaluateRaw) contexts are covered.

Fixes #168

Co-Authored-By: Claude Fable 5 <[email protected]>
@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d46d5108-916b-4db5-856c-ee4315251592

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/168-view-evaluator-has-trap

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.

@github-actions github-actions Bot added view Changes to the view module tests Chenges to the tests labels Jul 6, 2026
…heck

evaluate<T=unknown> resolves to any in the test type-check context, which
selected bun's Matchers<undefined> expect overload and broke .toBe(value).
Explicit type args make the matcher type unambiguous.

Co-Authored-By: Claude Fable 5 <[email protected]>
@JosunLP
JosunLP merged commit 8e6d337 into dev Jul 6, 2026
9 checks passed
@JosunLP
JosunLP deleted the fix/168-view-evaluator-has-trap branch July 6, 2026 19:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

tests Chenges to the tests view Changes to the view module

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant