syncable_cli/analyzer/hadolint/rules/
dl3042.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 "DL3042",
13 Severity::Warning,
14 "Avoid use of cache directory with pip. Use `pip install --no-cache-dir <package>`.",
15 |instr, shell| {
16 match instr {
17 Instruction::Run(_) => {
18 if let Some(shell) = shell {
19 !shell.any_command(|cmd| {
20 if shell.is_pip_install(cmd) {
21 !cmd.has_any_flag(&["no-cache-dir"])
23 } else {
24 false
25 }
26 })
27 } else {
28 true
29 }
30 }
31 _ => true,
32 }
33 },
34 )
35}
36
37#[cfg(test)]
38mod tests {
39 use super::*;
40 use crate::analyzer::hadolint::config::HadolintConfig;
41 use crate::analyzer::hadolint::lint::{LintResult, lint};
42
43 fn lint_dockerfile(content: &str) -> LintResult {
44 lint(content, &HadolintConfig::default())
45 }
46
47 #[test]
48 fn test_pip_install_without_no_cache() {
49 let result = lint_dockerfile("FROM python:3.11\nRUN pip install flask");
50 assert!(result.failures.iter().any(|f| f.code.as_str() == "DL3042"));
51 }
52
53 #[test]
54 fn test_pip_install_with_no_cache() {
55 let result = lint_dockerfile("FROM python:3.11\nRUN pip install --no-cache-dir flask");
56 assert!(!result.failures.iter().any(|f| f.code.as_str() == "DL3042"));
57 }
58
59 #[test]
60 fn test_pip3_install_without_no_cache() {
61 let result = lint_dockerfile("FROM python:3.11\nRUN pip3 install flask");
62 assert!(result.failures.iter().any(|f| f.code.as_str() == "DL3042"));
63 }
64
65 #[test]
66 fn test_pip3_install_with_no_cache() {
67 let result = lint_dockerfile("FROM python:3.11\nRUN pip3 install --no-cache-dir flask");
68 assert!(!result.failures.iter().any(|f| f.code.as_str() == "DL3042"));
69 }
70
71 #[test]
72 fn test_python_m_pip_without_no_cache() {
73 let result = lint_dockerfile("FROM python:3.11\nRUN python -m pip install flask");
74 assert!(result.failures.iter().any(|f| f.code.as_str() == "DL3042"));
75 }
76
77 #[test]
78 fn test_pip_freeze() {
79 let result = lint_dockerfile("FROM python:3.11\nRUN pip freeze > requirements.txt");
81 assert!(!result.failures.iter().any(|f| f.code.as_str() == "DL3042"));
82 }
83}