1use crate::evolution::Chromosome;
4use serde::{Deserialize, Serialize};
5use std::sync::Arc;
6
7#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
9pub struct MutationOp {
10 pub gene_name: String,
12 pub from: String,
14 pub to: String,
16 pub operator: String,
18}
19
20#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
22pub enum Lineage {
23 Genesis {
25 generation: u32,
27 },
28 Crossover {
30 parent_a: Arc<Chromosome>,
32 parent_b: Arc<Chromosome>,
34 strategy: String,
36 generation: u32,
38 },
39 Mutation {
41 parent: Arc<Chromosome>,
43 log: Vec<MutationOp>,
45 generation: u32,
47 },
48}
49
50impl Lineage {
51 #[must_use]
53 pub fn genesis(generation: u32) -> Self {
54 Self::Genesis { generation }
55 }
56
57 #[must_use]
59 pub fn crossover(
60 parent_a: &Chromosome,
61 parent_b: &Chromosome,
62 strategy: &str,
63 generation: u32,
64 ) -> Self {
65 Self::Crossover {
66 parent_a: Arc::new(parent_a.clone()),
67 parent_b: Arc::new(parent_b.clone()),
68 strategy: strategy.to_string(),
69 generation,
70 }
71 }
72
73 #[must_use]
75 pub fn mutation(parent: &Chromosome, log: Vec<MutationOp>, generation: u32) -> Self {
76 Self::Mutation {
77 parent: Arc::new(parent.clone()),
78 log,
79 generation,
80 }
81 }
82
83 #[must_use]
85 pub fn to_trace(&self) -> String {
86 match self {
87 Self::Genesis { generation } => format!("genesis[gen={generation}]"),
88 Self::Crossover {
89 parent_a,
90 parent_b,
91 strategy,
92 generation,
93 } => {
94 format!(
95 "crossover[gen={generation},strategy={strategy},a={{{}}},b={{{}}}]",
96 genes_to_string(&parent_a.genes),
97 genes_to_string(&parent_b.genes)
98 )
99 }
100 Self::Mutation {
101 parent,
102 log,
103 generation,
104 } => {
105 let ops: Vec<String> = log
106 .iter()
107 .map(|op| format!("{}:{}->{}[{}]", op.gene_name, op.from, op.to, op.operator))
108 .collect();
109 format!(
110 "mutation[gen={generation},parent={{{}}},ops=[{}]]",
111 genes_to_string(&parent.genes),
112 ops.join(",")
113 )
114 }
115 }
116 }
117}
118
119fn genes_to_string(genes: &[(String, String)]) -> String {
120 genes
121 .iter()
122 .map(|(n, v)| format!("{n}={v}"))
123 .collect::<Vec<_>>()
124 .join(",")
125}
126
127#[derive(Debug, Clone, Serialize, Deserialize)]
129pub struct BypassEntry {
130 pub payload_hash: String,
132 pub genes: Vec<(String, String)>,
134 pub lineage_trace: String,
136 pub fitness: f64,
138 pub evaluations: u32,
140 pub target_waf: Option<String>,
142 pub verified: bool,
144 pub schema_version: u32,
146}
147
148impl BypassEntry {
149 pub const CURRENT_SCHEMA: u32 = 1;
150
151 #[must_use]
152 pub fn from_chromosome(chromosome: &Chromosome, target_waf: Option<String>) -> Self {
153 use std::collections::hash_map::DefaultHasher;
154 use std::hash::{Hash, Hasher};
155 let mut hasher = DefaultHasher::new();
156 chromosome.genes.hash(&mut hasher);
157 let hash = hasher.finish();
158
159 Self {
160 payload_hash: format!("{:016x}", hash),
161 genes: chromosome.genes.clone(),
162 lineage_trace: chromosome.lineage.to_trace(),
163 fitness: chromosome.fitness,
164 evaluations: chromosome.evaluations,
165 target_waf,
166 verified: true,
167 schema_version: Self::CURRENT_SCHEMA,
168 }
169 }
170}
171
172#[derive(Debug, Clone, Default, Serialize, Deserialize)]
174pub struct BypassCorpus {
175 pub entries: Vec<BypassEntry>,
176 pub schema_version: u32,
177}
178
179impl BypassCorpus {
180 pub const CURRENT_SCHEMA: u32 = 1;
181
182 #[must_use]
183 pub fn new() -> Self {
184 Self {
185 entries: Vec::new(),
186 schema_version: Self::CURRENT_SCHEMA,
187 }
188 }
189
190 pub fn add(&mut self, entry: BypassEntry) {
192 if !self
194 .entries
195 .iter()
196 .any(|e| e.payload_hash == entry.payload_hash)
197 {
198 self.entries.push(entry);
199 }
200 }
201
202 pub fn save(&self, path: &std::path::Path) -> Result<(), crate::types::EvolutionError> {
204 use crate::types::EvolutionError;
205 let mut lines = Vec::new();
206 for entry in &self.entries {
207 let json = serde_json::to_string(entry)
208 .map_err(|e| EvolutionError::SerializationFailed(e.to_string()))?;
209 lines.push(json);
210 }
211 std::fs::write(path, lines.join("\n"))
212 .map_err(|e| EvolutionError::SerializationFailed(e.to_string()))?;
213 Ok(())
214 }
215
216 pub fn load(path: &std::path::Path) -> Result<Self, crate::types::EvolutionError> {
218 use crate::types::EvolutionError;
219 let content = std::fs::read_to_string(path)
220 .map_err(|e| EvolutionError::DeserializationFailed(e.to_string()))?;
221 let mut entries = Vec::new();
222 for line in content.lines().filter(|l| !l.trim().is_empty()) {
223 let entry: BypassEntry = serde_json::from_str(line)
224 .map_err(|e| EvolutionError::DeserializationFailed(e.to_string()))?;
225 entries.push(entry);
226 }
227 Ok(Self {
228 entries,
229 schema_version: Self::CURRENT_SCHEMA,
230 })
231 }
232}
233
234#[cfg(test)]
235mod tests {
236 use super::*;
237 use crate::evolution::Chromosome;
238
239 #[test]
240 fn bypass_entry_deduplicates() {
241 let mut corpus = BypassCorpus::new();
242 let chrom = Chromosome::new(vec![("encoding".into(), "UrlEncode".into())]);
243 let entry = BypassEntry::from_chromosome(&chrom, None);
244 corpus.add(entry.clone());
245 corpus.add(entry);
246 assert_eq!(corpus.entries.len(), 1);
247 }
248
249 #[test]
250 fn lineage_trace_roundtrips() {
251 let chrom = Chromosome::new(vec![("a".into(), "1".into())]);
252 let lineage = Lineage::genesis(0);
253 assert!(lineage.to_trace().contains("genesis"));
254
255 let cross = Lineage::crossover(&chrom, &chrom, "uniform", 1);
256 assert!(cross.to_trace().contains("crossover"));
257
258 let mutation = Lineage::mutation(&chrom, vec![], 2);
259 assert!(mutation.to_trace().contains("mutation"));
260 }
261}