syncable_cli/analyzer/hadolint/rules/
dl4001.rs1use crate::analyzer::hadolint::parser::instruction::Instruction;
6use crate::analyzer::hadolint::rules::{CheckFailure, RuleState, VeryCustomRule, very_custom_rule};
7use crate::analyzer::hadolint::shell::ParsedShell;
8use crate::analyzer::hadolint::types::Severity;
9
10pub fn rule() -> VeryCustomRule<
11 impl Fn(&mut RuleState, u32, &Instruction, Option<&ParsedShell>) + Send + Sync,
12 impl Fn(RuleState) -> Vec<CheckFailure> + Send + Sync,
13> {
14 very_custom_rule(
15 "DL4001",
16 Severity::Warning,
17 "Either use `wget` or `curl`, but not both.",
18 |state, line, instr, shell| {
19 if let Instruction::Run(_) = instr
20 && let Some(shell) = shell
21 {
22 if shell.any_command(|cmd| cmd.name == "wget") {
23 let existing = state
25 .data
26 .get_string("wget_lines")
27 .unwrap_or("")
28 .to_string();
29 let new = if existing.is_empty() {
30 line.to_string()
31 } else {
32 format!("{},{}", existing, line)
33 };
34 state.data.set_string("wget_lines", new);
35 }
36 if shell.any_command(|cmd| cmd.name == "curl") {
37 let existing = state
38 .data
39 .get_string("curl_lines")
40 .unwrap_or("")
41 .to_string();
42 let new = if existing.is_empty() {
43 line.to_string()
44 } else {
45 format!("{},{}", existing, line)
46 };
47 state.data.set_string("curl_lines", new);
48 }
49 }
50 },
51 |state| {
52 let wget_lines = state.data.get_string("wget_lines").unwrap_or("");
53 let curl_lines = state.data.get_string("curl_lines").unwrap_or("");
54
55 if !wget_lines.is_empty() && !curl_lines.is_empty() {
57 let mut failures = state.failures;
58 for line in wget_lines.split(',').filter_map(|s| s.parse::<u32>().ok()) {
59 failures.push(CheckFailure::new(
60 "DL4001",
61 Severity::Warning,
62 "Either use `wget` or `curl`, but not both.",
63 line,
64 ));
65 }
66 for line in curl_lines.split(',').filter_map(|s| s.parse::<u32>().ok()) {
67 failures.push(CheckFailure::new(
68 "DL4001",
69 Severity::Warning,
70 "Either use `wget` or `curl`, but not both.",
71 line,
72 ));
73 }
74 failures
75 } else {
76 state.failures
77 }
78 },
79 )
80}
81
82#[cfg(test)]
83mod tests {
84 use super::*;
85 use crate::analyzer::hadolint::config::HadolintConfig;
86 use crate::analyzer::hadolint::lint::{LintResult, lint};
87
88 fn lint_dockerfile(content: &str) -> LintResult {
89 lint(content, &HadolintConfig::default())
90 }
91
92 #[test]
93 fn test_only_wget() {
94 let result = lint_dockerfile("FROM ubuntu:20.04\nRUN wget http://example.com/file");
95 assert!(!result.failures.iter().any(|f| f.code.as_str() == "DL4001"));
96 }
97
98 #[test]
99 fn test_only_curl() {
100 let result = lint_dockerfile("FROM ubuntu:20.04\nRUN curl http://example.com/file");
101 assert!(!result.failures.iter().any(|f| f.code.as_str() == "DL4001"));
102 }
103
104 #[test]
105 fn test_both_wget_and_curl() {
106 let result = lint_dockerfile(
107 "FROM ubuntu:20.04\nRUN wget http://example.com/file\nRUN curl http://example.com/other",
108 );
109 assert!(result.failures.iter().any(|f| f.code.as_str() == "DL4001"));
110 }
111}