syncable_cli/analyzer/hadolint/rules/
dl3006.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 "DL3006",
15 Severity::Warning,
16 "Always tag the version of an image explicitly",
17 |state, line, instr, _shell| {
18 match instr {
19 Instruction::From(base) => {
20 if let Some(alias) = &base.alias {
22 state.data.insert_to_set("aliases", alias.as_str());
23 }
24
25 let image_name = &base.image.name;
27
28 if base.is_scratch() {
36 return;
37 }
38
39 if base.has_version() {
40 return;
41 }
42
43 if base.is_variable() {
44 return;
45 }
46
47 if state.data.set_contains("aliases", image_name) {
49 return;
50 }
51
52 state.add_failure(
54 "DL3006",
55 Severity::Warning,
56 "Always tag the version of an image explicitly",
57 line,
58 );
59 }
60 _ => {}
61 }
62 },
63 )
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69 use crate::analyzer::hadolint::parser::instruction::{BaseImage, ImageAlias};
70 use crate::analyzer::hadolint::rules::Rule;
71
72 #[test]
73 fn test_tagged_image() {
74 let rule = rule();
75 let mut state = RuleState::new();
76
77 let mut base = BaseImage::new("ubuntu");
78 base.tag = Some("20.04".to_string());
79 let instr = Instruction::From(base);
80
81 rule.check(&mut state, 1, &instr, None);
82 assert!(state.failures.is_empty());
83 }
84
85 #[test]
86 fn test_untagged_image() {
87 let rule = rule();
88 let mut state = RuleState::new();
89
90 let instr = Instruction::From(BaseImage::new("ubuntu"));
91 rule.check(&mut state, 1, &instr, None);
92 assert_eq!(state.failures.len(), 1);
93 assert_eq!(state.failures[0].code.as_str(), "DL3006");
94 }
95
96 #[test]
97 fn test_scratch_image() {
98 let rule = rule();
99 let mut state = RuleState::new();
100
101 let instr = Instruction::From(BaseImage::new("scratch"));
102 rule.check(&mut state, 1, &instr, None);
103 assert!(state.failures.is_empty());
104 }
105
106 #[test]
107 fn test_stage_reference() {
108 let rule = rule();
109 let mut state = RuleState::new();
110
111 let mut base1 = BaseImage::new("node");
113 base1.tag = Some("18".to_string());
114 base1.alias = Some(ImageAlias::new("builder"));
115 let instr1 = Instruction::From(base1);
116 rule.check(&mut state, 1, &instr1, None);
117
118 let instr2 = Instruction::From(BaseImage::new("builder"));
120 rule.check(&mut state, 10, &instr2, None);
121
122 assert!(state.failures.is_empty());
123 }
124
125 #[test]
126 fn test_variable_image() {
127 let rule = rule();
128 let mut state = RuleState::new();
129
130 let instr = Instruction::From(BaseImage::new("${BASE_IMAGE}"));
131 rule.check(&mut state, 1, &instr, None);
132 assert!(state.failures.is_empty());
133 }
134}