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