summaryrefslogtreecommitdiff
path: root/src/diagnostics.rs
diff options
context:
space:
mode:
authorAndrew Guschin <guschin@altlinux.org>2026-10-30 05:15:56 +0400
committerAndrew Guschin <guschin@altlinux.org>2026-10-30 05:15:56 +0400
commit3c8b130be89fbb0e58e2ee23f07ad56a3b9de2e7 (patch)
treed0e5d447f7324a2793bc6975ed57f172624d6311 /src/diagnostics.rs
parent7999fab25c5172207ee41e38732f4923565ff6b6 (diff)
Reject streamed diagnostic error documents and record VM qualification
Diffstat (limited to 'src/diagnostics.rs')
-rw-r--r--src/diagnostics.rs31
1 files changed, 20 insertions, 11 deletions
diff --git a/src/diagnostics.rs b/src/diagnostics.rs
index f6ed82a..d8f8620 100644
--- a/src/diagnostics.rs
+++ b/src/diagnostics.rs
@@ -1,3 +1,5 @@
+mod error_document;
+
use crate::model::Settings;
use anyhow::{Result, anyhow, ensure};
use futures_util::TryStreamExt;
@@ -113,6 +115,7 @@ pub async fn measure(
let mut reader = tokio_util::io::StreamReader::new(stream);
let mut buffer = [0u8; 16384];
let mut prefix = Vec::new();
+ let mut error_document = error_document::ErrorDocument::default();
let mut bytes = 0;
let reason;
loop {
@@ -144,8 +147,23 @@ pub async fn measure(
break;
}
if prefix.len() < 512 {
- prefix.extend_from_slice(&buffer[..chunk.min(512 - prefix.len())]);
+ let mut inspect = &buffer[..chunk];
+ if prefix.is_empty() {
+ inspect = inspect.trim_ascii_start();
+ while inspect
+ .first()
+ .is_some_and(|byte| matches!(byte, 0xef | 0xbb | 0xbf))
+ {
+ inspect = inspect[1..].trim_ascii_start();
+ }
+ }
+ prefix.extend_from_slice(&inspect[..inspect.len().min(512 - prefix.len())]);
}
+ error_document.feed(&buffer[..chunk]);
+ ensure!(
+ !error_document.confirmed_error(),
+ "Diagnostic endpoint returned an error document"
+ );
bytes += chunk as u64;
if !speed {
ensure!(bytes <= cap, "Ping body exceeds 4 KiB");
@@ -158,17 +176,8 @@ pub async fn measure(
!prefix.starts_with("<!doctype html") && !prefix.starts_with("<html"),
"Diagnostic endpoint returned an HTML document"
);
- let json_error = serde_json::from_str::<serde_json::Value>(&prefix)
- .ok()
- .is_some_and(|body| {
- ["error", "errors"].iter().any(|key| {
- body.get(key).is_some_and(|value| {
- !value.is_null() && value != false && value != 0 && value != ""
- })
- })
- });
ensure!(
- !json_error,
+ !error_document.is_error(),
"Diagnostic endpoint returned an error document"
);
ensure!(!speed || bytes > 0, "Speed response contained no data");