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