bugcrowd_vrt/types.rs
1use serde::{Deserialize, Serialize};
2
3/// Represents the type of a VRT node in the taxonomy hierarchy
4#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
5#[serde(rename_all = "lowercase")]
6pub enum VrtNodeType {
7 /// Top-level category (e.g., "AI Application Security")
8 Category,
9 /// Subcategory within a category (e.g., "Prompt Injection")
10 Subcategory,
11 /// Specific vulnerability variant (e.g., "System Prompt Leakage")
12 Variant,
13}
14
15/// A node in the VRT taxonomy tree
16///
17/// The VRT is hierarchical with three levels:
18/// - Category (top level)
19/// - Subcategory (children of categories)
20/// - Variant (children of subcategories, leaf nodes with priority ratings)
21#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
22pub struct VrtNode {
23 /// Unique identifier for this node (e.g., "prompt_injection")
24 pub id: String,
25
26 /// Human-readable name (e.g., "Prompt Injection")
27 pub name: String,
28
29 /// The type of this node in the hierarchy
30 #[serde(rename = "type")]
31 pub node_type: VrtNodeType,
32
33 /// Child nodes (categories have subcategories, subcategories have variants)
34 /// Only present for category and subcategory nodes
35 #[serde(default, skip_serializing_if = "Vec::is_empty")]
36 pub children: Vec<VrtNode>,
37
38 /// Priority/severity rating (1-5, where 1 is most severe)
39 /// Only present for variant nodes
40 #[serde(skip_serializing_if = "Option::is_none")]
41 pub priority: Option<u8>,
42}
43
44impl VrtNode {
45 /// Returns true if this node is a category
46 pub fn is_category(&self) -> bool {
47 self.node_type == VrtNodeType::Category
48 }
49
50 /// Returns true if this node is a subcategory
51 pub fn is_subcategory(&self) -> bool {
52 self.node_type == VrtNodeType::Subcategory
53 }
54
55 /// Returns true if this node is a variant
56 pub fn is_variant(&self) -> bool {
57 self.node_type == VrtNodeType::Variant
58 }
59
60 /// Returns true if this node has children
61 pub fn has_children(&self) -> bool {
62 !self.children.is_empty()
63 }
64
65 /// Recursively finds a node by ID in the tree
66 pub fn find_by_id(&self, id: &str) -> Option<&VrtNode> {
67 if self.id == id {
68 return Some(self);
69 }
70
71 for child in &self.children {
72 if let Some(found) = child.find_by_id(id) {
73 return Some(found);
74 }
75 }
76
77 None
78 }
79
80 /// Returns all variant nodes (leaf nodes with priority ratings) under this node
81 pub fn variants(&self) -> Vec<&VrtNode> {
82 let mut variants = Vec::new();
83
84 if self.is_variant() {
85 variants.push(self);
86 }
87
88 for child in &self.children {
89 variants.extend(child.variants());
90 }
91
92 variants
93 }
94}
95
96/// The complete VRT taxonomy
97///
98/// This is the root structure that represents the entire Bugcrowd VRT.
99/// It's a vector of top-level categories.
100pub type VrtTaxonomy = Vec<VrtNode>;