syncable_cli/analyzer/hadolint/rules/
dl3061.rs1use crate::analyzer::hadolint::parser::instruction::Instruction;
6use crate::analyzer::hadolint::rules::{SimpleRule, simple_rule};
7use crate::analyzer::hadolint::shell::ParsedShell;
8use crate::analyzer::hadolint::types::Severity;
9
10pub fn rule() -> SimpleRule<impl Fn(&Instruction, Option<&ParsedShell>) -> bool + Send + Sync> {
11 simple_rule(
12 "DL3061",
13 Severity::Error,
14 "Invalid image name in `FROM`.",
15 |instr, _shell| match instr {
16 Instruction::From(base_image) => is_valid_image_name(&base_image.image.name),
17 _ => true,
18 },
19 )
20}
21
22fn is_valid_image_name(name: &str) -> bool {
23 if name.is_empty() {
24 return false;
25 }
26
27 if name == "scratch" {
29 return true;
30 }
31
32 if name.starts_with('$') {
34 return true;
35 }
36
37 let valid_chars = |c: char| {
44 c.is_ascii_lowercase()
45 || c.is_ascii_digit()
46 || c == '-'
47 || c == '_'
48 || c == '.'
49 || c == '/'
50 || c == ':'
51 };
52
53 name.chars().all(valid_chars)
54}
55
56#[cfg(test)]
57mod tests {
58 use super::*;
59 use crate::analyzer::hadolint::config::HadolintConfig;
60 use crate::analyzer::hadolint::lint::{LintResult, lint};
61
62 fn lint_dockerfile(content: &str) -> LintResult {
63 lint(content, &HadolintConfig::default())
64 }
65
66 #[test]
67 fn test_valid_image() {
68 let result = lint_dockerfile("FROM ubuntu:20.04");
69 assert!(!result.failures.iter().any(|f| f.code.as_str() == "DL3061"));
70 }
71
72 #[test]
73 fn test_valid_registry_image() {
74 let result = lint_dockerfile("FROM registry.example.com/myimage:latest");
75 assert!(!result.failures.iter().any(|f| f.code.as_str() == "DL3061"));
76 }
77
78 #[test]
79 fn test_scratch() {
80 let result = lint_dockerfile("FROM scratch");
81 assert!(!result.failures.iter().any(|f| f.code.as_str() == "DL3061"));
82 }
83
84 #[test]
85 fn test_variable_image() {
86 let result = lint_dockerfile("ARG BASE=ubuntu\nFROM $BASE");
87 assert!(!result.failures.iter().any(|f| f.code.as_str() == "DL3061"));
88 }
89}