wowlab_tidy/languages/rust/rules/safety/
abs_home_path.rs1use crate::{Example, FileCtx, Violation, infra::parse, violation};
2
3#[rustfmt::skip]
4const EXAMPLES: &[Example] = &[
5 Example {
6 label: "Users path in string",
7 code: r#"let p = "/Users/john/file";"#,
8 pass: false,
9 },
10 Example {
11 label: "home path in string",
12 code: r#"let p = "/home/user/data";"#,
13 pass: false,
14 },
15 Example {
16 label: "Windows path in string",
17 code: r#"let p = "C:\\Users\\john\\file";"#,
18 pass: false,
19 },
20 Example {
21 label: "tmp path",
22 code: r#"let p = "/tmp/file";"#,
23 pass: true,
24 },
25 Example {
26 label: "comment with path",
27 code: "// /Users/foo",
28 pass: true,
29 },
30 Example {
31 label: "no quotes",
32 code: "let users = get_users();",
33 pass: true,
34 },
35];
36
37crate::line_rule!(
38 abs_home_path,
39 "Ban hardcoded home directory paths like `/Users/` or `/home/` in string literals.",
40 "Absolute home paths break on other machines and in CI. Use environment variables or relative paths.",
41 Medium,
42);
43
44const HOME_PATTERNS: &[&str] = &["/Users/", "/home/", "C:\\\\Users\\\\", "C:\\Users\\"];
45
46fn check_abs_home_path(ctx: &FileCtx<'_>) -> Vec<Violation> {
47 let mut out = Vec::new();
48
49 for (i, line) in ctx.lines.iter().enumerate() {
50 let lineno = i + 1;
51 let trimmed = line.trim();
52
53 if parse::is_comment(trimmed) {
54 continue;
55 }
56
57 if !line.contains('"') {
58 continue;
59 }
60
61 for pattern in HOME_PATTERNS {
62 if line.contains(pattern) {
63 out.push(violation(
64 ctx.rel,
65 lineno,
66 format!(
67 "hardcoded home directory path `{pattern}` in string literal \
68 (use env vars or config)"
69 ),
70 ));
71 break;
72 }
73 }
74 }
75
76 out
77}
78
79crate::tidy_test!(check_abs_home_path, {
80 crate::example_tests!(EXAMPLES, check_abs_home_path);
81});