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 {type_name} (not {allowed_binary_type}) binary `{}`.",
78 commit_prefix(content),
79 diff.name,
80 ));
81 }
82 } else {
83 result.add_error(format!(
84 "{}adds the {type_name} binary `{}`.",
85 commit_prefix(content),
86 diff.name,
87 ));
88 }
89 }
90 }
91
92 Ok(result)
93 }
94}
95
96#[cfg(feature = "config")]
97pub(crate) mod config {
98 use git_checks_config::{register_checks, CommitCheckConfig, IntoCheck, TopicCheckConfig};
99 use serde::Deserialize;
100 #[cfg(test)]
101 use serde_json::json;
102
103 use crate::RejectBinaries;
104
105 #[derive(Deserialize, Debug)]
112 pub struct RejectBinariesConfig {}
113
114 impl IntoCheck for RejectBinariesConfig {
115 type Check = RejectBinaries;
116
117 fn into_check(self) -> Self::Check {
118 Default::default()
119 }
120 }
121
122 register_checks! {
123 RejectBinariesConfig {
124 "reject_binaries" => CommitCheckConfig,
125 "reject_binaries/topic" => TopicCheckConfig,
126 },
127 }
128
129 #[test]
130 fn test_reject_binaries_config_empty() {
131 let json = json!({});
132 let check: RejectBinariesConfig = serde_json::from_value(json).unwrap();
133
134 let _ = check.into_check();
135 }
136}
137
138#[cfg(test)]
139mod tests {
140 use git_checks_core::{Check, TopicCheck};
141
142 use crate::test::*;
143 use crate::RejectBinaries;
144
145 const BAD_COMMIT: &str = "8ca74bdcc09a1a9e0b44c0b9e6cd8e6af9097c89";
146 const BAD_COMMIT_EXE: &str = "1de03da52b04d5394b75d222f246d3524572bd12";
147 const DELETE_COMMIT: &str = "141bc952382f9e9276e46a58dbb79aa8fe3ea435";
148 const ATTR_COMMIT: &str = "f27e6907d6c0d112e47f63b2b91178c1c23c9f4d";
149 const ATTR_COMMIT_BAD: &str = "2a9649acf5a4af7d1e8e393c11a7ab0c268a9b44";
150 const FIX_ATTR_COMMIT: &str = "1c53216257cb8af8fb0dc7b1577ef63605305318";
151
152 #[test]
153 fn test_reject_binaries_builder_default() {
154 assert!(RejectBinaries::builder().build().is_ok());
155 }
156
157 #[test]
158 fn test_reject_binaries_name_commit() {
159 let check = RejectBinaries::default();
160 assert_eq!(Check::name(&check), "reject-binaries");
161 }
162
163 #[test]
164 fn test_reject_binaries_name_topic() {
165 let check = RejectBinaries::default();
166 assert_eq!(TopicCheck::name(&check), "reject-binaries");
167 }
168
169 #[test]
170 fn test_reject_binaries() {
171 let check = RejectBinaries::default();
172 let result = run_check("test_reject_binaries", BAD_COMMIT, check);
173 test_result_errors(result, &[
174 "commit e5e1e2c8db62ac8f50f249d3cf3f334ddf158936 adds the ELF binary `elf-header`.",
175 "commit e5e1e2c8db62ac8f50f249d3cf3f334ddf158936 adds the Mach-O binary `macho-cigam-header`.",
176 "commit e5e1e2c8db62ac8f50f249d3cf3f334ddf158936 adds the Mach-O binary `macho-fat-cigam-header`.",
177 "commit e5e1e2c8db62ac8f50f249d3cf3f334ddf158936 adds the Mach-O binary `macho-fat-magic-header`.",
178 "commit e5e1e2c8db62ac8f50f249d3cf3f334ddf158936 adds the Mach-O binary `macho-magic-header`.",
179 "commit 8326e6bcc8cd6718e367d889bcb64739982b6c66 adds the AR binary `ar-header`.",
180 "commit 8ca74bdcc09a1a9e0b44c0b9e6cd8e6af9097c89 adds the PE binary `pe-le-header`.",
181 ]);
182 }
183
184 #[test]
185 fn test_reject_binaries_plus_x() {
186 let check = RejectBinaries::default();
187 let result = run_check("test_reject_binaries_plus_x", BAD_COMMIT_EXE, check);
188 test_result_errors(result, &[
189 "commit e5e1e2c8db62ac8f50f249d3cf3f334ddf158936 adds the ELF binary `elf-header`.",
190 "commit e5e1e2c8db62ac8f50f249d3cf3f334ddf158936 adds the Mach-O binary `macho-cigam-header`.",
191 "commit e5e1e2c8db62ac8f50f249d3cf3f334ddf158936 adds the Mach-O binary `macho-fat-cigam-header`.",
192 "commit e5e1e2c8db62ac8f50f249d3cf3f334ddf158936 adds the Mach-O binary `macho-fat-magic-header`.",
193 "commit e5e1e2c8db62ac8f50f249d3cf3f334ddf158936 adds the Mach-O binary `macho-magic-header`.",
194 "commit 261577469c6790190d866a928dc3bd8e91d238cf adds the ELF binary `elf-header`.",
195 "commit 261577469c6790190d866a928dc3bd8e91d238cf adds the Mach-O binary `macho-cigam-header`.",
196 "commit 261577469c6790190d866a928dc3bd8e91d238cf adds the Mach-O binary `macho-fat-cigam-header`.",
197 "commit 261577469c6790190d866a928dc3bd8e91d238cf adds the Mach-O binary `macho-fat-magic-header`.",
198 "commit 261577469c6790190d866a928dc3bd8e91d238cf adds the Mach-O binary `macho-magic-header`.",
199 "commit b8c710757905c59cf880ade2af288b6891b5723c adds the AR binary `ar-header`.",
200 "commit 1de03da52b04d5394b75d222f246d3524572bd12 adds the PE binary `pe-le-header`.",
201 ]);
202 }
203
204 #[test]
205 fn test_reject_binaries_delete_file() {
206 let check = RejectBinaries::default();
207 let conf = make_check_conf(&check);
208
209 let result = test_check_base(
210 "test_reject_binaries_delete_file",
211 DELETE_COMMIT,
212 BAD_COMMIT,
213 &conf,
214 );
215 test_result_ok(result);
216 }
217
218 #[test]
219 fn test_reject_binaries_delete_file_topic() {
220 let check = RejectBinaries::default();
221 run_topic_check_ok(
222 "test_reject_binaries_delete_file_topic",
223 DELETE_COMMIT,
224 check,
225 );
226 }
227
228 #[test]
229 fn test_reject_binaries_attr_ok() {
230 let check = RejectBinaries::default();
231 run_check_ok("test_reject_binaries_attr_ok", ATTR_COMMIT, check)
232 }
233
234 #[test]
235 fn test_reject_binaries_attr_bad() {
236 let check = RejectBinaries::default();
237 let result = run_check("test_reject_binaries_attr_bad", ATTR_COMMIT_BAD, check);
238 test_result_errors(result, &[
239 "commit 71fa463c4bedeb40807b6c73b08ce207b0fe0309 adds the ELF (not Mach-O) binary `elf-header`.",
240 "commit 71fa463c4bedeb40807b6c73b08ce207b0fe0309 adds the Mach-O (not ELF) binary `macho-cigam-header`.",
241 "commit 71fa463c4bedeb40807b6c73b08ce207b0fe0309 adds the Mach-O (not ELF) binary `macho-fat-cigam-header`.",
242 "commit 71fa463c4bedeb40807b6c73b08ce207b0fe0309 adds the Mach-O (not ELF) binary `macho-fat-magic-header`.",
243 "commit 71fa463c4bedeb40807b6c73b08ce207b0fe0309 adds the Mach-O (not ELF) binary `macho-magic-header`.",
244 "commit 107c49759fe7d603d83264cd2e013054f730f534 adds the AR (not ELF) binary `ar-header`.",
245 "commit 2a9649acf5a4af7d1e8e393c11a7ab0c268a9b44 adds the PE (not ELF) binary `pe-le-header`.",
246 ]);
247 }
248
249 #[test]
250 fn test_reject_binaries_topic() {
251 let check = RejectBinaries::default();
252 let result = run_topic_check("test_reject_binaries_topic", BAD_COMMIT, check);
253 test_result_errors(
254 result,
255 &[
256 "adds the AR binary `ar-header`.",
257 "adds the ELF binary `elf-header`.",
258 "adds the Mach-O binary `macho-cigam-header`.",
259 "adds the Mach-O binary `macho-fat-cigam-header`.",
260 "adds the Mach-O binary `macho-fat-magic-header`.",
261 "adds the Mach-O binary `macho-magic-header`.",
262 "adds the PE binary `pe-le-header`.",
263 ],
264 );
265 }
266
267 #[test]
268 fn test_reject_binaries_topic_attr_ok() {
269 let check = RejectBinaries::default();
270 run_topic_check_ok("test_reject_binaries_topic_attr_ok", ATTR_COMMIT, check)
271 }
272
273 #[test]
274 fn test_reject_binaries_topic_attr_bad() {
275 let check = RejectBinaries::default();
276 let result = run_topic_check(
277 "test_reject_binaries_topic_attr_bad",
278 ATTR_COMMIT_BAD,
279 check,
280 );
281 test_result_errors(
282 result,
283 &[
284 "adds the AR (not ELF) binary `ar-header`.",
285 "adds the ELF (not Mach-O) binary `elf-header`.",
286 "adds the Mach-O (not ELF) binary `macho-cigam-header`.",
287 "adds the Mach-O (not ELF) binary `macho-fat-cigam-header`.",
288 "adds the Mach-O (not ELF) binary `macho-fat-magic-header`.",
289 "adds the Mach-O (not ELF) binary `macho-magic-header`.",
290 "adds the PE (not ELF) binary `pe-le-header`.",
291 ],
292 );
293 }
294
295 #[test]
296 fn test_reject_binaries_topic_plus_x() {
297 let check = RejectBinaries::default();
298 let result = run_topic_check("test_reject_binaries_topic_plus_x", BAD_COMMIT_EXE, check);
299 test_result_errors(
300 result,
301 &[
302 "adds the AR binary `ar-header`.",
303 "adds the ELF binary `elf-header`.",
304 "adds the Mach-O binary `macho-cigam-header`.",
305 "adds the Mach-O binary `macho-fat-cigam-header`.",
306 "adds the Mach-O binary `macho-fat-magic-header`.",
307 "adds the Mach-O binary `macho-magic-header`.",
308 "adds the PE binary `pe-le-header`.",
309 ],
310 );
311 }
312
313 #[test]
314 fn test_reject_binaries_topic_fixed() {
315 let check = RejectBinaries::default();
316 run_topic_check_ok("test_reject_binaries_topic_fixed", DELETE_COMMIT, check);
317 }
318
319 #[test]
320 fn test_reject_binaries_topic_attr_fixed() {
321 let check = RejectBinaries::default();
322 run_topic_check_ok(
323 "test_reject_binaries_topic_attr_fixed",
324 FIX_ATTR_COMMIT,
325 check,
326 );
327 }
328}