1mod ast_mutator;
40pub mod cwe_bash;
41mod operators;
42
43pub use ast_mutator::{AstMutation, AstMutator};
44pub use cwe_bash::{generate_cwe_mutations, CweMutation};
45pub use operators::MutationOperator;
46
47use crate::{Error, Result};
48
49#[derive(Debug, Clone)]
51pub struct MutatedCode {
52 pub original: String,
54 pub mutated: String,
56 pub operator: MutationOperator,
58 pub location: (usize, usize),
60 pub description: String,
62}
63
64#[derive(Debug, Default)]
66pub struct Mutator {
67 enabled_operators: Vec<MutationOperator>,
69}
70
71impl Mutator {
72 #[must_use]
74 pub fn new() -> Self {
75 Self {
76 enabled_operators: MutationOperator::all(),
77 }
78 }
79
80 #[must_use]
82 pub fn with_operators(operators: Vec<MutationOperator>) -> Self {
83 Self {
84 enabled_operators: operators,
85 }
86 }
87
88 pub fn mutate(&self, code: &str) -> Result<Vec<MutatedCode>> {
94 if code.is_empty() {
95 return Err(Error::Mutation("cannot mutate empty code".to_string()));
96 }
97
98 contract_pre_mutation_soundness!(code);
99 let mut mutations = Vec::new();
100
101 for operator in &self.enabled_operators {
102 let operator_mutations = self.apply_operator(code, operator)?;
103 mutations.extend(operator_mutations);
104 }
105
106 Ok(mutations)
107 }
108
109 fn apply_operator(&self, code: &str, operator: &MutationOperator) -> Result<Vec<MutatedCode>> {
114 let mutations = match operator {
115 MutationOperator::Aor => self.apply_aor(code),
116 MutationOperator::Ror => self.apply_ror(code),
117 MutationOperator::Lor => self.apply_lor(code),
118 MutationOperator::Uoi => self.apply_uoi(code),
119 MutationOperator::Abs => self.apply_abs(code),
120 MutationOperator::Sdl => self.apply_sdl(code),
121 MutationOperator::Svr => self.apply_svr(code),
122 MutationOperator::Bsr => self.apply_bsr(code),
123 };
124
125 Ok(mutations)
126 }
127
128 fn apply_aor(&self, code: &str) -> Vec<MutatedCode> {
129 let mut mutations = Vec::new();
130
131 if code.contains('+') {
133 mutations.push(MutatedCode {
134 original: code.to_string(),
135 mutated: code.replace('+', "-"),
136 operator: MutationOperator::Aor,
137 location: (1, code.find('+').unwrap_or(0)),
138 description: "Replace + with -".to_string(),
139 });
140 }
141
142 mutations
143 }
144
145 fn apply_ror(&self, code: &str) -> Vec<MutatedCode> {
146 let mut mutations = Vec::new();
147
148 if code.contains('<') && !code.contains("<=") {
149 mutations.push(MutatedCode {
150 original: code.to_string(),
151 mutated: code.replace('<', "<="),
152 operator: MutationOperator::Ror,
153 location: (1, code.find('<').unwrap_or(0)),
154 description: "Replace < with <=".to_string(),
155 });
156 }
157
158 mutations
159 }
160
161 fn apply_lor(&self, _code: &str) -> Vec<MutatedCode> {
162 Vec::new()
164 }
165
166 fn apply_uoi(&self, _code: &str) -> Vec<MutatedCode> {
167 Vec::new()
169 }
170
171 fn apply_abs(&self, _code: &str) -> Vec<MutatedCode> {
172 Vec::new()
174 }
175
176 fn apply_sdl(&self, _code: &str) -> Vec<MutatedCode> {
177 Vec::new()
179 }
180
181 fn apply_svr(&self, _code: &str) -> Vec<MutatedCode> {
182 Vec::new()
184 }
185
186 fn apply_bsr(&self, code: &str) -> Vec<MutatedCode> {
187 let mut mutations = Vec::new();
188
189 if code.contains(" 0") || code.contains("=0") {
191 mutations.push(MutatedCode {
192 original: code.to_string(),
193 mutated: code.replace(" 0", " -1").replace("=0", "=-1"),
194 operator: MutationOperator::Bsr,
195 location: (1, 0),
196 description: "Replace 0 with -1".to_string(),
197 });
198 }
199
200 mutations
201 }
202
203 #[must_use]
205 pub fn enabled_operators(&self) -> &[MutationOperator] {
206 &self.enabled_operators
207 }
208}
209
210#[cfg(test)]
211mod tests {
212 use super::*;
213
214 #[test]
215 fn test_mutator_new() {
216 let mutator = Mutator::new();
217 assert_eq!(mutator.enabled_operators().len(), 8);
218 }
219
220 #[test]
221 fn test_mutator_default() {
222 let mutator = Mutator::default();
223 assert!(mutator.enabled_operators().is_empty());
224 }
225
226 #[test]
227 fn test_mutator_with_operators() {
228 let ops = vec![MutationOperator::Aor, MutationOperator::Ror];
229 let mutator = Mutator::with_operators(ops);
230 assert_eq!(mutator.enabled_operators().len(), 2);
231 }
232
233 #[test]
234 fn test_mutator_aor() {
235 let mutator = Mutator::with_operators(vec![MutationOperator::Aor]);
236 let mutations = mutator
237 .mutate("x = a + b")
238 .expect("mutation should succeed");
239 assert!(!mutations.is_empty());
240 assert!(mutations[0].mutated.contains('-'));
241 }
242
243 #[test]
244 fn test_mutator_aor_no_operator() {
245 let mutator = Mutator::with_operators(vec![MutationOperator::Aor]);
246 let mutations = mutator.mutate("x = 5").expect("mutation should succeed");
247 assert!(mutations.is_empty());
248 }
249
250 #[test]
251 fn test_mutator_ror() {
252 let mutator = Mutator::with_operators(vec![MutationOperator::Ror]);
253 let mutations = mutator
254 .mutate("if x < 5:")
255 .expect("mutation should succeed");
256 assert!(!mutations.is_empty());
257 assert!(mutations[0].mutated.contains("<="));
258 }
259
260 #[test]
261 fn test_mutator_ror_already_lte() {
262 let mutator = Mutator::with_operators(vec![MutationOperator::Ror]);
263 let mutations = mutator
264 .mutate("if x <= 5:")
265 .expect("mutation should succeed");
266 assert!(mutations.is_empty()); }
268
269 #[test]
270 fn test_mutator_ror_no_operator() {
271 let mutator = Mutator::with_operators(vec![MutationOperator::Ror]);
272 let mutations = mutator.mutate("x = 5").expect("mutation should succeed");
273 assert!(mutations.is_empty());
274 }
275
276 #[test]
277 fn test_mutator_lor() {
278 let mutator = Mutator::with_operators(vec![MutationOperator::Lor]);
279 let mutations = mutator.mutate("x and y").expect("mutation should succeed");
280 assert!(mutations.is_empty()); }
282
283 #[test]
284 fn test_mutator_uoi() {
285 let mutator = Mutator::with_operators(vec![MutationOperator::Uoi]);
286 let mutations = mutator.mutate("x = 5").expect("mutation should succeed");
287 assert!(mutations.is_empty()); }
289
290 #[test]
291 fn test_mutator_abs() {
292 let mutator = Mutator::with_operators(vec![MutationOperator::Abs]);
293 let mutations = mutator.mutate("x = -5").expect("mutation should succeed");
294 assert!(mutations.is_empty()); }
296
297 #[test]
298 fn test_mutator_sdl() {
299 let mutator = Mutator::with_operators(vec![MutationOperator::Sdl]);
300 let mutations = mutator.mutate("x = 5").expect("mutation should succeed");
301 assert!(mutations.is_empty()); }
303
304 #[test]
305 fn test_mutator_svr() {
306 let mutator = Mutator::with_operators(vec![MutationOperator::Svr]);
307 let mutations = mutator.mutate("x = y").expect("mutation should succeed");
308 assert!(mutations.is_empty()); }
310
311 #[test]
312 fn test_mutator_bsr_space_zero() {
313 let mutator = Mutator::with_operators(vec![MutationOperator::Bsr]);
314 let mutations = mutator.mutate("x = 0").expect("mutation should succeed");
315 assert!(!mutations.is_empty());
316 assert!(mutations[0].mutated.contains("-1"));
317 }
318
319 #[test]
320 fn test_mutator_bsr_equals_zero() {
321 let mutator = Mutator::with_operators(vec![MutationOperator::Bsr]);
322 let mutations = mutator.mutate("x=0").expect("mutation should succeed");
323 assert!(!mutations.is_empty());
324 assert!(mutations[0].mutated.contains("=-1"));
325 }
326
327 #[test]
328 fn test_mutator_bsr_no_zero() {
329 let mutator = Mutator::with_operators(vec![MutationOperator::Bsr]);
330 let mutations = mutator.mutate("x = 5").expect("mutation should succeed");
331 assert!(mutations.is_empty());
332 }
333
334 #[test]
335 fn test_mutator_empty_code() {
336 let mutator = Mutator::new();
337 let result = mutator.mutate("");
338 assert!(result.is_err());
339 }
340
341 #[test]
342 fn test_mutator_all_operators() {
343 let mutator = Mutator::new();
344 let mutations = mutator
346 .mutate("x = a + b if y < 0")
347 .expect("mutation should succeed");
348 assert!(!mutations.is_empty());
349 assert!(mutations
351 .iter()
352 .any(|m| m.operator == MutationOperator::Aor));
353 assert!(mutations
354 .iter()
355 .any(|m| m.operator == MutationOperator::Ror));
356 let mutations2 = mutator.mutate("x = 0").expect("mutation should succeed");
358 assert!(mutations2
359 .iter()
360 .any(|m| m.operator == MutationOperator::Bsr));
361 }
362
363 #[test]
364 fn test_mutated_code_debug() {
365 let mc = MutatedCode {
366 original: "x + 1".to_string(),
367 mutated: "x - 1".to_string(),
368 operator: MutationOperator::Aor,
369 location: (1, 2),
370 description: "Replace + with -".to_string(),
371 };
372 let debug = format!("{:?}", mc);
373 assert!(debug.contains("MutatedCode"));
374 assert!(debug.contains("Aor"));
375 }
376
377 #[test]
378 fn test_mutated_code_clone() {
379 let mc = MutatedCode {
380 original: "x + 1".to_string(),
381 mutated: "x - 1".to_string(),
382 operator: MutationOperator::Aor,
383 location: (1, 2),
384 description: "Replace + with -".to_string(),
385 };
386 let cloned = mc.clone();
387 assert_eq!(cloned.original, mc.original);
388 assert_eq!(cloned.mutated, mc.mutated);
389 }
390}