1use serde::{Deserialize, Deserializer, Serialize};
4use std::fmt;
5use std::num::NonZeroU64;
6use thiserror::Error;
7
8pub const PARSER_PROTOCOL_VERSION: u8 = 1;
10pub const PARSER_FRAME_HEADER_BYTES: usize = 8;
12pub const PARSER_MAX_SOURCE_BYTES: u32 = 16 * 1024 * 1024;
14pub const PARSER_MAX_FRAME_PAYLOAD_BYTES: u32 = PARSER_MAX_SOURCE_BYTES;
16pub const PARSER_MAX_CONTROL_BYTES: u32 = 256 * 1024;
18pub const PARSER_MAX_OUTPUT_BYTES: u32 = 128 * 1024;
20pub const PARSER_MAX_NODE_COUNT: u32 = 10_000_000;
22pub const PARSER_MAX_TREE_DEPTH: u32 = 16_384;
24pub const PARSER_MAX_WORK_UNITS: u32 = 10_000_000;
26pub const PARSER_MAX_PROGRESS_MESSAGES: u32 = 4_096;
28pub const PARSER_MAX_LANGUAGE_ID_BYTES: usize = 128;
30pub const PARSER_MAX_SYNTAX_KIND_BYTES: usize = 256;
32pub const PARSER_SESSION_ENTROPY_BYTES: usize = 32;
34pub const PARSER_MAX_STDERR_BYTES: usize = 64 * 1024;
36pub const PARSER_WORKER_PROCESS_MEMORY_BYTES: u64 = 1024 * 1024 * 1024;
38pub const PARSER_WORKER_JOB_MEMORY_BYTES: u64 = PARSER_WORKER_PROCESS_MEMORY_BYTES;
40pub const PARSER_WINDOWS_BROKER_MEMORY_LIMIT_EXIT_CODE: i32 = 124;
42pub const PARSER_WINDOWS_BROKER_ADMISSION_RECORD: [u8; 16] = [
44 0x50, 0x41, 0x54, 0x4c, 0x41, 0x53, 0x2d, 0x41, 0x44, 0x4d, 0x49, 0x54, 0x00, 0x00, 0x00, 0x01,
45];
46
47const FRAME_MAGIC: [u8; 2] = *b"PA";
49const BLAKE3_HEX_BYTES: usize = 64;
51
52#[derive(Debug, Error)]
54pub enum ParserProtocolError {
55 #[error("parser frame header has {actual} bytes; expected {expected}")]
57 HeaderTooShort {
58 actual: usize,
60 expected: usize,
62 },
63 #[error("parser frame marker is invalid")]
65 InvalidFrameMarker,
66 #[error("parser protocol version {actual} is unsupported; expected {expected}")]
68 UnsupportedVersion {
69 actual: u8,
71 expected: u8,
73 },
74 #[error("parser frame kind {actual} is unknown")]
76 UnknownFrameKind {
77 actual: u8,
79 },
80 #[error("parser {kind:?} payload declares {actual} bytes; maximum is {maximum}")]
82 FramePayloadTooLarge {
83 kind: ParserFrameKind,
85 actual: u32,
87 maximum: u32,
89 },
90 #[error("parser frame declares {declared} payload bytes but only {available} are available")]
92 TruncatedFrame {
93 declared: u32,
95 available: usize,
97 },
98 #[error("parser frame has {actual} payload bytes; declared exactly {declared}")]
100 TrailingFrameBytes {
101 declared: u32,
103 actual: usize,
105 },
106 #[error("parser frame kind {kind:?} is not valid for this operation")]
108 UnexpectedFrameKind {
109 kind: ParserFrameKind,
111 },
112 #[error("invalid parser {kind:?} control payload")]
114 InvalidControlJson {
115 kind: ParserFrameKind,
117 #[source]
119 source: serde_json::Error,
120 },
121 #[error("could not serialize parser {kind:?} control payload")]
123 ControlSerialization {
124 kind: ParserFrameKind,
126 #[source]
128 source: serde_json::Error,
129 },
130 #[error("invalid parser protocol field {field}: {reason}")]
132 InvalidField {
133 field: &'static str,
135 reason: &'static str,
137 },
138 #[error("parser source has {actual} bytes; request declares {expected}")]
140 SourceLengthMismatch {
141 expected: u32,
143 actual: usize,
145 },
146 #[error("parser source BLAKE3 digest does not match the request")]
148 SourceDigestMismatch,
149 #[error("parser ready {field} does not match the supervised launch")]
151 ReadyIdentityMismatch {
152 field: &'static str,
154 },
155 #[error("parser request {field} does not match the ready worker session")]
157 RequestIdentityMismatch {
158 field: &'static str,
160 },
161 #[error("parser response {field} does not match the request")]
163 ResponseIdentityMismatch {
164 field: &'static str,
166 },
167 #[error("parser {field} value {actual} exceeds request limit {maximum}")]
169 RequestLimitExceeded {
170 field: &'static str,
172 actual: u32,
174 maximum: u32,
176 },
177 #[error("parser progress field {field} is not monotonic")]
179 ProgressRegression {
180 field: &'static str,
182 },
183}
184
185#[derive(Clone, Copy, Debug, Eq, PartialEq)]
187#[repr(u8)]
188pub enum ParserFrameKind {
189 Request = 1,
191 RawSource = 2,
193 Progress = 3,
195 Completion = 4,
197 Failure = 5,
199 Ready = 6,
201 SessionOpen = 7,
203}
204
205impl ParserFrameKind {
206 #[must_use]
208 pub const fn as_u8(self) -> u8 {
209 self as u8
210 }
211
212 #[must_use]
214 pub const fn maximum_payload_bytes(self) -> u32 {
215 match self {
216 Self::RawSource => PARSER_MAX_SOURCE_BYTES,
217 Self::Request
218 | Self::Progress
219 | Self::Completion
220 | Self::Failure
221 | Self::Ready
222 | Self::SessionOpen => PARSER_MAX_CONTROL_BYTES,
223 }
224 }
225
226 #[must_use]
228 pub const fn is_control(self) -> bool {
229 !matches!(self, Self::RawSource)
230 }
231}
232
233impl TryFrom<u8> for ParserFrameKind {
234 type Error = ParserProtocolError;
235
236 fn try_from(value: u8) -> Result<Self, Self::Error> {
237 match value {
238 1 => Ok(Self::Request),
239 2 => Ok(Self::RawSource),
240 3 => Ok(Self::Progress),
241 4 => Ok(Self::Completion),
242 5 => Ok(Self::Failure),
243 6 => Ok(Self::Ready),
244 7 => Ok(Self::SessionOpen),
245 actual => Err(ParserProtocolError::UnknownFrameKind { actual }),
246 }
247 }
248}
249
250#[derive(Clone, Copy, Debug, Eq, PartialEq)]
252pub struct ParserFrameHeader {
253 kind: ParserFrameKind,
255 payload_len: u32,
257}
258
259impl ParserFrameHeader {
260 pub fn new(kind: ParserFrameKind, payload_len: u32) -> Result<Self, ParserProtocolError> {
266 let maximum = kind.maximum_payload_bytes();
267 if payload_len > maximum {
268 return Err(ParserProtocolError::FramePayloadTooLarge {
269 kind,
270 actual: payload_len,
271 maximum,
272 });
273 }
274 Ok(Self { kind, payload_len })
275 }
276
277 pub fn decode(bytes: &[u8]) -> Result<Self, ParserProtocolError> {
284 if bytes.len() < PARSER_FRAME_HEADER_BYTES {
285 return Err(ParserProtocolError::HeaderTooShort {
286 actual: bytes.len(),
287 expected: PARSER_FRAME_HEADER_BYTES,
288 });
289 }
290 if bytes[..2] != FRAME_MAGIC {
291 return Err(ParserProtocolError::InvalidFrameMarker);
292 }
293 let version = bytes[2];
294 if version != PARSER_PROTOCOL_VERSION {
295 return Err(ParserProtocolError::UnsupportedVersion {
296 actual: version,
297 expected: PARSER_PROTOCOL_VERSION,
298 });
299 }
300 let kind = ParserFrameKind::try_from(bytes[3])?;
301 let payload_len = u32::from_be_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]);
302 Self::new(kind, payload_len)
303 }
304
305 #[must_use]
307 pub fn encode(self) -> [u8; PARSER_FRAME_HEADER_BYTES] {
308 let payload_len = self.payload_len.to_be_bytes();
309 [
310 FRAME_MAGIC[0],
311 FRAME_MAGIC[1],
312 PARSER_PROTOCOL_VERSION,
313 self.kind.as_u8(),
314 payload_len[0],
315 payload_len[1],
316 payload_len[2],
317 payload_len[3],
318 ]
319 }
320
321 #[must_use]
323 pub const fn kind(self) -> ParserFrameKind {
324 self.kind
325 }
326
327 #[must_use]
329 pub const fn payload_len(self) -> u32 {
330 self.payload_len
331 }
332}
333
334#[derive(Clone, Copy, Debug, Eq, PartialEq)]
336pub struct ParserFrame<'a> {
337 header: ParserFrameHeader,
339 payload: &'a [u8],
341}
342
343impl<'a> ParserFrame<'a> {
344 pub fn decode_exact(bytes: &'a [u8]) -> Result<Self, ParserProtocolError> {
353 let header = ParserFrameHeader::decode(bytes)?;
354 let declared = header.payload_len();
355 let available = bytes.len().saturating_sub(PARSER_FRAME_HEADER_BYTES);
356 let declared_usize = declared as usize;
357 if available < declared_usize {
358 return Err(ParserProtocolError::TruncatedFrame {
359 declared,
360 available,
361 });
362 }
363 if available > declared_usize {
364 return Err(ParserProtocolError::TrailingFrameBytes {
365 declared,
366 actual: available,
367 });
368 }
369 Ok(Self {
370 header,
371 payload: &bytes[PARSER_FRAME_HEADER_BYTES..],
372 })
373 }
374
375 #[must_use]
377 pub const fn kind(self) -> ParserFrameKind {
378 self.header.kind()
379 }
380
381 #[must_use]
383 pub const fn payload(self) -> &'a [u8] {
384 self.payload
385 }
386}
387
388pub fn encode_parser_frame(
396 kind: ParserFrameKind,
397 payload: &[u8],
398) -> Result<Vec<u8>, ParserProtocolError> {
399 let payload_len = u32::try_from(payload.len()).map_err(|_source| {
400 ParserProtocolError::FramePayloadTooLarge {
401 kind,
402 actual: u32::MAX,
403 maximum: kind.maximum_payload_bytes(),
404 }
405 })?;
406 let header = ParserFrameHeader::new(kind, payload_len)?;
407 let mut encoded = Vec::with_capacity(PARSER_FRAME_HEADER_BYTES + payload.len());
408 encoded.extend_from_slice(&header.encode());
409 encoded.extend_from_slice(payload);
410 Ok(encoded)
411}
412
413#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)]
415#[serde(transparent)]
416pub struct ParserProtocolVersion(u8);
417
418impl ParserProtocolVersion {
419 #[must_use]
421 pub const fn current() -> Self {
422 Self(PARSER_PROTOCOL_VERSION)
423 }
424
425 #[must_use]
427 pub const fn get(self) -> u8 {
428 self.0
429 }
430}
431
432impl<'de> Deserialize<'de> for ParserProtocolVersion {
433 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
434 where
435 D: Deserializer<'de>,
436 {
437 let actual = u8::deserialize(deserializer)?;
438 if actual == PARSER_PROTOCOL_VERSION {
439 Ok(Self(actual))
440 } else {
441 Err(serde::de::Error::custom(
442 ParserProtocolError::UnsupportedVersion {
443 actual,
444 expected: PARSER_PROTOCOL_VERSION,
445 },
446 ))
447 }
448 }
449}
450
451#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
453#[serde(transparent)]
454pub struct ParserRequestIdentity(NonZeroU64);
455
456impl ParserRequestIdentity {
457 pub fn new(value: u64) -> Result<Self, ParserProtocolError> {
463 NonZeroU64::new(value)
464 .map(Self)
465 .ok_or(ParserProtocolError::InvalidField {
466 field: "request_id",
467 reason: "expected a non-zero integer",
468 })
469 }
470
471 #[must_use]
473 pub const fn get(self) -> u64 {
474 self.0.get()
475 }
476}
477
478#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize)]
480#[serde(transparent)]
481pub struct ParserContentDigest(String);
482
483impl ParserContentDigest {
484 pub fn new(value: impl Into<String>) -> Result<Self, ParserProtocolError> {
490 let value = value.into();
491 if value.len() != BLAKE3_HEX_BYTES
492 || !value
493 .bytes()
494 .all(|byte| byte.is_ascii_digit() || (b'a'..=b'f').contains(&byte))
495 {
496 return Err(ParserProtocolError::InvalidField {
497 field: "blake3",
498 reason: "expected 64 lowercase hexadecimal characters",
499 });
500 }
501 Ok(Self(value))
502 }
503
504 #[must_use]
506 pub fn for_bytes(bytes: &[u8]) -> Self {
507 Self(blake3::hash(bytes).to_hex().to_string())
508 }
509
510 #[must_use]
512 pub fn as_str(&self) -> &str {
513 &self.0
514 }
515}
516
517impl<'de> Deserialize<'de> for ParserContentDigest {
518 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
519 where
520 D: Deserializer<'de>,
521 {
522 let value = String::deserialize(deserializer)?;
523 Self::new(value).map_err(serde::de::Error::custom)
524 }
525}
526
527impl fmt::Display for ParserContentDigest {
528 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
529 formatter.write_str(self.as_str())
530 }
531}
532
533#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
539#[serde(transparent)]
540pub struct ParserArtifactIdentity(ParserContentDigest);
541
542impl ParserArtifactIdentity {
543 #[must_use]
545 pub const fn new(digest: ParserContentDigest) -> Self {
546 Self(digest)
547 }
548
549 #[must_use]
551 pub fn for_bytes(bytes: &[u8]) -> Self {
552 Self(ParserContentDigest::for_bytes(bytes))
553 }
554
555 #[must_use]
557 pub const fn digest(&self) -> &ParserContentDigest {
558 &self.0
559 }
560}
561
562#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
569#[serde(transparent)]
570pub struct ParserSessionIdentity(ParserContentDigest);
571
572impl ParserSessionIdentity {
573 #[must_use]
575 pub const fn new(digest: ParserContentDigest) -> Self {
576 Self(digest)
577 }
578
579 #[must_use]
581 pub fn for_entropy(entropy: &[u8]) -> Self {
582 Self(ParserContentDigest::for_bytes(entropy))
583 }
584
585 #[must_use]
587 pub const fn digest(&self) -> &ParserContentDigest {
588 &self.0
589 }
590}
591
592#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
599#[serde(deny_unknown_fields)]
600pub struct ParserSessionOpen {
601 protocol_version: ParserProtocolVersion,
603 session: ParserSessionIdentity,
605}
606
607impl ParserSessionOpen {
608 #[must_use]
610 pub const fn new(session: ParserSessionIdentity) -> Self {
611 Self {
612 protocol_version: ParserProtocolVersion::current(),
613 session,
614 }
615 }
616
617 #[must_use]
619 pub const fn session(&self) -> &ParserSessionIdentity {
620 &self.session
621 }
622}
623
624#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
626#[serde(rename_all = "snake_case")]
627pub enum ParserContainmentKind {
628 LinuxLandlockSeccomp,
630 WindowsAppContainerJob,
632}
633
634#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
636#[serde(deny_unknown_fields)]
637pub struct ParserReady {
638 protocol_version: ParserProtocolVersion,
640 session: ParserSessionIdentity,
642 artifact: ParserArtifactIdentity,
644 containment: ParserContainmentKind,
646}
647
648impl ParserReady {
649 #[must_use]
651 pub const fn new(
652 session: ParserSessionIdentity,
653 artifact: ParserArtifactIdentity,
654 containment: ParserContainmentKind,
655 ) -> Self {
656 Self {
657 protocol_version: ParserProtocolVersion::current(),
658 session,
659 artifact,
660 containment,
661 }
662 }
663
664 pub fn validate_for(
670 &self,
671 session: &ParserSessionIdentity,
672 artifact: &ParserArtifactIdentity,
673 containment: ParserContainmentKind,
674 ) -> Result<(), ParserProtocolError> {
675 if self.protocol_version != ParserProtocolVersion::current() {
676 return Err(ready_identity_mismatch("protocol_version"));
677 }
678 if &self.session != session {
679 return Err(ready_identity_mismatch("session"));
680 }
681 if &self.artifact != artifact {
682 return Err(ready_identity_mismatch("artifact"));
683 }
684 if self.containment != containment {
685 return Err(ready_identity_mismatch("containment"));
686 }
687 Ok(())
688 }
689
690 #[must_use]
692 pub const fn session(&self) -> &ParserSessionIdentity {
693 &self.session
694 }
695
696 #[must_use]
698 pub const fn artifact(&self) -> &ParserArtifactIdentity {
699 &self.artifact
700 }
701
702 #[must_use]
704 pub const fn containment(&self) -> ParserContainmentKind {
705 self.containment
706 }
707}
708
709#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize)]
711#[serde(transparent)]
712pub struct ParserLanguageIdentity(String);
713
714impl ParserLanguageIdentity {
715 pub fn new(value: impl Into<String>) -> Result<Self, ParserProtocolError> {
721 let value = value.into();
722 let mut bytes = value.bytes();
723 let starts_alphanumeric = bytes
724 .next()
725 .is_some_and(|byte| byte.is_ascii_lowercase() || byte.is_ascii_digit());
726 if value.len() > PARSER_MAX_LANGUAGE_ID_BYTES
727 || !starts_alphanumeric
728 || !bytes.all(|byte| {
729 byte.is_ascii_lowercase()
730 || byte.is_ascii_digit()
731 || matches!(byte, b'-' | b'_' | b'.' | b'+' | b'#')
732 })
733 {
734 return Err(ParserProtocolError::InvalidField {
735 field: "language_id",
736 reason: "expected a bounded lowercase ASCII language identity",
737 });
738 }
739 Ok(Self(value))
740 }
741
742 #[must_use]
744 pub fn as_str(&self) -> &str {
745 &self.0
746 }
747}
748
749impl<'de> Deserialize<'de> for ParserLanguageIdentity {
750 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
751 where
752 D: Deserializer<'de>,
753 {
754 let value = String::deserialize(deserializer)?;
755 Self::new(value).map_err(serde::de::Error::custom)
756 }
757}
758
759impl fmt::Display for ParserLanguageIdentity {
760 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
761 formatter.write_str(self.as_str())
762 }
763}
764
765#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
767pub struct ParserSourceIdentity {
768 byte_len: u32,
770 blake3: ParserContentDigest,
772}
773
774impl ParserSourceIdentity {
775 pub fn for_bytes(source: &[u8]) -> Result<Self, ParserProtocolError> {
781 let byte_len =
782 u32::try_from(source.len()).map_err(|_source| ParserProtocolError::InvalidField {
783 field: "source.byte_len",
784 reason: "source length exceeds u32",
785 })?;
786 Self::new(byte_len, ParserContentDigest::for_bytes(source))
787 }
788
789 pub fn new(byte_len: u32, blake3: ParserContentDigest) -> Result<Self, ParserProtocolError> {
795 if byte_len > PARSER_MAX_SOURCE_BYTES {
796 return Err(ParserProtocolError::InvalidField {
797 field: "source.byte_len",
798 reason: "source exceeds the hard byte ceiling",
799 });
800 }
801 Ok(Self { byte_len, blake3 })
802 }
803
804 pub fn validate_bytes(&self, source: &[u8]) -> Result<(), ParserProtocolError> {
810 if source.len() != self.byte_len as usize {
811 return Err(ParserProtocolError::SourceLengthMismatch {
812 expected: self.byte_len,
813 actual: source.len(),
814 });
815 }
816 if ParserContentDigest::for_bytes(source) != self.blake3 {
817 return Err(ParserProtocolError::SourceDigestMismatch);
818 }
819 Ok(())
820 }
821
822 #[must_use]
824 pub const fn byte_len(&self) -> u32 {
825 self.byte_len
826 }
827
828 #[must_use]
830 pub const fn blake3(&self) -> &ParserContentDigest {
831 &self.blake3
832 }
833}
834
835impl<'de> Deserialize<'de> for ParserSourceIdentity {
836 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
837 where
838 D: Deserializer<'de>,
839 {
840 let wire = ParserSourceIdentityWire::deserialize(deserializer)?;
841 Self::new(wire.byte_len, wire.blake3).map_err(serde::de::Error::custom)
842 }
843}
844
845#[derive(Deserialize)]
847#[serde(deny_unknown_fields)]
848struct ParserSourceIdentityWire {
849 byte_len: u32,
851 blake3: ParserContentDigest,
853}
854
855#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)]
857pub struct ParserRequestLimits {
858 output_bytes: u32,
860 node_count: u32,
862 tree_depth: u32,
864}
865
866impl ParserRequestLimits {
867 pub fn new(
873 output_bytes: u32,
874 node_count: u32,
875 tree_depth: u32,
876 ) -> Result<Self, ParserProtocolError> {
877 validate_nonzero_limit("limits.output_bytes", output_bytes, PARSER_MAX_OUTPUT_BYTES)?;
878 validate_nonzero_limit("limits.node_count", node_count, PARSER_MAX_NODE_COUNT)?;
879 validate_nonzero_limit("limits.tree_depth", tree_depth, PARSER_MAX_TREE_DEPTH)?;
880 Ok(Self {
881 output_bytes,
882 node_count,
883 tree_depth,
884 })
885 }
886
887 #[must_use]
889 pub const fn output_bytes(self) -> u32 {
890 self.output_bytes
891 }
892
893 #[must_use]
895 pub const fn node_count(self) -> u32 {
896 self.node_count
897 }
898
899 #[must_use]
901 pub const fn tree_depth(self) -> u32 {
902 self.tree_depth
903 }
904}
905
906impl<'de> Deserialize<'de> for ParserRequestLimits {
907 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
908 where
909 D: Deserializer<'de>,
910 {
911 let wire = ParserRequestLimitsWire::deserialize(deserializer)?;
912 Self::new(wire.output_bytes, wire.node_count, wire.tree_depth)
913 .map_err(serde::de::Error::custom)
914 }
915}
916
917#[derive(Deserialize)]
919#[serde(deny_unknown_fields)]
920struct ParserRequestLimitsWire {
921 output_bytes: u32,
923 node_count: u32,
925 tree_depth: u32,
927}
928
929#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
931#[serde(deny_unknown_fields)]
932pub struct ParserRequest {
933 protocol_version: ParserProtocolVersion,
935 session: ParserSessionIdentity,
937 request_id: ParserRequestIdentity,
939 artifact: ParserArtifactIdentity,
941 language: ParserLanguageIdentity,
943 source: ParserSourceIdentity,
945 limits: ParserRequestLimits,
947}
948
949impl ParserRequest {
950 #[must_use]
952 pub const fn new(
953 session: ParserSessionIdentity,
954 request_id: ParserRequestIdentity,
955 artifact: ParserArtifactIdentity,
956 language: ParserLanguageIdentity,
957 source: ParserSourceIdentity,
958 limits: ParserRequestLimits,
959 ) -> Self {
960 Self {
961 protocol_version: ParserProtocolVersion::current(),
962 session,
963 request_id,
964 artifact,
965 language,
966 source,
967 limits,
968 }
969 }
970
971 #[must_use]
973 pub const fn protocol_version(&self) -> ParserProtocolVersion {
974 self.protocol_version
975 }
976
977 #[must_use]
979 pub const fn session(&self) -> &ParserSessionIdentity {
980 &self.session
981 }
982
983 #[must_use]
985 pub const fn request_id(&self) -> ParserRequestIdentity {
986 self.request_id
987 }
988
989 #[must_use]
991 pub const fn artifact(&self) -> &ParserArtifactIdentity {
992 &self.artifact
993 }
994
995 #[must_use]
997 pub const fn language(&self) -> &ParserLanguageIdentity {
998 &self.language
999 }
1000
1001 #[must_use]
1003 pub const fn source(&self) -> &ParserSourceIdentity {
1004 &self.source
1005 }
1006
1007 #[must_use]
1009 pub const fn limits(&self) -> ParserRequestLimits {
1010 self.limits
1011 }
1012
1013 pub fn validate_for_session(
1019 &self,
1020 session: &ParserSessionIdentity,
1021 artifact: &ParserArtifactIdentity,
1022 ) -> Result<(), ParserProtocolError> {
1023 if &self.session != session {
1024 return Err(request_identity_mismatch("session"));
1025 }
1026 if &self.artifact != artifact {
1027 return Err(request_identity_mismatch("artifact"));
1028 }
1029 Ok(())
1030 }
1031
1032 pub fn validate_source_frame(&self, frame: ParserFrame<'_>) -> Result<(), ParserProtocolError> {
1038 if frame.kind() != ParserFrameKind::RawSource {
1039 return Err(ParserProtocolError::UnexpectedFrameKind { kind: frame.kind() });
1040 }
1041 self.source.validate_bytes(frame.payload())
1042 }
1043}
1044
1045#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1047#[serde(deny_unknown_fields)]
1048pub struct ParserResponseIdentity {
1049 protocol_version: ParserProtocolVersion,
1051 session: ParserSessionIdentity,
1053 request_id: ParserRequestIdentity,
1055 artifact: ParserArtifactIdentity,
1057 language: ParserLanguageIdentity,
1059 source: ParserSourceIdentity,
1061}
1062
1063impl ParserResponseIdentity {
1064 #[must_use]
1066 pub fn for_request(request: &ParserRequest) -> Self {
1067 Self {
1068 protocol_version: request.protocol_version,
1069 session: request.session.clone(),
1070 request_id: request.request_id,
1071 artifact: request.artifact.clone(),
1072 language: request.language.clone(),
1073 source: request.source.clone(),
1074 }
1075 }
1076
1077 pub fn validate_for(&self, request: &ParserRequest) -> Result<(), ParserProtocolError> {
1083 if self.protocol_version != request.protocol_version {
1084 return Err(response_identity_mismatch("protocol_version"));
1085 }
1086 if self.session != request.session {
1087 return Err(response_identity_mismatch("session"));
1088 }
1089 if self.request_id != request.request_id {
1090 return Err(response_identity_mismatch("request_id"));
1091 }
1092 if self.artifact != request.artifact {
1093 return Err(response_identity_mismatch("artifact"));
1094 }
1095 if self.language != request.language {
1096 return Err(response_identity_mismatch("language"));
1097 }
1098 if self.source != request.source {
1099 return Err(response_identity_mismatch("source"));
1100 }
1101 Ok(())
1102 }
1103
1104 #[must_use]
1106 pub const fn request_id(&self) -> ParserRequestIdentity {
1107 self.request_id
1108 }
1109
1110 #[must_use]
1112 pub const fn session(&self) -> &ParserSessionIdentity {
1113 &self.session
1114 }
1115}
1116
1117#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
1119#[serde(rename_all = "snake_case")]
1120pub enum ParserProgressStage {
1121 Accepted,
1123 Parsing,
1125 CollectingEvidence,
1127}
1128
1129impl ParserProgressStage {
1130 const fn rank(self) -> u8 {
1132 match self {
1133 Self::Accepted => 0,
1134 Self::Parsing => 1,
1135 Self::CollectingEvidence => 2,
1136 }
1137 }
1138}
1139
1140#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1142pub enum ParserProgressDisposition {
1143 Advanced,
1145 NoProgress,
1147}
1148
1149#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
1151pub struct ParserProgress {
1152 identity: ParserResponseIdentity,
1154 sequence: u32,
1156 stage: ParserProgressStage,
1158 completed_work: u32,
1160 total_work: Option<u32>,
1162}
1163
1164impl ParserProgress {
1165 pub fn new(
1171 identity: ParserResponseIdentity,
1172 sequence: u32,
1173 stage: ParserProgressStage,
1174 completed_work: u32,
1175 total_work: Option<u32>,
1176 ) -> Result<Self, ParserProtocolError> {
1177 validate_nonzero_limit("progress.sequence", sequence, PARSER_MAX_PROGRESS_MESSAGES)?;
1178 if completed_work > PARSER_MAX_WORK_UNITS {
1179 return Err(ParserProtocolError::InvalidField {
1180 field: "progress.completed_work",
1181 reason: "completed work exceeds the hard count ceiling",
1182 });
1183 }
1184 if let Some(total) = total_work {
1185 validate_nonzero_limit("progress.total_work", total, PARSER_MAX_WORK_UNITS)?;
1186 if completed_work > total {
1187 return Err(ParserProtocolError::InvalidField {
1188 field: "progress.completed_work",
1189 reason: "completed work exceeds total work",
1190 });
1191 }
1192 }
1193 Ok(Self {
1194 identity,
1195 sequence,
1196 stage,
1197 completed_work,
1198 total_work,
1199 })
1200 }
1201
1202 pub fn validate_for(
1213 &self,
1214 request: &ParserRequest,
1215 previous: Option<&Self>,
1216 ) -> Result<ParserProgressDisposition, ParserProtocolError> {
1217 self.identity.validate_for(request)?;
1218 let Some(previous) = previous else {
1219 if self.sequence != 1 {
1220 return Err(progress_regression("sequence"));
1221 }
1222 return Ok(ParserProgressDisposition::Advanced);
1223 };
1224 if self.identity != previous.identity {
1225 return Err(progress_regression("identity"));
1226 }
1227 if self.sequence != previous.sequence.saturating_add(1) {
1228 return Err(progress_regression("sequence"));
1229 }
1230 if self.stage.rank() < previous.stage.rank() {
1231 return Err(progress_regression("stage"));
1232 }
1233 if self.completed_work < previous.completed_work {
1234 return Err(progress_regression("completed_work"));
1235 }
1236 if self.total_work != previous.total_work {
1237 return Err(progress_regression("total_work"));
1238 }
1239 if self.stage == previous.stage && self.completed_work == previous.completed_work {
1240 Ok(ParserProgressDisposition::NoProgress)
1241 } else {
1242 Ok(ParserProgressDisposition::Advanced)
1243 }
1244 }
1245
1246 #[must_use]
1248 pub const fn sequence(&self) -> u32 {
1249 self.sequence
1250 }
1251}
1252
1253impl<'de> Deserialize<'de> for ParserProgress {
1254 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1255 where
1256 D: Deserializer<'de>,
1257 {
1258 let wire = ParserProgressWire::deserialize(deserializer)?;
1259 Self::new(
1260 wire.identity,
1261 wire.sequence,
1262 wire.stage,
1263 wire.completed_work,
1264 wire.total_work,
1265 )
1266 .map_err(serde::de::Error::custom)
1267 }
1268}
1269
1270#[derive(Deserialize)]
1272#[serde(deny_unknown_fields)]
1273struct ParserProgressWire {
1274 identity: ParserResponseIdentity,
1276 sequence: u32,
1278 stage: ParserProgressStage,
1280 completed_work: u32,
1282 total_work: Option<u32>,
1284}
1285
1286#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
1288#[serde(transparent)]
1289pub struct ParserSyntaxKind(String);
1290
1291impl ParserSyntaxKind {
1292 pub fn new(value: impl Into<String>) -> Result<Self, ParserProtocolError> {
1298 let value = value.into();
1299 if value.is_empty()
1300 || value.len() > PARSER_MAX_SYNTAX_KIND_BYTES
1301 || value.chars().any(char::is_control)
1302 {
1303 return Err(ParserProtocolError::InvalidField {
1304 field: "evidence.root_kind",
1305 reason: "expected bounded non-control UTF-8",
1306 });
1307 }
1308 Ok(Self(value))
1309 }
1310
1311 #[must_use]
1313 pub fn as_str(&self) -> &str {
1314 &self.0
1315 }
1316}
1317
1318impl<'de> Deserialize<'de> for ParserSyntaxKind {
1319 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1320 where
1321 D: Deserializer<'de>,
1322 {
1323 let value = String::deserialize(deserializer)?;
1324 Self::new(value).map_err(serde::de::Error::custom)
1325 }
1326}
1327
1328#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
1330pub struct ParserCompletionEvidence {
1331 root_kind: ParserSyntaxKind,
1333 root_start_byte: u32,
1335 root_end_byte: u32,
1337 root_has_error: bool,
1339 named_node_count: u32,
1341 error_node_count: u32,
1343 missing_node_count: u32,
1345 maximum_depth: u32,
1347}
1348
1349impl ParserCompletionEvidence {
1350 pub fn new(
1356 root_kind: ParserSyntaxKind,
1357 root_start_byte: u32,
1358 root_end_byte: u32,
1359 root_has_error: bool,
1360 named_node_count: u32,
1361 error_node_count: u32,
1362 missing_node_count: u32,
1363 maximum_depth: u32,
1364 ) -> Result<Self, ParserProtocolError> {
1365 if root_start_byte > root_end_byte {
1366 return Err(ParserProtocolError::InvalidField {
1367 field: "evidence.root_range",
1368 reason: "root start exceeds root end",
1369 });
1370 }
1371 for (field, count) in [
1372 ("evidence.named_node_count", named_node_count),
1373 ("evidence.error_node_count", error_node_count),
1374 ("evidence.missing_node_count", missing_node_count),
1375 ] {
1376 if count > PARSER_MAX_NODE_COUNT {
1377 return Err(ParserProtocolError::InvalidField {
1378 field,
1379 reason: "count exceeds the hard node ceiling",
1380 });
1381 }
1382 }
1383 if maximum_depth > PARSER_MAX_TREE_DEPTH {
1384 return Err(ParserProtocolError::InvalidField {
1385 field: "evidence.maximum_depth",
1386 reason: "depth exceeds the hard tree ceiling",
1387 });
1388 }
1389 Ok(Self {
1390 root_kind,
1391 root_start_byte,
1392 root_end_byte,
1393 root_has_error,
1394 named_node_count,
1395 error_node_count,
1396 missing_node_count,
1397 maximum_depth,
1398 })
1399 }
1400
1401 pub fn validate_for(&self, request: &ParserRequest) -> Result<(), ParserProtocolError> {
1407 let source_len = request.source.byte_len();
1408 if self.root_end_byte > source_len {
1409 return Err(request_limit_exceeded(
1410 "evidence.root_end_byte",
1411 self.root_end_byte,
1412 source_len,
1413 ));
1414 }
1415 let limits = request.limits;
1416 for (field, count) in [
1417 ("evidence.named_node_count", self.named_node_count),
1418 ("evidence.error_node_count", self.error_node_count),
1419 ("evidence.missing_node_count", self.missing_node_count),
1420 ] {
1421 if count > limits.node_count {
1422 return Err(request_limit_exceeded(field, count, limits.node_count));
1423 }
1424 }
1425 if self.maximum_depth > limits.tree_depth {
1426 return Err(request_limit_exceeded(
1427 "evidence.maximum_depth",
1428 self.maximum_depth,
1429 limits.tree_depth,
1430 ));
1431 }
1432 Ok(())
1433 }
1434
1435 #[must_use]
1437 pub const fn root_kind(&self) -> &ParserSyntaxKind {
1438 &self.root_kind
1439 }
1440
1441 #[must_use]
1443 pub const fn root_start_byte(&self) -> u32 {
1444 self.root_start_byte
1445 }
1446
1447 #[must_use]
1449 pub const fn root_end_byte(&self) -> u32 {
1450 self.root_end_byte
1451 }
1452
1453 #[must_use]
1455 pub const fn root_has_error(&self) -> bool {
1456 self.root_has_error
1457 }
1458
1459 #[must_use]
1461 pub const fn named_node_count(&self) -> u32 {
1462 self.named_node_count
1463 }
1464
1465 #[must_use]
1467 pub const fn error_node_count(&self) -> u32 {
1468 self.error_node_count
1469 }
1470
1471 #[must_use]
1473 pub const fn missing_node_count(&self) -> u32 {
1474 self.missing_node_count
1475 }
1476
1477 #[must_use]
1479 pub const fn maximum_depth(&self) -> u32 {
1480 self.maximum_depth
1481 }
1482}
1483
1484impl<'de> Deserialize<'de> for ParserCompletionEvidence {
1485 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1486 where
1487 D: Deserializer<'de>,
1488 {
1489 let wire = ParserCompletionEvidenceWire::deserialize(deserializer)?;
1490 Self::new(
1491 wire.root_kind,
1492 wire.root_start_byte,
1493 wire.root_end_byte,
1494 wire.root_has_error,
1495 wire.named_node_count,
1496 wire.error_node_count,
1497 wire.missing_node_count,
1498 wire.maximum_depth,
1499 )
1500 .map_err(serde::de::Error::custom)
1501 }
1502}
1503
1504#[derive(Deserialize)]
1506#[serde(deny_unknown_fields)]
1507struct ParserCompletionEvidenceWire {
1508 root_kind: ParserSyntaxKind,
1510 root_start_byte: u32,
1512 root_end_byte: u32,
1514 root_has_error: bool,
1516 named_node_count: u32,
1518 error_node_count: u32,
1520 missing_node_count: u32,
1522 maximum_depth: u32,
1524}
1525
1526#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1528#[serde(deny_unknown_fields)]
1529pub struct ParserCompletion {
1530 identity: ParserResponseIdentity,
1532 evidence: ParserCompletionEvidence,
1534}
1535
1536impl ParserCompletion {
1537 #[must_use]
1539 pub const fn new(identity: ParserResponseIdentity, evidence: ParserCompletionEvidence) -> Self {
1540 Self { identity, evidence }
1541 }
1542
1543 fn validate_for(&self, request: &ParserRequest) -> Result<(), ParserProtocolError> {
1549 self.identity.validate_for(request)?;
1550 self.evidence.validate_for(request)
1551 }
1552
1553 #[must_use]
1555 pub const fn evidence(&self) -> &ParserCompletionEvidence {
1556 &self.evidence
1557 }
1558}
1559
1560#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
1562#[serde(rename_all = "snake_case")]
1563pub enum ParserFailureCode {
1564 InvalidRequest,
1566 InvalidSource,
1568 LanguageMismatch,
1570 ArtifactMismatch,
1572 ParseRejected,
1574 LimitExceeded,
1576 Cancelled,
1578 ProtocolViolation,
1580 InternalFailure,
1582}
1583
1584#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
1586#[serde(deny_unknown_fields)]
1587pub struct ParserFailure {
1588 identity: ParserResponseIdentity,
1590 code: ParserFailureCode,
1592}
1593
1594impl ParserFailure {
1595 #[must_use]
1597 pub const fn new(identity: ParserResponseIdentity, code: ParserFailureCode) -> Self {
1598 Self { identity, code }
1599 }
1600
1601 pub fn validate_for(&self, request: &ParserRequest) -> Result<(), ParserProtocolError> {
1607 self.identity.validate_for(request)
1608 }
1609
1610 #[must_use]
1612 pub const fn code(&self) -> ParserFailureCode {
1613 self.code
1614 }
1615}
1616
1617#[derive(Clone, Debug, Eq, PartialEq)]
1619pub enum ParserControl {
1620 SessionOpen(ParserSessionOpen),
1622 Ready(ParserReady),
1624 Request(ParserRequest),
1626 Progress(ParserProgress),
1628 Completion(ParserCompletion),
1630 Failure(ParserFailure),
1632}
1633
1634impl ParserControl {
1635 #[must_use]
1637 pub const fn frame_kind(&self) -> ParserFrameKind {
1638 match self {
1639 Self::SessionOpen(_) => ParserFrameKind::SessionOpen,
1640 Self::Ready(_) => ParserFrameKind::Ready,
1641 Self::Request(_) => ParserFrameKind::Request,
1642 Self::Progress(_) => ParserFrameKind::Progress,
1643 Self::Completion(_) => ParserFrameKind::Completion,
1644 Self::Failure(_) => ParserFrameKind::Failure,
1645 }
1646 }
1647}
1648
1649pub fn encode_parser_control(control: &ParserControl) -> Result<Vec<u8>, ParserProtocolError> {
1658 let kind = control.frame_kind();
1659 let payload = match control {
1660 ParserControl::SessionOpen(value) => serde_json::to_vec(value),
1661 ParserControl::Ready(value) => serde_json::to_vec(value),
1662 ParserControl::Request(value) => serde_json::to_vec(value),
1663 ParserControl::Progress(value) => serde_json::to_vec(value),
1664 ParserControl::Completion(value) => serde_json::to_vec(value),
1665 ParserControl::Failure(value) => serde_json::to_vec(value),
1666 }
1667 .map_err(|source| ParserProtocolError::ControlSerialization { kind, source })?;
1668 encode_parser_frame(kind, &payload)
1669}
1670
1671pub fn decode_parser_session_open(
1678 frame: ParserFrame<'_>,
1679) -> Result<ParserSessionOpen, ParserProtocolError> {
1680 if frame.kind() != ParserFrameKind::SessionOpen {
1681 return Err(ParserProtocolError::UnexpectedFrameKind { kind: frame.kind() });
1682 }
1683 decode_control_json(frame.payload(), ParserFrameKind::SessionOpen)
1684}
1685
1686pub fn decode_parser_ready_for_launch(
1693 frame: ParserFrame<'_>,
1694 session: &ParserSessionIdentity,
1695 artifact: &ParserArtifactIdentity,
1696 containment: ParserContainmentKind,
1697) -> Result<ParserReady, ParserProtocolError> {
1698 if frame.kind() != ParserFrameKind::Ready {
1699 return Err(ParserProtocolError::UnexpectedFrameKind { kind: frame.kind() });
1700 }
1701 let ready: ParserReady = decode_control_json(frame.payload(), ParserFrameKind::Ready)?;
1702 ready.validate_for(session, artifact, containment)?;
1703 Ok(ready)
1704}
1705
1706pub fn decode_parser_request_for_session(
1713 frame: ParserFrame<'_>,
1714 session: &ParserSessionIdentity,
1715 artifact: &ParserArtifactIdentity,
1716) -> Result<ParserRequest, ParserProtocolError> {
1717 if frame.kind() != ParserFrameKind::Request {
1718 return Err(ParserProtocolError::UnexpectedFrameKind { kind: frame.kind() });
1719 }
1720 let request: ParserRequest = decode_control_json(frame.payload(), ParserFrameKind::Request)?;
1721 request.validate_for_session(session, artifact)?;
1722 Ok(request)
1723}
1724
1725pub fn decode_parser_progress_for_request(
1732 frame: ParserFrame<'_>,
1733 request: &ParserRequest,
1734 previous: Option<&ParserProgress>,
1735) -> Result<(ParserProgress, ParserProgressDisposition), ParserProtocolError> {
1736 if frame.kind() != ParserFrameKind::Progress {
1737 return Err(ParserProtocolError::UnexpectedFrameKind { kind: frame.kind() });
1738 }
1739 let progress: ParserProgress = decode_control_json(frame.payload(), ParserFrameKind::Progress)?;
1740 let disposition = progress.validate_for(request, previous)?;
1741 Ok((progress, disposition))
1742}
1743
1744pub fn decode_parser_completion_for_request(
1755 frame: ParserFrame<'_>,
1756 request: &ParserRequest,
1757) -> Result<ParserCompletion, ParserProtocolError> {
1758 if frame.kind() != ParserFrameKind::Completion {
1759 return Err(ParserProtocolError::UnexpectedFrameKind { kind: frame.kind() });
1760 }
1761 let output_bytes = u32::try_from(frame.payload().len()).map_err(|_source| {
1762 request_limit_exceeded(
1763 "completion.output_bytes",
1764 u32::MAX,
1765 request.limits.output_bytes,
1766 )
1767 })?;
1768 if output_bytes > request.limits.output_bytes {
1769 return Err(request_limit_exceeded(
1770 "completion.output_bytes",
1771 output_bytes,
1772 request.limits.output_bytes,
1773 ));
1774 }
1775 let completion: ParserCompletion =
1776 decode_control_json(frame.payload(), ParserFrameKind::Completion)?;
1777 completion.validate_for(request)?;
1778 Ok(completion)
1779}
1780
1781pub fn decode_parser_failure_for_request(
1788 frame: ParserFrame<'_>,
1789 request: &ParserRequest,
1790) -> Result<ParserFailure, ParserProtocolError> {
1791 if frame.kind() != ParserFrameKind::Failure {
1792 return Err(ParserProtocolError::UnexpectedFrameKind { kind: frame.kind() });
1793 }
1794 let failure: ParserFailure = decode_control_json(frame.payload(), ParserFrameKind::Failure)?;
1795 failure.validate_for(request)?;
1796 Ok(failure)
1797}
1798
1799fn decode_control_json<T>(payload: &[u8], kind: ParserFrameKind) -> Result<T, ParserProtocolError>
1801where
1802 T: for<'de> Deserialize<'de>,
1803{
1804 serde_json::from_slice(payload)
1805 .map_err(|source| ParserProtocolError::InvalidControlJson { kind, source })
1806}
1807
1808fn validate_nonzero_limit(
1810 field: &'static str,
1811 value: u32,
1812 maximum: u32,
1813) -> Result<(), ParserProtocolError> {
1814 if value == 0 || value > maximum {
1815 return Err(ParserProtocolError::InvalidField {
1816 field,
1817 reason: "expected a non-zero value within the hard ceiling",
1818 });
1819 }
1820 Ok(())
1821}
1822
1823const fn response_identity_mismatch(field: &'static str) -> ParserProtocolError {
1825 ParserProtocolError::ResponseIdentityMismatch { field }
1826}
1827
1828const fn ready_identity_mismatch(field: &'static str) -> ParserProtocolError {
1830 ParserProtocolError::ReadyIdentityMismatch { field }
1831}
1832
1833const fn request_identity_mismatch(field: &'static str) -> ParserProtocolError {
1835 ParserProtocolError::RequestIdentityMismatch { field }
1836}
1837
1838const fn request_limit_exceeded(
1840 field: &'static str,
1841 actual: u32,
1842 maximum: u32,
1843) -> ParserProtocolError {
1844 ParserProtocolError::RequestLimitExceeded {
1845 field,
1846 actual,
1847 maximum,
1848 }
1849}
1850
1851const fn progress_regression(field: &'static str) -> ParserProtocolError {
1853 ParserProtocolError::ProgressRegression { field }
1854}
1855
1856#[cfg(test)]
1857mod tests {
1858 use super::*;
1859 use std::fmt::Debug;
1860 use std::io;
1861
1862 #[test]
1863 fn control_and_raw_source_frames_round_trip_deterministically()
1864 -> Result<(), Box<dyn std::error::Error>> {
1865 let (request, source) = request_fixture(64 * 1024)?;
1866 let controls = [
1867 ParserControl::SessionOpen(ParserSessionOpen::new(request.session.clone())),
1868 ParserControl::Ready(ParserReady::new(
1869 request.session.clone(),
1870 request.artifact.clone(),
1871 ParserContainmentKind::LinuxLandlockSeccomp,
1872 )),
1873 ParserControl::Request(request.clone()),
1874 ParserControl::Progress(ParserProgress::new(
1875 ParserResponseIdentity::for_request(&request),
1876 1,
1877 ParserProgressStage::Accepted,
1878 0,
1879 Some(10),
1880 )?),
1881 ParserControl::Completion(ParserCompletion::new(
1882 ParserResponseIdentity::for_request(&request),
1883 evidence_fixture()?,
1884 )),
1885 ParserControl::Failure(ParserFailure::new(
1886 ParserResponseIdentity::for_request(&request),
1887 ParserFailureCode::ParseRejected,
1888 )),
1889 ];
1890 for control in controls {
1891 let first = encode_parser_control(&control)?;
1892 let second = encode_parser_control(&control)?;
1893 require_eq(&first, &second, "control serialization")?;
1894 let frame = ParserFrame::decode_exact(&first)?;
1895 match &control {
1896 ParserControl::SessionOpen(expected) => {
1897 require_eq(
1898 &decode_parser_session_open(frame)?,
1899 expected,
1900 "session-open round trip",
1901 )?;
1902 }
1903 ParserControl::Ready(expected) => {
1904 require_eq(
1905 &decode_parser_ready_for_launch(
1906 frame,
1907 request.session(),
1908 request.artifact(),
1909 ParserContainmentKind::LinuxLandlockSeccomp,
1910 )?,
1911 expected,
1912 "ready round trip",
1913 )?;
1914 }
1915 ParserControl::Request(expected) => {
1916 require_eq(
1917 &decode_parser_request_for_session(
1918 frame,
1919 request.session(),
1920 request.artifact(),
1921 )?,
1922 expected,
1923 "request round trip",
1924 )?;
1925 }
1926 ParserControl::Progress(expected) => {
1927 let (actual, disposition) =
1928 decode_parser_progress_for_request(frame, &request, None)?;
1929 require_eq(&actual, expected, "progress round trip")?;
1930 require_eq(
1931 &disposition,
1932 &ParserProgressDisposition::Advanced,
1933 "progress disposition",
1934 )?;
1935 }
1936 ParserControl::Completion(expected) => {
1937 require_eq(
1938 &decode_parser_completion_for_request(frame, &request)?,
1939 expected,
1940 "completion round trip",
1941 )?;
1942 }
1943 ParserControl::Failure(expected) => {
1944 require_eq(
1945 &decode_parser_failure_for_request(frame, &request)?,
1946 expected,
1947 "failure round trip",
1948 )?;
1949 }
1950 }
1951 }
1952
1953 let encoded_source = encode_parser_frame(ParserFrameKind::RawSource, source)?;
1954 let source_frame = ParserFrame::decode_exact(&encoded_source)?;
1955 require_eq(&source_frame.payload(), &source, "raw-source round trip")?;
1956 request.validate_source_frame(source_frame)?;
1957 Ok(())
1958 }
1959
1960 #[test]
1961 fn strict_controls_reject_unknown_fields_and_frame_kinds()
1962 -> Result<(), Box<dyn std::error::Error>> {
1963 let (request, _source) = request_fixture(64 * 1024)?;
1964 let mut value = serde_json::to_value(&request)?;
1965 let Some(object) = value.as_object_mut() else {
1966 return Err("request did not serialize as an object".into());
1967 };
1968 object.insert(
1969 "repository_path".to_string(),
1970 serde_json::json!("src/lib.rs"),
1971 );
1972 let frame_bytes =
1973 encode_parser_frame(ParserFrameKind::Request, &serde_json::to_vec(&value)?)?;
1974 let error = decode_parser_request_for_session(
1975 ParserFrame::decode_exact(&frame_bytes)?,
1976 request.session(),
1977 request.artifact(),
1978 );
1979 require(
1980 matches!(error, Err(ParserProtocolError::InvalidControlJson { .. })),
1981 "unknown request field was accepted",
1982 )?;
1983
1984 let mut unknown = ParserFrameHeader::new(ParserFrameKind::Request, 0)?.encode();
1985 unknown[3] = 99;
1986 require(
1987 matches!(
1988 ParserFrameHeader::decode(&unknown),
1989 Err(ParserProtocolError::UnknownFrameKind { actual: 99 })
1990 ),
1991 "unknown frame kind was accepted",
1992 )?;
1993 Ok(())
1994 }
1995
1996 #[test]
1997 fn protocol_version_and_response_identity_mismatches_are_rejected()
1998 -> Result<(), Box<dyn std::error::Error>> {
1999 let (request, _source) = request_fixture(64 * 1024)?;
2000 let mut value = serde_json::to_value(&request)?;
2001 value["protocol_version"] = serde_json::json!(PARSER_PROTOCOL_VERSION + 1);
2002 let frame_bytes =
2003 encode_parser_frame(ParserFrameKind::Request, &serde_json::to_vec(&value)?)?;
2004 require(
2005 matches!(
2006 decode_parser_request_for_session(
2007 ParserFrame::decode_exact(&frame_bytes)?,
2008 request.session(),
2009 request.artifact(),
2010 ),
2011 Err(ParserProtocolError::InvalidControlJson { .. })
2012 ),
2013 "unsupported control version was accepted",
2014 )?;
2015
2016 let (other_request, _other_source) = request_fixture(64 * 1024)?;
2017 let mismatched = ParserCompletion::new(
2018 ParserResponseIdentity {
2019 request_id: ParserRequestIdentity::new(2)?,
2020 ..ParserResponseIdentity::for_request(&other_request)
2021 },
2022 evidence_fixture()?,
2023 );
2024 let mismatched_frame = encode_parser_control(&ParserControl::Completion(mismatched))?;
2025 require(
2026 matches!(
2027 decode_parser_completion_for_request(
2028 ParserFrame::decode_exact(&mismatched_frame)?,
2029 &request,
2030 ),
2031 Err(ParserProtocolError::ResponseIdentityMismatch {
2032 field: "request_id"
2033 })
2034 ),
2035 "mismatched response request identity was accepted",
2036 )?;
2037 Ok(())
2038 }
2039
2040 #[test]
2041 fn ready_is_bound_to_the_exact_session_artifact_and_containment()
2042 -> Result<(), Box<dyn std::error::Error>> {
2043 let (request, _source) = request_fixture(64 * 1024)?;
2044 let ready = ParserReady::new(
2045 request.session.clone(),
2046 request.artifact.clone(),
2047 ParserContainmentKind::LinuxLandlockSeccomp,
2048 );
2049 let encoded = encode_parser_control(&ParserControl::Ready(ready))?;
2050
2051 for (session, artifact, containment, field) in [
2052 (
2053 ParserSessionIdentity::for_entropy(b"another-session"),
2054 request.artifact.clone(),
2055 ParserContainmentKind::LinuxLandlockSeccomp,
2056 "session",
2057 ),
2058 (
2059 request.session.clone(),
2060 ParserArtifactIdentity::for_bytes(b"another-artifact"),
2061 ParserContainmentKind::LinuxLandlockSeccomp,
2062 "artifact",
2063 ),
2064 (
2065 request.session,
2066 request.artifact,
2067 ParserContainmentKind::WindowsAppContainerJob,
2068 "containment",
2069 ),
2070 ] {
2071 require(
2072 matches!(
2073 decode_parser_ready_for_launch(
2074 ParserFrame::decode_exact(&encoded)?,
2075 &session,
2076 &artifact,
2077 containment,
2078 ),
2079 Err(ParserProtocolError::ReadyIdentityMismatch { field: actual })
2080 if actual == field
2081 ),
2082 "mismatched containment-ready identity was accepted",
2083 )?;
2084 }
2085 Ok(())
2086 }
2087
2088 #[test]
2089 fn session_open_carries_only_protocol_and_process_freshness()
2090 -> Result<(), Box<dyn std::error::Error>> {
2091 let open = ParserSessionOpen::new(ParserSessionIdentity::for_entropy(b"session"));
2092 let mut value = serde_json::to_value(&open)?;
2093 let Some(object) = value.as_object_mut() else {
2094 return Err("session opening did not serialize as an object".into());
2095 };
2096 require(
2097 object.len() == 2
2098 && object.contains_key("protocol_version")
2099 && object.contains_key("session"),
2100 "session opening exposed more than protocol and process freshness",
2101 )?;
2102 object.insert("grammar_id".to_owned(), serde_json::json!("rust"));
2103 let encoded =
2104 encode_parser_frame(ParserFrameKind::SessionOpen, &serde_json::to_vec(&value)?)?;
2105 require(
2106 matches!(
2107 decode_parser_session_open(ParserFrame::decode_exact(&encoded)?),
2108 Err(ParserProtocolError::InvalidControlJson { .. })
2109 ),
2110 "session opening accepted grammar input before READY",
2111 )?;
2112 Ok(())
2113 }
2114
2115 #[test]
2116 fn requests_and_responses_cannot_be_replayed_across_sessions()
2117 -> Result<(), Box<dyn std::error::Error>> {
2118 let (request, _source) = request_fixture(64 * 1024)?;
2119 let other_session = ParserSessionIdentity::for_entropy(b"another-session");
2120 let encoded_request = encode_parser_control(&ParserControl::Request(request.clone()))?;
2121 require(
2122 matches!(
2123 decode_parser_request_for_session(
2124 ParserFrame::decode_exact(&encoded_request)?,
2125 &other_session,
2126 request.artifact(),
2127 ),
2128 Err(ParserProtocolError::RequestIdentityMismatch { field: "session" })
2129 ),
2130 "request from another worker session was accepted",
2131 )?;
2132 require(
2133 matches!(
2134 decode_parser_request_for_session(
2135 ParserFrame::decode_exact(&encoded_request)?,
2136 request.session(),
2137 &ParserArtifactIdentity::for_bytes(b"another-artifact"),
2138 ),
2139 Err(ParserProtocolError::RequestIdentityMismatch { field: "artifact" })
2140 ),
2141 "request for another pack artifact was accepted",
2142 )?;
2143
2144 let other_request = ParserRequest::new(
2145 other_session,
2146 request.request_id,
2147 request.artifact.clone(),
2148 request.language.clone(),
2149 request.source.clone(),
2150 request.limits,
2151 );
2152 let progress = ParserProgress::new(
2153 ParserResponseIdentity::for_request(&request),
2154 1,
2155 ParserProgressStage::Accepted,
2156 0,
2157 Some(1),
2158 )?;
2159 let encoded_progress = encode_parser_control(&ParserControl::Progress(progress))?;
2160 require(
2161 matches!(
2162 decode_parser_progress_for_request(
2163 ParserFrame::decode_exact(&encoded_progress)?,
2164 &other_request,
2165 None,
2166 ),
2167 Err(ParserProtocolError::ResponseIdentityMismatch { field: "session" })
2168 ),
2169 "progress from another worker session was accepted",
2170 )?;
2171
2172 let completion = ParserCompletion::new(
2173 ParserResponseIdentity::for_request(&request),
2174 evidence_fixture()?,
2175 );
2176 let encoded_completion = encode_parser_control(&ParserControl::Completion(completion))?;
2177 require(
2178 matches!(
2179 decode_parser_completion_for_request(
2180 ParserFrame::decode_exact(&encoded_completion)?,
2181 &other_request,
2182 ),
2183 Err(ParserProtocolError::ResponseIdentityMismatch { field: "session" })
2184 ),
2185 "response from another worker session was accepted",
2186 )?;
2187
2188 let failure = ParserFailure::new(
2189 ParserResponseIdentity::for_request(&request),
2190 ParserFailureCode::ParseRejected,
2191 );
2192 let encoded_failure = encode_parser_control(&ParserControl::Failure(failure))?;
2193 require(
2194 matches!(
2195 decode_parser_failure_for_request(
2196 ParserFrame::decode_exact(&encoded_failure)?,
2197 &other_request,
2198 ),
2199 Err(ParserProtocolError::ResponseIdentityMismatch { field: "session" })
2200 ),
2201 "failure from another worker session was accepted",
2202 )?;
2203 Ok(())
2204 }
2205
2206 #[test]
2207 fn declared_source_overflow_is_rejected_from_header_alone()
2208 -> Result<(), Box<dyn std::error::Error>> {
2209 let mut header = ParserFrameHeader::new(ParserFrameKind::RawSource, 0)?.encode();
2210 header[4..8].copy_from_slice(&(PARSER_MAX_SOURCE_BYTES + 1).to_be_bytes());
2211 require(
2212 matches!(
2213 ParserFrameHeader::decode(&header),
2214 Err(ParserProtocolError::FramePayloadTooLarge {
2215 kind: ParserFrameKind::RawSource,
2216 actual,
2217 maximum: PARSER_MAX_SOURCE_BYTES,
2218 }) if actual == PARSER_MAX_SOURCE_BYTES + 1
2219 ),
2220 "oversized declared source was not rejected from the header",
2221 )?;
2222 let mut ready_header = ParserFrameHeader::new(ParserFrameKind::Ready, 0)?.encode();
2223 ready_header[4..8].copy_from_slice(&(PARSER_MAX_CONTROL_BYTES + 1).to_be_bytes());
2224 require(
2225 matches!(
2226 ParserFrameHeader::decode(&ready_header),
2227 Err(ParserProtocolError::FramePayloadTooLarge {
2228 kind: ParserFrameKind::Ready,
2229 actual,
2230 maximum: PARSER_MAX_CONTROL_BYTES,
2231 }) if actual == PARSER_MAX_CONTROL_BYTES + 1
2232 ),
2233 "oversized declared READY was not rejected from the header",
2234 )?;
2235 Ok(())
2236 }
2237
2238 #[test]
2239 fn source_length_and_digest_are_both_authenticated() -> Result<(), Box<dyn std::error::Error>> {
2240 let identity = ParserSourceIdentity::for_bytes(b"abc")?;
2241 require(
2242 matches!(
2243 identity.validate_bytes(b"ab"),
2244 Err(ParserProtocolError::SourceLengthMismatch { .. })
2245 ),
2246 "source length mismatch was accepted",
2247 )?;
2248 require(
2249 matches!(
2250 identity.validate_bytes(b"abd"),
2251 Err(ParserProtocolError::SourceDigestMismatch)
2252 ),
2253 "source digest mismatch was accepted",
2254 )?;
2255 identity.validate_bytes(b"abc")?;
2256 Ok(())
2257 }
2258
2259 #[test]
2260 fn progress_is_contiguous_monotonic_and_exposes_no_progress()
2261 -> Result<(), Box<dyn std::error::Error>> {
2262 let (request, _source) = request_fixture(64 * 1024)?;
2263 let identity = ParserResponseIdentity::for_request(&request);
2264 let first = ParserProgress::new(
2265 identity.clone(),
2266 1,
2267 ParserProgressStage::Accepted,
2268 0,
2269 Some(10),
2270 )?;
2271 require_eq(
2272 &first.validate_for(&request, None)?,
2273 &ParserProgressDisposition::Advanced,
2274 "initial progress",
2275 )?;
2276 let heartbeat = ParserProgress::new(
2277 identity.clone(),
2278 2,
2279 ParserProgressStage::Accepted,
2280 0,
2281 Some(10),
2282 )?;
2283 require_eq(
2284 &heartbeat.validate_for(&request, Some(&first))?,
2285 &ParserProgressDisposition::NoProgress,
2286 "no-progress heartbeat",
2287 )?;
2288 let advanced = ParserProgress::new(
2289 identity.clone(),
2290 3,
2291 ParserProgressStage::Parsing,
2292 4,
2293 Some(10),
2294 )?;
2295 require_eq(
2296 &advanced.validate_for(&request, Some(&heartbeat))?,
2297 &ParserProgressDisposition::Advanced,
2298 "advanced progress",
2299 )?;
2300 let regressed =
2301 ParserProgress::new(identity, 4, ParserProgressStage::Accepted, 4, Some(10))?;
2302 require(
2303 matches!(
2304 regressed.validate_for(&request, Some(&advanced)),
2305 Err(ParserProtocolError::ProgressRegression { field: "stage" })
2306 ),
2307 "progress stage regression was accepted",
2308 )?;
2309 Ok(())
2310 }
2311
2312 #[test]
2313 fn exact_frame_decode_rejects_trailing_bytes() -> Result<(), Box<dyn std::error::Error>> {
2314 let mut encoded = encode_parser_frame(ParserFrameKind::RawSource, b"abc")?;
2315 encoded.push(0);
2316 require(
2317 matches!(
2318 ParserFrame::decode_exact(&encoded),
2319 Err(ParserProtocolError::TrailingFrameBytes {
2320 declared: 3,
2321 actual: 4,
2322 })
2323 ),
2324 "trailing frame bytes were accepted",
2325 )?;
2326 Ok(())
2327 }
2328
2329 #[test]
2330 fn request_and_completion_limits_are_enforced() -> Result<(), Box<dyn std::error::Error>> {
2331 require(
2332 ParserRequestLimits::new(PARSER_MAX_OUTPUT_BYTES + 1, 1, 1).is_err(),
2333 "oversized output limit was accepted",
2334 )?;
2335 require(
2336 ParserRequestLimits::new(1, 0, 1).is_err(),
2337 "zero node limit was accepted",
2338 )?;
2339 require(
2340 ParserRequestLimits::new(1, 1, PARSER_MAX_TREE_DEPTH + 1).is_err(),
2341 "oversized depth limit was accepted",
2342 )?;
2343
2344 let (unbounded_request, _source) = request_fixture(PARSER_MAX_OUTPUT_BYTES)?;
2345 let completion = ParserCompletion::new(
2346 ParserResponseIdentity::for_request(&unbounded_request),
2347 evidence_fixture()?,
2348 );
2349 require(
2350 !completion.evidence().root_has_error(),
2351 "completion root error state did not round trip",
2352 )?;
2353 let mut missing_root_error = serde_json::to_value(&completion)?;
2354 missing_root_error
2355 .get_mut("evidence")
2356 .and_then(serde_json::Value::as_object_mut)
2357 .and_then(|evidence| evidence.remove("root_has_error"))
2358 .ok_or_else(|| io::Error::other("completion fixture lacked root_has_error"))?;
2359 let missing_root_error_frame = encode_parser_frame(
2360 ParserFrameKind::Completion,
2361 &serde_json::to_vec(&missing_root_error)?,
2362 )?;
2363 require(
2364 matches!(
2365 decode_parser_completion_for_request(
2366 ParserFrame::decode_exact(&missing_root_error_frame)?,
2367 &unbounded_request,
2368 ),
2369 Err(ParserProtocolError::InvalidControlJson {
2370 kind: ParserFrameKind::Completion,
2371 ..
2372 })
2373 ),
2374 "completion accepted missing root_has_error evidence",
2375 )?;
2376 let canonical_payload = serde_json::to_vec(&completion)?;
2377 let canonical_bytes = u32::try_from(canonical_payload.len())?;
2378 let request = ParserRequest::new(
2379 unbounded_request.session,
2380 unbounded_request.request_id,
2381 unbounded_request.artifact,
2382 unbounded_request.language,
2383 unbounded_request.source,
2384 ParserRequestLimits::new(canonical_bytes, 100, 16)?,
2385 );
2386 let canonical_frame = encode_parser_frame(ParserFrameKind::Completion, &canonical_payload)?;
2387 decode_parser_completion_for_request(
2388 ParserFrame::decode_exact(&canonical_frame)?,
2389 &request,
2390 )?;
2391
2392 let mut padded_payload = canonical_payload;
2393 padded_payload.push(b' ');
2394 let padded_frame = encode_parser_frame(ParserFrameKind::Completion, &padded_payload)?;
2395 require(
2396 matches!(
2397 decode_parser_completion_for_request(
2398 ParserFrame::decode_exact(&padded_frame)?,
2399 &request,
2400 ),
2401 Err(ParserProtocolError::RequestLimitExceeded {
2402 field: "completion.output_bytes",
2403 actual,
2404 maximum,
2405 })
2406 if actual == canonical_bytes + 1 && maximum == canonical_bytes
2407 ),
2408 "padded completion exceeded exact request bytes without rejection",
2409 )?;
2410 Ok(())
2411 }
2412
2413 fn request_fixture(
2415 output_bytes: u32,
2416 ) -> Result<(ParserRequest, &'static [u8]), Box<dyn std::error::Error>> {
2417 let source = b"fn main() {}";
2418 Ok((
2419 ParserRequest::new(
2420 ParserSessionIdentity::for_entropy(b"supervised-session"),
2421 ParserRequestIdentity::new(1)?,
2422 ParserArtifactIdentity::for_bytes(b"artifact"),
2423 ParserLanguageIdentity::new("rust")?,
2424 ParserSourceIdentity::for_bytes(source)?,
2425 ParserRequestLimits::new(output_bytes, 100, 16)?,
2426 ),
2427 source,
2428 ))
2429 }
2430
2431 fn evidence_fixture() -> Result<ParserCompletionEvidence, ParserProtocolError> {
2433 ParserCompletionEvidence::new(
2434 ParserSyntaxKind::new("source_file")?,
2435 0,
2436 12,
2437 false,
2438 5,
2439 0,
2440 0,
2441 3,
2442 )
2443 }
2444
2445 fn require(condition: bool, message: &'static str) -> Result<(), Box<dyn std::error::Error>> {
2447 if condition {
2448 Ok(())
2449 } else {
2450 Err(io::Error::other(message).into())
2451 }
2452 }
2453
2454 fn require_eq<T>(
2456 actual: &T,
2457 expected: &T,
2458 context: &'static str,
2459 ) -> Result<(), Box<dyn std::error::Error>>
2460 where
2461 T: Debug + PartialEq,
2462 {
2463 if actual == expected {
2464 Ok(())
2465 } else {
2466 Err(io::Error::other(format!(
2467 "{context}: expected {expected:?}, found {actual:?}"
2468 ))
2469 .into())
2470 }
2471 }
2472}