From b51b6f2e5fd10e6a333225b72a3e3aec54e5463e Mon Sep 17 00:00:00 2001 From: velzie Date: Wed, 23 Jul 2025 11:48:30 -0400 Subject: [PATCH 01/18] [core][rewriter] dpsc: partially implement WrapAccess --- rewriter/js/src/changes.rs | 28 ++++++++++++--- rewriter/js/src/rewrite.rs | 29 +++++++++++++--- rewriter/js/src/visitor.rs | 70 +++++++++++++++++++++++++------------- 3 files changed, 95 insertions(+), 32 deletions(-) diff --git a/rewriter/js/src/changes.rs b/rewriter/js/src/changes.rs index c0e72bd1..a05d6f36 100644 --- a/rewriter/js/src/changes.rs +++ b/rewriter/js/src/changes.rs @@ -30,9 +30,18 @@ pub(crate) use change; #[derive(Debug, PartialEq, Eq)] pub enum JsChangeType<'alloc: 'data, 'data> { /// insert `${cfg.wrapfn}(` - WrapFnLeft { wrap: bool }, + WrapFnLeft { enclose: bool }, /// insert `,strictchecker)` - WrapFnRight { wrap: bool }, + WrapFnRight { enclose: bool }, + + WrapAccessLeft { + ident: Atom<'data>, + enclose: bool, + }, + WrapAccessRight { + enclose: bool, + }, + /// insert `${cfg.setrealmfn}({}).` SetRealmFn, /// insert `${cfg.wrapthis}(` @@ -94,16 +103,27 @@ impl<'alloc: 'data, 'data> Transform<'data> for JsChange<'alloc, 'data> { use JsChangeType as Ty; use TransformLL as LL; match self.ty { - Ty::WrapFnLeft { wrap } => LL::insert(if wrap { + Ty::WrapFnLeft { enclose } => LL::insert(if enclose { transforms!["(", &cfg.wrapfn, "("] } else { transforms![&cfg.wrapfn, "("] }), - Ty::WrapFnRight { wrap } => LL::insert(if wrap { + Ty::WrapFnRight { enclose } => LL::insert(if enclose { transforms![",", STRICTCHECKER, "))"] } else { transforms![",", STRICTCHECKER, ")"] }), + Ty::WrapAccessLeft { + ident, + enclose, + } => if enclose { + LL::insert(transforms!["(", "$scramjet$wrap", ident, "("]) + } else {LL::insert(transforms!["$scramjet$wrap", ident, "("])}, + Ty::WrapAccessRight { enclose } => LL::insert(if enclose { + transforms!["))"] + } else { + transforms![")"] + }), Ty::SetRealmFn => LL::insert(transforms![&cfg.setrealmfn, "({})."]), Ty::WrapThisFn => LL::insert(transforms![&cfg.wrapthisfn, "("]), Ty::ScramErrFn { ident } => LL::insert(transforms!["$scramerr(", ident, ");"]), diff --git a/rewriter/js/src/rewrite.rs b/rewriter/js/src/rewrite.rs index a188323a..7baa309f 100644 --- a/rewriter/js/src/rewrite.rs +++ b/rewriter/js/src/rewrite.rs @@ -17,17 +17,24 @@ pub(crate) use rewrite; pub(crate) enum RewriteType<'alloc: 'data, 'data> { /// `(cfg.wrapfn(ident,strictchecker))` | `cfg.wrapfn(ident,strictchecker)` WrapFn { - wrap: bool, + enclose: bool, }, /// `cfg.setrealmfn({}).ident` SetRealmFn, /// `cfg.wrapthis(this)` WrapThisFn, + /// `(cfg.importfn("cfg.base"))` ImportFn, /// `cfg.metafn("cfg.base")` MetaFn, + /// `window.location` -> cfg.wraplocation(window) + WrapAccess { + ident: Atom<'data>, + propspan: Span, + enclose: bool, + }, // dead code only if debug is disabled #[allow(dead_code)] /// `$scramerr(name)` @@ -93,9 +100,23 @@ impl<'alloc: 'data, 'data> RewriteType<'alloc, 'data> { } match self { - Self::WrapFn { wrap } => smallvec![ - change!(span!(start), WrapFnLeft { wrap }), - change!(span!(end), WrapFnRight { wrap }), + Self::WrapFn { enclose } => smallvec![ + change!(span!(start), WrapFnLeft { enclose }), + change!(span!(end), WrapFnRight { enclose }), + ], + Self::WrapAccess { + ident, + propspan, + enclose, + } => smallvec![ + change!(span!(start), WrapAccessLeft { + ident, + enclose, + }), + change!(propspan, Delete), + change!(span!(end), WrapAccessRight { + enclose, + }), ], Self::SetRealmFn => smallvec![change!(span, SetRealmFn)], Self::WrapThisFn => smallvec![ diff --git a/rewriter/js/src/visitor.rs b/rewriter/js/src/visitor.rs index f5c8710d..cb4d465f 100644 --- a/rewriter/js/src/visitor.rs +++ b/rewriter/js/src/visitor.rs @@ -23,16 +23,10 @@ use crate::{ // // maybe move this out of this lib? const UNSAFE_GLOBALS: &[&str] = &[ - "window", - "self", - "globalThis", - "this", "parent", "top", "location", - "document", - "eval", - "frames", + "eval" ]; pub struct Visitor<'alloc, 'data, E> @@ -71,17 +65,25 @@ where fn rewrite_ident(&mut self, name: &Atom, span: Span) { if UNSAFE_GLOBALS.contains(&name.as_str()) { - self.jschanges.add(rewrite!(span, WrapFn { wrap: true })); + self.jschanges.add(rewrite!(span, WrapFn { enclose: true })); } } fn walk_member_expression(&mut self, it: &Expression) -> bool { match it { Expression::Identifier(s) => { - self.rewrite_ident(&s.name, s.span); - true + false } - Expression::StaticMemberExpression(s) => self.walk_member_expression(&s.object), + Expression::StaticMemberExpression(s) => { + if UNSAFE_GLOBALS.contains(&s.property.name.as_str()) { + // self.jschanges.add(rewrite!(s.span, WrapAccess { + // ident: s.property.name, + // propspan: s.property.span, + // } + // )); + } + self.walk_member_expression(&s.object) + }, Expression::ComputedMemberExpression(s) => self.walk_member_expression(&s.object), _ => false, } @@ -109,7 +111,7 @@ where // if UNSAFE_GLOBALS.contains(&it.name.as_str()) { self.jschanges - .add(rewrite!(it.span, WrapFn { wrap: false })); + .add(rewrite!(it.span, WrapFn { enclose: false })); } // } } @@ -132,21 +134,41 @@ where return; // unwise to walk the rest of the tree } - if !self.flags.strict_rewrites - && !UNSAFE_GLOBALS.contains(&s.property.name.as_str()) - && let Expression::Identifier(_) | Expression::ThisExpression(_) = &s.object - { - // cull tree - this should be safe - return; - } + if UNSAFE_GLOBALS.contains(&s.property.name.as_str()) { + self.jschanges.add(rewrite!(it.span(), WrapAccess { + ident: s.property.name, + propspan: Span::new(s.property.span.start-1, s.property.span.end), + enclose: false, + })); + } + // if !self.flags.strict_rewrites + // && !UNSAFE_GLOBALS.contains(&s.property.name.as_str()) + // && let Expression::Identifier(_) | Expression::ThisExpression(_) = &s.object + // { + // // cull tree - this should be safe + // return; + // } + + // if self.flags.scramitize + // && !matches!(s.object, Expression::MetaProperty(_) | Expression::Super(_)) + // { + // self.scramitize(s.object.span()); + // } + } - if self.flags.scramitize - && !matches!(s.object, Expression::MetaProperty(_) | Expression::Super(_)) - { - self.scramitize(s.object.span()); - } + match &it.object() { + Expression::Identifier(_) => { + return; + } + Expression::ThisExpression(_)=> { + // this is safe, we don't need to walk it + return; + } + _=>{} } + + walk::walk_member_expression(self, it); } fn visit_this_expression(&mut self, it: &ThisExpression) { From 3d35c629f6c6072039b7f795dcdad5593f45c9ed Mon Sep 17 00:00:00 2001 From: velzie Date: Wed, 23 Jul 2025 13:13:16 -0400 Subject: [PATCH 02/18] [core][rewriter] dpsc: implement WrapSet and remove WrapThis --- rewriter/js/src/cfg.rs | 5 ++++- rewriter/js/src/changes.rs | 37 +++++++++++++++++++++++-------------- rewriter/js/src/rewrite.rs | 36 +++++++++++++++++++++++------------- rewriter/js/src/visitor.rs | 24 +++++++++++++++++------- 4 files changed, 67 insertions(+), 35 deletions(-) diff --git a/rewriter/js/src/cfg.rs b/rewriter/js/src/cfg.rs index abf08c7a..e9283ef1 100644 --- a/rewriter/js/src/cfg.rs +++ b/rewriter/js/src/cfg.rs @@ -17,7 +17,10 @@ pub struct Config { pub prefix: String, pub wrapfn: String, - pub wrapthisfn: String, + pub wrapgetbase: String, + pub wrapsetbase: String, + pub wrapcomputedgetfn: String, + pub wrapcomputedsetfn: String, pub importfn: String, pub rewritefn: String, pub setrealmfn: String, diff --git a/rewriter/js/src/changes.rs b/rewriter/js/src/changes.rs index a05d6f36..a083435d 100644 --- a/rewriter/js/src/changes.rs +++ b/rewriter/js/src/changes.rs @@ -34,18 +34,21 @@ pub enum JsChangeType<'alloc: 'data, 'data> { /// insert `,strictchecker)` WrapFnRight { enclose: bool }, - WrapAccessLeft { + WrapGetLeft { ident: Atom<'data>, enclose: bool, }, - WrapAccessRight { + WrapGetRight { enclose: bool, }, + WrapSet { + ident: Atom<'data>, + propspan: Span + }, + /// insert `${cfg.setrealmfn}({}).` SetRealmFn, - /// insert `${cfg.wrapthis}(` - WrapThisFn, /// insert `$scramerr(ident);` ScramErrFn { ident: Atom<'data> }, /// insert `$scramitize(` @@ -113,19 +116,25 @@ impl<'alloc: 'data, 'data> Transform<'data> for JsChange<'alloc, 'data> { } else { transforms![",", STRICTCHECKER, ")"] }), - Ty::WrapAccessLeft { + Ty::WrapGetLeft { ident, enclose, - } => if enclose { - LL::insert(transforms!["(", "$scramjet$wrap", ident, "("]) - } else {LL::insert(transforms!["$scramjet$wrap", ident, "("])}, - Ty::WrapAccessRight { enclose } => LL::insert(if enclose { - transforms!["))"] - } else { - transforms![")"] - }), + } => LL::insert(if enclose { + transforms!["(", &cfg.wrapgetbase, ident, "("] + } else { + transforms![&cfg.wrapgetbase, ident, "("] + }), + Ty::WrapGetRight { enclose } => LL::insert(if enclose { + transforms!["))"] + } else { + transforms![")"] + }), + Ty::WrapSet { ident, propspan } => LL::insert(transforms![ + &cfg.wrapsetbase, + ident, + "(" + ]), Ty::SetRealmFn => LL::insert(transforms![&cfg.setrealmfn, "({})."]), - Ty::WrapThisFn => LL::insert(transforms![&cfg.wrapthisfn, "("]), Ty::ScramErrFn { ident } => LL::insert(transforms!["$scramerr(", ident, ");"]), Ty::ScramitizeFn => LL::insert(transforms![" $scramitize("]), Ty::EvalRewriteFn => LL::replace(transforms!["eval(", &cfg.rewritefn, "("]), diff --git a/rewriter/js/src/rewrite.rs b/rewriter/js/src/rewrite.rs index 7baa309f..2e566f7a 100644 --- a/rewriter/js/src/rewrite.rs +++ b/rewriter/js/src/rewrite.rs @@ -21,8 +21,6 @@ pub(crate) enum RewriteType<'alloc: 'data, 'data> { }, /// `cfg.setrealmfn({}).ident` SetRealmFn, - /// `cfg.wrapthis(this)` - WrapThisFn, /// `(cfg.importfn("cfg.base"))` ImportFn, @@ -30,11 +28,18 @@ pub(crate) enum RewriteType<'alloc: 'data, 'data> { MetaFn, /// `window.location` -> cfg.wraplocation(window) - WrapAccess { + WrapGet { ident: Atom<'data>, propspan: Span, enclose: bool, }, + /// `window.location` -> cfg.wraplocation(window) + WrapSet { + ident: Atom<'data>, + propspan: Span, + leftspan: Span, + rightspan: Span, + }, // dead code only if debug is disabled #[allow(dead_code)] /// `$scramerr(name)` @@ -104,31 +109,36 @@ impl<'alloc: 'data, 'data> RewriteType<'alloc, 'data> { change!(span!(start), WrapFnLeft { enclose }), change!(span!(end), WrapFnRight { enclose }), ], - Self::WrapAccess { + Self::WrapGet { ident, propspan, enclose, } => smallvec![ - change!(span!(start), WrapAccessLeft { + change!(span!(start), WrapGetLeft { ident, enclose, }), change!(propspan, Delete), - change!(span!(end), WrapAccessRight { + change!(span!(end), WrapGetRight { enclose, }), ], - Self::SetRealmFn => smallvec![change!(span, SetRealmFn)], - Self::WrapThisFn => smallvec![ - change!(span!(start), WrapThisFn), - change!( + Self::WrapSet { ident, propspan, leftspan, rightspan } => smallvec![ + change!(span!(start), WrapSet { + ident, + propspan, + }), + change!(propspan, Delete), + change!(Span::new(leftspan.end, rightspan.start), Replace { text: "," }), + change!( span!(end), ClosingParen { semi: false, - replace: false + replace: true } - ), - ], + ) + ], + Self::SetRealmFn => smallvec![change!(span, SetRealmFn)], Self::ImportFn => smallvec![change!(span, ImportFn)], Self::MetaFn => smallvec![change!(span, MetaFn)], Self::ScramErr { ident } => smallvec![change!(span!(end), ScramErrFn { ident })], diff --git a/rewriter/js/src/visitor.rs b/rewriter/js/src/visitor.rs index cb4d465f..f8450ac7 100644 --- a/rewriter/js/src/visitor.rs +++ b/rewriter/js/src/visitor.rs @@ -135,7 +135,7 @@ where } if UNSAFE_GLOBALS.contains(&s.property.name.as_str()) { - self.jschanges.add(rewrite!(it.span(), WrapAccess { + self.jschanges.add(rewrite!(it.span(), WrapGet { ident: s.property.name, propspan: Span::new(s.property.span.start-1, s.property.span.end), enclose: false, @@ -171,9 +171,6 @@ where walk::walk_member_expression(self, it); } - fn visit_this_expression(&mut self, it: &ThisExpression) { - self.jschanges.add(rewrite!(it.span, WrapThisFn)); - } fn visit_debugger_statement(&mut self, it: &DebuggerStatement) { // delete debugger statements entirely. some sites will spam debugger as an anti-debugging measure, and we don't want that! @@ -291,9 +288,9 @@ where walk::walk_unary_expression(self, it); } - fn visit_update_expression(&mut self, _it: &UpdateExpression<'data>) { - // then no, don't walk it, we don't care - } + // fn visit_update_expression(&mut self, _it: &UpdateExpression<'data>) { + // // this is like a ++ or -- operator + // } fn visit_meta_property(&mut self, it: &MetaProperty<'data>) { if it.meta.name == "import" { @@ -319,6 +316,19 @@ where return; } } + AssignmentTarget::StaticMemberExpression(s) =>{ + if UNSAFE_GLOBALS.contains(&s.property.name.as_str()) { + self.jschanges.add(rewrite!(it.span, WrapSet { + ident: s.property.name, + propspan: Span::new(s.property.span.start-1, s.property.span.end), + leftspan: s.span(), + rightspan: it.right.span(), + })); + } + + // more to walk + walk::walk_expression(self, &s.object); + } AssignmentTarget::ArrayAssignmentTarget(_) => { // [location] = ["https://example.com"] // this is such a ridiculously specific edge case. just ignore it From 5f07a127b4a9772244887ef28405b9497894ef69 Mon Sep 17 00:00:00 2001 From: velzie Date: Wed, 23 Jul 2025 13:35:40 -0400 Subject: [PATCH 03/18] [core][rewriter] dpsc: implement WrapGetComputed --- rewriter/js/src/changes.rs | 8 ++++++++ rewriter/js/src/rewrite.rs | 23 +++++++++++++++++++++ rewriter/js/src/visitor.rs | 42 +++++++++++++++++++++++--------------- 3 files changed, 57 insertions(+), 16 deletions(-) diff --git a/rewriter/js/src/changes.rs b/rewriter/js/src/changes.rs index a083435d..ca1b12a4 100644 --- a/rewriter/js/src/changes.rs +++ b/rewriter/js/src/changes.rs @@ -38,6 +38,9 @@ pub enum JsChangeType<'alloc: 'data, 'data> { ident: Atom<'data>, enclose: bool, }, + WrapGetComputedLeft { + enclose: bool, + }, WrapGetRight { enclose: bool, }, @@ -124,6 +127,11 @@ impl<'alloc: 'data, 'data> Transform<'data> for JsChange<'alloc, 'data> { } else { transforms![&cfg.wrapgetbase, ident, "("] }), + Ty::WrapGetComputedLeft { enclose } => LL::insert(if enclose { + transforms!["(", &cfg.wrapcomputedgetfn, "("] + } else { + transforms![&cfg.wrapcomputedgetfn, "("] + }), Ty::WrapGetRight { enclose } => LL::insert(if enclose { transforms!["))"] } else { diff --git a/rewriter/js/src/rewrite.rs b/rewriter/js/src/rewrite.rs index 2e566f7a..7fd3468e 100644 --- a/rewriter/js/src/rewrite.rs +++ b/rewriter/js/src/rewrite.rs @@ -33,6 +33,12 @@ pub(crate) enum RewriteType<'alloc: 'data, 'data> { propspan: Span, enclose: bool, }, + /// `window["location"]` -> cfg.wrapgetcomputed(window, "location") + WrapGetComputed { + leftspan: Span, + propspan: Span, + enclose: bool, + }, /// `window.location` -> cfg.wraplocation(window) WrapSet { ident: Atom<'data>, @@ -40,6 +46,12 @@ pub(crate) enum RewriteType<'alloc: 'data, 'data> { leftspan: Span, rightspan: Span, }, + /// `cfg.wrapcomputedsetfn(window, "location", t)` + WrapSetComputed { + propspan: Span, + leftspan: Span, + rightspan: Span, + }, // dead code only if debug is disabled #[allow(dead_code)] /// `$scramerr(name)` @@ -122,6 +134,16 @@ impl<'alloc: 'data, 'data> RewriteType<'alloc, 'data> { change!(span!(end), WrapGetRight { enclose, }), + ], + Self::WrapGetComputed { leftspan, propspan, enclose } => smallvec![ + change!(span!(start), WrapGetComputedLeft { + enclose + }), + // replace the bracket with , + change!(Span::new(leftspan.end, propspan.start), Replace { text: "," }), + // replace the other bracket with ) + change!(Span::new(propspan.end, propspan.end + 1), ClosingParen { semi: false, replace: true }), + ], Self::WrapSet { ident, propspan, leftspan, rightspan } => smallvec![ change!(span!(start), WrapSet { @@ -138,6 +160,7 @@ impl<'alloc: 'data, 'data> RewriteType<'alloc, 'data> { } ) ], + RewriteType::WrapSetComputed { .. } => todo!(), Self::SetRealmFn => smallvec![change!(span, SetRealmFn)], Self::ImportFn => smallvec![change!(span, ImportFn)], Self::MetaFn => smallvec![change!(span, MetaFn)], diff --git a/rewriter/js/src/visitor.rs b/rewriter/js/src/visitor.rs index f8450ac7..ebdc1f15 100644 --- a/rewriter/js/src/visitor.rs +++ b/rewriter/js/src/visitor.rs @@ -122,25 +122,35 @@ where } fn visit_member_expression(&mut self, it: &MemberExpression<'data>) { - // TODO - // you could break this with ["postMessage"] etc - // however this code only exists because of recaptcha whatever - // and it would slow down js execution a lot - if let MemberExpression::StaticMemberExpression(s) = it { - if s.property.name == "postMessage" { - self.jschanges.add(rewrite!(s.property.span, SetRealmFn)); - - walk::walk_expression(self, &s.object); - return; // unwise to walk the rest of the tree - } + match &it { + MemberExpression::StaticMemberExpression(s) =>{ + // TODO + // you could break this with ["postMessage"] etc + // however this code only exists because of recaptcha whatever + // and it would slow down js execution a lot + if s.property.name == "postMessage" { + self.jschanges.add(rewrite!(s.property.span, SetRealmFn)); + + walk::walk_expression(self, &s.object); + return; // unwise to walk the rest of the tree + } - if UNSAFE_GLOBALS.contains(&s.property.name.as_str()) { - self.jschanges.add(rewrite!(it.span(), WrapGet { - ident: s.property.name, - propspan: Span::new(s.property.span.start-1, s.property.span.end), + if UNSAFE_GLOBALS.contains(&s.property.name.as_str()) { + self.jschanges.add(rewrite!(it.span(), WrapGet { + ident: s.property.name, + propspan: Span::new(s.property.span.start-1, s.property.span.end), + enclose: false, + })); + } + } + MemberExpression::ComputedMemberExpression(s) => { + self.jschanges.add(rewrite!(it.span(), WrapGetComputed { + leftspan: s.object.span(), + propspan: s.expression.span(), enclose: false, })); - } + } + _=>{} // if !self.flags.strict_rewrites // && !UNSAFE_GLOBALS.contains(&s.property.name.as_str()) // && let Expression::Identifier(_) | Expression::ThisExpression(_) = &s.object From 237bf134135da1e8cf78e1cc54d3b688b4d3eb00 Mon Sep 17 00:00:00 2001 From: velzie Date: Thu, 24 Jul 2025 09:26:03 -0400 Subject: [PATCH 04/18] [core][rewriter] dpsc: fix nested WrapGet s --- rewriter/js/src/changes.rs | 2 +- rewriter/js/src/rewrite.rs | 6 ++++-- rewriter/js/src/visitor.rs | 2 +- rewriter/native/src/main.rs | 14 ++++++++++++-- rewriter/native/src/rewriter.rs | 5 ++++- 5 files changed, 22 insertions(+), 7 deletions(-) diff --git a/rewriter/js/src/changes.rs b/rewriter/js/src/changes.rs index ca1b12a4..ddcbfbe2 100644 --- a/rewriter/js/src/changes.rs +++ b/rewriter/js/src/changes.rs @@ -132,7 +132,7 @@ impl<'alloc: 'data, 'data> Transform<'data> for JsChange<'alloc, 'data> { } else { transforms![&cfg.wrapcomputedgetfn, "("] }), - Ty::WrapGetRight { enclose } => LL::insert(if enclose { + Ty::WrapGetRight { enclose } => LL::replace(if enclose { transforms!["))"] } else { transforms![")"] diff --git a/rewriter/js/src/rewrite.rs b/rewriter/js/src/rewrite.rs index 7fd3468e..b1a55f53 100644 --- a/rewriter/js/src/rewrite.rs +++ b/rewriter/js/src/rewrite.rs @@ -101,6 +101,8 @@ impl<'alloc: 'data, 'data> Rewrite<'alloc, 'data> { impl<'alloc: 'data, 'data> RewriteType<'alloc, 'data> { fn into_inner(self, span: Span) -> SmallVec<[JsChange<'alloc, 'data>; 2]> { + + dbg!(&self); macro_rules! span { (start) => { Span::new(span.start, span.start) @@ -130,8 +132,8 @@ impl<'alloc: 'data, 'data> RewriteType<'alloc, 'data> { ident, enclose, }), - change!(propspan, Delete), - change!(span!(end), WrapGetRight { + // change!(propspan, Delete), + change!(Span::new(propspan.start-1, propspan.end), WrapGetRight { enclose, }), ], diff --git a/rewriter/js/src/visitor.rs b/rewriter/js/src/visitor.rs index ebdc1f15..6d8d37e9 100644 --- a/rewriter/js/src/visitor.rs +++ b/rewriter/js/src/visitor.rs @@ -138,7 +138,7 @@ where if UNSAFE_GLOBALS.contains(&s.property.name.as_str()) { self.jschanges.add(rewrite!(it.span(), WrapGet { ident: s.property.name, - propspan: Span::new(s.property.span.start-1, s.property.span.end), + propspan: Span::new(s.property.span.start, s.property.span.end), enclose: false, })); } diff --git a/rewriter/native/src/main.rs b/rewriter/native/src/main.rs index 51de7835..492fd760 100644 --- a/rewriter/native/src/main.rs +++ b/rewriter/native/src/main.rs @@ -25,8 +25,14 @@ pub struct RewriterOptions { prefix: String, #[clap(long, default_value = "$wrap")] wrapfn: String, - #[clap(long, default_value = "$gwrap")] - wrapthisfn: String, + #[clap(long, default_value = "$get_")] + wrapgetbase: String, + #[clap(long, default_value = "$set_")] + wrapsetbase: String, + #[clap(long, default_value = "$computedget")] + wrapcomputedgetfn: String, + #[clap(long, default_value = "$computedset")] + wrapcomputedsetfn: String, #[clap(long, default_value = "$import")] importfn: String, #[clap(long, default_value = "$rewrite")] @@ -102,6 +108,10 @@ fn main() -> Result<()> { ); let unrewritten = NativeRewriter::unrewrite(&res); + // println!( + // "unrewritten:\n{}", + // str::from_utf8(&unrewritten).context("failed to parse unrewritten js")? + // ); eprintln!("errors:"); for err in res.errors { diff --git a/rewriter/native/src/rewriter.rs b/rewriter/native/src/rewriter.rs index 0094eedb..8454c461 100644 --- a/rewriter/native/src/rewriter.rs +++ b/rewriter/native/src/rewriter.rs @@ -52,7 +52,10 @@ impl NativeRewriter { Config { prefix: cfg.prefix.clone(), wrapfn: cfg.wrapfn.clone(), - wrapthisfn: cfg.wrapthisfn.clone(), + wrapgetbase: cfg.wrapgetbase.clone(), + wrapsetbase: cfg.wrapsetbase.clone(), + wrapcomputedgetfn: cfg.wrapcomputedgetfn.clone(), + wrapcomputedsetfn: cfg.wrapcomputedsetfn.clone(), importfn: cfg.importfn.clone(), rewritefn: cfg.rewritefn.clone(), metafn: cfg.metafn.clone(), From 4115c03d794141c96ea0c8140af73ba1d64ed9f2 Mon Sep 17 00:00:00 2001 From: velzie Date: Thu, 24 Jul 2025 09:42:44 -0400 Subject: [PATCH 05/18] [core][rewriter] dpsc: implement WrapSetComputed --- rewriter/js/src/changes.rs | 5 +++++ rewriter/js/src/rewrite.rs | 15 ++++++++++++++- rewriter/js/src/visitor.rs | 9 +++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/rewriter/js/src/changes.rs b/rewriter/js/src/changes.rs index ddcbfbe2..8ceac9d3 100644 --- a/rewriter/js/src/changes.rs +++ b/rewriter/js/src/changes.rs @@ -49,6 +49,7 @@ pub enum JsChangeType<'alloc: 'data, 'data> { ident: Atom<'data>, propspan: Span }, + WrapSetComputed, /// insert `${cfg.setrealmfn}({}).` SetRealmFn, @@ -142,6 +143,10 @@ impl<'alloc: 'data, 'data> Transform<'data> for JsChange<'alloc, 'data> { ident, "(" ]), + Ty::WrapSetComputed => LL::insert(transforms![ + &cfg.wrapcomputedsetfn, + "(" + ]), Ty::SetRealmFn => LL::insert(transforms![&cfg.setrealmfn, "({})."]), Ty::ScramErrFn { ident } => LL::insert(transforms!["$scramerr(", ident, ");"]), Ty::ScramitizeFn => LL::insert(transforms![" $scramitize("]), diff --git a/rewriter/js/src/rewrite.rs b/rewriter/js/src/rewrite.rs index b1a55f53..fdf32987 100644 --- a/rewriter/js/src/rewrite.rs +++ b/rewriter/js/src/rewrite.rs @@ -162,7 +162,20 @@ impl<'alloc: 'data, 'data> RewriteType<'alloc, 'data> { } ) ], - RewriteType::WrapSetComputed { .. } => todo!(), + RewriteType::WrapSetComputed { leftspan, rightspan, propspan } => smallvec![ + change!(span!(start), WrapSetComputed), + // replace the bracket with , + change!(Span::new(leftspan.end, propspan.start), Replace { text: "," }), + // replace the other bracket with another , + change!(Span::new(propspan.end, rightspan.start), Replace { text: "," }), + change!( + span!(end), + ClosingParen { + semi: false, + replace: true + } + ) + ], Self::SetRealmFn => smallvec![change!(span, SetRealmFn)], Self::ImportFn => smallvec![change!(span, ImportFn)], Self::MetaFn => smallvec![change!(span, MetaFn)], diff --git a/rewriter/js/src/visitor.rs b/rewriter/js/src/visitor.rs index 6d8d37e9..39d3fbc6 100644 --- a/rewriter/js/src/visitor.rs +++ b/rewriter/js/src/visitor.rs @@ -338,6 +338,15 @@ where // more to walk walk::walk_expression(self, &s.object); + } + AssignmentTarget::ComputedMemberExpression(s)=> { + self.jschanges.add(rewrite!(it.span, WrapSetComputed { + propspan: s.expression.span(), + leftspan: s.object.span(), + rightspan: it.right.span(), + })); + walk::walk_expression(self, &s.object); + walk::walk_expression(self, &s.expression); } AssignmentTarget::ArrayAssignmentTarget(_) => { // [location] = ["https://example.com"] From f7b9a73e0580d845049f54b4b180e71c19e1ad78 Mon Sep 17 00:00:00 2001 From: velzie Date: Thu, 24 Jul 2025 09:48:53 -0400 Subject: [PATCH 06/18] [core][rewriter] dpsc: fix nested computed gets --- rewriter/js/src/rewrite.rs | 2 -- rewriter/js/src/visitor.rs | 12 ------------ 2 files changed, 14 deletions(-) diff --git a/rewriter/js/src/rewrite.rs b/rewriter/js/src/rewrite.rs index fdf32987..7c282d49 100644 --- a/rewriter/js/src/rewrite.rs +++ b/rewriter/js/src/rewrite.rs @@ -101,8 +101,6 @@ impl<'alloc: 'data, 'data> Rewrite<'alloc, 'data> { impl<'alloc: 'data, 'data> RewriteType<'alloc, 'data> { fn into_inner(self, span: Span) -> SmallVec<[JsChange<'alloc, 'data>; 2]> { - - dbg!(&self); macro_rules! span { (start) => { Span::new(span.start, span.start) diff --git a/rewriter/js/src/visitor.rs b/rewriter/js/src/visitor.rs index 39d3fbc6..d39cdbdb 100644 --- a/rewriter/js/src/visitor.rs +++ b/rewriter/js/src/visitor.rs @@ -166,18 +166,6 @@ where // } } - match &it.object() { - Expression::Identifier(_) => { - return; - } - Expression::ThisExpression(_)=> { - // this is safe, we don't need to walk it - return; - } - _=>{} - } - - walk::walk_member_expression(self, it); } From 8006807315c02818f8b2c6b76f0251b381324b04 Mon Sep 17 00:00:00 2001 From: velzie Date: Thu, 24 Jul 2025 10:21:47 -0400 Subject: [PATCH 07/18] [core][rewriter] dspc: change eval() from a replace rewrite to an insert rewrite to fix nesting issues --- rewriter/js/src/changes.rs | 2 +- rewriter/js/src/rewrite.rs | 7 ++++--- rewriter/js/src/visitor.rs | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/rewriter/js/src/changes.rs b/rewriter/js/src/changes.rs index 8ceac9d3..42f34982 100644 --- a/rewriter/js/src/changes.rs +++ b/rewriter/js/src/changes.rs @@ -150,7 +150,7 @@ impl<'alloc: 'data, 'data> Transform<'data> for JsChange<'alloc, 'data> { Ty::SetRealmFn => LL::insert(transforms![&cfg.setrealmfn, "({})."]), Ty::ScramErrFn { ident } => LL::insert(transforms!["$scramerr(", ident, ");"]), Ty::ScramitizeFn => LL::insert(transforms![" $scramitize("]), - Ty::EvalRewriteFn => LL::replace(transforms!["eval(", &cfg.rewritefn, "("]), + Ty::EvalRewriteFn => LL::insert(transforms![&cfg.rewritefn, "("]), Ty::ShorthandObj { ident } => { LL::insert(transforms![":", &cfg.wrapfn, "(", ident, ")"]) } diff --git a/rewriter/js/src/rewrite.rs b/rewriter/js/src/rewrite.rs index 7c282d49..22d75cf2 100644 --- a/rewriter/js/src/rewrite.rs +++ b/rewriter/js/src/rewrite.rs @@ -130,8 +130,9 @@ impl<'alloc: 'data, 'data> RewriteType<'alloc, 'data> { ident, enclose, }), - // change!(propspan, Delete), - change!(Span::new(propspan.start-1, propspan.end), WrapGetRight { + change!(propspan, Delete), + change!(Span::new(propspan.start-1, propspan.start), Delete), + change!(Span::new(propspan.end, propspan.end), WrapGetRight { enclose, }), ], @@ -194,7 +195,7 @@ impl<'alloc: 'data, 'data> RewriteType<'alloc, 'data> { span!(inner span end), ClosingParen { semi: false, - replace: true + replace: false, } ) ], diff --git a/rewriter/js/src/visitor.rs b/rewriter/js/src/visitor.rs index d39cdbdb..d5e2d9c4 100644 --- a/rewriter/js/src/visitor.rs +++ b/rewriter/js/src/visitor.rs @@ -184,7 +184,7 @@ where self.jschanges.add(rewrite!( it.span, Eval { - inner: Span::new(s.span.end + 1, it.span.end), + inner: Span::new(s.span.end+1, it.span.end-1), } )); From 215fd1274f6fc5cf81ba8c44cb1ab91fdd090758 Mon Sep 17 00:00:00 2001 From: velzie Date: Thu, 24 Jul 2025 10:27:19 -0400 Subject: [PATCH 08/18] [core][rewriter] dpsc: nuke strictchecker --- rewriter/js/src/changes.rs | 9 ++++----- rewriter/js/src/rewrite.rs | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/rewriter/js/src/changes.rs b/rewriter/js/src/changes.rs index 42f34982..358ce4b9 100644 --- a/rewriter/js/src/changes.rs +++ b/rewriter/js/src/changes.rs @@ -17,8 +17,6 @@ use crate::{ rewrite::Rewrite, }; -// const STRICTCHECKER: &str = "(function(a){arguments[0]=false;return a})(true)"; -const STRICTCHECKER: &str = "(function(){return!this;})()"; macro_rules! change { ($span:expr, $($ty:tt)*) => { @@ -31,7 +29,7 @@ pub(crate) use change; pub enum JsChangeType<'alloc: 'data, 'data> { /// insert `${cfg.wrapfn}(` WrapFnLeft { enclose: bool }, - /// insert `,strictchecker)` + /// insert `)` WrapFnRight { enclose: bool }, WrapGetLeft { @@ -107,6 +105,7 @@ impl<'alloc: 'data, 'data> Transform<'data> for JsChange<'alloc, 'data> { (cfg, flags): &Self::ToLowLevelData, offset: i32, ) -> TransformLL<'data> { + dbg!(&self); use JsChangeType as Ty; use TransformLL as LL; match self.ty { @@ -116,9 +115,9 @@ impl<'alloc: 'data, 'data> Transform<'data> for JsChange<'alloc, 'data> { transforms![&cfg.wrapfn, "("] }), Ty::WrapFnRight { enclose } => LL::insert(if enclose { - transforms![",", STRICTCHECKER, "))"] + transforms!["))"] } else { - transforms![",", STRICTCHECKER, ")"] + transforms![")"] }), Ty::WrapGetLeft { ident, diff --git a/rewriter/js/src/rewrite.rs b/rewriter/js/src/rewrite.rs index 22d75cf2..d820f799 100644 --- a/rewriter/js/src/rewrite.rs +++ b/rewriter/js/src/rewrite.rs @@ -15,7 +15,7 @@ pub(crate) use rewrite; #[derive(Debug, PartialEq, Eq)] pub(crate) enum RewriteType<'alloc: 'data, 'data> { - /// `(cfg.wrapfn(ident,strictchecker))` | `cfg.wrapfn(ident,strictchecker)` + /// `(cfg.wrapfn(ident))` | `cfg.wrapfn(ident)` WrapFn { enclose: bool, }, From 08a99ddabcc2320a5265c9a9f8ba14624ab5d139 Mon Sep 17 00:00:00 2001 From: Toshit Chawda <45221816+r58Playz@users.noreply.github.com> Date: Fri, 25 Jul 2025 07:28:44 -0700 Subject: [PATCH 09/18] [core][rewriter] fix dpsc wrapget (#3) * fmt * fix --- rewriter/js/src/changes.rs | 83 +++++++++--------- rewriter/js/src/rewrite.rs | 114 +++++++++++++------------ rewriter/js/src/visitor.rs | 163 ++++++++++++++++++------------------ rewriter/native/src/main.rs | 2 +- rewriter/wasm/src/jsr.rs | 11 +-- 5 files changed, 197 insertions(+), 176 deletions(-) diff --git a/rewriter/js/src/changes.rs b/rewriter/js/src/changes.rs index 358ce4b9..32e48f9e 100644 --- a/rewriter/js/src/changes.rs +++ b/rewriter/js/src/changes.rs @@ -17,7 +17,6 @@ use crate::{ rewrite::Rewrite, }; - macro_rules! change { ($span:expr, $($ty:tt)*) => { $crate::changes::JsChange::new($span, $crate::changes::JsChangeType::$($ty)*) @@ -28,37 +27,45 @@ pub(crate) use change; #[derive(Debug, PartialEq, Eq)] pub enum JsChangeType<'alloc: 'data, 'data> { /// insert `${cfg.wrapfn}(` - WrapFnLeft { enclose: bool }, + WrapFnLeft { + enclose: bool, + }, /// insert `)` - WrapFnRight { enclose: bool }, + WrapFnRight { + enclose: bool, + }, WrapGetLeft { - ident: Atom<'data>, - enclose: bool, + ident: Atom<'data>, + enclose: bool, }, WrapGetComputedLeft { - enclose: bool, - }, + enclose: bool, + }, WrapGetRight { - enclose: bool, - }, + enclose: bool, + }, - WrapSet { - ident: Atom<'data>, - propspan: Span - }, - WrapSetComputed, + WrapSet { + ident: Atom<'data>, + propspan: Span, + }, + WrapSetComputed, /// insert `${cfg.setrealmfn}({}).` SetRealmFn, /// insert `$scramerr(ident);` - ScramErrFn { ident: Atom<'data> }, + ScramErrFn { + ident: Atom<'data>, + }, /// insert `$scramitize(` ScramitizeFn, /// insert `eval(${cfg.rewritefn}(` EvalRewriteFn, /// insert `: ${cfg.wrapfn}(ident)` - ShorthandObj { ident: Atom<'data> }, + ShorthandObj { + ident: Atom<'data>, + }, /// insert scramtag SourceTag, @@ -73,10 +80,15 @@ pub enum JsChangeType<'alloc: 'data, 'data> { }, /// insert `)` - ClosingParen { semi: bool, replace: bool }, + ClosingParen { + semi: bool, + replace: bool, + }, /// replace span with text - Replace { text: &'alloc str }, + Replace { + text: &'alloc str, + }, /// replace span with "" Delete, } @@ -105,7 +117,7 @@ impl<'alloc: 'data, 'data> Transform<'data> for JsChange<'alloc, 'data> { (cfg, flags): &Self::ToLowLevelData, offset: i32, ) -> TransformLL<'data> { - dbg!(&self); + dbg!(&self); use JsChangeType as Ty; use TransformLL as LL; match self.ty { @@ -119,33 +131,25 @@ impl<'alloc: 'data, 'data> Transform<'data> for JsChange<'alloc, 'data> { } else { transforms![")"] }), - Ty::WrapGetLeft { - ident, - enclose, - } => LL::insert(if enclose { - transforms!["(", &cfg.wrapgetbase, ident, "("] + Ty::WrapGetLeft { ident, enclose } => LL::insert(if enclose { + transforms!["(", &cfg.wrapgetbase, ident, "("] } else { - transforms![&cfg.wrapgetbase, ident, "("] + transforms![&cfg.wrapgetbase, ident, "("] }), Ty::WrapGetComputedLeft { enclose } => LL::insert(if enclose { - transforms!["(", &cfg.wrapcomputedgetfn, "("] - } else { - transforms![&cfg.wrapcomputedgetfn, "("] - }), + transforms!["(", &cfg.wrapcomputedgetfn, "("] + } else { + transforms![&cfg.wrapcomputedgetfn, "("] + }), Ty::WrapGetRight { enclose } => LL::replace(if enclose { transforms!["))"] } else { transforms![")"] }), - Ty::WrapSet { ident, propspan } => LL::insert(transforms![ - &cfg.wrapsetbase, - ident, - "(" - ]), - Ty::WrapSetComputed => LL::insert(transforms![ - &cfg.wrapcomputedsetfn, - "(" - ]), + Ty::WrapSet { ident, propspan } => { + LL::insert(transforms![&cfg.wrapsetbase, ident, "("]) + } + Ty::WrapSetComputed => LL::insert(transforms![&cfg.wrapcomputedsetfn, "("]), Ty::SetRealmFn => LL::insert(transforms![&cfg.setrealmfn, "({})."]), Ty::ScramErrFn { ident } => LL::insert(transforms!["$scramerr(", ident, ");"]), Ty::ScramitizeFn => LL::insert(transforms![" $scramitize("]), @@ -205,6 +209,8 @@ impl Ord for JsChange<'_, '_> { Ordering::Equal => match (&self.ty, &other.ty) { (Ty::ScramErrFn { .. }, _) => Ordering::Less, (_, Ty::ScramErrFn { .. }) => Ordering::Greater, + (Ty::WrapFnRight { .. }, _) => Ordering::Less, + (_, Ty::WrapFnRight { .. }) => Ordering::Greater, _ => Ordering::Equal, }, x => x, @@ -226,6 +232,7 @@ impl<'alloc: 'data, 'data> JsChanges<'alloc, 'data> { #[inline] pub fn add(&mut self, rewrite: Rewrite<'alloc, 'data>) { + dbg!(&rewrite); self.inner.add(rewrite.into_inner()); } diff --git a/rewriter/js/src/rewrite.rs b/rewriter/js/src/rewrite.rs index d820f799..b251e9de 100644 --- a/rewriter/js/src/rewrite.rs +++ b/rewriter/js/src/rewrite.rs @@ -27,31 +27,31 @@ pub(crate) enum RewriteType<'alloc: 'data, 'data> { /// `cfg.metafn("cfg.base")` MetaFn, - /// `window.location` -> cfg.wraplocation(window) + /// `window.attr` -> cfg.wrapattr(window) WrapGet { - ident: Atom<'data>, - propspan: Span, - enclose: bool, + ident: Atom<'data>, + propspan: Span, + enclose: bool, }, - /// `window["location"]` -> cfg.wrapgetcomputed(window, "location") + /// `window["attr"]` -> cfg.wrapgetcomputed(window, "attr") WrapGetComputed { - leftspan: Span, - propspan: Span, - enclose: bool, + leftspan: Span, + propspan: Span, + enclose: bool, }, - /// `window.location` -> cfg.wraplocation(window) + /// `window.attr` -> cfg.wrapattr(window) WrapSet { - ident: Atom<'data>, - propspan: Span, - leftspan: Span, - rightspan: Span, + ident: Atom<'data>, + propspan: Span, + leftspan: Span, + rightspan: Span, }, - /// `cfg.wrapcomputedsetfn(window, "location", t)` + /// `cfg.wrapcomputedsetfn(window, "attr", t)` WrapSetComputed { - propspan: Span, - leftspan: Span, - rightspan: Span, - }, + propspan: Span, + leftspan: Span, + rightspan: Span, + }, // dead code only if debug is disabled #[allow(dead_code)] /// `$scramerr(name)` @@ -84,6 +84,7 @@ pub(crate) enum RewriteType<'alloc: 'data, 'data> { Delete, } +#[derive(Debug)] pub(crate) struct Rewrite<'alloc, 'data> { span: Span, ty: RewriteType<'alloc, 'data>, @@ -114,6 +115,9 @@ impl<'alloc: 'data, 'data> RewriteType<'alloc, 'data> { ($span1:ident $span2:ident end) => { Span::new($span1.end, $span2.end) }; + ($span1:ident $span2:ident between) => { + Span::new($span1.end, $span2.start) + }; } match self { @@ -122,51 +126,57 @@ impl<'alloc: 'data, 'data> RewriteType<'alloc, 'data> { change!(span!(end), WrapFnRight { enclose }), ], Self::WrapGet { - ident, - propspan, - enclose, + ident, + propspan, + enclose, } => smallvec![ - change!(span!(start), WrapGetLeft { - ident, - enclose, - }), - change!(propspan, Delete), - change!(Span::new(propspan.start-1, propspan.start), Delete), - change!(Span::new(propspan.end, propspan.end), WrapGetRight { - enclose, - }), + change!(span!(start), WrapGetLeft { ident, enclose }), + change!(propspan.expand_left(1), WrapGetRight { enclose }), ], - Self::WrapGetComputed { leftspan, propspan, enclose } => smallvec![ - change!(span!(start), WrapGetComputedLeft { - enclose - }), + Self::WrapGetComputed { + leftspan, + propspan, + enclose, + } => smallvec![ + change!(span!(start), WrapGetComputedLeft { enclose }), // replace the bracket with , - change!(Span::new(leftspan.end, propspan.start), Replace { text: "," }), + change!(span!(leftspan propspan between), Replace { text: "," }), // replace the other bracket with ) - change!(Span::new(propspan.end, propspan.end + 1), ClosingParen { semi: false, replace: true }), - + change!( + propspan.expand_right(1), + ClosingParen { + semi: false, + replace: true + } + ), ], - Self::WrapSet { ident, propspan, leftspan, rightspan } => smallvec![ - change!(span!(start), WrapSet { - ident, - propspan, - }), - change!(propspan, Delete), - change!(Span::new(leftspan.end, rightspan.start), Replace { text: "," }), - change!( + Self::WrapSet { + ident, + propspan, + leftspan, + rightspan, + } => smallvec![ + change!(span!(start), WrapSet { ident, propspan }), + change!(propspan, Delete), + change!(span!(leftspan rightspan between), Replace { text: "," }), + change!( span!(end), ClosingParen { semi: false, replace: true } ) - ], - RewriteType::WrapSetComputed { leftspan, rightspan, propspan } => smallvec![ - change!(span!(start), WrapSetComputed), - // replace the bracket with , - change!(Span::new(leftspan.end, propspan.start), Replace { text: "," }), + ], + RewriteType::WrapSetComputed { + leftspan, + rightspan, + propspan, + } => smallvec![ + change!(span!(start), WrapSetComputed), + // replace the bracket with , + change!(span!(leftspan propspan between), Replace { text: "," }), // replace the other bracket with another , - change!(Span::new(propspan.end, rightspan.start), Replace { text: "," }), + change!(span!(propspan rightspan between), Replace { text: "," }), change!( span!(end), ClosingParen { @@ -174,7 +184,7 @@ impl<'alloc: 'data, 'data> RewriteType<'alloc, 'data> { replace: true } ) - ], + ], Self::SetRealmFn => smallvec![change!(span, SetRealmFn)], Self::ImportFn => smallvec![change!(span, ImportFn)], Self::MetaFn => smallvec![change!(span, MetaFn)], diff --git a/rewriter/js/src/visitor.rs b/rewriter/js/src/visitor.rs index d5e2d9c4..0e5be44b 100644 --- a/rewriter/js/src/visitor.rs +++ b/rewriter/js/src/visitor.rs @@ -22,12 +22,7 @@ use crate::{ // js MUST not be able to get a reference to any of these because sbx // // maybe move this out of this lib? -const UNSAFE_GLOBALS: &[&str] = &[ - "parent", - "top", - "location", - "eval" -]; +const UNSAFE_GLOBALS: &[&str] = &["parent", "top", "location", "eval"]; pub struct Visitor<'alloc, 'data, E> where @@ -52,7 +47,7 @@ where builder.push_str("__URL_REWRITER_ALREADY_ERRORED__"); } else if let Err(err) = self.rewriter - .rewrite(self.config, &self.flags, &url.value, &mut builder,module) + .rewrite(self.config, &self.flags, &url.value, &mut builder, module) { self.error.replace(err); builder.push_str("__URL_REWRITER_ERROR__"); @@ -71,19 +66,17 @@ where fn walk_member_expression(&mut self, it: &Expression) -> bool { match it { - Expression::Identifier(s) => { - false - } + Expression::Identifier(s) => false, Expression::StaticMemberExpression(s) => { - if UNSAFE_GLOBALS.contains(&s.property.name.as_str()) { - // self.jschanges.add(rewrite!(s.span, WrapAccess { - // ident: s.property.name, - // propspan: s.property.span, - // } - // )); - } - self.walk_member_expression(&s.object) - }, + if UNSAFE_GLOBALS.contains(&s.property.name.as_str()) { + // self.jschanges.add(rewrite!(s.span, WrapAccess { + // ident: s.property.name, + // propspan: s.property.span, + // } + // )); + } + self.walk_member_expression(&s.object) + } Expression::ComputedMemberExpression(s) => self.walk_member_expression(&s.object), _ => false, } @@ -122,51 +115,55 @@ where } fn visit_member_expression(&mut self, it: &MemberExpression<'data>) { - match &it { - MemberExpression::StaticMemberExpression(s) =>{ + match &it { + MemberExpression::StaticMemberExpression(s) => { // TODO // you could break this with ["postMessage"] etc // however this code only exists because of recaptcha whatever // and it would slow down js execution a lot - if s.property.name == "postMessage" { - self.jschanges.add(rewrite!(s.property.span, SetRealmFn)); - - walk::walk_expression(self, &s.object); - return; // unwise to walk the rest of the tree - } - - if UNSAFE_GLOBALS.contains(&s.property.name.as_str()) { - self.jschanges.add(rewrite!(it.span(), WrapGet { - ident: s.property.name, - propspan: Span::new(s.property.span.start, s.property.span.end), - enclose: false, - })); - } + if s.property.name == "postMessage" { + self.jschanges.add(rewrite!(s.property.span, SetRealmFn)); + + walk::walk_expression(self, &s.object); + return; // unwise to walk the rest of the tree + } + + if UNSAFE_GLOBALS.contains(&s.property.name.as_str()) { + self.jschanges.add(rewrite!( + it.span(), + WrapGet { + ident: s.property.name, + propspan: s.property.span, + enclose: false, + } + )); + } } MemberExpression::ComputedMemberExpression(s) => { - self.jschanges.add(rewrite!(it.span(), WrapGetComputed { - leftspan: s.object.span(), - propspan: s.expression.span(), - enclose: false, - })); - } - _=>{} - // if !self.flags.strict_rewrites - // && !UNSAFE_GLOBALS.contains(&s.property.name.as_str()) - // && let Expression::Identifier(_) | Expression::ThisExpression(_) = &s.object - // { - // // cull tree - this should be safe - // return; - // } - - // if self.flags.scramitize - // && !matches!(s.object, Expression::MetaProperty(_) | Expression::Super(_)) - // { - // self.scramitize(s.object.span()); - // } + self.jschanges.add(rewrite!( + it.span(), + WrapGetComputed { + leftspan: s.object.span(), + propspan: s.expression.span(), + enclose: false, + } + )); + } + _ => {} // if !self.flags.strict_rewrites + // && !UNSAFE_GLOBALS.contains(&s.property.name.as_str()) + // && let Expression::Identifier(_) | Expression::ThisExpression(_) = &s.object + // { + // // cull tree - this should be safe + // return; + // } + + // if self.flags.scramitize + // && !matches!(s.object, Expression::MetaProperty(_) | Expression::Super(_)) + // { + // self.scramitize(s.object.span()); + // } } - walk::walk_member_expression(self, it); } @@ -184,7 +181,7 @@ where self.jschanges.add(rewrite!( it.span, Eval { - inner: Span::new(s.span.end+1, it.span.end-1), + inner: Span::new(s.span.end + 1, it.span.end - 1), } )); @@ -201,7 +198,7 @@ where } fn visit_import_declaration(&mut self, it: &ImportDeclaration<'data>) { - self.rewrite_url(&it.source,true); + self.rewrite_url(&it.source, true); walk::walk_import_declaration(self, it); } fn visit_import_expression(&mut self, it: &ImportExpression<'data>) { @@ -213,11 +210,11 @@ where } fn visit_export_all_declaration(&mut self, it: &ExportAllDeclaration<'data>) { - self.rewrite_url(&it.source,true); + self.rewrite_url(&it.source, true); } fn visit_export_named_declaration(&mut self, it: &ExportNamedDeclaration<'data>) { if let Some(source) = &it.source { - self.rewrite_url(source,true); + self.rewrite_url(source, true); } // do not walk further, we don't want to rewrite the identifiers } @@ -314,27 +311,33 @@ where return; } } - AssignmentTarget::StaticMemberExpression(s) =>{ - if UNSAFE_GLOBALS.contains(&s.property.name.as_str()) { - self.jschanges.add(rewrite!(it.span, WrapSet { - ident: s.property.name, - propspan: Span::new(s.property.span.start-1, s.property.span.end), - leftspan: s.span(), - rightspan: it.right.span(), - })); - } - - // more to walk - walk::walk_expression(self, &s.object); + AssignmentTarget::StaticMemberExpression(s) => { + if UNSAFE_GLOBALS.contains(&s.property.name.as_str()) { + self.jschanges.add(rewrite!( + it.span, + WrapSet { + ident: s.property.name, + propspan: Span::new(s.property.span.start - 1, s.property.span.end), + leftspan: s.span(), + rightspan: it.right.span(), + } + )); + } + + // more to walk + walk::walk_expression(self, &s.object); } - AssignmentTarget::ComputedMemberExpression(s)=> { - self.jschanges.add(rewrite!(it.span, WrapSetComputed { - propspan: s.expression.span(), - leftspan: s.object.span(), - rightspan: it.right.span(), - })); - walk::walk_expression(self, &s.object); - walk::walk_expression(self, &s.expression); + AssignmentTarget::ComputedMemberExpression(s) => { + self.jschanges.add(rewrite!( + it.span, + WrapSetComputed { + propspan: s.expression.span(), + leftspan: s.object.span(), + rightspan: it.right.span(), + } + )); + walk::walk_expression(self, &s.object); + walk::walk_expression(self, &s.expression); } AssignmentTarget::ArrayAssignmentTarget(_) => { // [location] = ["https://example.com"] diff --git a/rewriter/native/src/main.rs b/rewriter/native/src/main.rs index 492fd760..56072268 100644 --- a/rewriter/native/src/main.rs +++ b/rewriter/native/src/main.rs @@ -110,7 +110,7 @@ fn main() -> Result<()> { let unrewritten = NativeRewriter::unrewrite(&res); // println!( // "unrewritten:\n{}", - // str::from_utf8(&unrewritten).context("failed to parse unrewritten js")? + // str::from_utf8(&unrewritten).context("failed to parse unrewritten js")? // ); eprintln!("errors:"); diff --git a/rewriter/wasm/src/jsr.rs b/rewriter/wasm/src/jsr.rs index 09ebda67..8caa8876 100644 --- a/rewriter/wasm/src/jsr.rs +++ b/rewriter/wasm/src/jsr.rs @@ -74,11 +74,12 @@ impl UrlRewriter for WasmUrlRewriter { .map_err(RewriterError::from)? .to_string(); - let mut rewritten = self.0 - .call1(&JsValue::NULL, &url.into()) - .map_err(RewriterError::from)? - .as_string() - .ok_or_else(|| RewriterError::not_str("url rewriter output"))?; + let mut rewritten = self + .0 + .call1(&JsValue::NULL, &url.into()) + .map_err(RewriterError::from)? + .as_string() + .ok_or_else(|| RewriterError::not_str("url rewriter output"))?; if module { rewritten.push_str("?type=module"); From 7d4f36e910b26dc1a5bf438b1028f8688f04d5c2 Mon Sep 17 00:00:00 2001 From: velzie Date: Fri, 25 Jul 2025 10:30:52 -0400 Subject: [PATCH 10/18] [core] add dpsc types to config --- rewriter/wasm/src/jsr.rs | 7 +++++-- src/client/shared/wrap.ts | 12 ++++++++---- src/controller/index.ts | 5 ++++- src/types.ts | 5 ++++- 4 files changed, 21 insertions(+), 8 deletions(-) diff --git a/rewriter/wasm/src/jsr.rs b/rewriter/wasm/src/jsr.rs index 09ebda67..844ac58e 100644 --- a/rewriter/wasm/src/jsr.rs +++ b/rewriter/wasm/src/jsr.rs @@ -28,7 +28,7 @@ extern "C" { #[wasm_bindgen(typescript_custom_section)] const REWRITER_OUTPUT: &'static str = r#" -export type JsRewriterOutput = { +export type JsRewriterOutput = { js: Uint8Array, map: Uint8Array, scramtag: string, @@ -49,8 +49,11 @@ fn get_config(scramjet: &Object) -> Result { Ok(Config { prefix: get_str(config, "prefix")?, + wrapgetbase: get_str(globals, "wrapgetbase")?, + wrapsetbase: get_str(globals, "wrapsetbase")?, + wrapcomputedgetfn: get_str(globals, "wrapcomputedgetfn")?, + wrapcomputedsetfn: get_str(globals, "wrapcomputedsetfn")?, wrapfn: get_str(globals, "wrapfn")?, - wrapthisfn: get_str(globals, "wrapthisfn")?, importfn: get_str(globals, "importfn")?, rewritefn: get_str(globals, "rewritefn")?, metafn: get_str(globals, "metafn")?, diff --git a/src/client/shared/wrap.ts b/src/client/shared/wrap.ts index e82757ad..e6f9fe1c 100644 --- a/src/client/shared/wrap.ts +++ b/src/client/shared/wrap.ts @@ -55,11 +55,15 @@ export default function (client: ScramjetClient, self: typeof globalThis) { writable: false, configurable: false, }); - Object.defineProperty(self, config.globals.wrapthisfn, { - value: function (i) { - if (i === self) return client.globalProxy; + Object.defineProperty(self, config.globals.wrapcomputedgetfn, { + value: function (val, prop) { + if (val === self) { + console.log(prop); - return i; + return null; + } + + return Reflect.get(val, prop); }, writable: false, configurable: false, diff --git a/src/controller/index.ts b/src/controller/index.ts index ff2c51e1..636d5780 100644 --- a/src/controller/index.ts +++ b/src/controller/index.ts @@ -17,7 +17,10 @@ export class ScramjetController { prefix: "/scramjet/", globals: { wrapfn: "$scramjet$wrap", - wrapthisfn: "$scramjet$wrapthis", + wrapgetbase: "$scramjet$get_", + wrapsetbase: "$scramjet$get_", + wrapcomputedgetfn: "$scramjet$get", + wrapcomputedsetfn: "$scramjet$get", trysetfn: "$scramjet$tryset", importfn: "$scramjet$import", rewritefn: "$scramjet$rewrite", diff --git a/src/types.ts b/src/types.ts index b5f8880f..6e61ad75 100644 --- a/src/types.ts +++ b/src/types.ts @@ -19,7 +19,10 @@ export interface ScramjetConfig { prefix: string; globals: { wrapfn: string; - wrapthisfn: string; + wrapsetbase: string; + wrapgetbase: string; + wrapcomputedgetfn: string; + wrapcomputedsetfn: string; trysetfn: string; importfn: string; rewritefn: string; From f87959456691168965d80e987b383517764f60ae Mon Sep 17 00:00:00 2001 From: velzie Date: Fri, 25 Jul 2025 10:41:38 -0400 Subject: [PATCH 11/18] [core][rewriter] dpsc: fix bad rewrite with wrapcomputedgetleft --- rewriter/js/src/changes.rs | 2 -- rewriter/js/src/rewrite.rs | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/rewriter/js/src/changes.rs b/rewriter/js/src/changes.rs index 32e48f9e..f6d90bb6 100644 --- a/rewriter/js/src/changes.rs +++ b/rewriter/js/src/changes.rs @@ -117,7 +117,6 @@ impl<'alloc: 'data, 'data> Transform<'data> for JsChange<'alloc, 'data> { (cfg, flags): &Self::ToLowLevelData, offset: i32, ) -> TransformLL<'data> { - dbg!(&self); use JsChangeType as Ty; use TransformLL as LL; match self.ty { @@ -232,7 +231,6 @@ impl<'alloc: 'data, 'data> JsChanges<'alloc, 'data> { #[inline] pub fn add(&mut self, rewrite: Rewrite<'alloc, 'data>) { - dbg!(&rewrite); self.inner.add(rewrite.into_inner()); } diff --git a/rewriter/js/src/rewrite.rs b/rewriter/js/src/rewrite.rs index b251e9de..967d20a0 100644 --- a/rewriter/js/src/rewrite.rs +++ b/rewriter/js/src/rewrite.rs @@ -143,7 +143,7 @@ impl<'alloc: 'data, 'data> RewriteType<'alloc, 'data> { change!(span!(leftspan propspan between), Replace { text: "," }), // replace the other bracket with ) change!( - propspan.expand_right(1), + Span::new(propspan.end, propspan.end + 1), ClosingParen { semi: false, replace: true From 72151719678ade693fc5c60cd2f790a372cd9c69 Mon Sep 17 00:00:00 2001 From: velzie Date: Fri, 25 Jul 2025 11:20:28 -0400 Subject: [PATCH 12/18] [core][rewriter] dpsc: switch from computed wraps to dynamic property wraps --- rewriter/js/src/cfg.rs | 6 +- rewriter/js/src/changes.rs | 43 +++------------ rewriter/js/src/rewrite.rs | 97 +++++---------------------------- rewriter/js/src/visitor.rs | 33 +++-------- rewriter/native/src/main.rs | 12 ++-- rewriter/native/src/rewriter.rs | 6 +- 6 files changed, 37 insertions(+), 160 deletions(-) diff --git a/rewriter/js/src/cfg.rs b/rewriter/js/src/cfg.rs index e9283ef1..8f2d8970 100644 --- a/rewriter/js/src/cfg.rs +++ b/rewriter/js/src/cfg.rs @@ -17,10 +17,8 @@ pub struct Config { pub prefix: String, pub wrapfn: String, - pub wrapgetbase: String, - pub wrapsetbase: String, - pub wrapcomputedgetfn: String, - pub wrapcomputedsetfn: String, + pub wrappropertybase: String, + pub wrappropertyfn: String, pub importfn: String, pub rewritefn: String, pub setrealmfn: String, diff --git a/rewriter/js/src/changes.rs b/rewriter/js/src/changes.rs index f6d90bb6..ef522ed4 100644 --- a/rewriter/js/src/changes.rs +++ b/rewriter/js/src/changes.rs @@ -35,22 +35,10 @@ pub enum JsChangeType<'alloc: 'data, 'data> { enclose: bool, }, - WrapGetLeft { - ident: Atom<'data>, - enclose: bool, - }, - WrapGetComputedLeft { - enclose: bool, - }, - WrapGetRight { - enclose: bool, - }, - - WrapSet { - ident: Atom<'data>, - propspan: Span, - }, - WrapSetComputed, + WrapPropertyLeft, + RewriteProperty { + ident: Atom<'data>, + }, /// insert `${cfg.setrealmfn}({}).` SetRealmFn, @@ -117,6 +105,7 @@ impl<'alloc: 'data, 'data> Transform<'data> for JsChange<'alloc, 'data> { (cfg, flags): &Self::ToLowLevelData, offset: i32, ) -> TransformLL<'data> { + dbg!(&&self); use JsChangeType as Ty; use TransformLL as LL; match self.ty { @@ -130,25 +119,9 @@ impl<'alloc: 'data, 'data> Transform<'data> for JsChange<'alloc, 'data> { } else { transforms![")"] }), - Ty::WrapGetLeft { ident, enclose } => LL::insert(if enclose { - transforms!["(", &cfg.wrapgetbase, ident, "("] - } else { - transforms![&cfg.wrapgetbase, ident, "("] - }), - Ty::WrapGetComputedLeft { enclose } => LL::insert(if enclose { - transforms!["(", &cfg.wrapcomputedgetfn, "("] - } else { - transforms![&cfg.wrapcomputedgetfn, "("] - }), - Ty::WrapGetRight { enclose } => LL::replace(if enclose { - transforms!["))"] - } else { - transforms![")"] - }), - Ty::WrapSet { ident, propspan } => { - LL::insert(transforms![&cfg.wrapsetbase, ident, "("]) - } - Ty::WrapSetComputed => LL::insert(transforms![&cfg.wrapcomputedsetfn, "("]), + Ty::WrapPropertyLeft => LL::insert(transforms![&cfg.wrappropertyfn, "("]), + Ty::RewriteProperty { ident } => LL::replace(transforms![&cfg.wrappropertybase,ident]), + Ty::SetRealmFn => LL::insert(transforms![&cfg.setrealmfn, "({})."]), Ty::ScramErrFn { ident } => LL::insert(transforms!["$scramerr(", ident, ");"]), Ty::ScramitizeFn => LL::insert(transforms![" $scramitize("]), diff --git a/rewriter/js/src/rewrite.rs b/rewriter/js/src/rewrite.rs index 967d20a0..4018ac5d 100644 --- a/rewriter/js/src/rewrite.rs +++ b/rewriter/js/src/rewrite.rs @@ -27,31 +27,11 @@ pub(crate) enum RewriteType<'alloc: 'data, 'data> { /// `cfg.metafn("cfg.base")` MetaFn, - /// `window.attr` -> cfg.wrapattr(window) - WrapGet { - ident: Atom<'data>, - propspan: Span, - enclose: bool, - }, - /// `window["attr"]` -> cfg.wrapgetcomputed(window, "attr") - WrapGetComputed { - leftspan: Span, - propspan: Span, - enclose: bool, - }, - /// `window.attr` -> cfg.wrapattr(window) - WrapSet { - ident: Atom<'data>, - propspan: Span, - leftspan: Span, - rightspan: Span, - }, - /// `cfg.wrapcomputedsetfn(window, "attr", t)` - WrapSetComputed { - propspan: Span, - leftspan: Span, - rightspan: Span, + RewriteProperty { + ident: Atom<'data>, }, + WrapProperty, + // dead code only if debug is disabled #[allow(dead_code)] /// `$scramerr(name)` @@ -102,6 +82,8 @@ impl<'alloc: 'data, 'data> Rewrite<'alloc, 'data> { impl<'alloc: 'data, 'data> RewriteType<'alloc, 'data> { fn into_inner(self, span: Span) -> SmallVec<[JsChange<'alloc, 'data>; 2]> { + + dbg!(&self); macro_rules! span { (start) => { Span::new(span.start, span.start) @@ -125,66 +107,13 @@ impl<'alloc: 'data, 'data> RewriteType<'alloc, 'data> { change!(span!(start), WrapFnLeft { enclose }), change!(span!(end), WrapFnRight { enclose }), ], - Self::WrapGet { - ident, - propspan, - enclose, - } => smallvec![ - change!(span!(start), WrapGetLeft { ident, enclose }), - change!(propspan.expand_left(1), WrapGetRight { enclose }), - ], - Self::WrapGetComputed { - leftspan, - propspan, - enclose, - } => smallvec![ - change!(span!(start), WrapGetComputedLeft { enclose }), - // replace the bracket with , - change!(span!(leftspan propspan between), Replace { text: "," }), - // replace the other bracket with ) - change!( - Span::new(propspan.end, propspan.end + 1), - ClosingParen { - semi: false, - replace: true - } - ), - ], - Self::WrapSet { - ident, - propspan, - leftspan, - rightspan, - } => smallvec![ - change!(span!(start), WrapSet { ident, propspan }), - change!(propspan, Delete), - change!(span!(leftspan rightspan between), Replace { text: "," }), - change!( - span!(end), - ClosingParen { - semi: false, - replace: true - } - ) - ], - RewriteType::WrapSetComputed { - leftspan, - rightspan, - propspan, - } => smallvec![ - change!(span!(start), WrapSetComputed), - // replace the bracket with , - change!(span!(leftspan propspan between), Replace { text: "," }), - // replace the other bracket with another , - change!(span!(propspan rightspan between), Replace { text: "," }), - change!( - span!(end), - ClosingParen { - semi: false, - replace: true - } - ) - ], + Self::RewriteProperty { ident } => smallvec![ + change!(span, RewriteProperty { ident }), + ], + Self::WrapProperty => smallvec![ + change!(span!(start), WrapPropertyLeft), + change!(span!(end), ClosingParen { semi: false, replace: false }), + ], Self::SetRealmFn => smallvec![change!(span, SetRealmFn)], Self::ImportFn => smallvec![change!(span, ImportFn)], Self::MetaFn => smallvec![change!(span, MetaFn)], diff --git a/rewriter/js/src/visitor.rs b/rewriter/js/src/visitor.rs index 0e5be44b..111a4f09 100644 --- a/rewriter/js/src/visitor.rs +++ b/rewriter/js/src/visitor.rs @@ -130,23 +130,15 @@ where if UNSAFE_GLOBALS.contains(&s.property.name.as_str()) { self.jschanges.add(rewrite!( - it.span(), - WrapGet { - ident: s.property.name, - propspan: s.property.span, - enclose: false, - } + s.property.span(), + RewriteProperty { ident: s.property.name } )); } } MemberExpression::ComputedMemberExpression(s) => { self.jschanges.add(rewrite!( - it.span(), - WrapGetComputed { - leftspan: s.object.span(), - propspan: s.expression.span(), - enclose: false, - } + s.expression.span(), + WrapProperty, )); } _ => {} // if !self.flags.strict_rewrites @@ -314,13 +306,8 @@ where AssignmentTarget::StaticMemberExpression(s) => { if UNSAFE_GLOBALS.contains(&s.property.name.as_str()) { self.jschanges.add(rewrite!( - it.span, - WrapSet { - ident: s.property.name, - propspan: Span::new(s.property.span.start - 1, s.property.span.end), - leftspan: s.span(), - rightspan: it.right.span(), - } + s.property.span(), + RewriteProperty { ident: s.property.name } )); } @@ -329,12 +316,8 @@ where } AssignmentTarget::ComputedMemberExpression(s) => { self.jschanges.add(rewrite!( - it.span, - WrapSetComputed { - propspan: s.expression.span(), - leftspan: s.object.span(), - rightspan: it.right.span(), - } + s.expression.span(), + WrapProperty, )); walk::walk_expression(self, &s.object); walk::walk_expression(self, &s.expression); diff --git a/rewriter/native/src/main.rs b/rewriter/native/src/main.rs index 56072268..e2714a7a 100644 --- a/rewriter/native/src/main.rs +++ b/rewriter/native/src/main.rs @@ -25,14 +25,10 @@ pub struct RewriterOptions { prefix: String, #[clap(long, default_value = "$wrap")] wrapfn: String, - #[clap(long, default_value = "$get_")] - wrapgetbase: String, - #[clap(long, default_value = "$set_")] - wrapsetbase: String, - #[clap(long, default_value = "$computedget")] - wrapcomputedgetfn: String, - #[clap(long, default_value = "$computedset")] - wrapcomputedsetfn: String, + #[clap(long, default_value = "$sj_")] + wrappropertybase: String, + #[clap(long, default_value = "$prop")] + wrappropertyfn: String, #[clap(long, default_value = "$import")] importfn: String, #[clap(long, default_value = "$rewrite")] diff --git a/rewriter/native/src/rewriter.rs b/rewriter/native/src/rewriter.rs index 8454c461..c55c7a9a 100644 --- a/rewriter/native/src/rewriter.rs +++ b/rewriter/native/src/rewriter.rs @@ -52,10 +52,8 @@ impl NativeRewriter { Config { prefix: cfg.prefix.clone(), wrapfn: cfg.wrapfn.clone(), - wrapgetbase: cfg.wrapgetbase.clone(), - wrapsetbase: cfg.wrapsetbase.clone(), - wrapcomputedgetfn: cfg.wrapcomputedgetfn.clone(), - wrapcomputedsetfn: cfg.wrapcomputedsetfn.clone(), + wrappropertybase: cfg.wrappropertybase.clone(), + wrappropertyfn: cfg.wrappropertyfn.clone(), importfn: cfg.importfn.clone(), rewritefn: cfg.rewritefn.clone(), metafn: cfg.metafn.clone(), From 787265b9389b337465951cdfa72ed73ad9a38724 Mon Sep 17 00:00:00 2001 From: velzie Date: Fri, 25 Jul 2025 11:44:35 -0400 Subject: [PATCH 13/18] [core] add dpsc globals --- rewriter/wasm/src/jsr.rs | 6 +-- src/client/shared/wrap.ts | 98 ++++++++++++++++++++++++++++++++------- src/controller/index.ts | 6 +-- src/types.ts | 6 +-- 4 files changed, 88 insertions(+), 28 deletions(-) diff --git a/rewriter/wasm/src/jsr.rs b/rewriter/wasm/src/jsr.rs index be1362d4..61720e77 100644 --- a/rewriter/wasm/src/jsr.rs +++ b/rewriter/wasm/src/jsr.rs @@ -49,10 +49,8 @@ fn get_config(scramjet: &Object) -> Result { Ok(Config { prefix: get_str(config, "prefix")?, - wrapgetbase: get_str(globals, "wrapgetbase")?, - wrapsetbase: get_str(globals, "wrapsetbase")?, - wrapcomputedgetfn: get_str(globals, "wrapcomputedgetfn")?, - wrapcomputedsetfn: get_str(globals, "wrapcomputedsetfn")?, + wrappropertybase: get_str(globals, "wrappropertybase")?, + wrappropertyfn: get_str(globals, "wrappropertyfn")?, wrapfn: get_str(globals, "wrapfn")?, importfn: get_str(globals, "importfn")?, rewritefn: get_str(globals, "rewritefn")?, diff --git a/src/client/shared/wrap.ts b/src/client/shared/wrap.ts index e6f9fe1c..b913097f 100644 --- a/src/client/shared/wrap.ts +++ b/src/client/shared/wrap.ts @@ -7,7 +7,6 @@ import { indirectEval } from "@client/shared/eval"; export function createWrapFn(client: ScramjetClient, self: typeof globalThis) { return function (identifier: any, strict: boolean) { - if (identifier === self) return client.globalProxy; if (identifier === self.location) return client.locationProxy; if (identifier === eval) return indirectEval.bind(client, strict); @@ -18,10 +17,8 @@ export function createWrapFn(client: ScramjetClient, self: typeof globalThis) { return self.parent[SCRAMJETCLIENT].globalProxy; } else { // ... then we should pretend we aren't nested and return the current window - return client.globalProxy; + return self; } - } else if (identifier === self.document) { - return client.documentProxy; } else if (identifier === self.top) { // instead of returning top, we need to return the uppermost parent that's inside a scramjet context let current = self; @@ -37,7 +34,7 @@ export function createWrapFn(client: ScramjetClient, self: typeof globalThis) { current = test; } - return current[SCRAMJETCLIENT].globalProxy; + return current; } } @@ -47,27 +44,96 @@ export function createWrapFn(client: ScramjetClient, self: typeof globalThis) { export const order = 4; export default function (client: ScramjetClient, self: typeof globalThis) { - // the main magic of the proxy. all attempts to access any "banned objects" will be redirected here, and instead served a proxy object - // this contrasts from how other proxies will leave the root object alone and instead attempt to catch every member access - // this presents some issues (see element.ts), but makes us a good bit faster at runtime! Object.defineProperty(self, config.globals.wrapfn, { value: client.wrapfn, writable: false, configurable: false, + enumerable: false, }); - Object.defineProperty(self, config.globals.wrapcomputedgetfn, { - value: function (val, prop) { - if (val === self) { - console.log(prop); + Object.defineProperty(self, config.globals.wrappropertyfn, { + value: function (str) { + if ( + str === "location" || + str === "parent" || + str === "top" || + str === "eval" + ) + return config.globals.wrappropertybase + str; - return null; - } - - return Reflect.get(val, prop); + return str; }, writable: false, configurable: false, + enumerable: false, }); + console.log(config.globals.wrappropertybase); + + Object.defineProperty( + self.Object.prototype, + config.globals.wrappropertybase + "location", + { + get: function () { + if (this === self) { + return client.locationProxy; + } + + return this.location; + }, + set(value: any) { + if (this === self) { + client.locationProxy = value; + + return; + } + this.location = value; + }, + configurable: false, + enumerable: false, + } + ); + Object.defineProperty( + self.Object.prototype, + config.globals.wrappropertybase + "parent", + { + get: function () { + return client.wrapfn(this.parent, false); + }, + set(value: any) { + // i guess?? + this.parent = value; + }, + configurable: false, + enumerable: false, + } + ); + Object.defineProperty( + self.Object.prototype, + config.globals.wrappropertybase + "top", + { + get: function () { + return client.wrapfn(this.top, false); + }, + set(value: any) { + this.top = value; + }, + configurable: false, + enumerable: false, + } + ); + Object.defineProperty( + self.Object.prototype, + config.globals.wrappropertybase + "eval", + { + get: function () { + return client.wrapfn(this.eval, true); + }, + set(value: any) { + this.eval = value; + }, + configurable: false, + enumerable: false, + } + ); self.$scramitize = function (v) { if (v === self) debugger; diff --git a/src/controller/index.ts b/src/controller/index.ts index 636d5780..aadce650 100644 --- a/src/controller/index.ts +++ b/src/controller/index.ts @@ -17,10 +17,8 @@ export class ScramjetController { prefix: "/scramjet/", globals: { wrapfn: "$scramjet$wrap", - wrapgetbase: "$scramjet$get_", - wrapsetbase: "$scramjet$get_", - wrapcomputedgetfn: "$scramjet$get", - wrapcomputedsetfn: "$scramjet$get", + wrappropertybase: "$scramjet__", + wrappropertyfn: "$scramjet$prop", trysetfn: "$scramjet$tryset", importfn: "$scramjet$import", rewritefn: "$scramjet$rewrite", diff --git a/src/types.ts b/src/types.ts index 6e61ad75..6845e876 100644 --- a/src/types.ts +++ b/src/types.ts @@ -19,10 +19,8 @@ export interface ScramjetConfig { prefix: string; globals: { wrapfn: string; - wrapsetbase: string; - wrapgetbase: string; - wrapcomputedgetfn: string; - wrapcomputedsetfn: string; + wrappropertybase: string; + wrappropertyfn: string; trysetfn: string; importfn: string; rewritefn: string; From 144d048fbd8f7f276bfd1268baafef5e2c268d8b Mon Sep 17 00:00:00 2001 From: velzie Date: Fri, 25 Jul 2025 11:54:25 -0400 Subject: [PATCH 14/18] [core] NUKE GLOBALPROXY --- src/client/document.ts | 34 --------- src/client/dom/element.ts | 88 ++--------------------- src/client/dom/open.ts | 26 +------ src/client/global.ts | 90 ------------------------ src/client/index.ts | 7 -- src/client/shared/event.ts | 13 +--- src/client/shared/unproxy.ts | 132 ----------------------------------- src/client/shared/wrap.ts | 2 +- 8 files changed, 11 insertions(+), 381 deletions(-) delete mode 100644 src/client/document.ts delete mode 100644 src/client/global.ts delete mode 100644 src/client/shared/unproxy.ts diff --git a/src/client/document.ts b/src/client/document.ts deleted file mode 100644 index 48065989..00000000 --- a/src/client/document.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { rewriteUrl } from "@rewriters/url"; -import { ScramjetClient } from "@client/index"; -import { getOwnPropertyDescriptorHandler } from "@client/helpers"; - -export function createDocumentProxy( - client: ScramjetClient, - self: typeof globalThis -) { - return new Proxy(self.document, { - get(target, prop) { - if (prop === "location") { - return client.locationProxy; - } - - if (prop === "defaultView") { - return client.globalProxy; - } - - const value = Reflect.get(target, prop); - - return value; - }, - set(target, prop, newValue) { - if (prop === "location") { - location.href = rewriteUrl(newValue, client.meta); - - return; - } - - return Reflect.set(target, prop, newValue); - }, - getOwnPropertyDescriptor: getOwnPropertyDescriptorHandler, - }); -} diff --git a/src/client/dom/element.ts b/src/client/dom/element.ts index 14053d9e..7d94f90c 100644 --- a/src/client/dom/element.ts +++ b/src/client/dom/element.ts @@ -410,15 +410,13 @@ export default function (client: ScramjetClient, self: typeof window) { const realwin = ctx.get() as Window; if (!realwin) return realwin; - if (SCRAMJETCLIENT in realwin) { - return realwin[SCRAMJETCLIENT].globalProxy; - } else { - // hook the iframe + if (!(SCRAMJETCLIENT in realwin)) { + // hook the iframe before the client can start to steal globals out of it const newclient = new ScramjetClient(realwin); newclient.hook(); - - return newclient.globalProxy; } + + return realwin; }, } ); @@ -438,14 +436,12 @@ export default function (client: ScramjetClient, self: typeof window) { ); if (!realwin) return realwin; - if (SCRAMJETCLIENT in realwin) { - return realwin[SCRAMJETCLIENT].documentProxy; - } else { + if (!(SCRAMJETCLIENT in realwin)) { const newclient = new ScramjetClient(realwin); newclient.hook(); - - return newclient.documentProxy; } + + return realwin.document; }, } ); @@ -467,76 +463,6 @@ export default function (client: ScramjetClient, self: typeof window) { } ); - client.Trap("TreeWalker.prototype.currentNode", { - get(ctx) { - return ctx.get(); - }, - set(ctx, value) { - if (value === client.documentProxy) { - return ctx.set(self.document); - } - - return ctx.set(value); - }, - }); - - client.Proxy("Document.prototype.open", { - apply(ctx) { - const doc = ctx.call() as Document; - - const scram: ScramjetClient = doc[SCRAMJETCLIENT]; - if (!scram) return ctx.return(doc); // ?? - - return ctx.return(scram.documentProxy); - }, - }); - - client.Trap("Node.prototype.ownerDocument", { - get(ctx) { - const doc = ctx.get() as Document | null; - if (!doc) return null; - - const scram: ScramjetClient = doc[SCRAMJETCLIENT]; - if (!scram) return doc; // ?? - - return scram.documentProxy; - }, - }); - - client.Trap( - [ - "Node.prototype.parentNode", - "Node.prototype.parentElement", - "Node.prototype.previousSibling", - "Node.prototype.nextSibling", - "Range.prototype.commonAncestorContainer", - "AbstractRange.prototype.endContainer", - "AbstractRange.prototype.startContainer", - ], - { - get(ctx) { - const n = ctx.get() as Node; - if (!(n instanceof Document)) return n; - - const scram: ScramjetClient = n[SCRAMJETCLIENT]; - if (!scram) return n; // ?? - - return scram.documentProxy; - }, - } - ); - client.Proxy("Node.prototype.getRootNode", { - apply(ctx) { - const n = ctx.call() as Node; - if (!(n instanceof Document)) return ctx.return(n); - - const scram: ScramjetClient = n[SCRAMJETCLIENT]; - if (!scram) return ctx.return(n); // ?? - - return ctx.return(scram.documentProxy); - }, - }); - client.Proxy("DOMParser.prototype.parseFromString", { apply(ctx) { if (ctx.args[1] === "text/html") { diff --git a/src/client/dom/open.ts b/src/client/dom/open.ts index b942d5f5..56ea7b5f 100644 --- a/src/client/dom/open.ts +++ b/src/client/dom/open.ts @@ -13,31 +13,7 @@ export default function (client: ScramjetClient) { const realwin = ctx.call(); - if (!realwin) return ctx.return(realwin); - - if (SCRAMJETCLIENT in realwin) { - return ctx.return(realwin[SCRAMJETCLIENT].globalProxy); - } else { - const newclient = new ScramjetClient(realwin); - // hook the opened window - newclient.hook(); - - return ctx.return(newclient.globalProxy); - } - }, - }); - - // opener will refer to the real window if it was opened by window.open - client.Trap("opener", { - get(ctx) { - const realwin = ctx.get() as Window; - - if (realwin && SCRAMJETCLIENT in realwin) { - return realwin[SCRAMJETCLIENT].globalProxy; - } else { - // the opener has to have been already hooked, so if we reach here then it's a real window - return undefined; - } + return ctx.return(realwin); }, }); diff --git a/src/client/global.ts b/src/client/global.ts deleted file mode 100644 index e09f2da6..00000000 --- a/src/client/global.ts +++ /dev/null @@ -1,90 +0,0 @@ -// import { encodeUrl } from "@/shared"; -import { iswindow } from "@client/entry"; -import { SCRAMJETCLIENT } from "@/symbols"; -import { ScramjetClient } from "@client/index"; -// import { config } from "@/shared"; -import { getOwnPropertyDescriptorHandler } from "@client/helpers"; - -export const UNSAFE_GLOBALS = [ - "window", - "self", - "globalThis", - "this", - "parent", - "top", - "location", - "document", - "eval", - "frames", -]; - -export function createGlobalProxy( - client: ScramjetClient, - self: typeof globalThis -): typeof globalThis { - return new Proxy(self, { - get(target, prop) { - const value = Reflect.get(target, prop); - - if ( - iswindow && - (typeof prop === "string" || typeof prop === "number") && - !isNaN(Number(prop)) && - value - ) { - const win: Self = value.self; - // indexing into window gives you the contentWindow of the subframes for some reason - // you can't *set* it so this should always be the right value - if (win) { - if (SCRAMJETCLIENT in win) { - // then we've already hooked this frame and we can just send over its proxy - return win[SCRAMJETCLIENT].globalProxy; - } else { - // this can happen if it's an about:blank iframe that we've never gotten the chance to inject into - // just make a new client for it and inject - const newclient = new ScramjetClient(win); - newclient.hook(); - - return newclient.globalProxy; - } - } - } - - if (prop === "$scramjet") return undefined; - - if (typeof prop === "string" && UNSAFE_GLOBALS.includes(prop)) { - // TODO strict mode detect - return client.wrapfn(value, true); - } - - return value; - }, - - set(target, prop, value) { - if (prop === "location") { - client.url = value; - - return; - } - - return Reflect.set(target, prop, value); - }, - has(target, prop) { - if (prop === "$scramjet") return false; - - return Reflect.has(target, prop); - }, - ownKeys(target) { - return Reflect.ownKeys(target).filter((key) => key !== "$scramjet"); - }, - defineProperty(target, property, attributes) { - if (!attributes.get && !attributes.set) { - attributes.writable = true; - } - attributes.configurable = true; - - return Reflect.defineProperty(target, property, attributes); - }, - getOwnPropertyDescriptor: getOwnPropertyDescriptorHandler, - }); -} diff --git a/src/client/index.ts b/src/client/index.ts index b2b5f779..36974dbb 100644 --- a/src/client/index.ts +++ b/src/client/index.ts @@ -1,7 +1,5 @@ import { ScramjetFrame } from "@/controller/frame"; import { SCRAMJETCLIENT, SCRAMJETFRAME } from "@/symbols"; -import { createDocumentProxy } from "@client/document"; -import { createGlobalProxy } from "@client/global"; import { getOwnPropertyDescriptorHandler } from "@client/helpers"; import { createLocationProxy } from "@client/location"; import { nativeGetOwnPropertyDescriptor } from "@client/natives"; @@ -68,8 +66,6 @@ export type Trap = { }; export class ScramjetClient { - documentProxy: any; - globalProxy: any; locationProxy: any; serviceWorker: ServiceWorkerContainer; bare: BareClientType; @@ -123,13 +119,10 @@ export class ScramjetClient { this.serviceWorker = this.global.navigator.serviceWorker; if (iswindow) { - this.documentProxy = createDocumentProxy(this, global); - global.document[SCRAMJETCLIENT] = this; } this.locationProxy = createLocationProxy(this, global); - this.globalProxy = createGlobalProxy(this, global); this.wrapfn = createWrapFn(this, global); this.sourcemaps = {}; this.natives = { diff --git a/src/client/shared/event.ts b/src/client/shared/event.ts index 1f2facf3..341ececf 100644 --- a/src/client/shared/event.ts +++ b/src/client/shared/event.ts @@ -4,7 +4,6 @@ import { SCRAMJETCLIENT } from "@/symbols"; import { ScramjetClient } from "@client/index"; import { getOwnPropertyDescriptorHandler } from "@client/helpers"; import { nativeGetOwnPropertyDescriptor } from "@client/natives"; -import { unproxy } from "@client/shared/unproxy"; const realOnEvent = Symbol.for("scramjet original onevent function"); @@ -26,9 +25,9 @@ export default function (client: ScramjetClient, self: Self) { source() { if (this.source === null) return null; - const scram: ScramjetClient = this.source[SCRAMJETCLIENT]; + // const scram: ScramjetClient = this.source[SCRAMJETCLIENT]; - if (scram) return scram.globalProxy; + // if (scram) return scram.globalProxy; return this.source; }, @@ -127,7 +126,6 @@ export default function (client: ScramjetClient, self: Self) { client.Proxy("EventTarget.prototype.addEventListener", { apply(ctx) { - unproxy(ctx, client); if (typeof ctx.args[1] !== "function") return; const origlistener = ctx.args[1]; @@ -148,7 +146,6 @@ export default function (client: ScramjetClient, self: Self) { client.Proxy("EventTarget.prototype.removeEventListener", { apply(ctx) { - unproxy(ctx, client); if (typeof ctx.args[1] !== "function") return; const arr = client.eventcallbacks.get(ctx.this); @@ -166,12 +163,6 @@ export default function (client: ScramjetClient, self: Self) { }, }); - client.Proxy("EventTarget.prototype.dispatchEvent", { - apply(ctx) { - unproxy(ctx, client); - }, - }); - const targets = [self.self, self.MessagePort.prototype] as Array; if (iswindow) targets.push(self.HTMLElement.prototype); if (self.Worker) targets.push(self.Worker.prototype); diff --git a/src/client/shared/unproxy.ts b/src/client/shared/unproxy.ts deleted file mode 100644 index f54bf1e5..00000000 --- a/src/client/shared/unproxy.ts +++ /dev/null @@ -1,132 +0,0 @@ -import { iswindow } from "@client/entry"; -import { SCRAMJETCLIENT } from "@/symbols"; -import { ProxyCtx, ScramjetClient } from "@client/index"; - -// we don't want to end up overriding a property on window that's derived from a prototype until we've proxied the prototype -export const order = 3; - -export default function (client: ScramjetClient, self: typeof window) { - // an automated approach to cleaning the documentProxy from dom functions - // it will trigger an illegal invocation if you pass the proxy to c++ code, we gotta hotswap it out with the real one - // admittedly this is pretty slow. worth investigating if there's ways to get back some of the lost performance - - for (const target of [self]) { - for (const prop in target) { - try { - const value = target[prop]; - if (typeof value === "function") { - client.RawProxy(target, prop, { - apply(ctx) { - unproxy(ctx, client); - }, - }); - } - } catch {} - } - } - - if (!iswindow) return; - - for (const target of [ - self.Node.prototype, - self.MutationObserver.prototype, - self.document, - self.MouseEvent.prototype, - self.Range.prototype, - ]) { - for (const prop in target) { - try { - const value = target[prop]; - if (typeof value === "function") { - client.RawProxy(target, prop, { - apply(ctx) { - unproxy(ctx, client); - }, - }); - } - } catch {} - } - } - - client.Proxy("IntersectionObserver", { - construct(ctx) { - unproxy(ctx, client); - if (typeof ctx.args[1] === "object" && "root" in ctx.args[1]) - if (ctx.args[1].root === client.documentProxy) - ctx.args[1].root = self.document; - }, - }); - - // this is probably not how stuff should be done but you cant run defineProperty on the window proxy so... - client.Proxy("Object.defineProperty", { - apply(ctx) { - unproxy(ctx, client); - }, - }); - - client.Proxy("Object.getOwnPropertyDescriptor", { - apply(ctx) { - const desc = ctx.call(); - - if (!desc) return; - - if (desc.get) { - client.RawProxy(desc, "get", { - apply(ctx) { - // value of this in the getter needs to be corrected - unproxy(ctx, client); - }, - }); - } - - if (desc.set) { - client.RawProxy(desc, "set", { - apply(ctx) { - unproxy(ctx, client); - }, - }); - } - - // i don't think we have to care about value but it's worth looking into - - ctx.return(desc); - }, - }); - client.Proxy("Function.prototype.bind", { - apply(ctx) { - if ( - (ctx.args[0] instanceof Window && ctx.args[0] !== client.globalProxy) || - (ctx.args[0] instanceof Document && - ctx.args[0] !== client.documentProxy) - ) { - const client = ctx.args[0][SCRAMJETCLIENT]; - console.log(ctx.this); - ctx.this = new Proxy(ctx.this, { - apply(target, that, args) { - if (that === client.globalProxy) that = client.global; - if (that === client.documentProxy) that = client.global.document; - - for (const i in args) { - if (args[i] === client.globalProxy) args[i] = client.global; - if (args[i] === client.documentProxy) - args[i] = client.global.document; - } - - return Reflect.apply(target, that, args); - }, - }); - } - }, - }); -} - -export function unproxy(ctx: ProxyCtx, client: ScramjetClient) { - const self = client.global; - if (ctx.this === client.globalProxy) ctx.this = self; - if (ctx.this === client.documentProxy) ctx.this = self.document; - - for (const i in ctx.args) { - if (ctx.args[i] === client.globalProxy) ctx.args[i] = self; - if (ctx.args[i] === client.documentProxy) ctx.args[i] = self.document; - } -} diff --git a/src/client/shared/wrap.ts b/src/client/shared/wrap.ts index b913097f..0d601c1e 100644 --- a/src/client/shared/wrap.ts +++ b/src/client/shared/wrap.ts @@ -14,7 +14,7 @@ export function createWrapFn(client: ScramjetClient, self: typeof globalThis) { if (identifier === self.parent) { if (SCRAMJETCLIENT in self.parent) { // ... then we're in a subframe, and the parent frame is also in a proxy context, so we should return its proxy - return self.parent[SCRAMJETCLIENT].globalProxy; + return self.parent; } else { // ... then we should pretend we aren't nested and return the current window return self; From d7ac6ec0240a14e4550e78f9774f610a7d5d7066 Mon Sep 17 00:00:00 2001 From: velzie Date: Tue, 5 Aug 2025 09:51:06 -0400 Subject: [PATCH 15/18] [core] dpsc: proxy location for document.location too --- src/client/shared/wrap.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/client/shared/wrap.ts b/src/client/shared/wrap.ts index 0d601c1e..aa114b66 100644 --- a/src/client/shared/wrap.ts +++ b/src/client/shared/wrap.ts @@ -73,14 +73,14 @@ export default function (client: ScramjetClient, self: typeof globalThis) { config.globals.wrappropertybase + "location", { get: function () { - if (this === self) { + if (this === self || this === self.document) { return client.locationProxy; } return this.location; }, set(value: any) { - if (this === self) { + if (this === self || this === self.document) { client.locationProxy = value; return; From 19ad30767771952e12107ac951c3d2424a6f44fa Mon Sep 17 00:00:00 2001 From: velzie Date: Tue, 5 Aug 2025 09:51:34 -0400 Subject: [PATCH 16/18] [core][rewriter] dpsc: fix eval rewrite --- rewriter/js/src/rewrite.rs | 4 +-- rewriter/js/src/visitor.rs | 74 +++++++++++++++++++++----------------- 2 files changed, 44 insertions(+), 34 deletions(-) diff --git a/rewriter/js/src/rewrite.rs b/rewriter/js/src/rewrite.rs index 4018ac5d..d8050b25 100644 --- a/rewriter/js/src/rewrite.rs +++ b/rewriter/js/src/rewrite.rs @@ -129,9 +129,9 @@ impl<'alloc: 'data, 'data> RewriteType<'alloc, 'data> { ) ], Self::Eval { inner } => smallvec![ - change!(span!(span inner start), EvalRewriteFn), + change!(Span::new(inner.start, inner.start), EvalRewriteFn), change!( - span!(inner span end), + Span::new(inner.end, inner.end), ClosingParen { semi: false, replace: false, diff --git a/rewriter/js/src/visitor.rs b/rewriter/js/src/visitor.rs index 357c496a..ef005b2f 100644 --- a/rewriter/js/src/visitor.rs +++ b/rewriter/js/src/visitor.rs @@ -3,13 +3,9 @@ use std::error::Error; use oxc::{ allocator::{Allocator, StringBuilder}, ast::ast::{ - AssignmentExpression, AssignmentTarget, CallExpression, DebuggerStatement, - ExportAllDeclaration, ExportNamedDeclaration, Expression, FunctionBody, - IdentifierReference, ImportDeclaration, ImportExpression, MemberExpression, MetaProperty, - NewExpression, ObjectExpression, ObjectPropertyKind, ReturnStatement, StringLiteral, - ThisExpression, UnaryExpression, UnaryOperator, UpdateExpression, + AssignmentExpression, AssignmentTarget, CallExpression, ComputedMemberExpression, DebuggerStatement, ExportAllDeclaration, ExportNamedDeclaration, Expression, FunctionBody, IdentifierReference, ImportDeclaration, ImportExpression, MemberExpression, MetaProperty, NewExpression, ObjectExpression, ObjectPropertyKind, ReturnStatement, StringLiteral, ThisExpression, UnaryExpression, UnaryOperator, UpdateExpression }, - ast_visit::{Visit, walk}, + ast_visit::{walk, Visit}, span::{Atom, GetSpan, Span}, }; @@ -64,23 +60,42 @@ where } } - fn walk_member_expression(&mut self, it: &Expression) -> bool { - match it { - Expression::Identifier(s) => false, - Expression::StaticMemberExpression(s) => { - if UNSAFE_GLOBALS.contains(&s.property.name.as_str()) { - // self.jschanges.add(rewrite!(s.span, WrapAccess { - // ident: s.property.name, - // propspan: s.property.span, - // } - // )); - } - self.walk_member_expression(&s.object) - } - Expression::ComputedMemberExpression(s) => self.walk_member_expression(&s.object), - _ => false, - } - } + // fn walk_member_expression(&mut self, it: &Expression) -> bool { + // match it { + // Expression::Identifier(s) => false, + // Expression::StaticMemberExpression(s) => { + // if UNSAFE_GLOBALS.contains(&s.property.name.as_str()) { + // // self.jschanges.add(rewrite!(s.span, WrapAccess { + // // ident: s.property.name, + // // propspan: s.property.span, + // // } + // // )); + // } + // self.walk_member_expression(&s.object) + // } + // Expression::ComputedMemberExpression(s) => self.walk_member_expression(&s.object), + // _ => false, + // } + // } + fn walk_computed_member_expression(&mut self, it: &ComputedMemberExpression<'data>) { + match &it.expression{ + Expression::NullLiteral(_) | Expression::BigIntLiteral(_) | Expression::NumericLiteral(_) | Expression::RegExpLiteral(_) | Expression::BooleanLiteral(_) => {}, + Expression::StringLiteral(lit) =>{ + if UNSAFE_GLOBALS.contains(&lit.value.as_str()) { + self.jschanges.add(rewrite!( + it.expression.span(), + WrapProperty, + )); + } + }, + _=> { + self.jschanges.add(rewrite!( + it.expression.span(), + WrapProperty, + )); + } + } + } fn scramitize(&mut self, span: Span) { self.jschanges.add(rewrite!(span, Scramitize)); @@ -110,7 +125,8 @@ where } fn visit_new_expression(&mut self, it: &NewExpression<'data>) { - self.walk_member_expression(&it.callee); + // ?? + // self.walk_member_expression(&it.callee); walk::walk_arguments(self, &it.arguments); } @@ -136,10 +152,7 @@ where } } MemberExpression::ComputedMemberExpression(s) => { - self.jschanges.add(rewrite!( - s.expression.span(), - WrapProperty, - )); + self.walk_computed_member_expression(s); } _ => {} // if !self.flags.strict_rewrites // && !UNSAFE_GLOBALS.contains(&s.property.name.as_str()) @@ -318,10 +331,7 @@ where walk::walk_expression(self, &s.object); } AssignmentTarget::ComputedMemberExpression(s) => { - self.jschanges.add(rewrite!( - s.expression.span(), - WrapProperty, - )); + self.walk_computed_member_expression(s); walk::walk_expression(self, &s.object); walk::walk_expression(self, &s.expression); } From b3cc3b83ea7fd2cb29800025421f8fbcf43f5559 Mon Sep 17 00:00:00 2001 From: velzie Date: Tue, 5 Aug 2025 09:53:07 -0400 Subject: [PATCH 17/18] [core][rewriter] dpsc: handle the comma operator correctly in wrapped properties --- rewriter/js/src/changes.rs | 4 +++- rewriter/js/src/rewrite.rs | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/rewriter/js/src/changes.rs b/rewriter/js/src/changes.rs index ef522ed4..c765fa39 100644 --- a/rewriter/js/src/changes.rs +++ b/rewriter/js/src/changes.rs @@ -36,6 +36,7 @@ pub enum JsChangeType<'alloc: 'data, 'data> { }, WrapPropertyLeft, + WrapPropertyRight, RewriteProperty { ident: Atom<'data>, }, @@ -119,7 +120,8 @@ impl<'alloc: 'data, 'data> Transform<'data> for JsChange<'alloc, 'data> { } else { transforms![")"] }), - Ty::WrapPropertyLeft => LL::insert(transforms![&cfg.wrappropertyfn, "("]), + Ty::WrapPropertyLeft => LL::insert(transforms![&cfg.wrappropertyfn, "(("]), + Ty::WrapPropertyRight => LL::insert(transforms!["))"]), Ty::RewriteProperty { ident } => LL::replace(transforms![&cfg.wrappropertybase,ident]), Ty::SetRealmFn => LL::insert(transforms![&cfg.setrealmfn, "({})."]), diff --git a/rewriter/js/src/rewrite.rs b/rewriter/js/src/rewrite.rs index d8050b25..b3fced88 100644 --- a/rewriter/js/src/rewrite.rs +++ b/rewriter/js/src/rewrite.rs @@ -112,7 +112,7 @@ impl<'alloc: 'data, 'data> RewriteType<'alloc, 'data> { ], Self::WrapProperty => smallvec![ change!(span!(start), WrapPropertyLeft), - change!(span!(end), ClosingParen { semi: false, replace: false }), + change!(span!(end), WrapPropertyRight), ], Self::SetRealmFn => smallvec![change!(span, SetRealmFn)], Self::ImportFn => smallvec![change!(span, ImportFn)], From 02052653ae7451edcf9d0d34ed6eaf3b15990a7c Mon Sep 17 00:00:00 2001 From: velzie Date: Tue, 5 Aug 2025 10:08:02 -0400 Subject: [PATCH 18/18] [core] dpsc: oops --- src/client/shared/wrap.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/client/shared/wrap.ts b/src/client/shared/wrap.ts index aa114b66..9dcdc73e 100644 --- a/src/client/shared/wrap.ts +++ b/src/client/shared/wrap.ts @@ -73,6 +73,8 @@ export default function (client: ScramjetClient, self: typeof globalThis) { config.globals.wrappropertybase + "location", { get: function () { + // if (this.location.constructor.toString().includes("Location")) { + if (this === self || this === self.document) { return client.locationProxy; } @@ -81,7 +83,7 @@ export default function (client: ScramjetClient, self: typeof globalThis) { }, set(value: any) { if (this === self || this === self.document) { - client.locationProxy = value; + client.url = value; return; }