mirror of
https://github.com/telemt/telemt.git
synced 2026-10-30 20:40:51 +03:00
Decoy Contract sanitized
Co-Authored-By: brekotis <93345790+brekotis@users.noreply.github.com>
This commit is contained in:
@@ -29,7 +29,7 @@ pub(super) fn render(
|
|||||||
|
|
||||||
let _ = writeln!(
|
let _ = writeln!(
|
||||||
out,
|
out,
|
||||||
"# HELP telemt_web_session_lifecycle_observations_total Authenticated activity after bounded lifecycle gaps"
|
"# HELP telemt_web_session_lifecycle_observations_total Authenticated WEB session lifecycle observations"
|
||||||
);
|
);
|
||||||
let _ = writeln!(
|
let _ = writeln!(
|
||||||
out,
|
out,
|
||||||
|
|||||||
+11
-3
@@ -151,7 +151,7 @@ pub(crate) async fn serve_connection(
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_request(
|
async fn handle_request(
|
||||||
request: Request<RequestBody>,
|
mut request: Request<RequestBody>,
|
||||||
peer: SocketAddr,
|
peer: SocketAddr,
|
||||||
client_ip_source: WebClientIpSource,
|
client_ip_source: WebClientIpSource,
|
||||||
trusted_proxy_cidrs: &[IpNetwork],
|
trusted_proxy_cidrs: &[IpNetwork],
|
||||||
@@ -209,7 +209,11 @@ async fn handle_request(
|
|||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
serve_decoy(request, vhost, false, &runtime).await
|
let sanitize_recovery = recovery::has_media_type(&request);
|
||||||
|
if sanitize_recovery {
|
||||||
|
strip_query(&mut request);
|
||||||
|
}
|
||||||
|
serve_decoy(request, vhost, sanitize_recovery, &runtime).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_root(
|
async fn handle_root(
|
||||||
@@ -228,8 +232,12 @@ async fn handle_root(
|
|||||||
}
|
}
|
||||||
let (candidate, canonical) = bridge_candidate(request.uri().query());
|
let (candidate, canonical) = bridge_candidate(request.uri().query());
|
||||||
let profile = match_profile(&vhost, &candidate);
|
let profile = match_profile(&vhost, &candidate);
|
||||||
|
let recovery_requested = matches!(representation, recovery::RootRepresentation::Recovery(_));
|
||||||
let Some(profile) = profile.filter(|_| canonical && request.method() == Method::GET) else {
|
let Some(profile) = profile.filter(|_| canonical && request.method() == Method::GET) else {
|
||||||
return serve_decoy(request, vhost, false, &runtime).await;
|
if recovery_requested {
|
||||||
|
strip_query(&mut request);
|
||||||
|
}
|
||||||
|
return serve_decoy(request, vhost, recovery_requested, &runtime).await;
|
||||||
};
|
};
|
||||||
let Some(client_ip) = client_ip(&request, peer, client_ip_source, trusted_proxy_cidrs) else {
|
let Some(client_ip) = client_ip(&request, peer, client_ip_source, trusted_proxy_cidrs) else {
|
||||||
strip_query(&mut request);
|
strip_query(&mut request);
|
||||||
|
|||||||
@@ -30,9 +30,7 @@ pub(super) fn classify(request: &Request<RequestBody>) -> RootRepresentation {
|
|||||||
let first = values.next();
|
let first = values.next();
|
||||||
let exact = first.is_some_and(|value| value.as_bytes() == MEDIA_TYPE.as_bytes())
|
let exact = first.is_some_and(|value| value.as_bytes() == MEDIA_TYPE.as_bytes())
|
||||||
&& values.next().is_none();
|
&& values.next().is_none();
|
||||||
let recovery_present = accepts
|
let recovery_present = has_media_type(request);
|
||||||
.iter()
|
|
||||||
.any(|value| value.as_bytes() == MEDIA_TYPE.as_bytes());
|
|
||||||
let authorization_present = request.headers().contains_key(header::AUTHORIZATION);
|
let authorization_present = request.headers().contains_key(header::AUTHORIZATION);
|
||||||
if !exact {
|
if !exact {
|
||||||
return if authorization_present || recovery_present {
|
return if authorization_present || recovery_present {
|
||||||
@@ -52,6 +50,20 @@ pub(super) fn classify(request: &Request<RequestBody>) -> RootRepresentation {
|
|||||||
.unwrap_or(RootRepresentation::Invalid)
|
.unwrap_or(RootRepresentation::Invalid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Detects a recovery media token even when its Accept syntax is noncanonical.
|
||||||
|
pub(super) fn has_media_type(request: &Request<RequestBody>) -> bool {
|
||||||
|
request.headers().get_all(header::ACCEPT).iter().any(|value| {
|
||||||
|
value.to_str().ok().is_some_and(|value| {
|
||||||
|
value.split(',').any(|entry| {
|
||||||
|
entry
|
||||||
|
.split(';')
|
||||||
|
.next()
|
||||||
|
.is_some_and(|media| media.trim().eq_ignore_ascii_case(MEDIA_TYPE))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Builds the bounded no-store recovery representation.
|
/// Builds the bounded no-store recovery representation.
|
||||||
pub(super) fn response(
|
pub(super) fn response(
|
||||||
bootstrap: &BootstrapResult,
|
bootstrap: &BootstrapResult,
|
||||||
|
|||||||
@@ -170,6 +170,26 @@ async fn malformed_or_over_capacity_recovery_is_indistinguishable_from_decoy() {
|
|||||||
assert_eq!(response_header(malformed_headers, "cache-control"), "no-store");
|
assert_eq!(response_header(malformed_headers, "cache-control"), "no-store");
|
||||||
assert_eq!(malformed_body, b"<!doctype html><title>decoy</title>");
|
assert_eq!(malformed_body, b"<!doctype html><title>decoy</title>");
|
||||||
|
|
||||||
|
let invalid_capability = recover(
|
||||||
|
&listener,
|
||||||
|
&runtime,
|
||||||
|
&base64::engine::general_purpose::URL_SAFE_NO_PAD.encode([99u8; 32]),
|
||||||
|
&format!("Bearer {}", "U".repeat(43)),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
assert_eq!(invalid_capability, malformed);
|
||||||
|
|
||||||
|
let malformed_accept = request(
|
||||||
|
&listener,
|
||||||
|
&runtime,
|
||||||
|
format!(
|
||||||
|
"GET /?bridge={encoded} HTTP/1.1\r\nHost: proxy.example.com\r\nX-Forwarded-For: 192.0.2.40\r\nAccept: {RECOVERY_TYPE}, */*\r\nConnection: close\r\n\r\n"
|
||||||
|
)
|
||||||
|
.into_bytes(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
assert_eq!(malformed_accept, malformed);
|
||||||
|
|
||||||
let _held = bridge_bootstrap(&listener, &runtime, &encoded).await;
|
let _held = bridge_bootstrap(&listener, &runtime, &encoded).await;
|
||||||
let over_capacity = recover(
|
let over_capacity = recover(
|
||||||
&listener,
|
&listener,
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ pub(crate) struct WebSessionStatus {
|
|||||||
pub(crate) control_items: usize,
|
pub(crate) control_items: usize,
|
||||||
/// Monotonic age since session creation.
|
/// Monotonic age since session creation.
|
||||||
pub(crate) age_ms: u64,
|
pub(crate) age_ms: u64,
|
||||||
/// Monotonic age since the latest carrier activity.
|
/// Monotonic age since the latest peer or carrier progress.
|
||||||
pub(crate) idle_ms: u64,
|
pub(crate) idle_ms: u64,
|
||||||
/// Monotonic age since the latest validated peer operation.
|
/// Monotonic age since the latest validated peer operation.
|
||||||
pub(crate) peer_idle_ms: u64,
|
pub(crate) peer_idle_ms: u64,
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ pub(super) const SESSION_CLOSE_SLOTS: usize =
|
|||||||
pub(super) const SESSION_OBSERVATION_SLOTS: usize =
|
pub(super) const SESSION_OBSERVATION_SLOTS: usize =
|
||||||
WebCarrier::ALL.len() * WebSessionLifecycleObservation::ALL.len();
|
WebCarrier::ALL.len() * WebSessionLifecycleObservation::ALL.len();
|
||||||
|
|
||||||
/// Stable observation emitted after an authenticated session lifecycle gap.
|
/// Stable authenticated session lifecycle observation.
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
#[repr(usize)]
|
#[repr(usize)]
|
||||||
pub(crate) enum WebSessionLifecycleObservation {
|
pub(crate) enum WebSessionLifecycleObservation {
|
||||||
|
|||||||
Reference in New Issue
Block a user