From 3e84328565b229f915ec93b083dcdc571871a5ab Mon Sep 17 00:00:00 2001 From: Krista House Date: Thu, 27 Aug 2026 13:09:02 -0400 Subject: [PATCH 1/2] test(dgw): add DVLS 2026.3 JREC fixtures Add versioned JREC compatibility coverage in dvls_compatibility tests with deterministic push/pull fixtures and property-based claim shape validation, including optional jet_reuse. Issue: DGW-407 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../tests/dvls_compatibility.rs | 128 +++++++++++++++++- 1 file changed, 127 insertions(+), 1 deletion(-) diff --git a/devolutions-gateway/tests/dvls_compatibility.rs b/devolutions-gateway/tests/dvls_compatibility.rs index 161c4714c..f5aef01e9 100644 --- a/devolutions-gateway/tests/dvls_compatibility.rs +++ b/devolutions-gateway/tests/dvls_compatibility.rs @@ -11,7 +11,7 @@ use picky::key::{PrivateKey, PublicKey}; use proptest::prelude::*; use rstest::{fixture, rstest}; use serde::{Deserialize, Serialize}; -use uuid::Uuid; +use uuid::{Uuid, uuid}; const KEY: &str = r#"-----BEGIN PRIVATE KEY----- MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDkrPiL/5dmGIT5 @@ -112,6 +112,132 @@ fn now() -> i64 { time::OffsetDateTime::now_utc().unix_timestamp() } +mod as_of_v2026_3_0_0 { + use super::*; + + const CTY_JREC: &str = "JREC"; + + #[derive(Clone, Copy, Serialize, Debug)] + #[serde(rename_all = "lowercase")] + enum DvlsRecordingOperation { + Push, + Pull, + } + + #[derive(Clone, Serialize, Debug)] + struct DvlsJrecClaims { + jet_aid: Uuid, + jet_rop: DvlsRecordingOperation, + jet_gw_id: Uuid, + #[serde(skip_serializing_if = "Option::is_none")] + jet_reuse: Option, + nbf: i64, + exp: i64, + jti: Uuid, + } + + fn dvls_recording_operation() -> impl Strategy { + prop_oneof![Just(DvlsRecordingOperation::Push), Just(DvlsRecordingOperation::Pull),] + } + + fn dvls_reuse_policy() -> impl Strategy> { + prop_oneof![Just(None), (1u32..=900u32).prop_map(Some),] + } + + fn dvls_jrec_claims(now: i64) -> impl Strategy { + ( + uuid_typed(), + dvls_recording_operation(), + uuid_typed(), + dvls_reuse_policy(), + uuid_typed(), + ) + .prop_map(move |(jet_aid, jet_rop, jet_gw_id, jet_reuse, jti)| DvlsJrecClaims { + jet_aid, + jet_rop, + jet_gw_id, + jet_reuse, + nbf: now, + exp: now + 1000, + jti, + }) + } + + #[rstest] + fn jrec_token_validation( + token_cache: TokenCache, + jrl: Mutex, + active_recordings: ActiveRecordings, + priv_key: PrivateKey, + pub_key: PublicKey, + now: i64, + ) { + proptest!(ProptestConfig::with_cases(32), |(claims in dvls_jrec_claims(now).no_shrink())| { + let jet_gw_id = claims.jet_gw_id; + encode_decode_round_trip( + &pub_key, + &priv_key, + claims, + Some(CTY_JREC.to_owned()), + Some(jet_gw_id), + &token_cache, + &jrl, + &active_recordings, + ).map_err(|e| TestCaseError::fail(format!("{e:#}")))?; + }); + } + + #[rstest] + #[case::push(DvlsJrecClaims { + jet_aid: uuid!("3e7c1854-f1eb-42d2-b9cb-9303036e50da"), + jet_rop: DvlsRecordingOperation::Push, + jet_gw_id: uuid!("ccbaad3f-4627-4666-8bb5-cb6a1a7db815"), + jet_reuse: None, + nbf: 1_700_000_000, + exp: 4_102_444_800, + jti: uuid!("2dd6fb87-5340-4a85-9e96-d383ebef8a41"), + })] + #[case::pull(DvlsJrecClaims { + jet_aid: uuid!("3e7c1854-f1eb-42d2-b9cb-9303036e50da"), + jet_rop: DvlsRecordingOperation::Pull, + jet_gw_id: uuid!("ccbaad3f-4627-4666-8bb5-cb6a1a7db815"), + jet_reuse: None, + nbf: 1_700_000_000, + exp: 4_102_444_800, + jti: uuid!("01f2b129-bfbf-44fb-8b6e-5cbaf7a71300"), + })] + #[case::push_with_reuse(DvlsJrecClaims { + jet_aid: uuid!("3e7c1854-f1eb-42d2-b9cb-9303036e50da"), + jet_rop: DvlsRecordingOperation::Push, + jet_gw_id: uuid!("ccbaad3f-4627-4666-8bb5-cb6a1a7db815"), + jet_reuse: Some(300), + nbf: 1_700_000_000, + exp: 4_102_444_800, + jti: uuid!("7de3320f-9760-4f6e-b2f4-b9e3f64f0f43"), + })] + fn samples( + #[case] claims: DvlsJrecClaims, + token_cache: TokenCache, + jrl: Mutex, + active_recordings: ActiveRecordings, + priv_key: PrivateKey, + pub_key: PublicKey, + ) { + let jet_gw_id = claims.jet_gw_id; + encode_decode_round_trip( + &pub_key, + &priv_key, + claims, + Some(CTY_JREC.to_owned()), + Some(jet_gw_id), + &token_cache, + &jrl, + &active_recordings, + ) + .unwrap(); + } +} + mod as_of_v2025_2_6_0 { use super::*; From 312d868f01ed0dc670b935f92dda2b6159a40afd Mon Sep 17 00:00:00 2001 From: Krista House Date: Thu, 27 Aug 2026 13:42:45 -0400 Subject: [PATCH 2/2] test(dgw): cover jrec disallow-reuse boundary Address PR feedback by covering the explicit DVLS JREC shape with jet_reuse set to 0. Update the generator to include 0..=900 and add a fixed push_disallow_reuse sample fixture. Issue: DGW-407 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- devolutions-gateway/tests/dvls_compatibility.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/devolutions-gateway/tests/dvls_compatibility.rs b/devolutions-gateway/tests/dvls_compatibility.rs index f5aef01e9..e727c8e01 100644 --- a/devolutions-gateway/tests/dvls_compatibility.rs +++ b/devolutions-gateway/tests/dvls_compatibility.rs @@ -141,7 +141,7 @@ mod as_of_v2026_3_0_0 { } fn dvls_reuse_policy() -> impl Strategy> { - prop_oneof![Just(None), (1u32..=900u32).prop_map(Some),] + prop_oneof![Just(None), (0u32..=900u32).prop_map(Some),] } fn dvls_jrec_claims(now: i64) -> impl Strategy { @@ -215,6 +215,15 @@ mod as_of_v2026_3_0_0 { exp: 4_102_444_800, jti: uuid!("7de3320f-9760-4f6e-b2f4-b9e3f64f0f43"), })] + #[case::push_disallow_reuse(DvlsJrecClaims { + jet_aid: uuid!("3e7c1854-f1eb-42d2-b9cb-9303036e50da"), + jet_rop: DvlsRecordingOperation::Push, + jet_gw_id: uuid!("ccbaad3f-4627-4666-8bb5-cb6a1a7db815"), + jet_reuse: Some(0), + nbf: 1_700_000_000, + exp: 4_102_444_800, + jti: uuid!("8a5a88fc-1184-4417-b70d-a9c2f9ec92a9"), + })] fn samples( #[case] claims: DvlsJrecClaims, token_cache: TokenCache,