syncable_cli/analyzer/hadolint/rules/
dl4003.rs1use crate::analyzer::hadolint::parser::instruction::Instruction;
7use crate::analyzer::hadolint::rules::{custom_rule, CustomRule, RuleState};
8use crate::analyzer::hadolint::shell::ParsedShell;
9use crate::analyzer::hadolint::types::Severity;
10
11pub fn rule() -> CustomRule<impl Fn(&mut RuleState, u32, &Instruction, Option<&ParsedShell>) + Send + Sync> {
12 custom_rule(
13 "DL4003",
14 Severity::Warning,
15 "Multiple `CMD` instructions found. If you list more than one `CMD` then only the last `CMD` will take effect",
16 |state, line, instr, _shell| {
17 match instr {
18 Instruction::From(_) => {
19 state.data.set_int("cmd_count", 0);
21 }
22 Instruction::Cmd(_) => {
23 let count = state.data.get_int("cmd_count") + 1;
24 state.data.set_int("cmd_count", count);
25
26 if count > 1 {
27 state.add_failure(
28 "DL4003",
29 Severity::Warning,
30 "Multiple `CMD` instructions found. If you list more than one `CMD` then only the last `CMD` will take effect",
31 line,
32 );
33 }
34 }
35 _ => {}
36 }
37 },
38 )
39}
40
41#[cfg(test)]
42mod tests {
43 use super::*;
44 use crate::analyzer::hadolint::parser::instruction::{Arguments, BaseImage};
45 use crate::analyzer::hadolint::rules::Rule;
46
47 #[test]
48 fn test_single_cmd() {
49 let rule = rule();
50 let mut state = RuleState::new();
51
52 let from = Instruction::From(BaseImage::new("ubuntu"));
53 let cmd = Instruction::Cmd(Arguments::List(vec!["node".to_string()]));
54
55 rule.check(&mut state, 1, &from, None);
56 rule.check(&mut state, 2, &cmd, None);
57 assert!(state.failures.is_empty());
58 }
59
60 #[test]
61 fn test_multiple_cmds() {
62 let rule = rule();
63 let mut state = RuleState::new();
64
65 let from = Instruction::From(BaseImage::new("ubuntu"));
66 let cmd1 = Instruction::Cmd(Arguments::List(vec!["node".to_string()]));
67 let cmd2 = Instruction::Cmd(Arguments::List(vec!["npm".to_string()]));
68
69 rule.check(&mut state, 1, &from, None);
70 rule.check(&mut state, 2, &cmd1, None);
71 rule.check(&mut state, 3, &cmd2, None);
72 assert_eq!(state.failures.len(), 1);
73 assert_eq!(state.failures[0].code.as_str(), "DL4003");
74 }
75
76 #[test]
77 fn test_multiple_stages_ok() {
78 let rule = rule();
79 let mut state = RuleState::new();
80
81 let from1 = Instruction::From(BaseImage::new("node"));
82 let cmd1 = Instruction::Cmd(Arguments::List(vec!["npm".to_string()]));
83 let from2 = Instruction::From(BaseImage::new("alpine"));
84 let cmd2 = Instruction::Cmd(Arguments::List(vec!["node".to_string()]));
85
86 rule.check(&mut state, 1, &from1, None);
87 rule.check(&mut state, 2, &cmd1, None);
88 rule.check(&mut state, 3, &from2, None);
89 rule.check(&mut state, 4, &cmd2, None);
90 assert!(state.failures.is_empty());
91 }
92}