fix(protocol): harden gateway contract validation
This commit is contained in:
+25
-1
@@ -1,6 +1,6 @@
|
||||
// Code generated by tools/generate.py; DO NOT EDIT.
|
||||
#![allow(non_snake_case)]
|
||||
pub const SCHEMA_SHA256: &str = "e35414af52d7a097dea05567fdab11f842a42e529fb6cbedd281c48dbf930b17";
|
||||
pub const SCHEMA_SHA256: &str = "e98c75ef81bbeac6be2b8f11202c1ffecec0aa515b48576a26756290e99d5dd8";
|
||||
pub const CURRENT_WIRE_VERSION: &str = "1";
|
||||
pub const N_MINUS_1_WIRE_VERSION: &str = "0";
|
||||
pub const N_MINUS_2_WIRE_VERSION: &str = "-1";
|
||||
@@ -10,6 +10,27 @@ pub type JsonObject = std::collections::BTreeMap<String, String>;
|
||||
pub struct ValidationError { pub field: &'static str, pub code: &'static str }
|
||||
impl ValidationError { pub const fn new(field: &'static str, code: &'static str) -> Self { Self { field, code } } }
|
||||
|
||||
fn base64url_value(value: u8) -> Option<u8> {
|
||||
match value {
|
||||
b'A'..=b'Z' => Some(value - b'A'),
|
||||
b'a'..=b'z' => Some(value - b'a' + 26),
|
||||
b'0'..=b'9' => Some(value - b'0' + 52),
|
||||
b'-' => Some(62),
|
||||
b'_' => Some(63),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
fn valid_base64_url(value: &str) -> bool {
|
||||
let bytes = value.as_bytes();
|
||||
if bytes.is_empty() || bytes.iter().any(|byte| base64url_value(*byte).is_none()) { return false; }
|
||||
match bytes.len() % 4 {
|
||||
0 => true,
|
||||
2 => base64url_value(*bytes.last().unwrap()).unwrap() & 0x0f == 0,
|
||||
3 => base64url_value(*bytes.last().unwrap()).unwrap() & 0x03 == 0,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct AllocationPolicy {
|
||||
minimumKbps: i64,
|
||||
@@ -259,6 +280,7 @@ impl ChannelFrame {
|
||||
if self.fragmentCount > 16 { return Err(ValidationError::new("fragment_count", "maximum")); }
|
||||
if self.timestampMs < 0 { return Err(ValidationError::new("timestamp_ms", "minimum")); }
|
||||
if self.payload.len() > 87384 { return Err(ValidationError::new("payload", "max_length")); }
|
||||
if self.payload.as_bytes().len() > 65536 { return Err(ValidationError::new("payload", "max_bytes")); }
|
||||
if self.fragmentIndex >= self.fragmentCount { return Err(ValidationError::new("fragment_index", "invalid_order")); }
|
||||
Ok(())
|
||||
}
|
||||
@@ -692,10 +714,12 @@ impl GatewayClipboardText {
|
||||
pub fn validate(&self) -> Result<(), ValidationError> {
|
||||
if self.direction != "client_to_provider" && self.direction != "provider_to_client" { return Err(ValidationError::new("direction", "invalid_value")); }
|
||||
if self.text.len() > 65536 { return Err(ValidationError::new("text", "max_length")); }
|
||||
if self.text.as_bytes().len() > 65536 { return Err(ValidationError::new("text", "max_bytes")); }
|
||||
if self.encoding != "utf-8" { return Err(ValidationError::new("encoding", "invalid_value")); }
|
||||
if self.loopToken.is_empty() { return Err(ValidationError::new("loop_token", "required")); }
|
||||
if !self.loopToken.is_empty() && self.loopToken.len() < 16 { return Err(ValidationError::new("loop_token", "min_length")); }
|
||||
if self.loopToken.len() > 128 { return Err(ValidationError::new("loop_token", "max_length")); }
|
||||
if !valid_base64_url(self.loopToken.as_str()) { return Err(ValidationError::new("loop_token", "invalid_format")); }
|
||||
Ok(())
|
||||
}
|
||||
pub fn direction(&self) -> &String { &self.direction }
|
||||
|
||||
Reference in New Issue
Block a user