1use derive_builder::Builder;
10use git_checks_core::impl_prelude::*;
11use lazy_static::lazy_static;
12use regex::Regex;
13
14#[derive(Builder, Debug, Default, Clone, Copy)]
18#[builder(field(private))]
19pub struct LfsPointer {}
20
21impl LfsPointer {
22 pub fn builder() -> LfsPointerBuilder {
24 Default::default()
25 }
26
27 fn check_line(
28 prev_key: Option<String>,
29 key: &str,
30 value: &str,
31 content: &dyn Content,
32 name: &str,
33 ) -> CheckResult {
34 let mut result = CheckResult::new();
35
36 if let Some(old_key) = prev_key {
37 if old_key == "version" {
38 } else if key < old_key.as_str() {
40 result.add_error(format!(
41 "{}unsorted key `{key}` found in LFS pointer `{name}`.",
42 commit_prefix_str(content, "not allowed;"),
43 ));
44 }
45 } else if key == "version" {
46 if value != GIT_LFS_SPEC_URL {
47 result.add_warning(format!(
48 "{}unexpected git-lfs version string in `{name}`: `{value}`.",
49 commit_prefix_str(content, "contains an"),
50 ));
51 }
52 } else {
53 result.add_error(format!(
54 "{}the first key in LFS pointer `{name}` must be `version`; found `{key}`.",
55 commit_prefix_str(content, "not allowed;"),
56 ));
57 }
58
59 match key {
60 "oid" => {
61 let mut split = value.splitn(2, ':');
62 let algo = split.next();
63 let digest = split.next();
64
65 const fn ascii_hex_len(bits: usize) -> usize {
66 bits >> 2
68 }
69
70 fn is_ascii_hex(digest: &str) -> bool {
71 digest
72 .bytes()
73 .all(|b| b.is_ascii_hexdigit() && !b.is_ascii_uppercase())
74 }
75
76 let mut check_algo_digest = |algo, digest: &str, bits| {
77 if digest.is_empty() {
78 result.add_error(format!(
79 "{}empty digest for {algo} algorithm in LFS pointer `{name}`.",
80 commit_prefix_str(content, "not allowed;"),
81 ));
82 } else if digest.len() != ascii_hex_len(bits) {
83 result.add_error(format!(
84 "{}invalid digest length in `{digest}` for {algo} algorithm in LFS pointer `{name}`.",
85 commit_prefix_str(content, "not allowed;"),
86 ));
87 } else if !is_ascii_hex(digest) {
88 result.add_error(format!(
89 "{}invalid digest content `{digest}` for {algo} algorithm in LFS pointer `{name}`.",
90 commit_prefix_str(content, "not allowed;"),
91 ));
92 }
93 };
94
95 match algo {
96 Some("") | None => {
97 result.add_error(format!(
98 "{}missing hash algorithm in LFS pointer `{name}`.",
99 commit_prefix_str(content, "not allowed;"),
100 ));
101 },
102 Some(algo) => {
103 if let Some(digest) = digest {
104 match algo {
105 algo @ "sha256" => check_algo_digest(algo, digest, 256),
106 algo => {
107 result.add_warning(format!(
108 "{}unrecognized hash algorithm `{algo}` in LFS pointer `{name}`.",
109 commit_prefix_str(content, "contains an"),
110 ));
111
112 if digest.is_empty() {
113 result.add_error(format!(
114 "{}empty digest for {algo} algorithm in LFS pointer `{name}`.",
115 commit_prefix_str(content, "not allowed;"),
116 ));
117 }
118 },
119 }
120 } else {
121 result.add_error(format!(
122 "{}missing digest for {algo} algorithm in LFS pointer `{name}`.",
123 commit_prefix_str(content, "not allowed;"),
124 ));
125 }
126 },
127 }
128 },
129 "size" => {
130 if let Ok(value) = value.parse::<u64>() {
131 if value == 0 {
132 result.add_error(format!(
133 "{}the `size` value in LFS pointer `{name}` must be greater than 0; \
134 found `{value}`.",
135 commit_prefix_str(content, "not allowed;"),
136 ));
137 }
138 } else {
139 result.add_error(format!(
140 "{}the `size` key value in LFS pointer `{name}` must be an unsigned \
141 integer; found `{value}`.",
142 commit_prefix_str(content, "not allowed;"),
143 ));
144 }
145 },
146 key => {
147 fn is_valid_key(key: &str) -> bool {
148 key.bytes().all(|b| {
149 b.is_ascii_lowercase() || b.is_ascii_digit() || b == b'.' || b == b'-'
150 })
151 }
152
153 if !is_valid_key(key) {
154 result.add_error(format!(
157 "{}the key `{key}` is not valid in LFS pointer `{name}`",
158 commit_prefix_str(content, "not allowed;"),
159 ));
160 }
161 },
162 }
163
164 result
165 }
166}
167
168const GIT_LFS_SPEC_URL: &str = "https://git-lfs.github.com/spec/v1";
169
170lazy_static! {
171 static ref LFS_LINE_RE: Regex = Regex::new(
172 "^(?P<key>[a-z0-9.-]*) \
173 (?P<value>[^\r\n]*)$",
174 )
175 .unwrap();
176}
177
178impl ContentCheck for LfsPointer {
179 fn name(&self) -> &str {
180 "lfs-pointer"
181 }
182
183 fn check(
184 &self,
185 ctx: &CheckGitContext,
186 content: &dyn Content,
187 ) -> Result<CheckResult, Box<dyn Error>> {
188 let mut result = CheckResult::new();
189
190 for diff in content.diffs() {
191 match diff.status {
192 StatusChange::Added | StatusChange::Modified(_) => (),
193 _ => continue,
194 }
195
196 let filter_attr = ctx.check_attr("filter", diff.name.as_path())?;
197 if let AttributeState::Value(filter_name) = filter_attr {
198 if filter_name != "lfs" {
199 continue;
200 }
201 } else {
202 continue;
203 }
204
205 let cat_file = ctx
206 .git()
207 .arg("cat-file")
208 .arg("blob")
209 .arg(diff.new_blob.as_str())
210 .output()
211 .map_err(|err| GitError::subcommand("cat-file", err))?;
212 let lfs_pointer = if let Ok(lfs_pointer) = String::from_utf8(cat_file.stdout) {
213 lfs_pointer
214 } else {
215 result.add_error(format!(
216 "{}invalid utf-8 sequence in an LFS pointer added in `{}`.",
217 commit_prefix_str(content, "not allowed;"),
218 diff.name,
219 ));
220 continue;
221 };
222
223 if lfs_pointer.is_empty() {
225 continue;
226 }
227
228 let (lfs_res, _, has_oid, has_size) = lfs_pointer.lines().enumerate().fold(
229 (CheckResult::new(), None, false, false),
230 |data, (count, line)| {
231 let (mut lfs_res, prev_key, has_oid, has_size) = data;
232
233 if let Some(lfs_line) = LFS_LINE_RE.captures(line) {
234 let key = lfs_line
235 .name("key")
236 .expect("the LFS regex should have a 'key' group");
237 let value = lfs_line
238 .name("value")
239 .expect("the LFS regex should have a 'value' group");
240
241 if Some(key.as_str()) == prev_key.as_deref() {
242 lfs_res.add_error(format!(
243 "{}duplicate key `{}` in an LFS pointer added in `{}`.",
244 commit_prefix_str(content, "not allowed;"),
245 key.as_str(),
246 diff.name,
247 ));
248 }
249
250 let line_res = Self::check_line(
251 prev_key,
252 key.as_str(),
253 value.as_str(),
254 content,
255 diff.name.as_str(),
256 );
257
258 let new_has_oid = has_oid || key.as_str() == "oid";
259 let new_has_size = has_size || key.as_str() == "size";
260
261 (
262 lfs_res.combine(line_res),
263 Some(key.as_str().into()),
264 new_has_oid,
265 new_has_size,
266 )
267 } else {
268 lfs_res.add_error(format!(
269 "{}invalid line in an LFS pointer added in `{}` on line {}.",
270 commit_prefix_str(content, "not allowed;"),
271 diff.name,
272 count + 1,
273 ));
274
275 (lfs_res, prev_key, has_oid, has_size)
276 }
277 },
278 );
279 result = result.combine(lfs_res);
280
281 if !has_oid {
282 result.add_error(format!(
283 "{}an LFS pointer is missing the `oid` key in `{}`.",
284 commit_prefix_str(content, "not allowed;"),
285 diff.name,
286 ));
287 }
288
289 if !has_size {
290 result.add_error(format!(
291 "{}an LFS pointer is missing the `size` key in `{}`.",
292 commit_prefix_str(content, "not allowed;"),
293 diff.name,
294 ));
295 }
296 }
297
298 Ok(result)
299 }
300}
301
302#[cfg(feature = "config")]
303pub(crate) mod config {
304 use git_checks_config::{register_checks, CommitCheckConfig, IntoCheck, TopicCheckConfig};
305 use serde::Deserialize;
306 #[cfg(test)]
307 use serde_json::json;
308
309 use crate::LfsPointer;
310
311 #[derive(Deserialize, Debug)]
318 pub struct LfsPointerConfig {}
319
320 impl IntoCheck for LfsPointerConfig {
321 type Check = LfsPointer;
322
323 fn into_check(self) -> Self::Check {
324 Default::default()
325 }
326 }
327
328 register_checks! {
329 LfsPointerConfig {
330 "lfs_pointer" => CommitCheckConfig,
331 "lfs_pointer/topic" => TopicCheckConfig,
332 },
333 }
334
335 #[test]
336 fn test_lfs_pointer_config_empty() {
337 let json = json!({});
338 let check: LfsPointerConfig = serde_json::from_value(json).unwrap();
339
340 let _ = check.into_check();
341 }
342}
343
344#[cfg(test)]
345mod tests {
346 use git_checks_core::{Check, TopicCheck};
347
348 use crate::test::*;
349 use crate::LfsPointer;
350
351 const LFS_INVALID_UTF8: &str = "c413d8954d903557de00be9d96b4daa264bd4b22";
352 const LFS_INVALID_LINE_FORMAT: &str = "709c2cd38863717231453bb4945897a0ebef3f80";
353 const LFS_MISSING_VERSION: &str = "0ec2ab0e229fdcbff18a8bfd55774e4f71b04bbb";
354 const LFS_MISPLACED_VERSION: &str = "c86f7b78fd6ca471d97f3861addf3a35c25f3504";
355 const LFS_INVALID_VERSION: &str = "f799c407f7678ba38f87720fb4217ece6c50f7e5";
356 const LFS_DUPLICATE_KEY: &str = "4d0ead1cac518874d1f7cd005569238b7b9bc7c3";
357 const LFS_MISSING_SIZE: &str = "e6f342338093141e365b1bb1596290c155198c60";
358 const LFS_MISSING_OID: &str = "ceeabe41a44a4469b6644017af725b0127d92302";
359 const LFS_UNRECOGNIZED_HASH: &str = "1aa2873adebe911caf594c376efc5c522f8d4c3a";
360 const LFS_MISSING_HASH: &str = "109806f98fd036cba88d07f94df162aad5086d0b";
361 const LFS_UNSORTED_KEYS: &str = "aaeedde2f188005c24e090bc44378b17770e3b94";
362 const LFS_EMPTY_POINTER: &str = "186cb934ce4b20f85ee6bfb834ab5652121e6436";
363 const LFS_VALID_POINTER: &str = "e088802e2abc6b7c2bb99e194bcc074a9cf9076c";
364 const LFS_SHA256_BAD_DIGEST_LEN: &str = "b00442c09d00aeb1ffabffea821e8b286da6b0df";
365 const LFS_SHA256_BAD_DIGEST_CONTENT: &str = "1cd551597d65f74a57928c024ff58edb532767e0";
366 const LFS_SHA256_EMPTY_DIGEST: &str = "6d377c8197b372df64689cb90db70904a2667dc9";
367 const LFS_UNKNOWN_ALGO_EMPTY_DIGEST: &str = "96c193edaf748309b7bb5c2e816abda4a06c80c0";
368 const LFS_MISSING_ALGO: &str = "6b4125e802283d716ceef0f4c7630a94810654fd";
369 const LFS_INVALID_KEY: &str = "335b860660b133c59c651b3cb22ac883fdb8d004";
370 const LFS_EMPTY_KEY: &str = "7516f6f1e68245da397fd459e0516ca3f2334aa5";
371 const LFS_DELETED_BAD_FILE: &str = "94c4f73c20e547ec9ea45b92a1b10b1b4e7d0505";
372 const LFS_NONINT_SIZE: &str = "3ed812f83b9bd5d8ed5538933254d07a42928e47";
373 const LFS_ZERO_SIZE: &str = "5523b49074a66611aaafcac32cf7d05a654f552c";
374 const LFS_NOT_LFS_FILTER: &str = "be247837cbdbcbbb8a7879c638c918e59bc61e56";
375
376 const LFS_INVALID_UTF8_FIXED: &str = "5997c958735f9d4eab0640fa54b4c094bd74e71b";
377 const LFS_INVALID_LINE_FORMAT_FIXED: &str = "501a70c9ff37a7ce9e7c853a737b93e3be6d73d1";
378 const LFS_MISSING_VERSION_FIXED: &str = "225108803b9a8679c48272dc83765ce5a6a281e1";
379 const LFS_MISPLACED_VERSION_FIXED: &str = "7f2c99eab3e1cb58e41bdfe0ae56dfa5c6529a26";
380 const LFS_INVALID_VERSION_FIXED: &str = "ce3f57bc200ebe52cce3b10580ce6adc76e830bd";
381 const LFS_DUPLICATE_KEY_FIXED: &str = "59dafc938b6fbf913624afd16b3732c79f88e4b5";
382 const LFS_MISSING_SIZE_FIXED: &str = "9f8196108b33fc91884be01b63dd82bb484b41be";
383 const LFS_MISSING_OID_FIXED: &str = "b88474b22be8e87d2276fb9e54bf30147638d9b2";
384 const LFS_UNRECOGNIZED_HASH_FIXED: &str = "68aaf7516f3f22705a87ad19799bd03a9c5ae999";
385 const LFS_MISSING_HASH_FIXED: &str = "31d34a4fb619de4e4877e426255bd206b4f579b9";
386 const LFS_UNSORTED_KEYS_FIXED: &str = "829033be4f407b6c9e472778364fbabb76431c2b";
387 const LFS_SHA256_BAD_DIGEST_LEN_FIXED: &str = "60a5483724576389baf6b5cd8300d5ce0c9ae2f3";
388 const LFS_SHA256_BAD_DIGEST_CONTENT_FIXED: &str = "ba2c9f71853b64a9460a95a5f6057c4fcabc59a4";
389 const LFS_SHA256_EMPTY_DIGEST_FIXED: &str = "b06ca3f0c77173164f9fbec13e1f2a2a6814b69c";
390 const LFS_UNKNOWN_ALGO_EMPTY_DIGEST_FIXED: &str = "d7acdabf44ba40589674e59dfce9e5b5515f951c";
391 const LFS_MISSING_ALGO_FIXED: &str = "081c396ca642026464c17ad193239316e3100439";
392 const LFS_INVALID_KEY_FIXED: &str = "40c8f64bd2f1da90fa7bfbae8aaae9bd654f94ca";
393 const LFS_EMPTY_KEY_FIXED: &str = "c4e2d178e6cfd82a82d7b9b68544a7e1ec50d17b";
394 const LFS_NONINT_SIZE_FIXED: &str = "442f3131aecf8993912ec0e26e48fa48ad924364";
395 const LFS_ZERO_SIZE_FIXED: &str = "e854ba50135b51b59dfd4b59cfcc6a8aae49b01b";
396
397 #[test]
398 fn test_lfs_pointer_builder_default() {
399 assert!(LfsPointer::builder().build().is_ok());
400 }
401
402 #[test]
403 fn test_lfs_pointer_name_commit() {
404 let check = LfsPointer::default();
405 assert_eq!(Check::name(&check), "lfs-pointer");
406 }
407
408 #[test]
409 fn test_lfs_pointer_name_topic() {
410 let check = LfsPointer::default();
411 assert_eq!(TopicCheck::name(&check), "lfs-pointer");
412 }
413
414 #[test]
415 fn test_lfs_invalid_utf8() {
416 let check = LfsPointer::default();
417 let result = run_check("test_lfs_invalid_utf8", LFS_INVALID_UTF8, check);
418 test_result_errors(result, &[
419 "commit c413d8954d903557de00be9d96b4daa264bd4b22 not allowed; invalid utf-8 sequence \
420 in an LFS pointer added in `invalid-utf8.lfs`.",
421 ]);
422 }
423
424 #[test]
425 fn test_lfs_invalid_utf8_topic() {
426 let check = LfsPointer::default();
427 let result = run_topic_check("test_lfs_invalid_utf8_topic", LFS_INVALID_UTF8, check);
428 test_result_errors(
429 result,
430 &["invalid utf-8 sequence in an LFS pointer added in `invalid-utf8.lfs`."],
431 );
432 }
433
434 #[test]
435 fn test_lfs_invalid_utf8_topic_fixed() {
436 let check = LfsPointer::default();
437 run_topic_check_ok(
438 "test_lfs_invalid_utf8_topic_fixed",
439 LFS_INVALID_UTF8_FIXED,
440 check,
441 )
442 }
443
444 #[test]
445 fn test_lfs_invalid_line_format() {
446 let check = LfsPointer::default();
447 let result = run_check(
448 "test_lfs_invalid_line_format",
449 LFS_INVALID_LINE_FORMAT,
450 check,
451 );
452 test_result_errors(result, &[
453 "commit 709c2cd38863717231453bb4945897a0ebef3f80 not allowed; invalid line in an LFS \
454 pointer added in `invalid-line-format.lfs` on line 2.",
455 "commit 709c2cd38863717231453bb4945897a0ebef3f80 not allowed; invalid line in an LFS \
456 pointer added in `invalid-line-format.lfs` on line 3.",
457 "commit 709c2cd38863717231453bb4945897a0ebef3f80 not allowed; invalid line in an LFS \
458 pointer added in `invalid-line-format.lfs` on line 4.",
459 "commit 709c2cd38863717231453bb4945897a0ebef3f80 not allowed; invalid line in an LFS \
460 pointer added in `invalid-line-format.lfs` on line 8.",
461 ]);
462 }
463
464 #[test]
465 fn test_lfs_invalid_line_format_topic() {
466 let check = LfsPointer::default();
467 let result = run_topic_check(
468 "test_lfs_invalid_line_format_topic",
469 LFS_INVALID_LINE_FORMAT,
470 check,
471 );
472 test_result_errors(
473 result,
474 &[
475 "invalid line in an LFS pointer added in `invalid-line-format.lfs` on line 2.",
476 "invalid line in an LFS pointer added in `invalid-line-format.lfs` on line 3.",
477 "invalid line in an LFS pointer added in `invalid-line-format.lfs` on line 4.",
478 "invalid line in an LFS pointer added in `invalid-line-format.lfs` on line 8.",
479 ],
480 );
481 }
482
483 #[test]
484 fn test_lfs_invalid_line_format_topic_fixed() {
485 let check = LfsPointer::default();
486 run_topic_check_ok(
487 "test_lfs_invalid_line_format_topic_fixed",
488 LFS_INVALID_LINE_FORMAT_FIXED,
489 check,
490 )
491 }
492
493 #[test]
494 fn test_lfs_missing_version() {
495 let check = LfsPointer::default();
496 let result = run_check("test_lfs_missing_version", LFS_MISSING_VERSION, check);
497 test_result_errors(result, &[
498 "commit 0ec2ab0e229fdcbff18a8bfd55774e4f71b04bbb not allowed; the first key in LFS \
499 pointer `missing-version.lfs` must be `version`; found `oid`.",
500 ]);
501 }
502
503 #[test]
504 fn test_lfs_missing_version_topic() {
505 let check = LfsPointer::default();
506 let result = run_topic_check("test_lfs_missing_version_topic", LFS_MISSING_VERSION, check);
507 test_result_errors(result, &[
508 "the first key in LFS pointer `missing-version.lfs` must be `version`; found `oid`.",
509 ]);
510 }
511
512 #[test]
513 fn test_lfs_missing_version_topic_fixed() {
514 let check = LfsPointer::default();
515 run_topic_check_ok(
516 "test_lfs_missing_version_topic_fixed",
517 LFS_MISSING_VERSION_FIXED,
518 check,
519 )
520 }
521
522 #[test]
523 fn test_lfs_misplaced_version() {
524 let check = LfsPointer::default();
525 let result = run_check("test_lfs_misplaced_version", LFS_MISPLACED_VERSION, check);
526 test_result_errors(result, &[
527 "commit c86f7b78fd6ca471d97f3861addf3a35c25f3504 not allowed; the first key in LFS \
528 pointer `misplaced-version.lfs` must be `version`; found `oid`.",
529 ]);
530 }
531
532 #[test]
533 fn test_lfs_misplaced_version_topic() {
534 let check = LfsPointer::default();
535 let result = run_topic_check(
536 "test_lfs_misplaced_version_topic",
537 LFS_MISPLACED_VERSION,
538 check,
539 );
540 test_result_errors(result, &[
541 "the first key in LFS pointer `misplaced-version.lfs` must be `version`; found `oid`.",
542 ]);
543 }
544
545 #[test]
546 fn test_lfs_misplaced_version_topic_fixed() {
547 let check = LfsPointer::default();
548 run_topic_check_ok(
549 "test_lfs_misplaced_version_topic_fixed",
550 LFS_MISPLACED_VERSION_FIXED,
551 check,
552 )
553 }
554
555 #[test]
556 fn test_lfs_invalid_version() {
557 let check = LfsPointer::default();
558 let result = run_check("test_lfs_invalid_version", LFS_INVALID_VERSION, check);
559 test_result_warnings(
560 result,
561 &[
562 "commit f799c407f7678ba38f87720fb4217ece6c50f7e5 contains an unexpected git-lfs \
563 version string in `invalid-version.lfs`: `https://hawser.github.com/spec/v1`.",
564 ],
565 );
566 }
567
568 #[test]
569 fn test_lfs_invalid_version_topic() {
570 let check = LfsPointer::default();
571 let result = run_topic_check("test_lfs_invalid_version_topic", LFS_INVALID_VERSION, check);
572 test_result_warnings(
573 result,
574 &[
575 "unexpected git-lfs version string in `invalid-version.lfs`: \
576 `https://hawser.github.com/spec/v1`.",
577 ],
578 );
579 }
580
581 #[test]
582 fn test_lfs_invalid_version_topic_fixed() {
583 let check = LfsPointer::default();
584 run_topic_check_ok(
585 "test_lfs_invalid_version_topic_fixed",
586 LFS_INVALID_VERSION_FIXED,
587 check,
588 )
589 }
590
591 #[test]
592 fn test_lfs_duplicate_key() {
593 let check = LfsPointer::default();
594 let result = run_check("test_lfs_duplicate_key", LFS_DUPLICATE_KEY, check);
595 test_result_errors(
596 result,
597 &[
598 "commit 4d0ead1cac518874d1f7cd005569238b7b9bc7c3 not allowed; duplicate key \
599 `duplicate` in an LFS pointer added in `duplicate-key.lfs`.",
600 ],
601 );
602 }
603
604 #[test]
605 fn test_lfs_duplicate_key_topic() {
606 let check = LfsPointer::default();
607 let result = run_topic_check("test_lfs_duplicate_key_topic", LFS_DUPLICATE_KEY, check);
608 test_result_errors(
609 result,
610 &["duplicate key `duplicate` in an LFS pointer added in `duplicate-key.lfs`."],
611 );
612 }
613
614 #[test]
615 fn test_lfs_duplicate_key_topic_fixed() {
616 let check = LfsPointer::default();
617 run_topic_check_ok(
618 "test_lfs_duplicate_key_topic_fixed",
619 LFS_DUPLICATE_KEY_FIXED,
620 check,
621 )
622 }
623
624 #[test]
625 fn test_lfs_missing_size() {
626 let check = LfsPointer::default();
627 let result = run_check("test_lfs_missing_size", LFS_MISSING_SIZE, check);
628 test_result_errors(
629 result,
630 &[
631 "commit e6f342338093141e365b1bb1596290c155198c60 not allowed; an LFS pointer is \
632 missing the `size` key in `missing-size.lfs`.",
633 ],
634 );
635 }
636
637 #[test]
638 fn test_lfs_missing_size_topic() {
639 let check = LfsPointer::default();
640 let result = run_topic_check("test_lfs_missing_size_topic", LFS_MISSING_SIZE, check);
641 test_result_errors(
642 result,
643 &["an LFS pointer is missing the `size` key in `missing-size.lfs`."],
644 );
645 }
646
647 #[test]
648 fn test_lfs_missing_size_topic_fixed() {
649 let check = LfsPointer::default();
650 run_topic_check_ok(
651 "test_lfs_missing_size_topic_fixed",
652 LFS_MISSING_SIZE_FIXED,
653 check,
654 )
655 }
656
657 #[test]
658 fn test_lfs_missing_oid() {
659 let check = LfsPointer::default();
660 let result = run_check("test_lfs_missing_oid", LFS_MISSING_OID, check);
661 test_result_errors(
662 result,
663 &[
664 "commit ceeabe41a44a4469b6644017af725b0127d92302 not allowed; an LFS pointer is \
665 missing the `oid` key in `missing-oid.lfs`.",
666 ],
667 );
668 }
669
670 #[test]
671 fn test_lfs_missing_oid_topic() {
672 let check = LfsPointer::default();
673 let result = run_topic_check("test_lfs_missing_oid_topic", LFS_MISSING_OID, check);
674 test_result_errors(
675 result,
676 &["an LFS pointer is missing the `oid` key in `missing-oid.lfs`."],
677 );
678 }
679
680 #[test]
681 fn test_lfs_missing_oid_topic_fixed() {
682 let check = LfsPointer::default();
683 run_topic_check_ok(
684 "test_lfs_missing_oid_topic_fixed",
685 LFS_MISSING_OID_FIXED,
686 check,
687 )
688 }
689
690 #[test]
691 fn test_lfs_unrecognized_hash() {
692 let check = LfsPointer::default();
693 let result = run_check("test_lfs_unrecognized_hash", LFS_UNRECOGNIZED_HASH, check);
694 test_result_warnings(
695 result,
696 &[
697 "commit 1aa2873adebe911caf594c376efc5c522f8d4c3a contains an unrecognized hash \
698 algorithm `md256` in LFS pointer `unrecognized-hash.lfs`.",
699 ],
700 );
701 }
702
703 #[test]
704 fn test_lfs_unrecognized_hash_topic() {
705 let check = LfsPointer::default();
706 let result = run_topic_check(
707 "test_lfs_unrecognized_hash_topic",
708 LFS_UNRECOGNIZED_HASH,
709 check,
710 );
711 test_result_warnings(
712 result,
713 &["unrecognized hash algorithm `md256` in LFS pointer `unrecognized-hash.lfs`."],
714 );
715 }
716
717 #[test]
718 fn test_lfs_unrecognized_hash_topic_fixed() {
719 let check = LfsPointer::default();
720 run_topic_check_ok(
721 "test_lfs_unrecognized_hash_topic_fixed",
722 LFS_UNRECOGNIZED_HASH_FIXED,
723 check,
724 )
725 }
726
727 #[test]
728 fn test_lfs_missing_hash() {
729 let check = LfsPointer::default();
730 let result = run_check("test_lfs_missing_hash", LFS_MISSING_HASH, check);
731 test_result_errors(
732 result,
733 &[
734 "commit 109806f98fd036cba88d07f94df162aad5086d0b not allowed; missing digest for \
735 cdbd0acd629e920c87bb4d4ff1f5e85ca9393b61629ef7b2bd64b1a7e1fe4b96 algorithm in \
736 LFS pointer `missing-hash.lfs`.",
737 ],
738 );
739 }
740
741 #[test]
742 fn test_lfs_missing_hash_topic() {
743 let check = LfsPointer::default();
744 let result = run_topic_check("test_lfs_missing_hash_topic", LFS_MISSING_HASH, check);
745 test_result_errors(result, &[
746 "missing digest for cdbd0acd629e920c87bb4d4ff1f5e85ca9393b61629ef7b2bd64b1a7e1fe4b96 \
747 algorithm in LFS pointer `missing-hash.lfs`.",
748 ]);
749 }
750
751 #[test]
752 fn test_lfs_missing_hash_topic_fixed() {
753 let check = LfsPointer::default();
754 run_topic_check_ok(
755 "test_lfs_missing_hash_topic_fixed",
756 LFS_MISSING_HASH_FIXED,
757 check,
758 )
759 }
760
761 #[test]
762 fn test_lfs_unsorted_keys() {
763 let check = LfsPointer::default();
764 let result = run_check("test_lfs_unsorted_keys", LFS_UNSORTED_KEYS, check);
765 test_result_errors(
766 result,
767 &[
768 "commit aaeedde2f188005c24e090bc44378b17770e3b94 not allowed; unsorted key `oid` \
769 found in LFS pointer `unsorted-keys.lfs`.",
770 ],
771 );
772 }
773
774 #[test]
775 fn test_lfs_unsorted_keys_topic() {
776 let check = LfsPointer::default();
777 let result = run_topic_check("test_lfs_unsorted_keys_topic", LFS_UNSORTED_KEYS, check);
778 test_result_errors(
779 result,
780 &["unsorted key `oid` found in LFS pointer `unsorted-keys.lfs`."],
781 );
782 }
783
784 #[test]
785 fn test_lfs_unsorted_keys_topic_fixed() {
786 let check = LfsPointer::default();
787 run_topic_check_ok(
788 "test_lfs_unsorted_keys_topic_fixed",
789 LFS_UNSORTED_KEYS_FIXED,
790 check,
791 )
792 }
793
794 #[test]
795 fn test_lfs_empty_pointer() {
796 let check = LfsPointer::default();
797 run_check_ok("test_lfs_empty_pointer", LFS_EMPTY_POINTER, check);
798 }
799
800 #[test]
801 fn test_lfs_empty_pointer_topic() {
802 let check = LfsPointer::default();
803 run_topic_check_ok("test_lfs_empty_pointer_topic", LFS_EMPTY_POINTER, check)
804 }
805
806 #[test]
807 fn test_lfs_valid_pointer() {
808 let check = LfsPointer::default();
809 run_check_ok("test_lfs_valid_pointer", LFS_VALID_POINTER, check);
810 }
811
812 #[test]
813 fn test_lfs_valid_pointer_topic() {
814 let check = LfsPointer::default();
815 run_topic_check_ok("test_lfs_valid_pointer_topic", LFS_VALID_POINTER, check)
816 }
817
818 #[test]
819 fn test_lfs_sha256_bad_digest_len() {
820 let check = LfsPointer::default();
821 let result = run_check(
822 "test_lfs_sha256_bad_digest_len",
823 LFS_SHA256_BAD_DIGEST_LEN,
824 check,
825 );
826 test_result_errors(
827 result,
828 &[
829 "commit b00442c09d00aeb1ffabffea821e8b286da6b0df not allowed; invalid digest \
830 length in `deadbeefdeadbeefdeadbeefdeadbeef` for sha256 algorithm in LFS pointer \
831 `sha256-digest-len-mismatch.lfs`.",
832 ],
833 );
834 }
835
836 #[test]
837 fn test_lfs_sha256_bad_digest_len_topic() {
838 let check = LfsPointer::default();
839 let result = run_topic_check(
840 "test_lfs_sha256_bad_digest_len_topic",
841 LFS_SHA256_BAD_DIGEST_LEN,
842 check,
843 );
844 test_result_errors(
845 result,
846 &[
847 "invalid digest length in `deadbeefdeadbeefdeadbeefdeadbeef` for sha256 algorithm \
848 in LFS pointer `sha256-digest-len-mismatch.lfs`.",
849 ],
850 );
851 }
852
853 #[test]
854 fn test_lfs_sha256_bad_digest_len_topic_fixed() {
855 let check = LfsPointer::default();
856 run_topic_check_ok(
857 "test_lfs_sha256_bad_digest_len_topic_fixed",
858 LFS_SHA256_BAD_DIGEST_LEN_FIXED,
859 check,
860 )
861 }
862
863 #[test]
864 fn test_lfs_sha256_bad_digest_content() {
865 let check = LfsPointer::default();
866 let result = run_check(
867 "test_lfs_sha256_bad_digest_content",
868 LFS_SHA256_BAD_DIGEST_CONTENT,
869 check,
870 );
871 test_result_errors(
872 result,
873 &[
874 "commit 1cd551597d65f74a57928c024ff58edb532767e0 not allowed; invalid digest \
875 content `deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefasciihex` for \
876 sha256 algorithm in LFS pointer `sha256-digest-content.lfs`.",
877 ],
878 );
879 }
880
881 #[test]
882 fn test_lfs_sha256_bad_digest_content_topic() {
883 let check = LfsPointer::default();
884 let result = run_topic_check(
885 "test_lfs_sha256_bad_digest_content_topic",
886 LFS_SHA256_BAD_DIGEST_CONTENT,
887 check,
888 );
889 test_result_errors(
890 result,
891 &["invalid digest content \
892 `deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefasciihex` for \
893 sha256 algorithm in LFS pointer `sha256-digest-content.lfs`."],
894 );
895 }
896
897 #[test]
898 fn test_lfs_sha256_bad_digest_content_topic_fixed() {
899 let check = LfsPointer::default();
900 run_topic_check_ok(
901 "test_lfs_sha256_bad_digest_content_topic_fixed",
902 LFS_SHA256_BAD_DIGEST_CONTENT_FIXED,
903 check,
904 )
905 }
906
907 #[test]
908 fn test_lfs_sha256_empty_digest() {
909 let check = LfsPointer::default();
910 let result = run_check(
911 "test_lfs_sha256_empty_digest",
912 LFS_SHA256_EMPTY_DIGEST,
913 check,
914 );
915 test_result_errors(
916 result,
917 &[
918 "commit 6d377c8197b372df64689cb90db70904a2667dc9 not allowed; empty digest for \
919 sha256 algorithm in LFS pointer `sha256-digest-empty.lfs`.",
920 ],
921 );
922 }
923
924 #[test]
925 fn test_lfs_sha256_empty_digest_topic() {
926 let check = LfsPointer::default();
927 let result = run_topic_check(
928 "test_lfs_sha256_empty_digest_topic",
929 LFS_SHA256_EMPTY_DIGEST,
930 check,
931 );
932 test_result_errors(
933 result,
934 &["empty digest for sha256 algorithm in LFS pointer `sha256-digest-empty.lfs`."],
935 );
936 }
937
938 #[test]
939 fn test_lfs_sha256_empty_digest_topic_fixed() {
940 let check = LfsPointer::default();
941 run_topic_check_ok(
942 "test_lfs_sha256_empty_digest_topic_fixed",
943 LFS_SHA256_EMPTY_DIGEST_FIXED,
944 check,
945 )
946 }
947
948 #[test]
949 fn test_lfs_unknown_algo_empty_digest() {
950 let check = LfsPointer::default();
951 let result = run_check(
952 "test_lfs_unknown_algo_empty_digest",
953 LFS_UNKNOWN_ALGO_EMPTY_DIGEST,
954 check,
955 );
956
957 assert_eq!(result.warnings().len(), 1);
958 assert_eq!(
959 result.warnings()[0],
960 "commit 96c193edaf748309b7bb5c2e816abda4a06c80c0 contains an unrecognized hash \
961 algorithm `notanalgo` in LFS pointer `unknown-digest-empty.lfs`.",
962 );
963 assert_eq!(result.alerts().len(), 0);
964 assert_eq!(result.errors().len(), 1);
965 assert_eq!(
966 result.errors()[0],
967 "commit 96c193edaf748309b7bb5c2e816abda4a06c80c0 not allowed; empty digest for \
968 notanalgo algorithm in LFS pointer `unknown-digest-empty.lfs`.",
969 );
970 assert!(!result.temporary());
971 assert!(!result.allowed());
972 assert!(!result.pass());
973 }
974
975 #[test]
976 fn test_lfs_unknown_algo_empty_digest_topic() {
977 let check = LfsPointer::default();
978 let result = run_topic_check(
979 "test_lfs_unknown_algo_empty_digest_topic",
980 LFS_UNKNOWN_ALGO_EMPTY_DIGEST,
981 check,
982 );
983
984 assert_eq!(result.warnings().len(), 1);
985 assert_eq!(
986 result.warnings()[0],
987 "unrecognized hash algorithm `notanalgo` in LFS pointer `unknown-digest-empty.lfs`.",
988 );
989 assert_eq!(result.alerts().len(), 0);
990 assert_eq!(result.errors().len(), 1);
991 assert_eq!(
992 result.errors()[0],
993 "empty digest for notanalgo algorithm in LFS pointer `unknown-digest-empty.lfs`.",
994 );
995 assert!(!result.temporary());
996 assert!(!result.allowed());
997 assert!(!result.pass());
998 }
999
1000 #[test]
1001 fn test_lfs_unknown_algo_empty_digest_topic_fixed() {
1002 let check = LfsPointer::default();
1003 let result = run_topic_check(
1004 "test_lfs_unknown_algo_empty_digest_topic_fixed",
1005 LFS_UNKNOWN_ALGO_EMPTY_DIGEST_FIXED,
1006 check,
1007 );
1008
1009 test_result_warnings(
1010 result,
1011 &["unrecognized hash algorithm `notanalgo` in LFS pointer \
1012 `unknown-digest-empty.lfs`."],
1013 );
1014 }
1015
1016 #[test]
1017 fn test_lfs_missing_algo() {
1018 let check = LfsPointer::default();
1019 let result = run_check("test_lfs_missing_algo", LFS_MISSING_ALGO, check);
1020 test_result_errors(
1021 result,
1022 &[
1023 "commit 6b4125e802283d716ceef0f4c7630a94810654fd not allowed; missing hash \
1024 algorithm in LFS pointer `missing-oid-algo.lfs`.",
1025 ],
1026 );
1027 }
1028
1029 #[test]
1030 fn test_lfs_missing_algo_topic() {
1031 let check = LfsPointer::default();
1032 let result = run_topic_check("test_lfs_missing_algo_topic", LFS_MISSING_ALGO, check);
1033 test_result_errors(
1034 result,
1035 &["missing hash algorithm in LFS pointer `missing-oid-algo.lfs`."],
1036 );
1037 }
1038
1039 #[test]
1040 fn test_lfs_missing_algo_topic_fixed() {
1041 let check = LfsPointer::default();
1042 run_topic_check_ok(
1043 "test_lfs_missing_algo_topic_fixed",
1044 LFS_MISSING_ALGO_FIXED,
1045 check,
1046 )
1047 }
1048
1049 #[test]
1050 fn test_lfs_invalid_key() {
1051 let check = LfsPointer::default();
1052 let result = run_check("test_lfs_invalid_key", LFS_INVALID_KEY, check);
1053 test_result_errors(
1054 result,
1055 &[
1056 "commit 335b860660b133c59c651b3cb22ac883fdb8d004 not allowed; invalid line in an \
1057 LFS pointer added in `invalid-key.lfs` on line 2.",
1058 ],
1059 );
1060 }
1061
1062 #[test]
1063 fn test_lfs_invalid_key_topic() {
1064 let check = LfsPointer::default();
1065 let result = run_topic_check("test_lfs_invalid_key_topic", LFS_INVALID_KEY, check);
1066 test_result_errors(
1067 result,
1068 &["invalid line in an LFS pointer added in `invalid-key.lfs` on line 2."],
1069 );
1070 }
1071
1072 #[test]
1073 fn test_lfs_invalid_key_topic_fixed() {
1074 let check = LfsPointer::default();
1075 run_topic_check_ok(
1076 "test_lfs_invalid_key_topic_fixed",
1077 LFS_INVALID_KEY_FIXED,
1078 check,
1079 )
1080 }
1081
1082 #[test]
1083 fn test_lfs_empty_key() {
1084 let check = LfsPointer::default();
1085 let result = run_check("test_lfs_empty_key", LFS_EMPTY_KEY, check);
1086 test_result_ok(result);
1087 }
1088
1089 #[test]
1090 fn test_lfs_empty_key_topic() {
1091 let check = LfsPointer::default();
1092 let result = run_topic_check("test_lfs_empty_key_topic", LFS_EMPTY_KEY, check);
1093 test_result_ok(result);
1094 }
1095
1096 #[test]
1097 fn test_lfs_empty_key_topic_fixed() {
1098 let check = LfsPointer::default();
1099 run_topic_check_ok("test_lfs_empty_key_topic_fixed", LFS_EMPTY_KEY_FIXED, check)
1100 }
1101
1102 #[test]
1103 fn test_lfs_delete_file() {
1104 let check = LfsPointer::default();
1105 let conf = make_check_conf(&check);
1106
1107 let result = test_check_base(
1108 "test_lfs_delete_file",
1109 LFS_DELETED_BAD_FILE,
1110 LFS_INVALID_UTF8,
1111 &conf,
1112 );
1113 test_result_ok(result);
1114 }
1115
1116 #[test]
1117 fn test_lfs_delete_file_topic() {
1118 let check = LfsPointer::default();
1119 let result = run_topic_check("test_lfs_delete_file_topic", LFS_DELETED_BAD_FILE, check);
1120 test_result_ok(result);
1121 }
1122
1123 #[test]
1124 fn test_lfs_invalid_size() {
1125 let check = LfsPointer::default();
1126 let result = run_check("test_lfs_invalid_size", LFS_NONINT_SIZE, check);
1127 test_result_errors(result, &[
1128 "commit 3ed812f83b9bd5d8ed5538933254d07a42928e47 not allowed; the `size` key value in \
1129 LFS pointer `invalid-size.lfs` must be an unsigned integer; found `notanint`.",
1130 ]);
1131 }
1132
1133 #[test]
1134 fn test_lfs_invalid_size_topic() {
1135 let check = LfsPointer::default();
1136 let result = run_topic_check("test_lfs_invalid_size_topic", LFS_NONINT_SIZE, check);
1137 test_result_errors(result, &[
1138 "the `size` key value in LFS pointer `invalid-size.lfs` must be an unsigned integer; \
1139 found `notanint`.",
1140 ]);
1141 }
1142
1143 #[test]
1144 fn test_lfs_invalid_size_topic_fixed() {
1145 let check = LfsPointer::default();
1146 run_topic_check_ok(
1147 "test_lfs_invalid_size_topic_fixed",
1148 LFS_NONINT_SIZE_FIXED,
1149 check,
1150 )
1151 }
1152
1153 #[test]
1154 fn test_lfs_zero_size() {
1155 let check = LfsPointer::default();
1156 let result = run_check("test_lfs_zero_size", LFS_ZERO_SIZE, check);
1157 test_result_errors(
1158 result,
1159 &[
1160 "commit 5523b49074a66611aaafcac32cf7d05a654f552c not allowed; the `size` value in \
1161 LFS pointer `zero-size.lfs` must be greater than 0; found `0`.",
1162 ],
1163 );
1164 }
1165
1166 #[test]
1167 fn test_lfs_zero_size_topic() {
1168 let check = LfsPointer::default();
1169 let result = run_topic_check("test_lfs_zero_size_topic", LFS_ZERO_SIZE, check);
1170 test_result_errors(result, &[
1171 "the `size` value in LFS pointer `zero-size.lfs` must be greater than 0; found `0`.",
1172 ]);
1173 }
1174
1175 #[test]
1176 fn test_lfs_zero_size_topic_fixed() {
1177 let check = LfsPointer::default();
1178 run_topic_check_ok("test_lfs_zero_size_topic_fixed", LFS_ZERO_SIZE_FIXED, check)
1179 }
1180
1181 #[test]
1182 fn test_lfs_not_lfs_filter() {
1183 let check = LfsPointer::default();
1184 run_check_ok("test_lfs_not_lfs_filter", LFS_NOT_LFS_FILTER, check);
1185 }
1186
1187 #[test]
1188 fn test_lfs_not_lfs_filter_topic() {
1189 let check = LfsPointer::default();
1190 run_topic_check_ok("test_lfs_not_lfs_filter_topic", LFS_NOT_LFS_FILTER, check);
1191 }
1192}