1use derive_builder::Builder;
10use git_checks_core::impl_prelude::*;
11
12use crate::binary_format;
13
14#[derive(Builder, Debug, Default, Clone, Copy)]
21#[builder(field(private))]
22pub struct RejectBinaries {}
23
24impl RejectBinaries {
25 pub fn builder() -> RejectBinariesBuilder {
27 Default::default()
28 }
29}
30
31impl ContentCheck for RejectBinaries {
32 fn name(&self) -> &str {
33 "reject-binaries"
34 }
35
36 fn check(
37 &self,
38 ctx: &CheckGitContext,
39 content: &dyn Content,
40 ) -> Result<CheckResult, Box<dyn Error>> {
41 let mut result = CheckResult::new();
42
43 for diff in content.diffs() {
44 match diff.status {
45 StatusChange::Added | StatusChange::Modified(_) => (),
46 _ => continue,
47 }
48
49 let binary_attr = ctx.check_attr("hooks-allow-binary", diff.name.as_path())?;
50
51 let allowed_binary_type = match binary_attr {
52 AttributeState::Set => continue,
54 AttributeState::Value(ref v) => Some(v),
55 _ => None,
57 };
58
59 let binary_type = {
60 let cat_file = ctx
61 .git()
62 .arg("cat-file")
63 .arg("blob")
64 .arg(diff.new_blob.as_str())
65 .output()
66 .map_err(|err| GitError::subcommand("cat-file", err))?;
67
68 let stdout = cat_file.stdout;
69 binary_format::detect_binary_format(stdout)
70 };
71
72 if let Some(binary_type) = binary_type {
73 let type_name = binary_type.name();
74 if let Some(allowed_binary_type) = allowed_binary_type {
75 if allowed_binary_type != type_name {
76 result.add_error(format!(
77 "{}adds the {} (not {}) binary `{}`.",
78 commit_prefix(content),
79 type_name,
80 allowed_binary_type,
81 diff.name,
82 ));
83 }
84 } else {
85 result.add_error(format!(
86 "{}adds the {} binary `{}`.",
87 commit_prefix(content),
88 type_name,
89 diff.name,
90 ));
91 }
92 }
93 }
94
95 Ok(result)
96 }
97}
98
99#[cfg(feature = "config")]
100pub(crate) mod config {
101 use git_checks_config::{register_checks, CommitCheckConfig, IntoCheck, TopicCheckConfig};
102 use serde::Deserialize;
103 #[cfg(test)]
104 use serde_json::json;
105
106 use crate::RejectBinaries;
107
108 #[derive(Deserialize, Debug)]
115 pub struct RejectBinariesConfig {}
116
117 impl IntoCheck for RejectBinariesConfig {
118 type Check = RejectBinaries;
119
120 fn into_check(self) -> Self::Check {
121 Default::default()
122 }
123 }
124
125 register_checks! {
126 RejectBinariesConfig {
127 "reject_binaries" => CommitCheckConfig,
128 "reject_binaries/topic" => TopicCheckConfig,
129 },
130 }
131
132 #[test]
133 fn test_reject_binaries_config_empty() {
134 let json = json!({});
135 let check: RejectBinariesConfig = serde_json::from_value(json).unwrap();
136
137 let _ = check.into_check();
138 }
139}
140
141#[cfg(test)]
142mod tests {
143 use git_checks_core::{Check, TopicCheck};
144
145 use crate::test::*;
146 use crate::RejectBinaries;
147
148 const BAD_COMMIT: &str = "8ca74bdcc09a1a9e0b44c0b9e6cd8e6af9097c89";
149 const BAD_COMMIT_EXE: &str = "1de03da52b04d5394b75d222f246d3524572bd12";
150 const DELETE_COMMIT: &str = "141bc952382f9e9276e46a58dbb79aa8fe3ea435";
151 const ATTR_COMMIT: &str = "f27e6907d6c0d112e47f63b2b91178c1c23c9f4d";
152 const ATTR_COMMIT_BAD: &str = "2a9649acf5a4af7d1e8e393c11a7ab0c268a9b44";
153 const FIX_ATTR_COMMIT: &str = "1c53216257cb8af8fb0dc7b1577ef63605305318";
154
155 #[test]
156 fn test_reject_binaries_builder_default() {
157 assert!(RejectBinaries::builder().build().is_ok());
158 }
159
160 #[test]
161 fn test_reject_binaries_name_commit() {
162 let check = RejectBinaries::default();
163 assert_eq!(Check::name(&check), "reject-binaries");
164 }
165
166 #[test]
167 fn test_reject_binaries_name_topic() {
168 let check = RejectBinaries::default();
169 assert_eq!(TopicCheck::name(&check), "reject-binaries");
170 }
171
172 #[test]
173 fn test_reject_binaries() {
174 let check = RejectBinaries::default();
175 let result = run_check("test_reject_binaries", BAD_COMMIT, check);
176 test_result_errors(result, &[
177 "commit e5e1e2c8db62ac8f50f249d3cf3f334ddf158936 adds the ELF binary `elf-header`.",
178 "commit e5e1e2c8db62ac8f50f249d3cf3f334ddf158936 adds the Mach-O binary `macho-cigam-header`.",
179 "commit e5e1e2c8db62ac8f50f249d3cf3f334ddf158936 adds the Mach-O binary `macho-fat-cigam-header`.",
180 "commit e5e1e2c8db62ac8f50f249d3cf3f334ddf158936 adds the Mach-O binary `macho-fat-magic-header`.",
181 "commit e5e1e2c8db62ac8f50f249d3cf3f334ddf158936 adds the Mach-O binary `macho-magic-header`.",
182 "commit 8326e6bcc8cd6718e367d889bcb64739982b6c66 adds the AR binary `ar-header`.",
183 "commit 8ca74bdcc09a1a9e0b44c0b9e6cd8e6af9097c89 adds the PE binary `pe-le-header`.",
184 ]);
185 }
186
187 #[test]
188 fn test_reject_binaries_plus_x() {
189 let check = RejectBinaries::default();
190 let result = run_check("test_reject_binaries_plus_x", BAD_COMMIT_EXE, check);
191 test_result_errors(result, &[
192 "commit e5e1e2c8db62ac8f50f249d3cf3f334ddf158936 adds the ELF binary `elf-header`.",
193 "commit e5e1e2c8db62ac8f50f249d3cf3f334ddf158936 adds the Mach-O binary `macho-cigam-header`.",
194 "commit e5e1e2c8db62ac8f50f249d3cf3f334ddf158936 adds the Mach-O binary `macho-fat-cigam-header`.",
195 "commit e5e1e2c8db62ac8f50f249d3cf3f334ddf158936 adds the Mach-O binary `macho-fat-magic-header`.",
196 "commit e5e1e2c8db62ac8f50f249d3cf3f334ddf158936 adds the Mach-O binary `macho-magic-header`.",
197 "commit 261577469c6790190d866a928dc3bd8e91d238cf adds the ELF binary `elf-header`.",
198 "commit 261577469c6790190d866a928dc3bd8e91d238cf adds the Mach-O binary `macho-cigam-header`.",
199 "commit 261577469c6790190d866a928dc3bd8e91d238cf adds the Mach-O binary `macho-fat-cigam-header`.",
200 "commit 261577469c6790190d866a928dc3bd8e91d238cf adds the Mach-O binary `macho-fat-magic-header`.",
201 "commit 261577469c6790190d866a928dc3bd8e91d238cf adds the Mach-O binary `macho-magic-header`.",
202 "commit b8c710757905c59cf880ade2af288b6891b5723c adds the AR binary `ar-header`.",
203 "commit 1de03da52b04d5394b75d222f246d3524572bd12 adds the PE binary `pe-le-header`.",
204 ]);
205 }
206
207 #[test]
208 fn test_reject_binaries_delete_file() {
209 let check = RejectBinaries::default();
210 let conf = make_check_conf(&check);
211
212 let result = test_check_base(
213 "test_reject_binaries_delete_file",
214 DELETE_COMMIT,
215 BAD_COMMIT,
216 &conf,
217 );
218 test_result_ok(result);
219 }
220
221 #[test]
222 fn test_reject_binaries_delete_file_topic() {
223 let check = RejectBinaries::default();
224 run_topic_check_ok(
225 "test_reject_binaries_delete_file_topic",
226 DELETE_COMMIT,
227 check,
228 );
229 }
230
231 #[test]
232 fn test_reject_binaries_attr_ok() {
233 let check = RejectBinaries::default();
234 run_check_ok("test_reject_binaries_attr_ok", ATTR_COMMIT, check)
235 }
236
237 #[test]
238 fn test_reject_binaries_attr_bad() {
239 let check = RejectBinaries::default();
240 let result = run_check("test_reject_binaries_attr_bad", ATTR_COMMIT_BAD, check);
241 test_result_errors(result, &[
242 "commit 71fa463c4bedeb40807b6c73b08ce207b0fe0309 adds the ELF (not Mach-O) binary `elf-header`.",
243 "commit 71fa463c4bedeb40807b6c73b08ce207b0fe0309 adds the Mach-O (not ELF) binary `macho-cigam-header`.",
244 "commit 71fa463c4bedeb40807b6c73b08ce207b0fe0309 adds the Mach-O (not ELF) binary `macho-fat-cigam-header`.",
245 "commit 71fa463c4bedeb40807b6c73b08ce207b0fe0309 adds the Mach-O (not ELF) binary `macho-fat-magic-header`.",
246 "commit 71fa463c4bedeb40807b6c73b08ce207b0fe0309 adds the Mach-O (not ELF) binary `macho-magic-header`.",
247 "commit 107c49759fe7d603d83264cd2e013054f730f534 adds the AR (not ELF) binary `ar-header`.",
248 "commit 2a9649acf5a4af7d1e8e393c11a7ab0c268a9b44 adds the PE (not ELF) binary `pe-le-header`.",
249 ]);
250 }
251
252 #[test]
253 fn test_reject_binaries_topic() {
254 let check = RejectBinaries::default();
255 let result = run_topic_check("test_reject_binaries_topic", BAD_COMMIT, check);
256 test_result_errors(
257 result,
258 &[
259 "adds the AR binary `ar-header`.",
260 "adds the ELF binary `elf-header`.",
261 "adds the Mach-O binary `macho-cigam-header`.",
262 "adds the Mach-O binary `macho-fat-cigam-header`.",
263 "adds the Mach-O binary `macho-fat-magic-header`.",
264 "adds the Mach-O binary `macho-magic-header`.",
265 "adds the PE binary `pe-le-header`.",
266 ],
267 );
268 }
269
270 #[test]
271 fn test_reject_binaries_topic_attr_ok() {
272 let check = RejectBinaries::default();
273 run_topic_check_ok("test_reject_binaries_topic_attr_ok", ATTR_COMMIT, check)
274 }
275
276 #[test]
277 fn test_reject_binaries_topic_attr_bad() {
278 let check = RejectBinaries::default();
279 let result = run_topic_check(
280 "test_reject_binaries_topic_attr_bad",
281 ATTR_COMMIT_BAD,
282 check,
283 );
284 test_result_errors(
285 result,
286 &[
287 "adds the AR (not ELF) binary `ar-header`.",
288 "adds the ELF (not Mach-O) binary `elf-header`.",
289 "adds the Mach-O (not ELF) binary `macho-cigam-header`.",
290 "adds the Mach-O (not ELF) binary `macho-fat-cigam-header`.",
291 "adds the Mach-O (not ELF) binary `macho-fat-magic-header`.",
292 "adds the Mach-O (not ELF) binary `macho-magic-header`.",
293 "adds the PE (not ELF) binary `pe-le-header`.",
294 ],
295 );
296 }
297
298 #[test]
299 fn test_reject_binaries_topic_plus_x() {
300 let check = RejectBinaries::default();
301 let result = run_topic_check("test_reject_binaries_topic_plus_x", BAD_COMMIT_EXE, check);
302 test_result_errors(
303 result,
304 &[
305 "adds the AR binary `ar-header`.",
306 "adds the ELF binary `elf-header`.",
307 "adds the Mach-O binary `macho-cigam-header`.",
308 "adds the Mach-O binary `macho-fat-cigam-header`.",
309 "adds the Mach-O binary `macho-fat-magic-header`.",
310 "adds the Mach-O binary `macho-magic-header`.",
311 "adds the PE binary `pe-le-header`.",
312 ],
313 );
314 }
315
316 #[test]
317 fn test_reject_binaries_topic_fixed() {
318 let check = RejectBinaries::default();
319 run_topic_check_ok("test_reject_binaries_topic_fixed", DELETE_COMMIT, check);
320 }
321
322 #[test]
323 fn test_reject_binaries_topic_attr_fixed() {
324 let check = RejectBinaries::default();
325 run_topic_check_ok(
326 "test_reject_binaries_topic_attr_fixed",
327 FIX_ATTR_COMMIT,
328 check,
329 );
330 }
331}