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>;