mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-24 10:08:00 -06:00
fix(js): charge both copies of a written XFA value before keeping either
set_value charged the snapshot's copy, changed the field, then charged the effect's copy in push. When only the first fit, the effect was dropped while later reads in the script saw the new value. Both copies are charged at once before the field changes.
This commit is contained in:
@@ -455,6 +455,27 @@ mod xfa_model {
|
||||
assert_eq!(o.effects, vec![XfaEffect::SetValue { som: "form1[0].page1[0].total[0]".into(), value: "5".into() }]);
|
||||
}
|
||||
|
||||
/// A long value written again and again stops at the run's byte budget, and the write that
|
||||
/// does not fit is left out of the field the script reads as well as of the effect.
|
||||
#[test]
|
||||
fn a_value_past_the_byte_budget_is_left_out_of_reads_and_effects_alike() {
|
||||
let o = go(
|
||||
"var s = 'x'; while (s.length < 65536) s += s; \
|
||||
for (var i = 0; i < 300; i++) details.note.rawValue = ('00000' + i).slice(-6) + s.slice(6); \
|
||||
details.note.rawValue.slice(0, 6)",
|
||||
"form1[0].page1[0].total[0]",
|
||||
"calculate",
|
||||
);
|
||||
assert_eq!(o.error, None, "{o:?}");
|
||||
let set: Vec<&str> = o
|
||||
.effects
|
||||
.iter()
|
||||
.filter_map(|e| if let XfaEffect::SetValue { value, .. } = e { Some(value.get(..6).unwrap_or(value)) } else { None })
|
||||
.collect();
|
||||
assert_eq!(set.len(), 1);
|
||||
assert_eq!(o.result.as_deref(), set.first().copied());
|
||||
}
|
||||
|
||||
/// The script runs through `eval`, so code built from strings at run time is held to the
|
||||
/// script's own size and nesting checks instead of being refused outright.
|
||||
#[test]
|
||||
|
||||
+19
-9
@@ -315,7 +315,15 @@ impl XHost {
|
||||
/// earlier one; past [`MAX_EFFECTS`] (or [`MAX_ALERTS`] message boxes, or [`MAX_PAYLOAD`]
|
||||
/// bytes of text) effects are counted and dropped.
|
||||
pub(crate) fn push(&mut self, e: XfaEffect) {
|
||||
let e = match e {
|
||||
let e = self.clip_effect(e);
|
||||
if self.room(payload(&e)) {
|
||||
self.record(e);
|
||||
}
|
||||
}
|
||||
|
||||
/// `e` with its text cut to the lengths an effect keeps.
|
||||
fn clip_effect(&mut self, e: XfaEffect) -> XfaEffect {
|
||||
match e {
|
||||
XfaEffect::MessageBox(m) => XfaEffect::MessageBox(clip(m)),
|
||||
XfaEffect::LaunchUrl(u) => XfaEffect::LaunchUrl(clip(u)),
|
||||
XfaEffect::SetFocus(s) => XfaEffect::SetFocus(clip(s)),
|
||||
@@ -328,10 +336,11 @@ impl XHost {
|
||||
XfaEffect::SetValue { som, value }
|
||||
}
|
||||
other => other,
|
||||
};
|
||||
if !self.room(payload(&e)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/// Keep `e`, whose bytes are already charged, against the count limits.
|
||||
fn record(&mut self, e: XfaEffect) {
|
||||
let key = match &e {
|
||||
XfaEffect::SetValue { som, .. } => Some((0u8, som.clone())),
|
||||
XfaEffect::SetPresence { som, .. } => Some((1, som.clone())),
|
||||
@@ -415,16 +424,17 @@ impl XHost {
|
||||
if cut {
|
||||
self.clipped_values += 1;
|
||||
}
|
||||
// The snapshot's copy counts as well as the effect's: past the budget the write is left
|
||||
// out of both.
|
||||
if !self.room(text.len()) {
|
||||
// The snapshot's copy and the effect's are charged together, before either is kept:
|
||||
// past the byte budget the write is left out of both.
|
||||
let effect = XfaEffect::SetValue { som, value: text.clone() };
|
||||
if !self.room(text.len().saturating_add(payload(&effect))) {
|
||||
return true;
|
||||
}
|
||||
if let Some(n) = self.nodes.get_mut(i) {
|
||||
n.value = text.clone();
|
||||
n.value = text;
|
||||
n.formatted = None;
|
||||
}
|
||||
self.push(XfaEffect::SetValue { som, value: text });
|
||||
self.record(effect);
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user