syncable_cli/analyzer/hadolint/rules/
dl3025.rs

1//! DL3025: Use arguments JSON notation for CMD and ENTRYPOINT arguments
2//!
3//! Using exec form (JSON notation) for CMD and ENTRYPOINT ensures proper
4//! signal handling and avoids shell processing issues.
5
6use crate::analyzer::hadolint::parser::instruction::Instruction;
7use crate::analyzer::hadolint::rules::{simple_rule, SimpleRule};
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        "DL3025",
14        Severity::Warning,
15        "Use arguments JSON notation for CMD and ENTRYPOINT arguments",
16        |instr, _shell| {
17            match instr {
18                Instruction::Cmd(args) | Instruction::Entrypoint(args) => {
19                    args.is_exec_form()
20                }
21                _ => true,
22            }
23        },
24    )
25}
26
27#[cfg(test)]
28mod tests {
29    use super::*;
30    use crate::analyzer::hadolint::parser::instruction::Arguments;
31    use crate::analyzer::hadolint::rules::{Rule, RuleState};
32
33    #[test]
34    fn test_exec_form() {
35        let rule = rule();
36        let mut state = RuleState::new();
37
38        let args = Arguments::List(vec!["node".to_string(), "app.js".to_string()]);
39        let instr = Instruction::Cmd(args);
40        rule.check(&mut state, 1, &instr, None);
41        assert!(state.failures.is_empty());
42    }
43
44    #[test]
45    fn test_shell_form() {
46        let rule = rule();
47        let mut state = RuleState::new();
48
49        let args = Arguments::Text("node app.js".to_string());
50        let instr = Instruction::Cmd(args);
51        rule.check(&mut state, 1, &instr, None);
52        assert_eq!(state.failures.len(), 1);
53        assert_eq!(state.failures[0].code.as_str(), "DL3025");
54    }
55
56    #[test]
57    fn test_entrypoint_exec() {
58        let rule = rule();
59        let mut state = RuleState::new();
60
61        let args = Arguments::List(vec!["./entrypoint.sh".to_string()]);
62        let instr = Instruction::Entrypoint(args);
63        rule.check(&mut state, 1, &instr, None);
64        assert!(state.failures.is_empty());
65    }
66
67    #[test]
68    fn test_entrypoint_shell() {
69        let rule = rule();
70        let mut state = RuleState::new();
71
72        let args = Arguments::Text("./entrypoint.sh".to_string());
73        let instr = Instruction::Entrypoint(args);
74        rule.check(&mut state, 1, &instr, None);
75        assert_eq!(state.failures.len(), 1);
76    }
77}