Skip to content

Commit 96fa79f

Browse files
committed
fix(governance): countersign only the committed unit and a near-present event time
countersign_fulfillment is an unrestricted grant, so the non-author could call it directly to collect the author's signature over any unit or a backdated time. It now refuses an observed unit that differs from the committed one and an event time more than five minutes from its clock. The event form's empty quantity falls back to 1 as intended (an empty number input binds null).
1 parent 7ceb868 commit 96fa79f

2 files changed

Lines changed: 32 additions & 1 deletion

File tree

‎dnas/nondominium/zomes/coordinator/zome_gouvernance/src/fulfillment.rs‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -606,6 +606,31 @@ pub fn countersign_fulfillment(
606606
"countersign_fulfillment: invalid observed_quantity: {e}"
607607
)))
608608
})?;
609+
// What I committed to bounds what I countersign: the observed quantity
610+
// keeps the committed unit, and the event time is close to my own clock,
611+
// so a direct call cannot collect my signature for another unit or a
612+
// backdated event.
613+
if let Some(committed) = &commitment.resource_quantity {
614+
if committed.has_unit != input.observed_quantity.has_unit {
615+
return Err(
616+
GovernanceError::InvalidInput(format!(
617+
"countersign_fulfillment: observed unit {:?} differs from the committed unit {:?}",
618+
input.observed_quantity.has_unit, committed.has_unit
619+
))
620+
.into(),
621+
);
622+
}
623+
}
624+
let my_now = sys_time()?.as_micros();
625+
let skew = (my_now - input.event_time.as_micros()).abs();
626+
if skew > COUNTERSIGN_MAX_CLOCK_SKEW_MICROS {
627+
return Err(
628+
GovernanceError::InvalidInput(
629+
"countersign_fulfillment: event time is too far from my clock".to_string(),
630+
)
631+
.into(),
632+
);
633+
}
609634

610635
// Rebuild the payload from what I can see (the commitment I fetched, the
611636
// observed quantity and the event time) and sign my own hash, never the
@@ -727,6 +752,9 @@ fn build_fulfillment_signing_data(
727752
data
728753
}
729754

755+
/// How far the initiator's event time may be from the countersigner's clock.
756+
const COUNTERSIGN_MAX_CLOCK_SKEW_MICROS: i64 = 5 * 60 * 1_000_000;
757+
730758
fn blake3_hash(data: &[u8]) -> [u8; 32] {
731759
let mut hasher = Hasher::new();
732760
hasher.update(data);

‎ui/src/lib/components/ndo/EconomicEventCreateForm.svelte‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,10 @@
158158
resource_inventoried_as: resource,
159159
resource_quantity: {
160160
// A Measure may be 0; only a missing or non-numeric value falls back to 1.
161-
numerical_value: Number.isFinite(Number(quantity)) && String(quantity) !== '' ? Number(quantity) : 1,
161+
numerical_value:
162+
quantity == null || String(quantity).trim() === '' || !Number.isFinite(Number(quantity))
163+
? 1
164+
: Number(quantity),
162165
has_unit: unit.trim() || 'each'
163166
},
164167
note: note.trim() || null,

0 commit comments

Comments
 (0)