syncable_cli/analyzer/hadolint/rules/
dl3044.rs

1//! DL3044: Do not refer to an environment variable within the same ENV statement
2//!
3//! ENV variable references within the same statement may not work as expected.
4
5use crate::analyzer::hadolint::parser::instruction::Instruction;
6use crate::analyzer::hadolint::rules::{simple_rule, SimpleRule};
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        "DL3044",
13        Severity::Error,
14        "Do not refer to an environment variable within the same `ENV` statement where it is defined.",
15        |instr, _shell| {
16            match instr {
17                Instruction::Env(pairs) => {
18                    // Check if any value references a variable defined earlier in the same statement
19                    // For each pair, only check against variables defined BEFORE it
20                    let mut defined_vars: Vec<&str> = Vec::new();
21
22                    for (key, value) in pairs {
23                        for var in &defined_vars {
24                            // Check for $VAR or ${VAR} patterns
25                            if value.contains(&format!("${}", var))
26                                || value.contains(&format!("${{{}}}", var))
27                            {
28                                return false;
29                            }
30                        }
31                        // Add this key to defined vars for checking subsequent pairs
32                        defined_vars.push(key.as_str());
33                    }
34                    true
35                }
36                _ => true,
37            }
38        },
39    )
40}
41
42#[cfg(test)]
43mod tests {
44    use super::*;
45    use crate::analyzer::hadolint::lint::{lint, LintResult};
46    use crate::analyzer::hadolint::config::HadolintConfig;
47
48    fn lint_dockerfile(content: &str) -> LintResult {
49        lint(content, &HadolintConfig::default())
50    }
51
52    #[test]
53    fn test_self_reference() {
54        let result = lint_dockerfile("FROM ubuntu:20.04\nENV PATH=/app:$PATH");
55        // Note: PATH is not defined in this statement, so it's OK
56        // This rule checks for referencing a var defined IN THE SAME statement
57        assert!(!result.failures.iter().any(|f| f.code.as_str() == "DL3044"));
58    }
59
60    #[test]
61    fn test_same_statement_reference() {
62        let result = lint_dockerfile("FROM ubuntu:20.04\nENV FOO=bar BAR=$FOO");
63        assert!(result.failures.iter().any(|f| f.code.as_str() == "DL3044"));
64    }
65
66    #[test]
67    fn test_no_reference() {
68        let result = lint_dockerfile("FROM ubuntu:20.04\nENV FOO=bar BAR=baz");
69        assert!(!result.failures.iter().any(|f| f.code.as_str() == "DL3044"));
70    }
71}