diff --git a/crates/engine/src/cmd/extra/data.rs b/crates/engine/src/cmd/extra/data.rs index a3f69bc2e..a99d87d31 100644 --- a/crates/engine/src/cmd/extra/data.rs +++ b/crates/engine/src/cmd/extra/data.rs @@ -70,14 +70,19 @@ pub(super) fn from_csv(s: &mut Session, p: &Json) -> Result { for o in &arr { if let Some(m) = o.as_object() { for k in m.keys() { - if seen.insert(k.as_str()) { + if !seen.contains(k.as_str()) { + // Stop collecting at the column limit. + if keys.len() >= gridcraft_core::MAX_COLS as usize { + return Err(EngineError::Other("The data has more fields than a sheet has columns.".into())); + } + seen.insert(k.as_str()); keys.push(k.clone()); } } } } - if keys.len() > gridcraft_core::MAX_COLS as usize { - return Err(EngineError::Other("The data has more fields than a sheet has columns.".into())); + if arr.len() as u64 + 1 > u64::from(gridcraft_core::MAX_ROWS) { + return Err(EngineError::Other("The data has more rows than a sheet has.".into())); } if (arr.len() as u64 + 1).saturating_mul(keys.len().max(1) as u64) > MAX_IMPORT_CELLS { return Err(too_many_cells()); diff --git a/crates/engine/src/tests.rs b/crates/engine/src/tests.rs index 22e71330a..8c6f48734 100644 --- a/crates/engine/src/tests.rs +++ b/crates/engine/src/tests.rs @@ -1312,6 +1312,18 @@ fn scripts_leave_file_access_working_after_they_run() { let _ = std::fs::remove_dir_all(&dir); } +#[test] +fn get_data_refuses_json_with_too_many_fields_or_rows_before_building_rows() { + let mut s = s(); + let wide = format!("[{}]", (0..=gridcraft_core::MAX_COLS).map(|i| format!("{{\"k{i}\":1}}")).collect::>().join(",")); + let err = s.execute("data.fromTextCsv", json!({"text": wide})).unwrap_err().to_string(); + assert!(err.contains("more fields"), "{err}"); + // MAX_ROWS objects plus the header row are one too many. + let tall = format!("[{}]", vec!["{}"; gridcraft_core::MAX_ROWS as usize].join(",")); + let err = s.execute("data.fromTextCsv", json!({"text": tall})).unwrap_err().to_string(); + assert!(err.contains("more rows"), "{err}"); +} + #[test] fn scripts_cannot_reach_outside_the_workbook_even_when_trusted() { let dir = std::env::temp_dir().join(format!("gridcraft-script-deny-{}", std::process::id()));