syncable_cli/analyzer/hadolint/rules/
dl3000.rs1use crate::analyzer::hadolint::parser::instruction::Instruction;
7use crate::analyzer::hadolint::rules::{SimpleRule, simple_rule};
8use crate::analyzer::hadolint::shell::ParsedShell;
9use crate::analyzer::hadolint::types::Severity;
10
11pub fn rule() -> SimpleRule<impl Fn(&Instruction, Option<&ParsedShell>) -> bool + Send + Sync> {
12 simple_rule(
13 "DL3000",
14 Severity::Error,
15 "Use absolute WORKDIR",
16 |instr, _shell| {
17 match instr {
18 Instruction::Workdir(path) => {
19 path.starts_with('/') || path.starts_with('$') || is_windows_absolute(path)
21 }
22 _ => true,
23 }
24 },
25 )
26}
27
28fn is_windows_absolute(path: &str) -> bool {
30 let chars: Vec<char> = path.chars().collect();
31 chars.len() >= 2 && chars[0].is_ascii_alphabetic() && chars[1] == ':'
32}
33
34#[cfg(test)]
35mod tests {
36 use super::*;
37 use crate::analyzer::hadolint::rules::{Rule, RuleState};
38
39 #[test]
40 fn test_absolute_path() {
41 let rule = rule();
42 let mut state = RuleState::new();
43
44 let instr = Instruction::Workdir("/app".to_string());
46 rule.check(&mut state, 1, &instr, None);
47 assert!(state.failures.is_empty());
48 }
49
50 #[test]
51 fn test_relative_path() {
52 let rule = rule();
53 let mut state = RuleState::new();
54
55 let instr = Instruction::Workdir("app".to_string());
57 rule.check(&mut state, 1, &instr, None);
58 assert_eq!(state.failures.len(), 1);
59 assert_eq!(state.failures[0].code.as_str(), "DL3000");
60 }
61
62 #[test]
63 fn test_variable_path() {
64 let rule = rule();
65 let mut state = RuleState::new();
66
67 let instr = Instruction::Workdir("$APP_DIR".to_string());
69 rule.check(&mut state, 1, &instr, None);
70 assert!(state.failures.is_empty());
71 }
72}