shadowplay/puppet_pp_lint/
lint_argument.rs1use crate::puppet_parser::range::Range;
2use serde::{Deserialize, Serialize};
3
4use crate::puppet_pp_lint::lint::{EarlyLintPass, LintError, LintPass};
5
6#[derive(Clone, Serialize, Deserialize)]
7pub struct ArgumentLooksSensitive {
8 #[serde(with = "serde_regex")]
9 regex: regex::Regex,
10}
11
12impl Default for ArgumentLooksSensitive {
13 fn default() -> Self {
14 let regex = regex::Regex::new("(:?passw|secret$|token$)").unwrap();
15 Self { regex }
16 }
17}
18
19impl LintPass for ArgumentLooksSensitive {
20 fn name(&self) -> &str {
21 "ArgumentLooksSensitive"
22 }
23
24 fn description(&self) -> &str {
25 "Warns if argument name looks like sensitive, but argument is not typed with type Sensitive"
26 }
27}
28
29impl EarlyLintPass for ArgumentLooksSensitive {
30 fn check_argument(
31 &self,
32 arg: &crate::puppet_lang::argument::Argument<Range>,
33 ) -> Vec<super::lint::LintError> {
34 let lc_name = arg.name.to_lowercase();
35 if self.regex.is_match(&lc_name) {
36 match &arg.type_spec {
37 None => vec![LintError::new(
38 Box::new(self.clone()),
39 &format!("Assuming argument {:?} contains a secret value, it is not typed with 'Sensitive'", arg.name),
40 &arg.extra,
41 )],
42 Some(t)
43 if !matches!(
44 t.data,
45 crate::puppet_lang::typing::TypeSpecificationVariant::Sensitive(_)
46 ) =>
47 {
48 vec![LintError::new(
49 Box::new(self.clone()),
50 &format!("Assuming argument {:?} contains a secret value, it is not typed with 'Sensitive' type", arg.name),
51 &arg.extra,
52 )]
53 }
54 Some(_) => vec![],
55 }
56 } else {
57 vec![]
58 }
59 }
60}
61
62#[derive(Clone, Serialize, Deserialize)]
63pub struct SensitiveArgumentWithDefault;
64
65impl LintPass for SensitiveArgumentWithDefault {
66 fn name(&self) -> &str {
67 "SensitiveArgumentWithDefault"
68 }
69 fn description(&self) -> &str {
70 "Warns if argument typed with Sensitive contains default value"
71 }
72}
73
74impl EarlyLintPass for SensitiveArgumentWithDefault {
75 fn check_argument(
76 &self,
77 arg: &crate::puppet_lang::argument::Argument<Range>,
78 ) -> Vec<super::lint::LintError> {
79 if let Some(t) = &arg.type_spec {
80 if matches!(
81 t.data,
82 crate::puppet_lang::typing::TypeSpecificationVariant::Sensitive(_)
83 ) && arg.default.is_some()
84 {
85 return vec![LintError::new(
86 Box::new(self.clone()),
87 "Sensitive argument with default value",
88 &arg.extra,
89 )];
90 }
91 }
92 vec![]
93 }
94}
95
96#[derive(Clone, Serialize, Deserialize)]
97pub struct ArgumentTyped;
98
99impl LintPass for ArgumentTyped {
100 fn name(&self) -> &str {
101 "ArgumentTyped"
102 }
103
104 fn description(&self) -> &str {
105 "Warns if argument is not typed"
106 }
107}
108
109impl EarlyLintPass for ArgumentTyped {
110 fn check_argument(
111 &self,
112 arg: &crate::puppet_lang::argument::Argument<Range>,
113 ) -> Vec<super::lint::LintError> {
114 if arg.type_spec.is_none() {
115 return vec![LintError::new(
116 Box::new(self.clone()),
117 "Argument is not typed",
118 &arg.extra,
119 )];
120 }
121 vec![]
122 }
123}
124
125#[derive(Clone, Serialize, Deserialize)]
126pub struct ReadableArgumentsName {
127 #[serde(with = "serde_regex")]
128 regex: regex::Regex,
129}
130
131impl Default for ReadableArgumentsName {
132 fn default() -> Self {
133 let regex = regex::Regex::new("^.$").unwrap();
134 Self { regex }
135 }
136}
137
138impl LintPass for ReadableArgumentsName {
139 fn name(&self) -> &str {
140 "ReadableArgumentsName"
141 }
142
143 fn description(&self) -> &str {
144 "Warns if argument name is not readable enough"
145 }
146}
147
148impl EarlyLintPass for ReadableArgumentsName {
149 fn check_argument(
150 &self,
151 arg: &crate::puppet_lang::argument::Argument<Range>,
152 ) -> Vec<super::lint::LintError> {
153 if self.regex.is_match(&arg.name) {
154 return vec![LintError::new(
155 Box::new(self.clone()),
156 &format!("Argument '{}' name is too short", arg.name),
157 &arg.extra,
158 )];
159 }
160 vec![]
161 }
162}
163
164#[derive(Clone, Serialize, Deserialize)]
165pub struct LowerCaseArgumentName;
166
167impl LintPass for LowerCaseArgumentName {
168 fn name(&self) -> &str {
169 "LowerCaseArgumentName"
170 }
171
172 fn description(&self) -> &str {
173 "Warns if argument name is not lowercase, as suggested by Puppet's style guide"
174 }
175}
176
177impl EarlyLintPass for LowerCaseArgumentName {
178 fn check_argument(
179 &self,
180 arg: &crate::puppet_lang::argument::Argument<Range>,
181 ) -> Vec<super::lint::LintError> {
182 if arg.name.chars().any(|c| c.is_uppercase()) {
183 return vec![LintError::new_with_url(
184 Box::new(self.clone()),
185 "Argument name with upper case letters.",
186 "https://puppet.com/docs/puppet/7/style_guide.html#style_guide_variables-variable-format",
187 &arg.extra,
188 )];
189 }
190 vec![]
191 }
192}