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