diff --git a/crates/bashkit-python/src/lib.rs b/crates/bashkit-python/src/lib.rs index 83472770..5591caa1 100644 --- a/crates/bashkit-python/src/lib.rs +++ b/crates/bashkit-python/src/lib.rs @@ -219,10 +219,10 @@ impl PyBash { let mut limits = ExecutionLimits::new(); if let Some(mc) = max_commands { - limits = limits.max_commands(mc as usize); + limits = limits.max_commands(usize::try_from(mc).unwrap_or(usize::MAX)); } if let Some(mli) = max_loop_iterations { - limits = limits.max_loop_iterations(mli as usize); + limits = limits.max_loop_iterations(usize::try_from(mli).unwrap_or(usize::MAX)); } builder = builder.limits(limits); @@ -313,10 +313,10 @@ impl PyBash { } let mut limits = ExecutionLimits::new(); if let Some(mc) = max_commands { - limits = limits.max_commands(mc as usize); + limits = limits.max_commands(usize::try_from(mc).unwrap_or(usize::MAX)); } if let Some(mli) = max_loop_iterations { - limits = limits.max_loop_iterations(mli as usize); + limits = limits.max_loop_iterations(usize::try_from(mli).unwrap_or(usize::MAX)); } builder = builder.limits(limits); *bash = builder.build(); @@ -396,10 +396,10 @@ impl BashTool { let mut limits = ExecutionLimits::new(); if let Some(mc) = max_commands { - limits = limits.max_commands(mc as usize); + limits = limits.max_commands(usize::try_from(mc).unwrap_or(usize::MAX)); } if let Some(mli) = max_loop_iterations { - limits = limits.max_loop_iterations(mli as usize); + limits = limits.max_loop_iterations(usize::try_from(mli).unwrap_or(usize::MAX)); } builder = builder.limits(limits); @@ -622,10 +622,10 @@ impl ScriptedTool { if self.max_commands.is_some() || self.max_loop_iterations.is_some() { let mut limits = ExecutionLimits::new(); if let Some(mc) = self.max_commands { - limits = limits.max_commands(mc as usize); + limits = limits.max_commands(usize::try_from(mc).unwrap_or(usize::MAX)); } if let Some(mli) = self.max_loop_iterations { - limits = limits.max_loop_iterations(mli as usize); + limits = limits.max_loop_iterations(usize::try_from(mli).unwrap_or(usize::MAX)); } builder = builder.limits(limits); } diff --git a/crates/bashkit/src/network/client.rs b/crates/bashkit/src/network/client.rs index be99868d..5336c111 100644 --- a/crates/bashkit/src/network/client.rs +++ b/crates/bashkit/src/network/client.rs @@ -233,7 +233,7 @@ impl HttpClient { // Check Content-Length header to fail fast on large responses if let Some(content_length) = response.content_length() { - if content_length as usize > self.max_response_bytes { + if usize::try_from(content_length).unwrap_or(usize::MAX) > self.max_response_bytes { return Err(Error::Network(format!( "response too large: {} bytes (max: {} bytes)", content_length, self.max_response_bytes @@ -416,7 +416,7 @@ impl HttpClient { // Check Content-Length header to fail fast on large responses if let Some(content_length) = response.content_length() { - if content_length as usize > self.max_response_bytes { + if usize::try_from(content_length).unwrap_or(usize::MAX) > self.max_response_bytes { return Err(Error::Network(format!( "response too large: {} bytes (max: {} bytes)", content_length, self.max_response_bytes @@ -528,6 +528,15 @@ mod tests { assert!(result.unwrap_err().to_string().contains("access denied")); } + #[test] + fn test_u64_to_usize_no_truncation() { + // On 64-bit: fits fine. On 32-bit: saturates to usize::MAX rather than truncating. + let large: u64 = 5_368_709_120; // 5GB + let result = usize::try_from(large).unwrap_or(usize::MAX); + // Should never silently become a smaller value + assert!(result >= large.min(usize::MAX as u64) as usize); + } + // Note: Integration tests that actually make network requests // should be in a separate test file and marked with #[ignore] // to avoid network dependencies in unit tests.