syncable_cli/analyzer/hadolint/rules/
dl3047.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 "DL3047",
14 Severity::Info,
15 "Avoid using both `wget` and `curl` since they serve the same purpose.",
16 |state, line, instr, shell| {
17 match instr {
18 Instruction::From(_) => {
19 state.data.set_bool("seen_wget", false);
21 state.data.set_bool("seen_curl", false);
22 state.data.set_bool("reported_dl3047", false);
23 }
24 Instruction::Run(_) => {
25 if let Some(shell) = shell {
26 let uses_wget = shell.using_program("wget");
27 let uses_curl = shell.using_program("curl");
28
29 if uses_wget {
30 state.data.set_bool("seen_wget", true);
31 }
32 if uses_curl {
33 state.data.set_bool("seen_curl", true);
34 }
35
36 let seen_both = state.data.get_bool("seen_wget") && state.data.get_bool("seen_curl");
38 let already_reported = state.data.get_bool("reported_dl3047");
39
40 if seen_both && !already_reported {
41 state.add_failure(
42 "DL3047",
43 Severity::Info,
44 "Avoid using both `wget` and `curl` since they serve the same purpose.",
45 line,
46 );
47 state.data.set_bool("reported_dl3047", true);
48 }
49 }
50 }
51 _ => {}
52 }
53 },
54 )
55}
56
57#[cfg(test)]
58mod tests {
59 use super::*;
60 use crate::analyzer::hadolint::lint::{lint, LintResult};
61 use crate::analyzer::hadolint::config::HadolintConfig;
62
63 fn lint_dockerfile(content: &str) -> LintResult {
64 lint(content, &HadolintConfig::default())
65 }
66
67 #[test]
68 fn test_wget_only() {
69 let result = lint_dockerfile("FROM ubuntu:20.04\nRUN wget https://example.com/file");
70 assert!(!result.failures.iter().any(|f| f.code.as_str() == "DL3047"));
71 }
72
73 #[test]
74 fn test_curl_only() {
75 let result = lint_dockerfile("FROM ubuntu:20.04\nRUN curl -O https://example.com/file");
76 assert!(!result.failures.iter().any(|f| f.code.as_str() == "DL3047"));
77 }
78
79 #[test]
80 fn test_both_wget_and_curl() {
81 let result = lint_dockerfile(
82 "FROM ubuntu:20.04\nRUN wget https://example.com/file1\nRUN curl -O https://example.com/file2"
83 );
84 assert!(result.failures.iter().any(|f| f.code.as_str() == "DL3047"));
85 }
86
87 #[test]
88 fn test_both_in_same_run() {
89 let result = lint_dockerfile(
90 "FROM ubuntu:20.04\nRUN wget https://a.com/f && curl -O https://b.com/g"
91 );
92 assert!(result.failures.iter().any(|f| f.code.as_str() == "DL3047"));
93 }
94
95 #[test]
96 fn test_different_stages() {
97 let result = lint_dockerfile(
99 "FROM ubuntu:20.04 AS stage1\nRUN wget https://a.com/f\nFROM ubuntu:20.04 AS stage2\nRUN curl https://b.com/g"
100 );
101 assert!(!result.failures.iter().any(|f| f.code.as_str() == "DL3047"));
102 }
103}