This commit is contained in:
Mick Grove 2026-02-09 12:27:03 -08:00
commit 2a8bb9c361
2 changed files with 81 additions and 8 deletions

View file

@ -219,6 +219,9 @@ fn render_extractor(
globals: &Object,
) -> Result<ResponseExtractor> {
let render = |template_str: &str| -> Result<String> {
if !template_str.contains("{{") && !template_str.contains("{%") {
return Ok(template_str.to_string());
}
let template = parser
.parse(template_str)
.map_err(|e| anyhow!("Failed to parse extractor template: {}", e))?;
@ -238,6 +241,15 @@ fn render_extractor(
}
}
fn truncate_with_ellipsis(input: &str, max_chars: usize) -> String {
let truncated: String = input.chars().take(max_chars).collect();
if input.chars().count() > max_chars {
format!("{}...", truncated)
} else {
input.to_string()
}
}
/// Extract a value from an HTTP response using the specified extractor.
fn extract_value_from_response(
extractor: &ResponseExtractor,
@ -338,10 +350,11 @@ async fn execute_http_revocation(
let headers = response.headers().clone();
let body = response.text().await.context("Failed to read response body")?;
debug!("Revocation response status: {}", status);
debug!("Revocation response body: {}", body);
let display_body = truncate_with_ellipsis(&body, 500);
let body_len = body.chars().count();
let display_body = if body.len() > 500 { format!("{}...", &body[..500]) } else { body.clone() };
debug!("Revocation response status: {}", status);
debug!("Revocation response body (len={}): {}", body_len, display_body);
let matchers = http_revocation
.request
@ -404,8 +417,11 @@ async fn execute_revocation_step(
.await
.with_context(|| format!("Failed to read response body for {}", step_name))?;
let display_body = truncate_with_ellipsis(&body, 500);
let body_len = body.chars().count();
debug!("Step {} response status: {}", step_number, status);
debug!("Step {} response body: {}", step_number, body);
debug!("Step {} response body (len={}): {}", step_number, body_len, display_body);
// Extract variables from the response if configured
if let Some(extractors) = &step.extract {
@ -437,7 +453,7 @@ async fn execute_revocation_step(
step_number,
e,
status,
body
display_body
));
}
}
@ -481,8 +497,7 @@ async fn execute_multi_step_revocation(
if is_final_step {
// Final step: validate response to determine success
let display_body =
if body.len() > 500 { format!("{}...", &body[..500]) } else { body.clone() };
let display_body = truncate_with_ellipsis(&body, 500);
let matchers = step
.request
@ -1310,4 +1325,23 @@ mod tests {
_ => panic!("Expected JsonPath variant"),
}
}
// ---- truncate_with_ellipsis ----
#[test]
fn truncate_with_ellipsis_no_truncation() {
let input = "ok";
let output = truncate_with_ellipsis(input, 500);
assert_eq!(output, input);
}
#[test]
fn truncate_with_ellipsis_handles_unicode() {
let input = "é".repeat(501);
let output = truncate_with_ellipsis(&input, 500);
assert!(output.ends_with("..."));
assert_eq!(output.chars().count(), 503);
assert!(output.chars().take(500).all(|ch| ch == 'é'));
}
}

View file

@ -669,7 +669,8 @@ impl DetailsReporter {
const MAX_RESPONSE_LENGTH: usize = 512;
let truncated_body: String =
validation_body_str.chars().take(MAX_RESPONSE_LENGTH).collect();
let ellipsis = if validation_body_str.len() > MAX_RESPONSE_LENGTH { "..." } else { "" };
let ellipsis =
if validation_body_str.chars().count() > MAX_RESPONSE_LENGTH { "..." } else { "" };
format!("{}{}", truncated_body, ellipsis)
};
@ -1323,6 +1324,20 @@ mod tests {
(report_match, blob_path)
}
fn build_validation_response(validation_body: &str, full_response: bool) -> String {
let temp = tempdir().unwrap();
let datastore =
Arc::new(Mutex::new(findings_store::FindingsStore::new(temp.path().to_path_buf())));
let reporter = DetailsReporter { datastore, styles: Styles::new(false), only_valid: false };
let (report_match, _) = sample_report_match(validation_body, StatusCode::OK.as_u16(), true);
let mut scan_args = sample_scan_args();
scan_args.full_validation_response = full_response;
let record = reporter.build_finding_record(&report_match, &scan_args);
record.finding.validation.response
}
#[test]
fn build_finding_record_uses_git_blob_path() {
let temp = tempdir().unwrap();
@ -1370,6 +1385,30 @@ mod tests {
);
}
#[test]
fn validation_response_truncates_when_flag_off() {
let body = "a".repeat(513);
let response = build_validation_response(&body, false);
assert_eq!(response, format!("{}...", "a".repeat(512)));
}
#[test]
fn validation_response_full_when_flag_on() {
let body = "a".repeat(513);
let response = build_validation_response(&body, true);
assert_eq!(response, body);
}
#[test]
fn validation_response_truncation_counts_chars() {
let body = "é".repeat(513);
let response = build_validation_response(&body, false);
assert!(response.ends_with("..."));
assert_eq!(response.chars().count(), 515);
assert!(response.chars().take(512).all(|ch| ch == 'é'));
}
use super::build_git_urls;
#[test]