diff --git a/rewriter/Cargo.toml b/rewriter/Cargo.toml index fb223d9..bb8f13e 100644 --- a/rewriter/Cargo.toml +++ b/rewriter/Cargo.toml @@ -11,3 +11,9 @@ panic = "abort" [workspace.dependencies] oxc = "0.41.0" + +[workspace.lints.clippy] +pedantic = { level = "warn", priority = -1 } +struct-excessive-bools = "allow" +missing-errors-doc = "allow" +cast-possible-truncation = "allow" diff --git a/rewriter/native/Cargo.toml b/rewriter/native/Cargo.toml index 0146a06..9ea0463 100644 --- a/rewriter/native/Cargo.toml +++ b/rewriter/native/Cargo.toml @@ -3,6 +3,9 @@ name = "native" version = "0.1.0" edition = "2021" +[lints] +workspace = true + [dependencies] anyhow = "1.0.94" oxc = { workspace = true } diff --git a/rewriter/native/src/main.rs b/rewriter/native/src/main.rs index 5778dd8..61e25d3 100644 --- a/rewriter/native/src/main.rs +++ b/rewriter/native/src/main.rs @@ -10,6 +10,7 @@ fn dorewrite(data: &str) -> Result { let url = Url::from_str("https://google.com/glorngle/si.js").context("failed to make url")?; rewrite( data, + 1024, Config { prefix: "/scrammedjet/".to_string(), base: url.to_string(), diff --git a/rewriter/rewriter/Cargo.toml b/rewriter/rewriter/Cargo.toml index 8fc2481..b8c6d6d 100644 --- a/rewriter/rewriter/Cargo.toml +++ b/rewriter/rewriter/Cargo.toml @@ -3,6 +3,9 @@ name = "rewriter" version = "0.1.0" edition = "2021" +[lints] +workspace = true + [dependencies] oxc = { workspace = true } smallvec = "1.13.2" diff --git a/rewriter/rewriter/src/changes.rs b/rewriter/rewriter/src/changes.rs index 944a74b..06c1a85 100644 --- a/rewriter/rewriter/src/changes.rs +++ b/rewriter/rewriter/src/changes.rs @@ -364,9 +364,9 @@ pub(crate) struct JsChanges { } impl JsChanges { - pub fn new() -> Self { + pub fn new(capacity: usize) -> Self { Self { - inner: Default::default(), + inner: Vec::with_capacity(capacity), } } @@ -391,8 +391,8 @@ impl JsChanges { }; } - // TODO: add sourcemaps - let map = Vec::with_capacity(js.len() * 2); + let mut map = Vec::with_capacity(js.len() * 2); + map.extend_from_slice(&(self.inner.len() as u32).to_le_bytes()); self.inner.sort_unstable(); @@ -406,6 +406,13 @@ impl JsChanges { let inner = change.to_inner(cfg); match inner { JsChangeInner::Insert { loc, str } => { + // INSERT op + map.push(0); + // start + map.extend_from_slice(&loc.to_le_bytes()); + // size + map.extend_from_slice(&(str.len() as u32).to_le_bytes()); + let loc = loc as usize; buffer.extend_from_slice(tryget!(start..loc).as_bytes()); for str in str { @@ -414,6 +421,15 @@ impl JsChanges { buffer.extend_from_slice(tryget!(loc..end).as_bytes()); } JsChangeInner::Replace { str } => { + // REPLACE op + map.push(1); + // start + map.extend_from_slice(&span.start.to_le_bytes()); + // end + map.extend_from_slice(&span.end.to_le_bytes()); + // oldstr + map.extend_from_slice(tryget!(start..end).as_bytes()); + for str in str { buffer.extend_from_slice(str.as_bytes()); } diff --git a/rewriter/rewriter/src/lib.rs b/rewriter/rewriter/src/lib.rs index ecba221..5e06a86 100644 --- a/rewriter/rewriter/src/lib.rs +++ b/rewriter/rewriter/src/lib.rs @@ -26,10 +26,15 @@ pub enum RewriterError { pub struct RewriteResult { pub js: Vec, pub sourcemap: Vec, + pub sourcetag: String, pub errors: Vec, } -pub fn rewrite(js: &str, config: Config) -> Result +pub fn rewrite( + js: &str, + capacity: usize, + config: Config, +) -> Result where E: Fn(String) -> String, E: Clone, @@ -49,14 +54,14 @@ where if ret.panicked { let mut errors = String::new(); for error in ret.errors { - errors.push_str(&format!("{}", error)); + errors.push_str(&format!("{error}")); errors.push('\n'); } return Err(RewriterError::OxcPanicked(errors)); } let mut visitor = Visitor { - jschanges: JsChanges::new(), + jschanges: JsChanges::new(capacity), config, }; visitor.visit_program(&ret.program); @@ -70,6 +75,7 @@ where Ok(RewriteResult { js, sourcemap, + sourcetag: config.sourcetag, errors: ret.errors, }) } diff --git a/rewriter/rewriter/src/visitor.rs b/rewriter/rewriter/src/visitor.rs index 2ff47c4..b299fa1 100644 --- a/rewriter/rewriter/src/visitor.rs +++ b/rewriter/rewriter/src/visitor.rs @@ -117,40 +117,31 @@ where } fn visit_member_expression(&mut self, it: &MemberExpression) { - match it { - MemberExpression::StaticMemberExpression(s) => { - if s.property.name == "postMessage" { - self.jschanges.add(Rewrite::SetRealmFn { - span: s.property.span, - }); + // 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::SetRealmFn { + span: s.property.span, + }); - walk::walk_expression(self, &s.object); - return; // unwise to walk the rest of the tree - } + walk::walk_expression(self, &s.object); + return; // unwise to walk the rest of the tree + } - if !self.config.strict_rewrites - && !UNSAFE_GLOBALS.contains(&s.property.name.as_str()) - { - if let Expression::Identifier(_) = &s.object { - // cull tree - this should be safe - return; - } - if let Expression::ThisExpression(_) = &s.object { - return; - } - } - - if self.config.scramitize - && !matches!(s.object, Expression::MetaProperty(_) | Expression::Super(_)) - { - self.scramitize(s.object.span()); + if !self.config.strict_rewrites && !UNSAFE_GLOBALS.contains(&s.property.name.as_str()) { + if let Expression::Identifier(_) | Expression::ThisExpression(_) = &s.object { + // cull tree - this should be safe + return; } } - _ => { - // 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 self.config.scramitize + && !matches!(s.object, Expression::MetaProperty(_) | Expression::Super(_)) + { + self.scramitize(s.object.span()); } } diff --git a/rewriter/wasm/Cargo.toml b/rewriter/wasm/Cargo.toml index 548ef54..e74bd53 100644 --- a/rewriter/wasm/Cargo.toml +++ b/rewriter/wasm/Cargo.toml @@ -3,6 +3,9 @@ name = "wasm" version = "0.1.0" edition = "2021" +[lints] +workspace = true + [lib] crate-type = ["cdylib"] diff --git a/rewriter/wasm/src/lib.rs b/rewriter/wasm/src/lib.rs index 3a92a2e..29c8a26 100644 --- a/rewriter/wasm/src/lib.rs +++ b/rewriter/wasm/src/lib.rs @@ -12,7 +12,13 @@ use web_sys::Url; #[wasm_bindgen(typescript_custom_section)] const REWRITER_OUTPUT: &'static str = r#" -type RewriterOutput = { js: Uint8Array, errors: string[], duration: bigint }; +type RewriterOutput = { + js: Uint8Array, + map: Uint8Array, + scramtag: string, + errors: string[], + duration: bigint +}; "#; #[wasm_bindgen(inline_js = r#" @@ -67,10 +73,10 @@ fn get_str(obj: &JsValue, k: &str) -> Result { } fn set_obj(obj: &Object, k: &str, v: &JsValue) -> Result<()> { - if !Reflect::set(&obj.into(), &k.into(), v)? { - Err(RewriterError::ReflectSetFail(k.to_string())) - } else { + if Reflect::set(&obj.into(), &k.into(), v)? { Ok(()) + } else { + Err(RewriterError::ReflectSetFail(k.to_string())) } } @@ -111,6 +117,7 @@ fn get_config(scramjet: &Object, url: String) -> Result f64 { (duration.as_secs() as f64) * 1_000f64 + (duration.subsec_nanos() as f64) / 1_000_000f64 } @@ -131,6 +138,8 @@ fn create_rewriter_output( let obj = Object::new(); set_obj(&obj, "js", &out.js.into())?; + set_obj(&obj, "map", &out.sourcemap.into())?; + set_obj(&obj, "scramtag", &out.sourcetag.into())?; #[cfg(feature = "debug")] set_obj(&obj, "errors", &errs.into())?; #[cfg(not(feature = "debug"))] @@ -148,7 +157,7 @@ pub fn rewrite_js( scramjet: &Object, ) -> Result { let before = Instant::now(); - let out = rewrite(&js, get_config(scramjet, url)?)?; + let out = rewrite(&js, 1024, get_config(scramjet, url)?)?; let after = Instant::now(); create_rewriter_output(out, script_url, js, after - before) @@ -165,7 +174,7 @@ pub fn rewrite_js_from_arraybuffer( let js = unsafe { String::from_utf8_unchecked(js) }; let before = Instant::now(); - let out = rewrite(&js, get_config(scramjet, url)?)?; + let out = rewrite(&js, 1024, get_config(scramjet, url)?)?; let after = Instant::now(); create_rewriter_output(out, script_url, js, after - before) diff --git a/src/client/shared/sourcemaps.ts b/src/client/shared/sourcemaps.ts index 80747dc..0c8b8e2 100644 --- a/src/client/shared/sourcemaps.ts +++ b/src/client/shared/sourcemaps.ts @@ -1,18 +1,70 @@ -import { flagEnabled } from "../../scramjet"; +import { $scramjet, flagEnabled } from "../../scramjet"; import { ScramjetClient } from "../client"; -type Mapping = [string, number, number]; +enum RewriteType { + Insert = 0, + Replace = 1, +} -const sourcemaps: Record = {}; +type Rewrite = + | { + type: RewriteType.Insert; + // start of insertion + start: number; + // size of insertion + size: number; + } + | { + type: RewriteType.Replace; + // start of replacement + start: number; + // end of replacement + end: number; + // old string + str: string; + }; + +const sourcemaps: Record = {}; export const enabled = (client: ScramjetClient) => flagEnabled("sourcemaps", client.url); export default function (client: ScramjetClient, self: Self) { // every script will push a sourcemap - Object.defineProperty(self, "$scramjet$pushsourcemap", { - value: (maps: Mapping[], tag: string) => { - sourcemaps[tag] = maps; + Object.defineProperty(self, $scramjet.config.globals.pushsourcemapfn, { + value: (buf: Array, tag: string) => { + const sourcemap = Uint8Array.from(buf); + const view = new DataView(sourcemap.buffer); + const decoder = new TextDecoder("utf-8"); + + const rewrites = []; + + const rewritelen = view.getUint32(0, true); + let cursor = 0; + for (let i = 0; i < rewritelen; i++) { + const type = view.getUint8(cursor) as RewriteType; + cursor += 1; + + if (type == RewriteType.Insert) { + const start = view.getUint32(cursor, true); + cursor += 4; + const size = view.getUint32(cursor, true); + cursor += 4; + + rewrites.push({ type, start, size }); + } else if (type == RewriteType.Replace) { + const start = view.getUint32(cursor, true); + cursor += 4; + const end = view.getUint32(cursor, true); + cursor += 4; + + const str = decoder.decode(sourcemap.subarray(start, end)); + + rewrites.push({ type, start, end, str }); + } + } + + sourcemaps[tag] = rewrites; }, enumerable: false, writable: false, @@ -60,6 +112,7 @@ export default function (client: ScramjetClient, self: Self) { let j = 0; while (j < maps.length) { + /* TODO const [str, start, end] = maps[j]; if (start < absindex) { j++; @@ -74,6 +127,7 @@ export default function (client: ScramjetClient, self: Self) { i = start - absindex + offset + str.length; j++; + */ } newString += stringified.slice(i);