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