use flare_core::common::compression::{CompressionAlgorithm, CompressionUtil};
use flare_core::common::encryption::EncryptionAlgorithm;
use flare_core::common::message::MessageParser;
use flare_core::common::protocol::{
Command, FrameBuilder, SerializationFormat, flare::core::commands::command::Type as CmdType,
ping,
};
use proptest::prelude::*;
fn parser() -> MessageParser {
MessageParser::new(
SerializationFormat::Protobuf,
CompressionAlgorithm::None,
EncryptionAlgorithm::None,
)
}
fn valid_frame_bytes() -> Vec<u8> {
let frame = FrameBuilder::new()
.with_command(Command {
r#type: Some(CmdType::System(ping())),
})
.build();
parser().serialize(&frame).expect("种子帧必须能序列化")
}
proptest! {
#[test]
fn parse_never_panics_on_arbitrary_bytes(data in prop::collection::vec(any::<u8>(), 0..4096)) {
let _ = parser().parse(&data);
}
#[test]
fn strict_parse_never_panics(data in prop::collection::vec(any::<u8>(), 0..4096)) {
let _ = parser().parse_with_fallback(&data, false);
}
#[test]
fn parse_with_each_format_never_panics(
data in prop::collection::vec(any::<u8>(), 0..2048),
use_json in any::<bool>(),
) {
let format = if use_json {
SerializationFormat::Json
} else {
SerializationFormat::Protobuf
};
let _ = parser().parse_with_format(&data, format);
}
#[test]
fn decompress_never_panics_and_respects_limit(
data in prop::collection::vec(any::<u8>(), 0..8192),
) {
if let Ok(out) = CompressionUtil::decompress(&data, CompressionAlgorithm::Gzip) {
prop_assert!(
out.len() <= 16 * 1024 * 1024,
"解压输出 {} 字节,超过 16MB 上限——解压炸弹防护被绕过",
out.len()
);
}
}
#[test]
fn mutated_valid_frame_never_panics(
positions in prop::collection::vec((any::<prop::sample::Index>(), any::<u8>()), 1..12),
) {
let mut data = valid_frame_bytes();
prop_assume!(!data.is_empty());
for (idx, byte) in positions {
let i = idx.index(data.len());
data[i] = byte;
}
let _ = parser().parse(&data);
}
#[test]
fn mutated_and_truncated_frame_never_panics(
positions in prop::collection::vec((any::<prop::sample::Index>(), any::<u8>()), 1..8),
cut in any::<prop::sample::Index>(),
) {
let mut data = valid_frame_bytes();
prop_assume!(!data.is_empty());
for (idx, byte) in positions {
let i = idx.index(data.len());
data[i] = byte;
}
let end = cut.index(data.len() + 1);
let _ = parser().parse(&data[..end]);
}
#[test]
fn valid_frame_with_trailing_garbage_never_panics(
garbage in prop::collection::vec(any::<u8>(), 1..512),
) {
let mut data = valid_frame_bytes();
data.extend_from_slice(&garbage);
let _ = parser().parse(&data);
}
#[test]
fn truncated_valid_frame_never_panics(
data in prop::collection::vec(any::<u8>(), 8..1024),
cut in 0usize..1024,
) {
let end = cut.min(data.len());
let _ = parser().parse(&data[..end]);
}
}