1use super::arrays::{
6 Context, DeprecatedFilenameTransition, FilenameTransition, FilenameTransitionList, FsUse,
7 GenericFsContext, IPv6Node, InfinitiBandEndPort, InfinitiBandPartitionKey, InitialSid,
8 MIN_POLICY_VERSION_FOR_INFINITIBAND_PARTITION_KEY, NamedContextPair, Node, Port,
9 RangeTransition, SimpleArray,
10};
11use super::error::{ParseError, ValidateError};
12use crate::new_policy::TypeSet;
13use crate::new_policy::bitmap::IdSet;
14
15use super::constraints::evaluate_constraint;
16use super::parser::{PolicyCursor, PolicyData};
17use super::security_context::SecurityContext;
18use super::view::Hashable;
19use super::{
20 AccessDecision, AccessVector, CategoryId, ClassId, MlsLevel, Parse, PolicyValidationContext,
21 RoleId, SELINUX_AVD_FLAGS_PERMISSIVE, SensitivityId, TypeId, UserId, Validate,
22 XpermsAccessDecision, XpermsKind,
23};
24
25use crate::new_policy::rules::{
26 ExtendedPermissions, HasRuleKey, RuleKind, XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES,
27 XPERMS_TYPE_IOCTL_PREFIXES, XPERMS_TYPE_NLMSG, XpermsBitmap,
28};
29use crate::new_policy::traits::{HasPolicyId, PolicyId};
30use crate::new_policy::{Class, NewPolicy};
31use crate::policy::arrays::FsContext;
32use crate::policy::view::CustomKeyHashedView;
33use crate::{NullessByteStr, PolicyCap};
34use std::ops::Deref;
35use std::sync::Arc;
36
37use anyhow::Context as _;
38use std::collections::HashSet;
39use std::fmt::Debug;
40use std::hash::Hash;
41use std::iter::Iterator;
42use zerocopy::little_endian as le;
43
44const MAXIMUM_POLICY_SIZE: usize = 1 << 24;
46
47#[derive(Debug)]
49pub struct ParsedPolicy {
50 data: PolicyData,
52
53 new_policy: Arc<NewPolicy>,
55
56 filename_transition_list: FilenameTransitionList,
57 initial_sids: SimpleArray<InitialSid>,
58 filesystems: SimpleArray<NamedContextPair>,
59 ports: SimpleArray<Port>,
60 network_interfaces: SimpleArray<NamedContextPair>,
61 nodes: SimpleArray<Node>,
62 fs_uses: SimpleArray<FsUse>,
63 ipv6_nodes: SimpleArray<IPv6Node>,
64 infinitiband_partition_keys: Option<SimpleArray<InfinitiBandPartitionKey>>,
65 infinitiband_end_ports: Option<SimpleArray<InfinitiBandEndPort>>,
66 generic_fs_contexts: CustomKeyHashedView<GenericFsContext>,
69 range_transitions: SimpleArray<RangeTransition>,
70 attribute_maps: Vec<TypeSet>,
72}
73
74impl Deref for ParsedPolicy {
75 type Target = NewPolicy;
76 fn deref(&self) -> &Self::Target {
77 &self.new_policy
78 }
79}
80
81impl ParsedPolicy {
82 pub fn has_policycap(&self, policy_cap: PolicyCap) -> bool {
84 self.new_policy.policy_capabilities().contains(policy_cap)
85 }
86
87 pub(super) fn compute_access_decision(
99 &self,
100 source_context: &SecurityContext,
101 target_context: &SecurityContext,
102 target_class: &Class,
103 ) -> AccessDecision {
104 let mut access_decision = self.compute_explicitly_allowed(
105 source_context.type_(),
106 target_context.type_(),
107 target_class,
108 );
109 access_decision.allow -=
110 self.compute_denied_by_constraints(source_context, target_context, target_class);
111 access_decision
112 }
113
114 pub(super) fn compute_explicitly_allowed(
119 &self,
120 source_type: TypeId,
121 target_type: TypeId,
122 target_class: &Class,
123 ) -> AccessDecision {
124 let target_class_id = target_class.id();
125
126 let mut computed_access_vector = AccessVector::NONE;
127 let mut computed_audit_allow = AccessVector::NONE;
128 let mut computed_audit_deny = AccessVector::ALL;
129
130 let source_attribute_set: &TypeSet =
131 &self.attribute_maps[(source_type.as_u32() - 1) as usize];
132 let target_attribute_set: &TypeSet =
133 &self.attribute_maps[(target_type.as_u32() - 1) as usize];
134
135 for source_id in source_attribute_set.iter() {
136 for target_id in target_attribute_set.iter() {
137 for rule in self.new_policy.access_vector_rules().find_av_rules(
138 source_id,
139 target_id,
140 target_class_id,
141 ) {
142 match rule.kind() {
143 RuleKind::Allow => computed_access_vector |= rule.access_vector(),
144 RuleKind::AuditAllow => computed_audit_allow |= rule.access_vector(),
145 RuleKind::DontAudit => computed_audit_deny &= rule.access_vector(),
146 _ => {}
147 }
148 }
149 }
150 }
151
152 if let Some(parent) = self.types().get_by_id(source_type).unwrap().bounded_by() {
156 let access = if source_type == target_type {
159 self.compute_explicitly_allowed(parent, parent, target_class)
160 } else {
161 self.compute_explicitly_allowed(parent, target_type, target_class)
162 };
163 computed_access_vector &= access.allow;
164 }
165
166 let mut flags = 0;
167 if self.permissive_map().contains(source_type) {
168 flags |= SELINUX_AVD_FLAGS_PERMISSIVE;
169 }
170 AccessDecision {
171 allow: computed_access_vector,
172 auditallow: computed_audit_allow,
173 auditdeny: computed_audit_deny,
174 flags,
175 todo_bug: None,
176 }
177 }
178
179 fn compute_denied_by_constraints(
181 &self,
182 source_context: &SecurityContext,
183 target_context: &SecurityContext,
184 target_class: &Class,
185 ) -> AccessVector {
186 let mut denied = AccessVector::NONE;
187 for constraint in target_class.constraints() {
188 if !evaluate_constraint(constraint.constraint_expr(), source_context, target_context) {
189 denied |= constraint.access_vector();
190 }
191 }
192 denied
193 }
194
195 pub(super) fn compute_xperms_access_decision(
198 &self,
199 xperms_kind: XpermsKind,
200 source_context: &SecurityContext,
201 target_context: &SecurityContext,
202 target_class: &Class,
203 xperms_prefix: u8,
204 ) -> XpermsAccessDecision {
205 let target_class_id = target_class.id();
206
207 let mut explicit_allow: Option<XpermsBitmap> = None;
208 let mut auditallow = XpermsBitmap::NONE;
209 let mut auditdeny = XpermsBitmap::ALL;
210
211 let xperms_types = match xperms_kind {
212 XpermsKind::Ioctl => {
213 [XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES, XPERMS_TYPE_IOCTL_PREFIXES].as_slice()
214 }
215 XpermsKind::Nlmsg => [XPERMS_TYPE_NLMSG].as_slice(),
216 };
217 let bitmap_if_prefix_matches =
218 |xperms_prefix: u8, xperms: &ExtendedPermissions| match xperms_kind {
219 XpermsKind::Ioctl => match xperms.xperms_type() {
220 XPERMS_TYPE_IOCTL_PREFIX_AND_POSTFIXES => (xperms.xperms_optional_prefix()
221 == xperms_prefix)
222 .then_some(*xperms.xperms_bitmap()),
223 XPERMS_TYPE_IOCTL_PREFIXES => {
224 xperms.xperms_bitmap().contains(xperms_prefix).then_some(XpermsBitmap::ALL)
225 }
226 _ => None,
227 },
228 XpermsKind::Nlmsg => match xperms.xperms_type() {
229 XPERMS_TYPE_NLMSG => (xperms.xperms_optional_prefix() == xperms_prefix)
230 .then_some(*xperms.xperms_bitmap()),
231 _ => None,
232 },
233 };
234
235 let source_attribute_set: &TypeSet =
236 &self.attribute_maps[(source_context.type_().as_u32() - 1) as usize];
237 let target_attribute_set: &TypeSet =
238 &self.attribute_maps[(target_context.type_().as_u32() - 1) as usize];
239
240 for source_id in source_attribute_set.iter() {
241 for target_id in target_attribute_set.iter() {
242 for rule in self.new_policy.access_vector_rules().find_xperm_rules(
243 source_id,
244 target_id,
245 target_class_id,
246 ) {
247 let xperms = rule.extended_permissions();
248 if rule.kind() == RuleKind::AllowXperm
249 && xperms_types.contains(&xperms.xperms_type())
250 {
251 explicit_allow.get_or_insert(XpermsBitmap::NONE);
252 }
253
254 if let Some(xperms_bitmap) = bitmap_if_prefix_matches(xperms_prefix, xperms) {
255 match rule.kind() {
256 RuleKind::AllowXperm => {
257 (*explicit_allow.get_or_insert(XpermsBitmap::NONE)) |=
258 xperms_bitmap;
259 }
260 RuleKind::AuditAllowXperm => auditallow |= xperms_bitmap,
261 RuleKind::DontAuditXperm => auditdeny -= xperms_bitmap,
262 _ => {}
263 }
264 }
265 }
266 }
267 }
268 let allow = explicit_allow.unwrap_or(XpermsBitmap::ALL);
269 XpermsAccessDecision { allow, auditallow, auditdeny }
270 }
271
272 pub(super) fn initial_context(&self, mut id: crate::InitialSid) -> &Context {
274 if id == crate::InitialSid::Init && !self.has_policycap(PolicyCap::UserspaceInitialContext)
276 {
277 id = crate::InitialSid::Kernel
278 }
279
280 let id = le::U32::from(id as u32);
282 &self.initial_sids.data.iter().find(|initial| initial.id() == id).unwrap().context()
283 }
284
285 pub(super) fn fs_uses(&self) -> &[FsUse] {
286 &self.fs_uses.data
287 }
288
289 pub(super) fn genfscon_find_all(&self, fs_type: &str) -> impl Iterator<Item = FsContext> {
290 let query = GenericFsContext::for_query(fs_type);
291 self.generic_fs_contexts.find_all(query, &self.data)
292 }
293
294 pub(super) fn range_transitions(&self) -> &[RangeTransition] {
295 &self.range_transitions.data
296 }
297
298 pub(super) fn compute_filename_transition(
299 &self,
300 source_type: TypeId,
301 target_type: TypeId,
302 class: ClassId,
303 name: NullessByteStr<'_>,
304 ) -> Option<TypeId> {
305 match &self.filename_transition_list {
306 FilenameTransitionList::PolicyVersionGeq33(list) => {
307 let entry = list.data.iter().find(|transition| {
308 transition.target_type() == target_type
309 && transition.target_class() == class
310 && transition.name_bytes() == name.as_bytes()
311 })?;
312 entry
313 .outputs()
314 .iter()
315 .find(|entry| entry.has_source_type(source_type))
316 .map(|x| x.out_type())
317 }
318 FilenameTransitionList::PolicyVersionLeq32(list) => list
319 .data
320 .iter()
321 .find(|transition| {
322 transition.target_class() == class
323 && transition.target_type() == target_type
324 && transition.source_type() == source_type
325 && transition.name_bytes() == name.as_bytes()
326 })
327 .map(|x| x.out_type()),
328 }
329 }
330
331 fn validate_mls_level(
334 &self,
335 level: &MlsLevel,
336 sensitivity_ids: &HashSet<SensitivityId>,
337 category_ids: &HashSet<CategoryId>,
338 ) -> Result<(), anyhow::Error> {
339 validate_id(sensitivity_ids, level.sensitivity(), "sensitivity")?;
340 for id in level.category_ids() {
341 validate_id(category_ids, id, "category")?;
342 }
343 Ok(())
344 }
345
346 fn validate_mls_range(
353 &self,
354 low_level: &MlsLevel,
355 high_level: &Option<MlsLevel>,
356 sensitivity_ids: &HashSet<SensitivityId>,
357 category_ids: &HashSet<CategoryId>,
358 ) -> Result<(), anyhow::Error> {
359 self.validate_mls_level(low_level, sensitivity_ids, category_ids)?;
360 if let Some(high) = high_level {
361 self.validate_mls_level(high, sensitivity_ids, category_ids)?;
362 if !high.dominates(low_level) {
363 return Err(ValidateError::InvalidMlsRange {
364 low: low_level.to_string(self).into(),
365 high: high.to_string(self).into(),
366 }
367 .into());
368 }
369 }
370 Ok(())
371 }
372
373 fn validate_context(
374 &self,
375 context: &Context,
376 user_ids: &HashSet<UserId>,
377 role_ids: &HashSet<RoleId>,
378 type_ids: &HashSet<TypeId>,
379 sensitivity_ids: &HashSet<SensitivityId>,
380 category_ids: &HashSet<CategoryId>,
381 ) -> Result<(), anyhow::Error> {
382 validate_id(user_ids, context.user_id(), "user")?;
383 validate_id(role_ids, context.role_id(), "role")?;
384 validate_id(type_ids, context.type_id(), "type")?;
385 self.validate_mls_range(
386 context.low_level(),
387 context.high_level(),
388 sensitivity_ids,
389 category_ids,
390 )?;
391 Ok(())
392 }
393}
394
395impl ParsedPolicy {
396 pub(super) fn parse(data: PolicyData) -> Result<Self, anyhow::Error> {
399 let policy_size = data.len();
400 if MAXIMUM_POLICY_SIZE <= policy_size {
401 return Err(anyhow::Error::from(ParseError::UnsupportedlyLarge {
402 observed: policy_size,
403 limit: MAXIMUM_POLICY_SIZE,
404 }));
405 }
406 let new_policy =
407 NewPolicy::parse(&data).map_err(|e| anyhow::anyhow!("new parser failed: {:?}", e))?;
408 new_policy.validate().context("validating new policy structure")?;
409
410 let rest_data = new_policy.rest_bytes();
411 let (policy, excess_bytes) = parse_policy_remaining(new_policy, rest_data)?;
412 if excess_bytes > 0 {
413 return Err(anyhow::Error::from(ParseError::TrailingBytes { num_bytes: excess_bytes }));
414 }
415 Ok(policy)
416 }
417}
418
419fn parse_policy_remaining(
421 new_policy: NewPolicy,
422 rest_data: PolicyData,
423) -> Result<(ParsedPolicy, usize), anyhow::Error> {
424 let tail = PolicyCursor::new(&rest_data);
425
426 let (filename_transition_list, tail) = if new_policy.policy_version() >= 33 {
427 let (filename_transition_list, tail) = SimpleArray::<FilenameTransition>::parse(tail)
428 .map_err(Into::<anyhow::Error>::into)
429 .context("parsing standard filename transitions")?;
430 (FilenameTransitionList::PolicyVersionGeq33(filename_transition_list), tail)
431 } else {
432 let (filename_transition_list, tail) =
433 SimpleArray::<DeprecatedFilenameTransition>::parse(tail)
434 .map_err(Into::<anyhow::Error>::into)
435 .context("parsing deprecated filename transitions")?;
436 (FilenameTransitionList::PolicyVersionLeq32(filename_transition_list), tail)
437 };
438
439 let (initial_sids, tail) = SimpleArray::<InitialSid>::parse(tail)
440 .map_err(Into::<anyhow::Error>::into)
441 .context("parsing initial sids")?;
442
443 let (filesystems, tail) = SimpleArray::<NamedContextPair>::parse(tail)
444 .map_err(Into::<anyhow::Error>::into)
445 .context("parsing filesystem contexts")?;
446
447 let (ports, tail) = SimpleArray::<Port>::parse(tail)
448 .map_err(Into::<anyhow::Error>::into)
449 .context("parsing ports")?;
450
451 let (network_interfaces, tail) = SimpleArray::<NamedContextPair>::parse(tail)
452 .map_err(Into::<anyhow::Error>::into)
453 .context("parsing network interfaces")?;
454
455 let (nodes, tail) = SimpleArray::<Node>::parse(tail)
456 .map_err(Into::<anyhow::Error>::into)
457 .context("parsing nodes")?;
458
459 let (fs_uses, tail) = SimpleArray::<FsUse>::parse(tail)
460 .map_err(Into::<anyhow::Error>::into)
461 .context("parsing fs uses")?;
462
463 let (ipv6_nodes, tail) = SimpleArray::<IPv6Node>::parse(tail)
464 .map_err(Into::<anyhow::Error>::into)
465 .context("parsing ipv6 nodes")?;
466
467 let (infinitiband_partition_keys, infinitiband_end_ports, tail) =
468 if new_policy.policy_version() >= MIN_POLICY_VERSION_FOR_INFINITIBAND_PARTITION_KEY {
469 let (infinity_band_partition_keys, tail) =
470 SimpleArray::<InfinitiBandPartitionKey>::parse(tail)
471 .map_err(Into::<anyhow::Error>::into)
472 .context("parsing infiniti band partition keys")?;
473 let (infinitiband_end_ports, tail) = SimpleArray::<InfinitiBandEndPort>::parse(tail)
474 .map_err(Into::<anyhow::Error>::into)
475 .context("parsing infiniti band end ports")?;
476 (Some(infinity_band_partition_keys), Some(infinitiband_end_ports), tail)
477 } else {
478 (None, None, tail)
479 };
480
481 let (generic_fs_contexts, tail) = CustomKeyHashedView::<GenericFsContext>::parse(tail)
482 .map_err(Into::<anyhow::Error>::into)
483 .context("parsing generic filesystem contexts")?;
484
485 let (range_transitions, tail) = SimpleArray::<RangeTransition>::parse(tail)
486 .map_err(Into::<anyhow::Error>::into)
487 .context("parsing range transitions")?;
488
489 let primary_names_count = new_policy.types().primary_names_count();
490 let mut attribute_maps = Vec::with_capacity(primary_names_count as usize);
491 let mut tail = tail;
492
493 for i in 0..primary_names_count {
494 let (item, next_tail) = TypeSet::parse(tail)
495 .map_err(Into::<anyhow::Error>::into)
496 .with_context(|| format!("parsing {}th attribute map", i))?;
497 attribute_maps.push(item);
498 tail = next_tail;
499 }
500 let tail = tail;
501 let attribute_maps = attribute_maps;
502
503 let excess_bytes = rest_data.len() - tail.offset() as usize;
504
505 Ok((
506 ParsedPolicy {
507 data: rest_data,
508 new_policy: Arc::new(new_policy),
509
510 filename_transition_list,
511 initial_sids,
512 filesystems,
513 ports,
514 network_interfaces,
515 nodes,
516 fs_uses,
517 ipv6_nodes,
518 infinitiband_partition_keys,
519 infinitiband_end_ports,
520 generic_fs_contexts,
521 range_transitions,
522 attribute_maps,
523 },
524 excess_bytes,
525 ))
526}
527
528impl ParsedPolicy {
529 pub fn validate(&self) -> Result<(), anyhow::Error> {
530 let need_init_sid = self.has_policycap(PolicyCap::UserspaceInitialContext);
531 let context = PolicyValidationContext {
532 data: self.data.clone(),
533 need_init_sid,
534 new_policy: self.new_policy.clone(),
535 };
536
537 self.filename_transition_list
538 .validate(&context)
539 .map_err(Into::<anyhow::Error>::into)
540 .context("validating filename_transition_list")?;
541 self.initial_sids
542 .validate(&context)
543 .map_err(Into::<anyhow::Error>::into)
544 .context("validating initial_sids")?;
545 self.filesystems
546 .validate(&context)
547 .map_err(Into::<anyhow::Error>::into)
548 .context("validating filesystems")?;
549 self.ports
550 .validate(&context)
551 .map_err(Into::<anyhow::Error>::into)
552 .context("validating ports")?;
553 self.network_interfaces
554 .validate(&context)
555 .map_err(Into::<anyhow::Error>::into)
556 .context("validating network_interfaces")?;
557 self.nodes
558 .validate(&context)
559 .map_err(Into::<anyhow::Error>::into)
560 .context("validating nodes")?;
561 self.fs_uses
562 .validate(&context)
563 .map_err(Into::<anyhow::Error>::into)
564 .context("validating fs_uses")?;
565 self.ipv6_nodes
566 .validate(&context)
567 .map_err(Into::<anyhow::Error>::into)
568 .context("validating ipv6 nodes")?;
569 self.infinitiband_partition_keys
570 .validate(&context)
571 .map_err(Into::<anyhow::Error>::into)
572 .context("validating infinitiband_partition_keys")?;
573 self.infinitiband_end_ports
574 .validate(&context)
575 .map_err(Into::<anyhow::Error>::into)
576 .context("validating infinitiband_end_ports")?;
577 self.generic_fs_contexts
578 .validate(&context)
579 .map_err(Into::<anyhow::Error>::into)
580 .context("validating generic_fs_contexts")?;
581 self.range_transitions
582 .validate(&context)
583 .map_err(Into::<anyhow::Error>::into)
584 .context("validating range_transitions")?;
585 self.attribute_maps
586 .validate(&context)
587 .map_err(Into::<anyhow::Error>::into)
588 .context("validating attribute_maps")?;
589
590 let user_ids: HashSet<UserId> = self.new_policy.users().iter().map(|x| x.id()).collect();
592 let role_ids: HashSet<RoleId> = self.roles().iter().map(|x| x.id()).collect();
593 let type_ids: HashSet<TypeId> = self.new_policy.types().iter().map(|t| t.id()).collect();
594 let sensitivity_ids: HashSet<SensitivityId> =
595 self.new_policy.sensitivities().iter().map(|x| x.id()).collect();
596 let category_ids: HashSet<CategoryId> =
597 self.new_policy.categories().iter().map(|x| x.id()).collect();
598
599 for initial_sid in &self.initial_sids.data {
603 self.validate_context(
604 initial_sid.context(),
605 &user_ids,
606 &role_ids,
607 &type_ids,
608 &sensitivity_ids,
609 &category_ids,
610 )?;
611 }
612
613 for fs_use in &self.fs_uses.data {
617 self.validate_context(
618 fs_use.context(),
619 &user_ids,
620 &role_ids,
621 &type_ids,
622 &sensitivity_ids,
623 &category_ids,
624 )?;
625 }
626
627 for entry in self.generic_fs_contexts.iter(&self.data) {
631 let entry = entry?;
632 for fs_context_view in entry.values().data().iter(&self.data) {
633 let fs_context = fs_context_view.parse(&self.data);
634 self.validate_context(
635 fs_context.context(),
636 &user_ids,
637 &role_ids,
638 &type_ids,
639 &sensitivity_ids,
640 &category_ids,
641 )?;
642 }
643 }
644
645 Ok(())
649 }
650}
651
652fn validate_id<IdType: Debug + Eq + Hash>(
653 id_set: &HashSet<IdType>,
654 id: IdType,
655 debug_kind: &'static str,
656) -> Result<(), anyhow::Error> {
657 if !id_set.contains(&id) {
658 return Err(ValidateError::UnknownId { kind: debug_kind, id: format!("{:?}", id) }.into());
659 }
660 Ok(())
661}
662
663impl<T: PolicyId, const WITH_ID_ZERO: bool> Parse for IdSet<T, WITH_ID_ZERO> {
664 type Error = anyhow::Error;
665
666 fn parse<'a>(cursor: PolicyCursor<'a>) -> Result<(Self, PolicyCursor<'a>), Self::Error> {
667 let offset = cursor.offset() as usize;
668 let slice = &cursor.data().as_ref()[offset..];
669 let mut new_cursor = crate::new_policy::parser::PolicyCursor::new(slice);
670 let id_set = <Self as crate::new_policy::traits::Parse>::parse(&mut new_cursor)
671 .map_err(|e| anyhow::anyhow!("Parse error: {:?}", e))?;
672 let bytes_parsed = new_cursor.offset();
673 let new_offset = cursor.offset() + bytes_parsed as u32;
674 Ok((id_set, PolicyCursor::new_at(cursor.data(), new_offset)))
675 }
676}
677
678impl<T: PolicyId + crate::new_policy::traits::Validate, const WITH_ID_ZERO: bool> Validate
679 for IdSet<T, WITH_ID_ZERO>
680{
681 type Error = anyhow::Error;
682
683 fn validate(&self, context: &PolicyValidationContext) -> Result<(), Self::Error> {
684 crate::new_policy::traits::Validate::validate(self, &context.new_policy).map_err(Into::into)
685 }
686}
687
688#[cfg(test)]
689mod tests {
690 use super::*;
691 use std::sync::Arc;
692
693 #[test]
694 fn test_id_set_parse_compatibility() {
695 let bytes = [
696 64, 0, 0, 0, 128, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, ];
706 let data: PolicyData = Arc::from(bytes);
707 let cursor = PolicyCursor::new(&data);
708 let (id_set, tail) = TypeSet::parse(cursor).unwrap();
709 assert_eq!(tail.offset(), bytes.len() as u32);
710 assert!(id_set.contains(TypeId::from_u32(1).unwrap()));
711 assert!(!id_set.contains(TypeId::from_u32(2).unwrap()));
712 assert!(id_set.contains(TypeId::from_u32(3).unwrap()));
713 assert!(id_set.contains(TypeId::from_u32(66).unwrap()));
714 }
715}