cfsolve 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -0
- data/README.md +10 -9
- data/UPSTREAM.md +6 -6
- data/lib/cfsolve/profiles/safari-26-ios/answers.json +2562 -1887
- data/lib/cfsolve/profiles/safari-26-ios/challenge-page-answers.json +1873 -1198
- data/lib/cfsolve/profiles/safari-26-ios.json +0 -8
- data/lib/cfsolve/version.rb +2 -2
- data/vendor/cfsolve/README.md +0 -35
- data/vendor/cfsolve/examples/build_profile.rs +17 -15
- data/vendor/cfsolve/examples/capture_session.rs +37 -0
- data/vendor/cfsolve/examples/check_answers.rs +66 -0
- data/vendor/cfsolve/examples/compare_session.rs +190 -0
- data/vendor/cfsolve/examples/stage2_snapshot.rs +4 -1
- data/vendor/cfsolve/profiles/safari-26-ios/answers.json +2562 -1887
- data/vendor/cfsolve/profiles/safari-26-ios/challenge-page-answers.json +1873 -1198
- data/vendor/cfsolve/profiles/safari-26-ios.json +0 -8
- data/vendor/cfsolve/scripts/capture_cssom.mjs +42 -0
- data/vendor/cfsolve/scripts/serve_cssom.py +111 -0
- data/vendor/cfsolve/src/capture/profile.rs +7 -18
- data/vendor/cfsolve/src/capture/recorded.rs +42 -7
- data/vendor/cfsolve/src/challenge/installed/audit.rs +131 -0
- data/vendor/cfsolve/src/challenge/installed.rs +122 -271
- data/vendor/cfsolve/src/challenge/payload.rs +107 -5
- data/vendor/cfsolve/src/challenge/state.rs +98 -16
- data/vendor/cfsolve/src/deobfuscate/encoded_strings.rs +3 -2
- data/vendor/cfsolve/src/deobfuscate/helper_objects.rs +17 -32
- data/vendor/cfsolve/src/deobfuscate/register_arrays.rs +18 -5
- data/vendor/cfsolve/src/embedding_page/extra_params.rs +0 -14
- data/vendor/cfsolve/src/embedding_page/page.rs +28 -2
- data/vendor/cfsolve/src/http/client.rs +11 -0
- data/vendor/cfsolve/src/js/ast.rs +12 -3
- data/vendor/cfsolve/src/js/objects.rs +9 -2
- data/vendor/cfsolve/src/listing/fold.rs +0 -83
- data/vendor/cfsolve/src/listing/query.rs +25 -244
- data/vendor/cfsolve/src/listing.rs +0 -1
- data/vendor/cfsolve/src/session/initial.rs +14 -5
- data/vendor/cfsolve/src/session/probes.rs +160 -125
- data/vendor/cfsolve/src/session/timing.rs +58 -53
- data/vendor/cfsolve/src/session/widget.rs +5 -6
- data/vendor/cfsolve/src/stage2/analysis/analyze.rs +50 -33
- data/vendor/cfsolve/src/stage2/analysis/discovery.rs +61 -66
- data/vendor/cfsolve/src/stage2/analysis/envelope.rs +31 -21
- data/vendor/cfsolve/src/stage2/analysis/recognize.rs +3 -5
- data/vendor/cfsolve/src/stage2/analysis.rs +0 -1
- data/vendor/cfsolve/src/stage2/audit_globals.rs +310 -0
- data/vendor/cfsolve/src/stage2/benchmark.rs +5 -8
- data/vendor/cfsolve/src/stage2/call_stack.rs +72 -16
- data/vendor/cfsolve/src/stage2/interaction/native.rs +2 -1
- data/vendor/cfsolve/src/stage2/interaction/plan.rs +0 -3
- data/vendor/cfsolve/src/stage2/payload/recover.rs +54 -23
- data/vendor/cfsolve/src/stage2/probes/ice.rs +11 -10
- data/vendor/cfsolve/src/stage2/probes/media.rs +47 -33
- data/vendor/cfsolve/src/stage2/probes/performance.rs +1 -0
- data/vendor/cfsolve/src/stage2/probes/rects.rs +12 -17
- data/vendor/cfsolve/src/stage2/probes/stack.rs +50 -103
- data/vendor/cfsolve/src/stage2/probes/websocket_echo.rs +17 -86
- data/vendor/cfsolve/src/stage2/probes.rs +0 -1
- data/vendor/cfsolve/src/stage2/records.rs +4 -13
- data/vendor/cfsolve/src/stage2/reference.json +1 -1
- data/vendor/cfsolve/src/stage2/reference.rs +98 -100
- data/vendor/cfsolve/src/stage2/registry.rs +41 -34
- data/vendor/cfsolve/src/stage2/replay.rs +182 -254
- data/vendor/cfsolve/src/stage2/stylesheets.rs +176 -0
- data/vendor/cfsolve/src/stage2/trace.rs +18 -0
- data/vendor/cfsolve/src/stage2/vocabulary.rs +111 -401
- data/vendor/cfsolve/src/stage2.rs +2 -1
- data/vendor/cfsolve/src/vm/disassemble.rs +39 -10
- data/vendor/cfsolve/src/vm/interpret/access.rs +5 -21
- data/vendor/cfsolve/src/vm/interpret/builtin.rs +6 -53
- data/vendor/cfsolve/src/vm/interpret/call.rs +86 -9
- data/vendor/cfsolve/src/vm/interpret/compile.rs +626 -0
- data/vendor/cfsolve/src/vm/interpret/copies.rs +232 -120
- data/vendor/cfsolve/src/vm/interpret/evaluate/tests.rs +96 -8
- data/vendor/cfsolve/src/vm/interpret/evaluate.rs +219 -92
- data/vendor/cfsolve/src/vm/interpret/explore.rs +155 -39
- data/vendor/cfsolve/src/vm/interpret/frozen.rs +171 -0
- data/vendor/cfsolve/src/vm/interpret/intrinsic.rs +350 -0
- data/vendor/cfsolve/src/vm/interpret/plan.rs +71 -44
- data/vendor/cfsolve/src/vm/interpret/run.rs +202 -13
- data/vendor/cfsolve/src/vm/interpret.rs +7 -2
- data/vendor/cfsolve/src/vm/machine.rs +6 -2
- data/vendor/cfsolve/src/vm/packed_names.rs +307 -0
- data/vendor/cfsolve/src/vm/session.rs +36 -14
- data/vendor/cfsolve/src/vm/value.rs +13 -0
- data/vendor/cfsolve/src/vm.rs +1 -0
- data/vendor/cfsolve/vendor/btls-sys/src/lib.rs +3 -0
- metadata +13 -6
- data/vendor/cfsolve/src/listing/normalize.rs +0 -264
- data/vendor/cfsolve/src/stage2/analysis/outputs.rs +0 -327
- data/vendor/cfsolve/src/stage2/probes/stylesheets.rs +0 -249
- data/vendor/cfsolve/src/stage2/reference-expanded.json +0 -42
- data/vendor/cfsolve/src/stage2/session_names.rs +0 -470
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
//! Queries value sources, function reachability, and common program reads such as `Date.now()`,
|
|
2
2
|
//! challenge options, and record timestamps.
|
|
3
3
|
//!
|
|
4
|
-
//! [`Index::
|
|
5
|
-
//! returns
|
|
4
|
+
//! [`Index::register_before`] finds the nearest earlier instruction assigning a register and
|
|
5
|
+
//! returns the first write from any of its paths. [`Index::assignment`] resolves each write
|
|
6
|
+
//! through its own path, including path-local temporaries.
|
|
6
7
|
|
|
7
8
|
use std::collections::{BTreeMap, BTreeSet};
|
|
8
9
|
|
|
@@ -10,9 +11,7 @@ use anyhow::{Context, Result, ensure};
|
|
|
10
11
|
use swc_core::ecma::ast::BinaryOp;
|
|
11
12
|
|
|
12
13
|
use super::Index;
|
|
13
|
-
use super::fold::{Frame, constant};
|
|
14
14
|
use crate::candidates;
|
|
15
|
-
use crate::js::JsValue;
|
|
16
15
|
use crate::vm::{Constant, Effect, Instruction, Listing, Term};
|
|
17
16
|
|
|
18
17
|
/// A property write, `object.key = value` or `object[key] = value`. Dynamic keys resolve to the
|
|
@@ -56,55 +55,6 @@ pub(crate) fn walk(
|
|
|
56
55
|
const DEREFERENCE_FUEL: usize = 8;
|
|
57
56
|
|
|
58
57
|
impl Index<'_> {
|
|
59
|
-
/// Recovers arrays populated through numeric stores, rejecting holes and conflicting writes.
|
|
60
|
-
pub(crate) fn string_arrays(&self, entry: u32) -> Vec<Vec<String>> {
|
|
61
|
-
let mut arrays = BTreeMap::<u32, BTreeMap<usize, String>>::new();
|
|
62
|
-
let mut invalid = BTreeSet::new();
|
|
63
|
-
for pc in self.reachable(entry) {
|
|
64
|
-
let Some(instruction) = self.listing().instructions.get(&pc) else {
|
|
65
|
-
continue;
|
|
66
|
-
};
|
|
67
|
-
for effect in instruction.effects() {
|
|
68
|
-
let Effect::SetProperty { object, key, value } = effect else {
|
|
69
|
-
continue;
|
|
70
|
-
};
|
|
71
|
-
let Some((origin, Term::Array(initial))) = self.dereference(pc, object) else {
|
|
72
|
-
continue;
|
|
73
|
-
};
|
|
74
|
-
if !initial.is_empty() {
|
|
75
|
-
continue;
|
|
76
|
-
}
|
|
77
|
-
let Some((_, Term::Const(Constant::Number(index)))) = self.dereference(pc, key)
|
|
78
|
-
else {
|
|
79
|
-
invalid.insert(origin);
|
|
80
|
-
continue;
|
|
81
|
-
};
|
|
82
|
-
let Some(value) = self.text(pc, value) else {
|
|
83
|
-
invalid.insert(origin);
|
|
84
|
-
continue;
|
|
85
|
-
};
|
|
86
|
-
if !index.is_finite() || index < 0.0 || index.fract() != 0.0 {
|
|
87
|
-
invalid.insert(origin);
|
|
88
|
-
continue;
|
|
89
|
-
}
|
|
90
|
-
let array = arrays.entry(origin).or_default();
|
|
91
|
-
if array
|
|
92
|
-
.insert(index as usize, value.clone())
|
|
93
|
-
.is_some_and(|old| old != value)
|
|
94
|
-
{
|
|
95
|
-
invalid.insert(origin);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
arrays
|
|
100
|
-
.into_iter()
|
|
101
|
-
.filter(|(origin, array)| {
|
|
102
|
-
!invalid.contains(origin) && array.keys().copied().eq(0..array.len())
|
|
103
|
-
})
|
|
104
|
-
.map(|(_, array)| array.into_values().collect())
|
|
105
|
-
.collect()
|
|
106
|
-
}
|
|
107
|
-
|
|
108
58
|
/// Caches property writes in program-counter order. Resolves dynamic keys through the
|
|
109
59
|
/// nearest earlier register assignment and skips stores whose keys are not known strings.
|
|
110
60
|
#[must_use]
|
|
@@ -118,18 +68,22 @@ impl Index<'_> {
|
|
|
118
68
|
let Term::Reg(object) = **object else {
|
|
119
69
|
continue;
|
|
120
70
|
};
|
|
121
|
-
let
|
|
71
|
+
let key = match &**key {
|
|
72
|
+
Term::Reg(register) => self.register_before(pc, *register),
|
|
73
|
+
key => Some(key),
|
|
74
|
+
};
|
|
75
|
+
let Some(Term::Const(Constant::String(key))) = key else {
|
|
122
76
|
continue;
|
|
123
77
|
};
|
|
124
78
|
if stores.last().is_some_and(|store: &Store| {
|
|
125
|
-
store.pc == pc && store.object == object && store.key == key
|
|
79
|
+
store.pc == pc && store.object == object && store.key == *key
|
|
126
80
|
}) {
|
|
127
81
|
continue;
|
|
128
82
|
}
|
|
129
83
|
stores.push(Store {
|
|
130
84
|
pc,
|
|
131
85
|
object,
|
|
132
|
-
key,
|
|
86
|
+
key: key.clone(),
|
|
133
87
|
value: (**value).clone(),
|
|
134
88
|
});
|
|
135
89
|
}
|
|
@@ -161,6 +115,18 @@ impl Index<'_> {
|
|
|
161
115
|
reached
|
|
162
116
|
}
|
|
163
117
|
|
|
118
|
+
/// Returns the first write to `register` on any path of the nearest assigning instruction
|
|
119
|
+
/// before `pc`.
|
|
120
|
+
fn register_before(&self, pc: u32, register: u8) -> Option<&Term> {
|
|
121
|
+
let at = self.assigning_before(pc, register)?;
|
|
122
|
+
self.listing().instructions[&at]
|
|
123
|
+
.effects()
|
|
124
|
+
.find_map(|effect| match effect {
|
|
125
|
+
Effect::Set(target, value) if *target == register => Some(value),
|
|
126
|
+
_ => None,
|
|
127
|
+
})
|
|
128
|
+
}
|
|
129
|
+
|
|
164
130
|
/// Finds the nearest assignment to `register` before `before`, retaining its program
|
|
165
131
|
/// counter so operands resolve at assignment time. Resolves temporaries only through the
|
|
166
132
|
/// path that computed them; missing path values leave the assignment unknown.
|
|
@@ -205,56 +171,10 @@ impl Index<'_> {
|
|
|
205
171
|
/// The string `term` holds at `pc`.
|
|
206
172
|
#[must_use]
|
|
207
173
|
pub(crate) fn text(&self, pc: u32, term: &Term) -> Option<String> {
|
|
208
|
-
self.
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
/// Resolves the small constant expressions left in an inline string decoder when a
|
|
212
|
-
/// control-flow join prevents whole-program folding. Every operand must be a literal.
|
|
213
|
-
fn literal(&self, pc: u32, term: &Term, fuel: usize) -> Option<JsValue> {
|
|
214
|
-
if fuel == 0 {
|
|
215
|
-
return None;
|
|
174
|
+
match self.dereference(pc, term)? {
|
|
175
|
+
(_, Term::Const(Constant::String(text))) => Some(text),
|
|
176
|
+
_ => None,
|
|
216
177
|
}
|
|
217
|
-
let (pc, term) = self.dereference(pc, term)?;
|
|
218
|
-
let literal = |term: &Term| -> Option<Term> {
|
|
219
|
-
Some(Term::Const(match self.literal(pc, term, fuel - 1)? {
|
|
220
|
-
JsValue::String(text) => Constant::String(text),
|
|
221
|
-
JsValue::Number(number) => Constant::Number(number),
|
|
222
|
-
JsValue::Bool(value) => Constant::Bool(value),
|
|
223
|
-
JsValue::Null => Constant::Null,
|
|
224
|
-
JsValue::Undefined => Constant::Undefined,
|
|
225
|
-
_ => return None,
|
|
226
|
-
}))
|
|
227
|
-
};
|
|
228
|
-
let resolved = match term {
|
|
229
|
-
Term::Const(_) => term,
|
|
230
|
-
Term::Binary(op, left, right) => {
|
|
231
|
-
Term::Binary(op, Box::new(literal(&left)?), Box::new(literal(&right)?))
|
|
232
|
-
}
|
|
233
|
-
Term::Call {
|
|
234
|
-
callee,
|
|
235
|
-
this,
|
|
236
|
-
arguments,
|
|
237
|
-
} => {
|
|
238
|
-
let (at, Term::Get(object, method)) = self.dereference(pc, &callee)? else {
|
|
239
|
-
return None;
|
|
240
|
-
};
|
|
241
|
-
let object = self.literal(at, &object, fuel - 1)?.as_str()?.to_owned();
|
|
242
|
-
let method = self.literal(at, &method, fuel - 1)?.as_str()?.to_owned();
|
|
243
|
-
Term::Call {
|
|
244
|
-
callee: Box::new(Term::Get(
|
|
245
|
-
Box::new(Term::Const(Constant::String(object))),
|
|
246
|
-
Box::new(Term::Const(Constant::String(method))),
|
|
247
|
-
)),
|
|
248
|
-
this: match this {
|
|
249
|
-
Some(this) => Some(Box::new(literal(&this)?)),
|
|
250
|
-
None => None,
|
|
251
|
-
},
|
|
252
|
-
arguments: arguments.iter().map(literal).collect::<Option<_>>()?,
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
_ => return None,
|
|
256
|
-
};
|
|
257
|
-
constant(&Frame::<()>::default(), &resolved)
|
|
258
178
|
}
|
|
259
179
|
|
|
260
180
|
/// Resolves `term` to an `object[key]` read with a string key.
|
|
@@ -356,142 +276,3 @@ impl Index<'_> {
|
|
|
356
276
|
Ok([started, finished, duration])
|
|
357
277
|
}
|
|
358
278
|
}
|
|
359
|
-
|
|
360
|
-
#[cfg(test)]
|
|
361
|
-
mod tests {
|
|
362
|
-
use super::*;
|
|
363
|
-
use crate::vm::{Exit, Path};
|
|
364
|
-
|
|
365
|
-
fn text(value: &str) -> Term {
|
|
366
|
-
Term::Const(Constant::String(value.into()))
|
|
367
|
-
}
|
|
368
|
-
fn number(value: f64) -> Term {
|
|
369
|
-
Term::Const(Constant::Number(value))
|
|
370
|
-
}
|
|
371
|
-
fn call(object: Term, method: &str, arguments: Vec<Term>) -> Term {
|
|
372
|
-
Term::Call {
|
|
373
|
-
callee: Box::new(Term::Get(Box::new(object), Box::new(text(method)))),
|
|
374
|
-
this: None,
|
|
375
|
-
arguments,
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
fn store(object: u8, key: Term, value: Term) -> Effect {
|
|
379
|
-
Effect::SetProperty {
|
|
380
|
-
object: Box::new(Term::Reg(object)),
|
|
381
|
-
key: Box::new(key),
|
|
382
|
-
value: Box::new(value),
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
fn listing(effects: Vec<Effect>) -> Listing {
|
|
386
|
-
let count = effects.len();
|
|
387
|
-
let mut listing = Listing::default();
|
|
388
|
-
for (pc, effect) in effects.into_iter().enumerate() {
|
|
389
|
-
listing.instructions.insert(
|
|
390
|
-
pc as u32,
|
|
391
|
-
Instruction {
|
|
392
|
-
pc: pc as u32,
|
|
393
|
-
key: 0,
|
|
394
|
-
opcode: 0,
|
|
395
|
-
paths: vec![Path {
|
|
396
|
-
conditions: vec![],
|
|
397
|
-
effects: vec![effect],
|
|
398
|
-
exit: if pc + 1 == count {
|
|
399
|
-
Exit::Return
|
|
400
|
-
} else {
|
|
401
|
-
Exit::Next {
|
|
402
|
-
pc: pc as u32 + 1,
|
|
403
|
-
key: 0,
|
|
404
|
-
}
|
|
405
|
-
},
|
|
406
|
-
}],
|
|
407
|
-
},
|
|
408
|
-
);
|
|
409
|
-
}
|
|
410
|
-
listing
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
#[test]
|
|
414
|
-
fn resolves_stored_keys_through_inline_string_decoders() {
|
|
415
|
-
let listing = listing(vec![
|
|
416
|
-
Effect::Set(1, text("\0\u{6}Field7padding")),
|
|
417
|
-
Effect::Set(2, number(2.0)),
|
|
418
|
-
Effect::Set(3, number(256.0)),
|
|
419
|
-
Effect::Set(4, call(Term::Reg(1), "charCodeAt", vec![number(0.0)])),
|
|
420
|
-
Effect::Set(5, call(Term::Reg(1), "charCodeAt", vec![number(1.0)])),
|
|
421
|
-
Effect::Set(
|
|
422
|
-
4,
|
|
423
|
-
Term::Binary(
|
|
424
|
-
BinaryOp::Mul,
|
|
425
|
-
Box::new(Term::Reg(4)),
|
|
426
|
-
Box::new(Term::Reg(3)),
|
|
427
|
-
),
|
|
428
|
-
),
|
|
429
|
-
Effect::Set(
|
|
430
|
-
4,
|
|
431
|
-
Term::Binary(
|
|
432
|
-
BinaryOp::Add,
|
|
433
|
-
Box::new(Term::Reg(4)),
|
|
434
|
-
Box::new(Term::Reg(5)),
|
|
435
|
-
),
|
|
436
|
-
),
|
|
437
|
-
Effect::Set(
|
|
438
|
-
4,
|
|
439
|
-
Term::Binary(
|
|
440
|
-
BinaryOp::Add,
|
|
441
|
-
Box::new(Term::Reg(4)),
|
|
442
|
-
Box::new(Term::Reg(2)),
|
|
443
|
-
),
|
|
444
|
-
),
|
|
445
|
-
Effect::Set(
|
|
446
|
-
1,
|
|
447
|
-
call(Term::Reg(1), "slice", vec![Term::Reg(2), Term::Reg(4)]),
|
|
448
|
-
),
|
|
449
|
-
store(9, Term::Reg(1), number(1.0)),
|
|
450
|
-
store(
|
|
451
|
-
9,
|
|
452
|
-
call(Term::Reg(99), "slice", vec![number(0.0), number(1.0)]),
|
|
453
|
-
number(2.0),
|
|
454
|
-
),
|
|
455
|
-
]);
|
|
456
|
-
let index = Index::new(&listing);
|
|
457
|
-
assert_eq!(index.stores().len(), 1);
|
|
458
|
-
assert_eq!(index.stores()[0].key, "Field7");
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
#[test]
|
|
462
|
-
fn string_methods_use_the_explicit_receiver() {
|
|
463
|
-
let mut term = call(text("other"), "slice", vec![number(0.0), number(3.0)]);
|
|
464
|
-
let Term::Call { this, .. } = &mut term else {
|
|
465
|
-
unreachable!()
|
|
466
|
-
};
|
|
467
|
-
*this = Some(Box::new(text("fresh")));
|
|
468
|
-
let program = listing(vec![]);
|
|
469
|
-
let index = Index::new(&program);
|
|
470
|
-
assert_eq!(index.text(0, &term).as_deref(), Some("fre"));
|
|
471
|
-
let Term::Call { this, .. } = &mut term else {
|
|
472
|
-
unreachable!()
|
|
473
|
-
};
|
|
474
|
-
*this = Some(Box::new(Term::Reg(99)));
|
|
475
|
-
assert_eq!(index.text(0, &term), None);
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
#[test]
|
|
479
|
-
fn collects_complete_string_arrays_and_rejects_uncertain_contents() {
|
|
480
|
-
let listing = listing(vec![
|
|
481
|
-
Effect::Set(1, Term::Array(vec![])),
|
|
482
|
-
store(1, number(1.0), text("second")),
|
|
483
|
-
store(1, number(0.0), text("first")),
|
|
484
|
-
Effect::Set(2, Term::Array(vec![])),
|
|
485
|
-
store(2, number(1.0), text("gap")),
|
|
486
|
-
Effect::Set(3, Term::Array(vec![])),
|
|
487
|
-
store(3, number(0.0), text("before")),
|
|
488
|
-
store(3, number(0.0), text("after")),
|
|
489
|
-
Effect::Set(4, Term::Array(vec![])),
|
|
490
|
-
store(4, Term::Reg(99), text("unknown index")),
|
|
491
|
-
]);
|
|
492
|
-
assert_eq!(
|
|
493
|
-
Index::new(&listing).string_arrays(0),
|
|
494
|
-
[vec!["first", "second"]]
|
|
495
|
-
);
|
|
496
|
-
}
|
|
497
|
-
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
//! Prepares the first payload: renders the embedding page, fetches and parses the iframe, and
|
|
2
2
|
//! merges page parameters with iframe state.
|
|
3
3
|
|
|
4
|
+
use std::mem;
|
|
5
|
+
|
|
4
6
|
use anyhow::{Context, Result};
|
|
5
7
|
use url::Url;
|
|
6
8
|
|
|
@@ -20,6 +22,7 @@ use crate::resource_timing::LoadTiming;
|
|
|
20
22
|
use crate::script::Source;
|
|
21
23
|
use crate::stage2::call_stack::{self, CaseOpcodes, ScriptPosition};
|
|
22
24
|
use crate::stage2::payload::Environment;
|
|
25
|
+
use crate::stage2::replay::Globals;
|
|
23
26
|
use crate::{deep_stack, performance_clock};
|
|
24
27
|
|
|
25
28
|
/// Default loader URL for pages that load Turnstile dynamically without a script tag.
|
|
@@ -68,6 +71,8 @@ impl Fetched {
|
|
|
68
71
|
|
|
69
72
|
/// The evaluated first-payload template, before sender fields, and state for later payloads.
|
|
70
73
|
pub(crate) struct FirstPayload {
|
|
74
|
+
/// Signatures of the session-named globals the initial program installs.
|
|
75
|
+
pub audit_globals: Globals,
|
|
71
76
|
/// The extracted program with fully populated request options.
|
|
72
77
|
pub program: ExtractedProgram,
|
|
73
78
|
pub state: ScriptState,
|
|
@@ -94,11 +99,11 @@ impl FirstPayload {
|
|
|
94
99
|
options_artifact: &str,
|
|
95
100
|
complete: impl FnOnce(&mut Options) -> (Option<&'mark str>, i64),
|
|
96
101
|
) -> Result<Self> {
|
|
97
|
-
|
|
98
|
-
let
|
|
102
|
+
let installed = mem::take(&mut program.installed);
|
|
103
|
+
let audit_globals = Globals::installed(&installed);
|
|
99
104
|
let fields = &program.payload_fields;
|
|
100
105
|
let options = &mut program.request.options;
|
|
101
|
-
options.extend(installed.options
|
|
106
|
+
options.extend(installed.options);
|
|
102
107
|
if let Some(log) = &state.post_log {
|
|
103
108
|
log.start(options);
|
|
104
109
|
}
|
|
@@ -110,10 +115,11 @@ impl FirstPayload {
|
|
|
110
115
|
intervals,
|
|
111
116
|
));
|
|
112
117
|
sink.json(options_artifact, options)?;
|
|
113
|
-
let window = installed.window
|
|
118
|
+
let window = installed.window;
|
|
114
119
|
let globals = payload::infer_globals(fields, &state, options, &window);
|
|
115
120
|
let plaintext = payload::build(fields, options, &globals)?;
|
|
116
121
|
Ok(Self {
|
|
122
|
+
audit_globals,
|
|
117
123
|
program,
|
|
118
124
|
state,
|
|
119
125
|
window,
|
|
@@ -126,6 +132,7 @@ impl FirstPayload {
|
|
|
126
132
|
|
|
127
133
|
/// Iframe state after merging page parameters, shared by the first and later payloads.
|
|
128
134
|
pub(crate) struct Merged {
|
|
135
|
+
pub audit_globals: Globals,
|
|
129
136
|
/// The first payload, which the probe payload extends.
|
|
130
137
|
pub first: Payload,
|
|
131
138
|
pub state: ScriptState,
|
|
@@ -214,7 +221,8 @@ pub(crate) async fn render_page(
|
|
|
214
221
|
sink,
|
|
215
222
|
rendered,
|
|
216
223
|
script,
|
|
217
|
-
page.layout
|
|
224
|
+
page.layout
|
|
225
|
+
.placed(page.layout.embedding_pages.get(document.url.as_str())),
|
|
218
226
|
document.referrer_policy,
|
|
219
227
|
Embedding::Site,
|
|
220
228
|
))
|
|
@@ -386,6 +394,7 @@ pub(crate) fn merge(
|
|
|
386
394
|
));
|
|
387
395
|
sink.json("payload.json", &first)?;
|
|
388
396
|
let merged = Merged {
|
|
397
|
+
audit_globals: built.audit_globals,
|
|
389
398
|
first,
|
|
390
399
|
state: built.state,
|
|
391
400
|
window: built.window,
|