Browse code

Support for extracting attachments from OneNote section files

Includes rudimentary support for getting slices from FMap's and for
interacting with libclamav's context structure.

For now will use a Cisco-Talos org fork of the onenote_parser
until the feature to read open a onenote section from a slice (instead
of from a filepath) is added to the upstream.

Micah Snyder authored on 2023/09/30 04:49:29
Showing 32 changed files
... ...
@@ -10,9 +10,9 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
10 10
 
11 11
 [[package]]
12 12
 name = "aho-corasick"
13
-version = "1.0.5"
13
+version = "1.1.2"
14 14
 source = "registry+https://github.com/rust-lang/crates.io-index"
15
-checksum = "0c378d78423fdad8089616f827526ee33c19f2fddbd5de1629152c9593ba4783"
15
+checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
16 16
 dependencies = [
17 17
  "memchr",
18 18
 ]
... ...
@@ -25,9 +25,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
25 25
 
26 26
 [[package]]
27 27
 name = "base64"
28
-version = "0.21.3"
28
+version = "0.21.5"
29 29
 source = "registry+https://github.com/rust-lang/crates.io-index"
30
-checksum = "414dcefbc63d77c526a76b3afcf6fbb9b5e2791c19c3aa2297733208750c6e53"
30
+checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9"
31 31
 
32 32
 [[package]]
33 33
 name = "bindgen"
... ...
@@ -48,7 +48,7 @@ dependencies = [
48 48
  "regex",
49 49
  "rustc-hash",
50 50
  "shlex",
51
- "syn 2.0.31",
51
+ "syn 2.0.38",
52 52
  "which",
53 53
 ]
54 54
 
... ...
@@ -66,9 +66,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
66 66
 
67 67
 [[package]]
68 68
 name = "bitflags"
69
-version = "2.4.0"
69
+version = "2.4.1"
70 70
 source = "registry+https://github.com/rust-lang/crates.io-index"
71
-checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
71
+checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
72 72
 
73 73
 [[package]]
74 74
 name = "block-buffer"
... ...
@@ -80,12 +80,6 @@ dependencies = [
80 80
 ]
81 81
 
82 82
 [[package]]
83
-name = "bumpalo"
84
-version = "3.13.0"
85
-source = "registry+https://github.com/rust-lang/crates.io-index"
86
-checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1"
87
-
88
-[[package]]
89 83
 name = "bytemuck"
90 84
 version = "1.14.0"
91 85
 source = "registry+https://github.com/rust-lang/crates.io-index"
... ...
@@ -93,9 +87,15 @@ checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6"
93 93
 
94 94
 [[package]]
95 95
 name = "byteorder"
96
-version = "1.4.3"
96
+version = "1.5.0"
97
+source = "registry+https://github.com/rust-lang/crates.io-index"
98
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
99
+
100
+[[package]]
101
+name = "bytes"
102
+version = "1.5.0"
97 103
 source = "registry+https://github.com/rust-lang/crates.io-index"
98
-checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
104
+checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
99 105
 
100 106
 [[package]]
101 107
 name = "cbindgen"
... ...
@@ -116,15 +116,6 @@ dependencies = [
116 116
 ]
117 117
 
118 118
 [[package]]
119
-name = "cc"
120
-version = "1.0.83"
121
-source = "registry+https://github.com/rust-lang/crates.io-index"
122
-checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
123
-dependencies = [
124
- "libc",
125
-]
126
-
127
-[[package]]
128 119
 name = "cexpr"
129 120
 version = "0.6.0"
130 121
 source = "registry+https://github.com/rust-lang/crates.io-index"
... ...
@@ -148,10 +139,12 @@ dependencies = [
148 148
  "cbindgen",
149 149
  "flate2",
150 150
  "hex",
151
+ "hex-literal",
151 152
  "image",
152 153
  "libc",
153 154
  "log",
154 155
  "num-traits",
156
+ "onenote_parser",
155 157
  "rustdct",
156 158
  "sha1",
157 159
  "sha2",
... ...
@@ -180,9 +173,9 @@ checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
180 180
 
181 181
 [[package]]
182 182
 name = "cpufeatures"
183
-version = "0.2.9"
183
+version = "0.2.11"
184 184
 source = "registry+https://github.com/rust-lang/crates.io-index"
185
-checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1"
185
+checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0"
186 186
 dependencies = [
187 187
  "libc",
188 188
 ]
... ...
@@ -197,16 +190,6 @@ dependencies = [
197 197
 ]
198 198
 
199 199
 [[package]]
200
-name = "crossbeam-channel"
201
-version = "0.5.8"
202
-source = "registry+https://github.com/rust-lang/crates.io-index"
203
-checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200"
204
-dependencies = [
205
- "cfg-if",
206
- "crossbeam-utils",
207
-]
208
-
209
-[[package]]
210 200
 name = "crossbeam-deque"
211 201
 version = "0.8.3"
212 202
 source = "registry+https://github.com/rust-lang/crates.io-index"
... ...
@@ -272,31 +255,40 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
272 272
 checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
273 273
 
274 274
 [[package]]
275
-name = "errno"
276
-version = "0.3.3"
275
+name = "encoding_rs"
276
+version = "0.8.33"
277 277
 source = "registry+https://github.com/rust-lang/crates.io-index"
278
-checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd"
278
+checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1"
279 279
 dependencies = [
280
- "errno-dragonfly",
281
- "libc",
282
- "windows-sys",
280
+ "cfg-if",
283 281
 ]
284 282
 
285 283
 [[package]]
286
-name = "errno-dragonfly"
287
-version = "0.1.2"
284
+name = "enum-primitive-derive"
285
+version = "0.2.2"
286
+source = "registry+https://github.com/rust-lang/crates.io-index"
287
+checksum = "c375b9c5eadb68d0a6efee2999fef292f45854c3444c86f09d8ab086ba942b0e"
288
+dependencies = [
289
+ "num-traits",
290
+ "quote",
291
+ "syn 1.0.109",
292
+]
293
+
294
+[[package]]
295
+name = "errno"
296
+version = "0.3.5"
288 297
 source = "registry+https://github.com/rust-lang/crates.io-index"
289
-checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
298
+checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860"
290 299
 dependencies = [
291
- "cc",
292 300
  "libc",
301
+ "windows-sys",
293 302
 ]
294 303
 
295 304
 [[package]]
296 305
 name = "exr"
297
-version = "1.7.0"
306
+version = "1.71.0"
298 307
 source = "registry+https://github.com/rust-lang/crates.io-index"
299
-checksum = "d1e481eb11a482815d3e9d618db8c42a93207134662873809335a92327440c18"
308
+checksum = "832a761f35ab3e6664babfbdc6cef35a4860e816ec3916dcfd0882954e98a8a8"
300 309
 dependencies = [
301 310
  "bit_field",
302 311
  "flume",
... ...
@@ -310,24 +302,24 @@ dependencies = [
310 310
 
311 311
 [[package]]
312 312
 name = "fastrand"
313
-version = "2.0.0"
313
+version = "2.0.1"
314 314
 source = "registry+https://github.com/rust-lang/crates.io-index"
315
-checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764"
315
+checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
316 316
 
317 317
 [[package]]
318 318
 name = "fdeflate"
319
-version = "0.3.0"
319
+version = "0.3.1"
320 320
 source = "registry+https://github.com/rust-lang/crates.io-index"
321
-checksum = "d329bdeac514ee06249dabc27877490f17f5d371ec693360768b838e19f3ae10"
321
+checksum = "64d6dafc854908ff5da46ff3f8f473c6984119a2876a383a860246dd7841a868"
322 322
 dependencies = [
323 323
  "simd-adler32",
324 324
 ]
325 325
 
326 326
 [[package]]
327 327
 name = "flate2"
328
-version = "1.0.27"
328
+version = "1.0.28"
329 329
 source = "registry+https://github.com/rust-lang/crates.io-index"
330
-checksum = "c6c98ee8095e9d1dcbf2fcc6d95acccb90d1c81db1e44725c6a984b1dbdfb010"
330
+checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e"
331 331
 dependencies = [
332 332
  "crc32fast",
333 333
  "miniz_oxide",
... ...
@@ -335,30 +327,14 @@ dependencies = [
335 335
 
336 336
 [[package]]
337 337
 name = "flume"
338
-version = "0.10.14"
338
+version = "0.11.0"
339 339
 source = "registry+https://github.com/rust-lang/crates.io-index"
340
-checksum = "1657b4441c3403d9f7b3409e47575237dac27b1b5726df654a6ecbf92f0f7577"
340
+checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181"
341 341
 dependencies = [
342
- "futures-core",
343
- "futures-sink",
344
- "nanorand",
345
- "pin-project",
346 342
  "spin",
347 343
 ]
348 344
 
349 345
 [[package]]
350
-name = "futures-core"
351
-version = "0.3.28"
352
-source = "registry+https://github.com/rust-lang/crates.io-index"
353
-checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c"
354
-
355
-[[package]]
356
-name = "futures-sink"
357
-version = "0.3.28"
358
-source = "registry+https://github.com/rust-lang/crates.io-index"
359
-checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e"
360
-
361
-[[package]]
362 346
 name = "generic-array"
363 347
 version = "0.14.7"
364 348
 source = "registry+https://github.com/rust-lang/crates.io-index"
... ...
@@ -369,19 +345,6 @@ dependencies = [
369 369
 ]
370 370
 
371 371
 [[package]]
372
-name = "getrandom"
373
-version = "0.2.10"
374
-source = "registry+https://github.com/rust-lang/crates.io-index"
375
-checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
376
-dependencies = [
377
- "cfg-if",
378
- "js-sys",
379
- "libc",
380
- "wasi",
381
- "wasm-bindgen",
382
-]
383
-
384
-[[package]]
385 372
 name = "gif"
386 373
 version = "0.12.0"
387 374
 source = "registry+https://github.com/rust-lang/crates.io-index"
... ...
@@ -419,18 +382,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
419 419
 checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
420 420
 
421 421
 [[package]]
422
-name = "hermit-abi"
423
-version = "0.3.2"
424
-source = "registry+https://github.com/rust-lang/crates.io-index"
425
-checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b"
426
-
427
-[[package]]
428 422
 name = "hex"
429 423
 version = "0.4.3"
430 424
 source = "registry+https://github.com/rust-lang/crates.io-index"
431 425
 checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
432 426
 
433 427
 [[package]]
428
+name = "hex-literal"
429
+version = "0.4.1"
430
+source = "registry+https://github.com/rust-lang/crates.io-index"
431
+checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46"
432
+
433
+[[package]]
434 434
 name = "home"
435 435
 version = "0.5.5"
436 436
 source = "registry+https://github.com/rust-lang/crates.io-index"
... ...
@@ -469,6 +432,15 @@ dependencies = [
469 469
 ]
470 470
 
471 471
 [[package]]
472
+name = "itertools"
473
+version = "0.10.5"
474
+source = "registry+https://github.com/rust-lang/crates.io-index"
475
+checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
476
+dependencies = [
477
+ "either",
478
+]
479
+
480
+[[package]]
472 481
 name = "itoa"
473 482
 version = "1.0.9"
474 483
 source = "registry+https://github.com/rust-lang/crates.io-index"
... ...
@@ -484,15 +456,6 @@ dependencies = [
484 484
 ]
485 485
 
486 486
 [[package]]
487
-name = "js-sys"
488
-version = "0.3.64"
489
-source = "registry+https://github.com/rust-lang/crates.io-index"
490
-checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a"
491
-dependencies = [
492
- "wasm-bindgen",
493
-]
494
-
495
-[[package]]
496 487
 name = "lazy_static"
497 488
 version = "1.4.0"
498 489
 source = "registry+https://github.com/rust-lang/crates.io-index"
... ...
@@ -512,9 +475,9 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
512 512
 
513 513
 [[package]]
514 514
 name = "libc"
515
-version = "0.2.147"
515
+version = "0.2.149"
516 516
 source = "registry+https://github.com/rust-lang/crates.io-index"
517
-checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
517
+checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b"
518 518
 
519 519
 [[package]]
520 520
 name = "libloading"
... ...
@@ -528,15 +491,15 @@ dependencies = [
528 528
 
529 529
 [[package]]
530 530
 name = "linux-raw-sys"
531
-version = "0.4.5"
531
+version = "0.4.10"
532 532
 source = "registry+https://github.com/rust-lang/crates.io-index"
533
-checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503"
533
+checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f"
534 534
 
535 535
 [[package]]
536 536
 name = "lock_api"
537
-version = "0.4.10"
537
+version = "0.4.11"
538 538
 source = "registry+https://github.com/rust-lang/crates.io-index"
539
-checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16"
539
+checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
540 540
 dependencies = [
541 541
  "autocfg",
542 542
  "scopeguard",
... ...
@@ -550,9 +513,9 @@ checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
550 550
 
551 551
 [[package]]
552 552
 name = "memchr"
553
-version = "2.6.3"
553
+version = "2.6.4"
554 554
 source = "registry+https://github.com/rust-lang/crates.io-index"
555
-checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c"
555
+checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
556 556
 
557 557
 [[package]]
558 558
 name = "memoffset"
... ...
@@ -580,15 +543,6 @@ dependencies = [
580 580
 ]
581 581
 
582 582
 [[package]]
583
-name = "nanorand"
584
-version = "0.7.0"
585
-source = "registry+https://github.com/rust-lang/crates.io-index"
586
-checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3"
587
-dependencies = [
588
- "getrandom",
589
-]
590
-
591
-[[package]]
592 583
 name = "nom"
593 584
 version = "7.1.3"
594 585
 source = "registry+https://github.com/rust-lang/crates.io-index"
... ...
@@ -630,54 +584,46 @@ dependencies = [
630 630
 
631 631
 [[package]]
632 632
 name = "num-traits"
633
-version = "0.2.16"
633
+version = "0.2.17"
634 634
 source = "registry+https://github.com/rust-lang/crates.io-index"
635
-checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2"
635
+checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
636 636
 dependencies = [
637 637
  "autocfg",
638 638
 ]
639 639
 
640 640
 [[package]]
641
-name = "num_cpus"
642
-version = "1.16.0"
643
-source = "registry+https://github.com/rust-lang/crates.io-index"
644
-checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
645
-dependencies = [
646
- "hermit-abi",
647
- "libc",
648
-]
649
-
650
-[[package]]
651 641
 name = "once_cell"
652 642
 version = "1.18.0"
653 643
 source = "registry+https://github.com/rust-lang/crates.io-index"
654 644
 checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
655 645
 
656 646
 [[package]]
657
-name = "peeking_take_while"
658
-version = "0.1.2"
659
-source = "registry+https://github.com/rust-lang/crates.io-index"
660
-checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
647
+name = "onenote_parser"
648
+version = "0.3.1"
649
+source = "git+https://github.com/Cisco-Talos/onenote.rs.git?branch=CLAM-2329-new-from-slice#8b450447e58143004b68dd21c11b710fdb79be92"
650
+dependencies = [
651
+ "bytes",
652
+ "encoding_rs",
653
+ "enum-primitive-derive",
654
+ "itertools",
655
+ "num-traits",
656
+ "paste",
657
+ "thiserror",
658
+ "uuid",
659
+ "widestring",
660
+]
661 661
 
662 662
 [[package]]
663
-name = "pin-project"
664
-version = "1.1.3"
663
+name = "paste"
664
+version = "1.0.14"
665 665
 source = "registry+https://github.com/rust-lang/crates.io-index"
666
-checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422"
667
-dependencies = [
668
- "pin-project-internal",
669
-]
666
+checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
670 667
 
671 668
 [[package]]
672
-name = "pin-project-internal"
673
-version = "1.1.3"
669
+name = "peeking_take_while"
670
+version = "0.1.2"
674 671
 source = "registry+https://github.com/rust-lang/crates.io-index"
675
-checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405"
676
-dependencies = [
677
- "proc-macro2",
678
- "quote",
679
- "syn 2.0.31",
680
-]
672
+checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
681 673
 
682 674
 [[package]]
683 675
 name = "png"
... ...
@@ -699,7 +645,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
699 699
 checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d"
700 700
 dependencies = [
701 701
  "proc-macro2",
702
- "syn 2.0.31",
702
+ "syn 2.0.38",
703 703
 ]
704 704
 
705 705
 [[package]]
... ...
@@ -713,9 +659,9 @@ dependencies = [
713 713
 
714 714
 [[package]]
715 715
 name = "proc-macro2"
716
-version = "1.0.66"
716
+version = "1.0.69"
717 717
 source = "registry+https://github.com/rust-lang/crates.io-index"
718
-checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
718
+checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
719 719
 dependencies = [
720 720
  "unicode-ident",
721 721
 ]
... ...
@@ -740,9 +686,9 @@ dependencies = [
740 740
 
741 741
 [[package]]
742 742
 name = "rayon"
743
-version = "1.7.0"
743
+version = "1.8.0"
744 744
 source = "registry+https://github.com/rust-lang/crates.io-index"
745
-checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b"
745
+checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1"
746 746
 dependencies = [
747 747
  "either",
748 748
  "rayon-core",
... ...
@@ -750,30 +696,28 @@ dependencies = [
750 750
 
751 751
 [[package]]
752 752
 name = "rayon-core"
753
-version = "1.11.0"
753
+version = "1.12.0"
754 754
 source = "registry+https://github.com/rust-lang/crates.io-index"
755
-checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d"
755
+checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed"
756 756
 dependencies = [
757
- "crossbeam-channel",
758 757
  "crossbeam-deque",
759 758
  "crossbeam-utils",
760
- "num_cpus",
761 759
 ]
762 760
 
763 761
 [[package]]
764 762
 name = "redox_syscall"
765
-version = "0.3.5"
763
+version = "0.4.1"
766 764
 source = "registry+https://github.com/rust-lang/crates.io-index"
767
-checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
765
+checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
768 766
 dependencies = [
769 767
  "bitflags 1.3.2",
770 768
 ]
771 769
 
772 770
 [[package]]
773 771
 name = "regex"
774
-version = "1.9.5"
772
+version = "1.10.2"
775 773
 source = "registry+https://github.com/rust-lang/crates.io-index"
776
-checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47"
774
+checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
777 775
 dependencies = [
778 776
  "aho-corasick",
779 777
  "memchr",
... ...
@@ -783,9 +727,9 @@ dependencies = [
783 783
 
784 784
 [[package]]
785 785
 name = "regex-automata"
786
-version = "0.3.8"
786
+version = "0.4.3"
787 787
 source = "registry+https://github.com/rust-lang/crates.io-index"
788
-checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795"
788
+checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
789 789
 dependencies = [
790 790
  "aho-corasick",
791 791
  "memchr",
... ...
@@ -794,9 +738,9 @@ dependencies = [
794 794
 
795 795
 [[package]]
796 796
 name = "regex-syntax"
797
-version = "0.7.5"
797
+version = "0.8.2"
798 798
 source = "registry+https://github.com/rust-lang/crates.io-index"
799
-checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da"
799
+checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
800 800
 
801 801
 [[package]]
802 802
 name = "rustc-hash"
... ...
@@ -830,11 +774,11 @@ dependencies = [
830 830
 
831 831
 [[package]]
832 832
 name = "rustix"
833
-version = "0.38.11"
833
+version = "0.38.21"
834 834
 source = "registry+https://github.com/rust-lang/crates.io-index"
835
-checksum = "c0c3dde1fc030af041adc40e79c0e7fbcf431dd24870053d187d7c66e4b87453"
835
+checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3"
836 836
 dependencies = [
837
- "bitflags 2.4.0",
837
+ "bitflags 2.4.1",
838 838
  "errno",
839 839
  "libc",
840 840
  "linux-raw-sys",
... ...
@@ -855,29 +799,29 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
855 855
 
856 856
 [[package]]
857 857
 name = "serde"
858
-version = "1.0.188"
858
+version = "1.0.190"
859 859
 source = "registry+https://github.com/rust-lang/crates.io-index"
860
-checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e"
860
+checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7"
861 861
 dependencies = [
862 862
  "serde_derive",
863 863
 ]
864 864
 
865 865
 [[package]]
866 866
 name = "serde_derive"
867
-version = "1.0.188"
867
+version = "1.0.190"
868 868
 source = "registry+https://github.com/rust-lang/crates.io-index"
869
-checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2"
869
+checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3"
870 870
 dependencies = [
871 871
  "proc-macro2",
872 872
  "quote",
873
- "syn 2.0.31",
873
+ "syn 2.0.38",
874 874
 ]
875 875
 
876 876
 [[package]]
877 877
 name = "serde_json"
878
-version = "1.0.105"
878
+version = "1.0.108"
879 879
 source = "registry+https://github.com/rust-lang/crates.io-index"
880
-checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360"
880
+checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
881 881
 dependencies = [
882 882
  "itoa",
883 883
  "ryu",
... ...
@@ -886,9 +830,9 @@ dependencies = [
886 886
 
887 887
 [[package]]
888 888
 name = "sha1"
889
-version = "0.10.5"
889
+version = "0.10.6"
890 890
 source = "registry+https://github.com/rust-lang/crates.io-index"
891
-checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3"
891
+checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
892 892
 dependencies = [
893 893
  "cfg-if",
894 894
  "cpufeatures",
... ...
@@ -897,9 +841,9 @@ dependencies = [
897 897
 
898 898
 [[package]]
899 899
 name = "sha2"
900
-version = "0.10.7"
900
+version = "0.10.8"
901 901
 source = "registry+https://github.com/rust-lang/crates.io-index"
902
-checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8"
902
+checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
903 903
 dependencies = [
904 904
  "cfg-if",
905 905
  "cpufeatures",
... ...
@@ -920,9 +864,9 @@ checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
920 920
 
921 921
 [[package]]
922 922
 name = "smallvec"
923
-version = "1.11.0"
923
+version = "1.11.1"
924 924
 source = "registry+https://github.com/rust-lang/crates.io-index"
925
-checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9"
925
+checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a"
926 926
 
927 927
 [[package]]
928 928
 name = "spin"
... ...
@@ -952,9 +896,9 @@ dependencies = [
952 952
 
953 953
 [[package]]
954 954
 name = "syn"
955
-version = "2.0.31"
955
+version = "2.0.38"
956 956
 source = "registry+https://github.com/rust-lang/crates.io-index"
957
-checksum = "718fa2415bcb8d8bd775917a1bf12a7931b6dfa890753378538118181e0cb398"
957
+checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b"
958 958
 dependencies = [
959 959
  "proc-macro2",
960 960
  "quote",
... ...
@@ -963,9 +907,9 @@ dependencies = [
963 963
 
964 964
 [[package]]
965 965
 name = "tempfile"
966
-version = "3.8.0"
966
+version = "3.8.1"
967 967
 source = "registry+https://github.com/rust-lang/crates.io-index"
968
-checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef"
968
+checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5"
969 969
 dependencies = [
970 970
  "cfg-if",
971 971
  "fastrand",
... ...
@@ -976,22 +920,22 @@ dependencies = [
976 976
 
977 977
 [[package]]
978 978
 name = "thiserror"
979
-version = "1.0.48"
979
+version = "1.0.50"
980 980
 source = "registry+https://github.com/rust-lang/crates.io-index"
981
-checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7"
981
+checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2"
982 982
 dependencies = [
983 983
  "thiserror-impl",
984 984
 ]
985 985
 
986 986
 [[package]]
987 987
 name = "thiserror-impl"
988
-version = "1.0.48"
988
+version = "1.0.50"
989 989
 source = "registry+https://github.com/rust-lang/crates.io-index"
990
-checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35"
990
+checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8"
991 991
 dependencies = [
992 992
  "proc-macro2",
993 993
  "quote",
994
- "syn 2.0.31",
994
+ "syn 2.0.38",
995 995
 ]
996 996
 
997 997
 [[package]]
... ...
@@ -1026,15 +970,15 @@ dependencies = [
1026 1026
 
1027 1027
 [[package]]
1028 1028
 name = "typenum"
1029
-version = "1.16.0"
1029
+version = "1.17.0"
1030 1030
 source = "registry+https://github.com/rust-lang/crates.io-index"
1031
-checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
1031
+checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
1032 1032
 
1033 1033
 [[package]]
1034 1034
 name = "unicode-ident"
1035
-version = "1.0.11"
1035
+version = "1.0.12"
1036 1036
 source = "registry+https://github.com/rust-lang/crates.io-index"
1037
-checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c"
1037
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
1038 1038
 
1039 1039
 [[package]]
1040 1040
 name = "unicode-segmentation"
... ...
@@ -1043,70 +987,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1043 1043
 checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
1044 1044
 
1045 1045
 [[package]]
1046
-name = "version_check"
1047
-version = "0.9.4"
1048
-source = "registry+https://github.com/rust-lang/crates.io-index"
1049
-checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
1050
-
1051
-[[package]]
1052
-name = "wasi"
1053
-version = "0.11.0+wasi-snapshot-preview1"
1054
-source = "registry+https://github.com/rust-lang/crates.io-index"
1055
-checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
1056
-
1057
-[[package]]
1058
-name = "wasm-bindgen"
1059
-version = "0.2.87"
1046
+name = "uuid"
1047
+version = "1.5.0"
1060 1048
 source = "registry+https://github.com/rust-lang/crates.io-index"
1061
-checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342"
1062
-dependencies = [
1063
- "cfg-if",
1064
- "wasm-bindgen-macro",
1065
-]
1049
+checksum = "88ad59a7560b41a70d191093a945f0b87bc1deeda46fb237479708a1d6b6cdfc"
1066 1050
 
1067 1051
 [[package]]
1068
-name = "wasm-bindgen-backend"
1069
-version = "0.2.87"
1070
-source = "registry+https://github.com/rust-lang/crates.io-index"
1071
-checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd"
1072
-dependencies = [
1073
- "bumpalo",
1074
- "log",
1075
- "once_cell",
1076
- "proc-macro2",
1077
- "quote",
1078
- "syn 2.0.31",
1079
- "wasm-bindgen-shared",
1080
-]
1081
-
1082
-[[package]]
1083
-name = "wasm-bindgen-macro"
1084
-version = "0.2.87"
1085
-source = "registry+https://github.com/rust-lang/crates.io-index"
1086
-checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d"
1087
-dependencies = [
1088
- "quote",
1089
- "wasm-bindgen-macro-support",
1090
-]
1091
-
1092
-[[package]]
1093
-name = "wasm-bindgen-macro-support"
1094
-version = "0.2.87"
1095
-source = "registry+https://github.com/rust-lang/crates.io-index"
1096
-checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
1097
-dependencies = [
1098
- "proc-macro2",
1099
- "quote",
1100
- "syn 2.0.31",
1101
- "wasm-bindgen-backend",
1102
- "wasm-bindgen-shared",
1103
-]
1104
-
1105
-[[package]]
1106
-name = "wasm-bindgen-shared"
1107
-version = "0.2.87"
1052
+name = "version_check"
1053
+version = "0.9.4"
1108 1054
 source = "registry+https://github.com/rust-lang/crates.io-index"
1109
-checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1"
1055
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
1110 1056
 
1111 1057
 [[package]]
1112 1058
 name = "weezl"
... ...
@@ -1127,6 +1017,12 @@ dependencies = [
1127 1127
 ]
1128 1128
 
1129 1129
 [[package]]
1130
+name = "widestring"
1131
+version = "1.0.2"
1132
+source = "registry+https://github.com/rust-lang/crates.io-index"
1133
+checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8"
1134
+
1135
+[[package]]
1130 1136
 name = "winapi"
1131 1137
 version = "0.3.9"
1132 1138
 source = "registry+https://github.com/rust-lang/crates.io-index"
... ...
@@ -1291,6 +1291,13 @@ int recvloop(int *socketds, unsigned nsockets, struct cl_engine *engine, unsigne
1291 1291
         logg(LOGG_INFO, "HWP3 support disabled.\n");
1292 1292
     }
1293 1293
 
1294
+    if (optget(opts, "ScanOneNote")->enabled) {
1295
+        logg(LOGG_INFO, "OneNote support enabled.\n");
1296
+        options.parse |= CL_SCAN_PARSE_ONENOTE;
1297
+    } else {
1298
+        logg(LOGG_INFO, "OneNote support disabled.\n");
1299
+    }
1300
+
1294 1301
     if (optget(opts, "PhishingScanURLs")->enabled) {
1295 1302
         /* TODO: Remove deprecated option in a future feature release */
1296 1303
         if ((optget(opts, "PhishingAlwaysBlockCloak")->enabled) ||
... ...
@@ -304,6 +304,7 @@ void help(void)
304 304
     mprintf(LOGG_INFO, "    --scan-html[=yes(*)/no]              Scan HTML files\n");
305 305
     mprintf(LOGG_INFO, "    --scan-xmldocs[=yes(*)/no]           Scan xml-based document files\n");
306 306
     mprintf(LOGG_INFO, "    --scan-hwp3[=yes(*)/no]              Scan HWP3 files\n");
307
+    mprintf(LOGG_INFO, "    --scan-onenote[=yes(*)/no]           Scan OneNote files\n");
307 308
     mprintf(LOGG_INFO, "    --scan-archive[=yes(*)/no]           Scan archive files (supported by libclamav)\n");
308 309
     mprintf(LOGG_INFO, "    --alert-broken[=yes/no(*)]           Alert on broken executable files (PE & ELF)\n");
309 310
     mprintf(LOGG_INFO, "    --alert-broken-media[=yes/no(*)]     Alert on broken graphics files (JPEG, TIFF, PNG, GIF)\n");
... ...
@@ -1552,6 +1552,9 @@ int scanmanager(const struct optstruct *opts)
1552 1552
     if (optget(opts, "scan-hwp3")->enabled)
1553 1553
         options.parse |= CL_SCAN_PARSE_HWP3;
1554 1554
 
1555
+    if (optget(opts, "scan-onenote")->enabled)
1556
+        options.parse |= CL_SCAN_PARSE_ONENOTE;
1557
+
1555 1558
     /* TODO: Remove deprecated option in a future feature release */
1556 1559
     if ((optget(opts, "algorithmic-detection")->enabled) && /* && used due to default-yes for both options */
1557 1560
         (optget(opts, "heuristic-alerts")->enabled)) {
... ...
@@ -434,6 +434,8 @@ const struct clam_option __clam_options[] = {
434 434
 
435 435
     {"ScanHWP3", "scan-hwp3", 0, CLOPT_TYPE_BOOL, MATCH_BOOL, 1, NULL, 0, OPT_CLAMD | OPT_CLAMSCAN, "This option enables scanning HWP3 files.\nIf you turn off this option, the original files will still be scanned, but\nwithout additional processing.", "yes"},
436 436
 
437
+    {"ScanOneNote", "scan-onenote", 0, CLOPT_TYPE_BOOL, MATCH_BOOL, 1, NULL, 0, OPT_CLAMD | OPT_CLAMSCAN, "This option enables scanning OneNote files.\nIf you turn off this option, the original files will still be scanned, but\nwithout additional processing.", "yes"},
438
+
437 439
     {"ScanArchive", "scan-archive", 0, CLOPT_TYPE_BOOL, MATCH_BOOL, 1, NULL, 0, OPT_CLAMD | OPT_CLAMSCAN, "Scan within archives and compressed files.\nIf you turn off this option, the original files will still be scanned, but\nwithout unpacking and additional processing.", "yes"},
438 440
 
439 441
     {"ForceToDisk", "force-to-disk", 0, CLOPT_TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMD | OPT_CLAMSCAN, "This option causes memory or nested map scans to dump the content to disk.\nIf you turn on this option, more data is written to disk and is available\nwhen the leave-temps option is enabled at the cost of more disk writes.", "no"},
... ...
@@ -479,6 +479,13 @@ If you turn off this option, the original files will still be scanned, but witho
479 479
 .br
480 480
 Default: yes
481 481
 .TP
482
+\fBScanOneNote BOOL\fR
483
+This option enables scanning OneNote files.
484
+.br
485
+If you turn off this option, the original files will still be scanned, but without additional processing.
486
+.br
487
+Default: yes
488
+.TP
482 489
 \fBScanArchive BOOL\fR
483 490
 Scan within archives and compressed files.
484 491
 .br
... ...
@@ -426,6 +426,12 @@ Example
426 426
 # Default: yes
427 427
 #ScanHWP3 yes
428 428
 
429
+# This option enables scanning of OneNote files.
430
+# If you turn off this option, the original files will still be scanned, but
431
+# without additional processing.
432
+# Default: yes
433
+#ScanOneNote yes
434
+
429 435
 
430 436
 ##
431 437
 ## Mail files
... ...
@@ -180,6 +180,7 @@ struct cl_scan_options {
180 180
 #define CL_SCAN_PARSE_OLE2                          0x80
181 181
 #define CL_SCAN_PARSE_HTML                          0x100
182 182
 #define CL_SCAN_PARSE_PE                            0x200
183
+#define CL_SCAN_PARSE_ONENOTE                       0x400
183 184
 
184 185
 /* heuristic alerting options */
185 186
 #define CL_SCAN_HEURISTIC_BROKEN                    0x2    /* alert on broken PE and broken ELF files */
... ...
@@ -118,6 +118,7 @@ static struct dconf_module modules[] = {
118 118
     {"DOCUMENT", "OOXML", DOC_CONF_OOXML, 1},
119 119
     {"DOCUMENT", "MSPML", DOC_CONF_MSXML, 1},
120 120
     {"DOCUMENT", "HWP", DOC_CONF_HWP, 1},
121
+    {"DOCUMENT", "ONENOTE", DOC_CONF_ONENOTE, 1},
121 122
 
122 123
     {"MAIL", "MBOX", MAIL_CONF_MBOX, 1},
123 124
     {"MAIL", "TNEF", MAIL_CONF_TNEF, 1},
... ...
@@ -109,6 +109,7 @@ struct cli_dconf {
109 109
 #define DOC_CONF_MSXML        0x80
110 110
 #define DOC_CONF_OOXML        0x100
111 111
 #define DOC_CONF_HWP          0x200
112
+#define DOC_CONF_ONENOTE      0x400
112 113
 
113 114
 /* Mail flags */
114 115
 #define MAIL_CONF_MBOX 0x1
... ...
@@ -138,6 +138,7 @@ static const struct ftmap_s {
138 138
     { "CL_TYPE_EGG",          CL_TYPE_EGG          },
139 139
     { "CL_TYPE_EGGSFX",       CL_TYPE_EGGSFX       },
140 140
     { "CL_TYPE_UDF",          CL_TYPE_UDF          },
141
+    { "CL_TYPE_ONENOTE",      CL_TYPE_ONENOTE      },
141 142
     { NULL,                   CL_TYPE_IGNORED      }
142 143
 };
143 144
 // clang-format on
... ...
@@ -92,6 +92,7 @@ typedef enum cli_file {
92 92
     CL_TYPE_OOXML_HWP,
93 93
     CL_TYPE_PS,
94 94
     CL_TYPE_EGG,
95
+    CL_TYPE_ONENOTE,
95 96
 
96 97
     /* Section for partition types */
97 98
     CL_TYPE_PART_ANY, /* unknown partition type */
... ...
@@ -204,5 +204,6 @@ static const char *ftypes_int[] = {
204 204
     "0:0:4d4d:TIFF Big Endian:CL_TYPE_ANY:CL_TYPE_GRAPHICS:81:121",
205 205
     "1:*:377abcaf271c:7zip-SFX:CL_TYPE_ANY:CL_TYPE_7ZSFX:74",
206 206
     "1:0:3c3f786d6c2076657273696f6e3d22312e3022{0-1024}70726f6769643d22576f72642e446f63756d656e74223f3e:Microsoft Word 2003 XML Document:CL_TYPE_ANY:CL_TYPE_XML_WORD:80",
207
+    "0:0:e4525c7b8cd8a74daeb15378d02996d3:Microsoft OneNote Document:CL_TYPE_ANY:CL_TYPE_ONENOTE:200",
207 208
     NULL};
208 209
 #endif
... ...
@@ -292,6 +292,7 @@ CLAMAV_PRIVATE {
292 292
     readdb_parse_ldb_subsignature;
293 293
     fuzzy_hash_calculate_image;
294 294
     ffierror_fmt;
295
+    cli_magic_scan_buff;
295 296
 
296 297
     __cli_strcasestr;
297 298
     __cli_strndup;
... ...
@@ -191,6 +191,7 @@ typedef struct recursion_level_tag {
191 191
 } recursion_level_t;
192 192
 
193 193
 typedef void *evidence_t;
194
+typedef void *onedump_t;
194 195
 
195 196
 /* internal clamav context */
196 197
 typedef struct cli_ctx_tag {
... ...
@@ -568,6 +569,7 @@ extern LIBCLAMAV_EXPORT int have_rar;
568 568
 #define SCAN_PARSE_OLE2 (ctx->options->parse & CL_SCAN_PARSE_OLE2)
569 569
 #define SCAN_PARSE_HTML (ctx->options->parse & CL_SCAN_PARSE_HTML)
570 570
 #define SCAN_PARSE_PE (ctx->options->parse & CL_SCAN_PARSE_PE)
571
+#define SCAN_PARSE_ONENOTE (ctx->options->parse & CL_SCAN_PARSE_ONENOTE)
571 572
 
572 573
 #define SCAN_HEURISTIC_BROKEN (ctx->options->heuristic & CL_SCAN_HEURISTIC_BROKEN)
573 574
 #define SCAN_HEURISTIC_BROKEN_MEDIA (ctx->options->heuristic & CL_SCAN_HEURISTIC_BROKEN_MEDIA)
... ...
@@ -4591,6 +4591,11 @@ cl_error_t cli_magic_scan(cli_ctx *ctx, cli_file_t type)
4591 4591
                 ret = cli_scanegg(ctx);
4592 4592
             break;
4593 4593
 
4594
+        case CL_TYPE_ONENOTE:
4595
+            if (SCAN_PARSE_ONENOTE && (DCONF_ARCH & DOC_CONF_ONENOTE))
4596
+                ret = scan_onenote(ctx);
4597
+            break;
4598
+
4594 4599
         case CL_TYPE_OOXML_WORD:
4595 4600
         case CL_TYPE_OOXML_PPT:
4596 4601
         case CL_TYPE_OOXML_XL:
... ...
@@ -20,6 +20,8 @@ base64 = "0.21.0"
20 20
 sha1 = "0.10.5"
21 21
 unicode-segmentation = "1.10.1"
22 22
 bindgen = "0.65"
23
+onenote_parser = { git = "https://github.com/Cisco-Talos/onenote.rs.git", branch = "CLAM-2329-new-from-slice" }
24
+hex-literal = "0.4.1"
23 25
 
24 26
 [lib]
25 27
 crate-type = ["staticlib"]
... ...
@@ -52,6 +52,7 @@ const BINDGEN_FUNCTIONS: &[&str] = &[
52 52
     "cli_versig2",
53 53
     "cli_getdsig",
54 54
     "cli_get_debug_flag",
55
+    "cli_magic_scan_buff",
55 56
 ];
56 57
 
57 58
 // Generate bindings for these types (structs, enums):
... ...
@@ -61,6 +62,7 @@ const BINDGEN_TYPES: &[&str] = &[
61 61
     "cli_ac_result",
62 62
     "css_image_extractor_t",
63 63
     "css_image_handle_t",
64
+    "onedump_t",
64 65
 ];
65 66
 
66 67
 // Find the required functions and types in these headers:
... ...
@@ -70,6 +72,8 @@ const BINDGEN_HEADERS: &[&str] = &[
70 70
     "../libclamav/others.h",
71 71
     "../libclamav/dsig.h",
72 72
     "../libclamav/htmlnorm.h",
73
+    "../libclamav/fmap.h",
74
+    "../libclamav/scanners.h",
73 75
 ];
74 76
 
75 77
 // Find the required headers in these directories:
... ...
@@ -135,7 +139,7 @@ fn execute_bindgen() -> Result<(), &'static str> {
135 135
         // Silence code-style warnings for generated bindings.
136 136
         .raw_line("#![allow(non_snake_case, non_camel_case_types, non_upper_case_globals)]")
137 137
         // Make the bindings pretty.
138
-        .rustfmt_bindings(true)
138
+        .formatter(bindgen::Formatter::Rustfmt)
139 139
         // Disable the layout tests.
140 140
         // We're committing to source control. Pointer width, integer size, etc
141 141
         // are probably not the same when generated as when compiled.
... ...
@@ -36,6 +36,7 @@ include = [
36 36
   "evidence::evidence_num_indicators_type",
37 37
   "evidence::evidence_add_indicator",
38 38
   "evidence::IndicatorType",
39
+  "scanners::scan_onenote",
39 40
 ]
40 41
 
41 42
 # prefix = "CAPI_"
... ...
@@ -36,7 +36,6 @@ use crate::validate_str_param;
36 36
 use flate2::{read::GzDecoder, write::GzEncoder, Compression};
37 37
 use log::{debug, error, warn};
38 38
 use sha2::{Digest, Sha256};
39
-use thiserror::Error;
40 39
 
41 40
 /// Size of a digital signature
42 41
 const SIG_SIZE: usize = 350;
... ...
@@ -88,8 +87,8 @@ struct Context {
88 88
 }
89 89
 
90 90
 /// Possible errors returned by cdiff_apply() and script2cdiff
91
-#[derive(Debug, Error)]
92
-pub enum CdiffError {
91
+#[derive(thiserror::Error, Debug)]
92
+pub enum Error {
93 93
     #[error("Error in header: {0}")]
94 94
     Header(#[from] HeaderError),
95 95
 
... ...
@@ -151,7 +150,7 @@ pub enum CdiffError {
151 151
 
152 152
 /// Errors particular to input handling (i.e., syntax, or side effects from
153 153
 /// handling input)
154
-#[derive(Error, Debug)]
154
+#[derive(thiserror::Error, Debug)]
155 155
 pub enum InputError {
156 156
     #[error("Unsupported command provided: {0}")]
157 157
     UnknownCommand(String),
... ...
@@ -199,7 +198,7 @@ pub enum InputError {
199 199
 }
200 200
 
201 201
 /// Errors encountered while processing
202
-#[derive(Debug, Error)]
202
+#[derive(thiserror::Error, Debug)]
203 203
 pub enum ProcessingError {
204 204
     #[error("File {0} not closed before calling action MOVE")]
205 205
     NotClosedBeforeAction(String),
... ...
@@ -238,7 +237,7 @@ pub enum ProcessingError {
238 238
     IoError(#[from] std::io::Error),
239 239
 }
240 240
 
241
-#[derive(Error, Debug)]
241
+#[derive(thiserror::Error, Debug)]
242 242
 pub enum HeaderError {
243 243
     #[error("invalid magic")]
244 244
     BadMagic,
... ...
@@ -253,7 +252,7 @@ pub enum HeaderError {
253 253
     IoError(#[from] std::io::Error),
254 254
 }
255 255
 
256
-#[derive(Error, Debug)]
256
+#[derive(thiserror::Error, Debug)]
257 257
 pub enum SignatureError {
258 258
     #[error("IO error: {0}")]
259 259
     IoError(#[from] std::io::Error),
... ...
@@ -265,7 +264,7 @@ pub enum SignatureError {
265 265
     TooLarge,
266 266
 }
267 267
 
268
-#[derive(Error, Debug)]
268
+#[derive(thiserror::Error, Debug)]
269 269
 pub enum InvalidNumber {
270 270
     #[error("not unicode")]
271 271
     NotUnicode(#[from] std::str::Utf8Error),
... ...
@@ -462,7 +461,7 @@ pub extern "C" fn _script2cdiff(
462 462
 /// signature from the sha256 of the contents written.
463 463
 ///
464 464
 /// This function will panic if any of the &str parameters contain interior NUL bytes
465
-pub fn script2cdiff(script_file_name: &str, builder: &str, server: &str) -> Result<(), CdiffError> {
465
+pub fn script2cdiff(script_file_name: &str, builder: &str, server: &str) -> Result<(), Error> {
466 466
     // Make a copy of the script file name to use for the cdiff file
467 467
     let cdiff_file_name_string = script_file_name.to_string();
468 468
     let mut cdiff_file_name = cdiff_file_name_string.as_str();
... ...
@@ -476,18 +475,18 @@ pub fn script2cdiff(script_file_name: &str, builder: &str, server: &str) -> Resu
476 476
     // Get right-most hyphen index
477 477
     let hyphen_index = cdiff_file_name
478 478
         .rfind('-')
479
-        .ok_or(CdiffError::FilenameMissingHyphen)?;
479
+        .ok_or(Error::FilenameMissingHyphen)?;
480 480
 
481 481
     // Get the version, which should be to the right of the hyphen
482 482
     let version_string = cdiff_file_name
483 483
         .get((hyphen_index + 1)..)
484
-        .ok_or(CdiffError::FilenameMissingVersion)?;
484
+        .ok_or(Error::FilenameMissingVersion)?;
485 485
 
486 486
     // Parse the version into usize
487 487
     let version = version_string
488 488
         .to_string()
489 489
         .parse::<usize>()
490
-        .map_err(CdiffError::VersionParse)?;
490
+        .map_err(Error::VersionParse)?;
491 491
 
492 492
     // Add .cdiff suffix
493 493
     let cdiff_file_name = format!("{}.{}", cdiff_file_name, "cdiff");
... ...
@@ -495,21 +494,21 @@ pub fn script2cdiff(script_file_name: &str, builder: &str, server: &str) -> Resu
495 495
 
496 496
     // Open cdiff_file_name for writing
497 497
     let mut cdiff_file: File = File::create(&cdiff_file_name)
498
-        .map_err(|e| CdiffError::FileCreate(cdiff_file_name.to_owned(), e))?;
498
+        .map_err(|e| Error::FileCreate(cdiff_file_name.to_owned(), e))?;
499 499
 
500 500
     // Open the original script file for reading
501 501
     let script_file: File = File::open(script_file_name)
502
-        .map_err(|e| CdiffError::FileOpen(script_file_name.to_owned(), e))?;
502
+        .map_err(|e| Error::FileOpen(script_file_name.to_owned(), e))?;
503 503
 
504 504
     // Get file length
505 505
     let script_file_len = script_file
506 506
         .metadata()
507
-        .map_err(|e| CdiffError::FileMeta(script_file_name.to_owned(), e))?
507
+        .map_err(|e| Error::FileMeta(script_file_name.to_owned(), e))?
508 508
         .len();
509 509
 
510 510
     // Write header to cdiff file
511 511
     write!(cdiff_file, "ClamAV-Diff:{}:{}:", version, script_file_len)
512
-        .map_err(|e| CdiffError::FileWrite(script_file_name.to_owned(), e))?;
512
+        .map_err(|e| Error::FileWrite(script_file_name.to_owned(), e))?;
513 513
 
514 514
     // Set up buffered reader and gz writer
515 515
     let mut reader = BufReader::new(script_file);
... ...
@@ -521,12 +520,12 @@ pub fn script2cdiff(script_file_name: &str, builder: &str, server: &str) -> Resu
521 521
     // Get cdiff file writer back from flate2
522 522
     let mut cdiff_file = gz
523 523
         .finish()
524
-        .map_err(|e| CdiffError::FileWrite(cdiff_file_name.to_owned(), e))?;
524
+        .map_err(|e| Error::FileWrite(cdiff_file_name.to_owned(), e))?;
525 525
 
526 526
     // Get the new cdiff file len
527 527
     let cdiff_file_len = cdiff_file
528 528
         .metadata()
529
-        .map_err(|e| CdiffError::FileMeta(cdiff_file_name.to_owned(), e))?
529
+        .map_err(|e| Error::FileMeta(cdiff_file_name.to_owned(), e))?
530 530
         .len();
531 531
     debug!(
532 532
         "script2cdiff() - wrote {} bytes to {}",
... ...
@@ -536,7 +535,7 @@ pub fn script2cdiff(script_file_name: &str, builder: &str, server: &str) -> Resu
536 536
     // Calculate SHA2-256 to get the sigature
537 537
     // TODO: Do this while the file is being written
538 538
     let bytes = std::fs::read(&cdiff_file_name)
539
-        .map_err(|e| CdiffError::FileRead(cdiff_file_name.to_owned(), e))?;
539
+        .map_err(|e| Error::FileRead(cdiff_file_name.to_owned(), e))?;
540 540
     let sha256 = {
541 541
         let mut hasher = Sha256::new();
542 542
         hasher.update(&bytes);
... ...
@@ -561,12 +560,12 @@ pub fn script2cdiff(script_file_name: &str, builder: &str, server: &str) -> Resu
561 561
     // Write cdiff footer delimiter
562 562
     cdiff_file
563 563
         .write_all(b":")
564
-        .map_err(|e| CdiffError::FileWrite(cdiff_file_name.to_owned(), e))?;
564
+        .map_err(|e| Error::FileWrite(cdiff_file_name.to_owned(), e))?;
565 565
 
566 566
     // Write dsig to cdiff footer
567 567
     cdiff_file
568 568
         .write_all(dsig.to_bytes())
569
-        .map_err(|e| CdiffError::FileWrite(cdiff_file_name, e))?;
569
+        .map_err(|e| Error::FileWrite(cdiff_file_name, e))?;
570 570
 
571 571
     // Exit success
572 572
     Ok(())
... ...
@@ -609,7 +608,7 @@ pub extern "C" fn _cdiff_apply(fd: i32, mode: u16) -> i32 {
609 609
 /// A cdiff file contains a footer that is the signed signature of the sha256
610 610
 /// file contains of the header and the body. The footer begins after the first
611 611
 /// ':' character to the left of EOF.
612
-pub fn cdiff_apply(file: &mut File, mode: ApplyMode) -> Result<(), CdiffError> {
612
+pub fn cdiff_apply(file: &mut File, mode: ApplyMode) -> Result<(), Error> {
613 613
     let path = std::env::current_dir().unwrap();
614 614
     debug!("cdiff_apply() - current directory is {}", path.display());
615 615
 
... ...
@@ -649,7 +648,7 @@ pub fn cdiff_apply(file: &mut File, mode: ApplyMode) -> Result<(), CdiffError> {
649 649
             };
650 650
             debug!("cdiff_apply() - cli_versig2() result = {}", versig_result);
651 651
             if versig_result != 0 {
652
-                return Err(CdiffError::InvalidDigitalSignature);
652
+                return Err(Error::InvalidDigitalSignature);
653 653
             }
654 654
 
655 655
             // Read file length from header
... ...
@@ -1042,7 +1041,7 @@ fn process_lines<T>(
1042 1042
     ctx: &mut Context,
1043 1043
     reader: &mut T,
1044 1044
     uncompressed_size: usize,
1045
-) -> Result<(), CdiffError>
1045
+) -> Result<(), Error>
1046 1046
 where
1047 1047
     T: BufRead,
1048 1048
 {
... ...
@@ -1059,7 +1058,7 @@ where
1059 1059
                 match linebuf.first() {
1060 1060
                     // Skip comment lines
1061 1061
                     Some(b'#') => continue,
1062
-                    _ => process_line(ctx, &linebuf).map_err(|e| CdiffError::Input {
1062
+                    _ => process_line(ctx, &linebuf).map_err(|e| Error::Input {
1063 1063
                         line: line_no,
1064 1064
                         err: e,
1065 1065
                         operation: String::from_utf8_lossy(&linebuf).to_string(),
... ...
@@ -1150,7 +1149,7 @@ fn read_size(file: &mut File) -> Result<(u32, usize), HeaderError> {
1150 1150
 }
1151 1151
 
1152 1152
 /// Calculate the sha256 of the first len bytes of a file
1153
-fn get_hash(file: &mut File, len: usize) -> Result<[u8; 32], CdiffError> {
1153
+fn get_hash(file: &mut File, len: usize) -> Result<[u8; 32], Error> {
1154 1154
     let mut hasher = Sha256::new();
1155 1155
 
1156 1156
     // Seek to beginning of file
... ...
@@ -1193,7 +1192,7 @@ mod tests {
1193 1193
     use std::path::Path;
1194 1194
 
1195 1195
     /// CdiffTestError enumerates all possible errors returned by this testing library.
1196
-    #[derive(Error, Debug)]
1196
+    #[derive(thiserror::Error, Debug)]
1197 1197
     pub enum CdiffTestError {
1198 1198
         /// Represents all other cases of `std::io::Error`.
1199 1199
         #[error(transparent)]
... ...
@@ -1545,7 +1544,7 @@ mod tests {
1545 1545
     fn script2cdiff_missing_hyphen() {
1546 1546
         assert!(matches!(
1547 1547
             script2cdiff("", "", ""),
1548
-            Err(CdiffError::FilenameMissingHyphen)
1548
+            Err(Error::FilenameMissingHyphen)
1549 1549
         ));
1550 1550
     }
1551 1551
 }
... ...
@@ -24,14 +24,13 @@ use std::{ffi::CStr, mem::ManuallyDrop, os::raw::c_char};
24 24
 
25 25
 use base64::{engine::general_purpose as base64_engine_standard, Engine as _};
26 26
 use log::{debug, error, warn};
27
-use thiserror::Error;
28 27
 use unicode_segmentation::UnicodeSegmentation;
29 28
 
30 29
 use crate::sys;
31 30
 
32
-/// CdiffError enumerates all possible errors returned by this library.
33
-#[derive(Error, Debug)]
34
-pub enum CssExtractError {
31
+/// Error enumerates all possible errors returned by this library.
32
+#[derive(thiserror::Error, Debug)]
33
+pub enum Error {
35 34
     #[error("Invalid format")]
36 35
     Format,
37 36
 
... ...
@@ -56,7 +55,7 @@ pub struct CssImageExtractor<'a> {
56 56
 }
57 57
 
58 58
 impl<'a> CssImageExtractor<'a> {
59
-    pub fn new(css: &'a str) -> Result<Self, CssExtractError> {
59
+    pub fn new(css: &'a str) -> Result<Self, Error> {
60 60
         Ok(Self { remaining: css })
61 61
     }
62 62
 
... ...
@@ -152,7 +151,7 @@ impl<'a> CssImageExtractor<'a> {
152 152
             };
153 153
 
154 154
             // Trim off " at end.
155
-            let c = url_parameter.graphemes(true).rev().next();
155
+            let c = url_parameter.graphemes(true).next_back();
156 156
             if let Some(c) = c {
157 157
                 if c == "\"" {
158 158
                     (url_parameter, _) = url_parameter.split_at(url_parameter.len() - 1);
159 159
new file mode 100644
... ...
@@ -0,0 +1,98 @@
0
+/*
1
+ *  Rust equivalent of libclamav's scanners.c module
2
+ *
3
+ *  Copyright (C) 2023 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
4
+ *
5
+ *  Authors: Micah Snyder
6
+ *
7
+ *  This program is free software; you can redistribute it and/or modify
8
+ *  it under the terms of the GNU General Public License version 2 as
9
+ *  published by the Free Software Foundation.
10
+ *
11
+ *  This program is distributed in the hope that it will be useful,
12
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ *  GNU General Public License for more details.
15
+ *
16
+ *  You should have received a copy of the GNU General Public License
17
+ *  along with this program; if not, write to the Free Software
18
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19
+ *  MA 02110-1301, USA.
20
+ */
21
+
22
+use std::{convert::TryInto, path::PathBuf, slice};
23
+
24
+use crate::{fmap::FMap, sys::cli_ctx, util::str_from_ptr};
25
+
26
+/// Error enumerates all possible errors returned by this library.
27
+#[derive(thiserror::Error, Debug)]
28
+pub enum Error {
29
+    #[error("Invalid format")]
30
+    Format,
31
+
32
+    #[error("Invalid NULL pointer: {0}")]
33
+    NullPointer(&'static str),
34
+
35
+    #[error("{0} parameter is NULL")]
36
+    NullParam(&'static str),
37
+
38
+    #[error("No more files to extract")]
39
+    NoMoreFiles,
40
+
41
+    #[error("Invalid FMap: {0}")]
42
+    BadMap(#[from] crate::fmap::Error),
43
+
44
+    #[error("String not UTF8: {0}")]
45
+    Utf8(#[from] std::str::Utf8Error),
46
+}
47
+
48
+/// Get the ctx.sub_filepath as an Option<'str>
49
+///
50
+/// # Safety
51
+///
52
+/// Must be a valid ctx pointer.
53
+pub unsafe fn sub_filepath(ctx: *mut cli_ctx) -> Result<Option<PathBuf>, Error> {
54
+    if ctx.is_null() {
55
+        return Err(Error::NullPointer("ctx"));
56
+    }
57
+
58
+    Ok(str_from_ptr(unsafe { *ctx }.sub_filepath)
59
+        .map_err(Error::Utf8)?
60
+        .map(PathBuf::from))
61
+}
62
+
63
+/// Get the ctx.target_filepath as an Option<'str>
64
+///
65
+/// # Safety
66
+///
67
+/// Must be a valid ctx pointer.
68
+pub unsafe fn target_filepath(ctx: *mut cli_ctx) -> Result<Option<PathBuf>, Error> {
69
+    if ctx.is_null() {
70
+        return Err(Error::NullPointer("ctx"));
71
+    }
72
+
73
+    Ok(str_from_ptr(unsafe { *ctx }.target_filepath)
74
+        .map_err(Error::Utf8)?
75
+        .map(PathBuf::from))
76
+}
77
+
78
+/// Get the fmap for the current layer.
79
+///
80
+/// # Safety
81
+///
82
+/// Must be a valid ctx pointer.
83
+pub unsafe fn current_fmap(ctx: *mut cli_ctx) -> Result<FMap, Error> {
84
+    if ctx.is_null() {
85
+        return Err(Error::NullPointer("ctx"));
86
+    }
87
+
88
+    let recursion_stack_size = unsafe { *ctx }.recursion_stack_size as usize;
89
+    let recursion_level = unsafe { *ctx }.recursion_level as usize;
90
+
91
+    let recursion_stack =
92
+        unsafe { slice::from_raw_parts((*ctx).recursion_stack, recursion_stack_size) };
93
+
94
+    let current_level = recursion_stack[recursion_level];
95
+
96
+    current_level.fmap.try_into().map_err(Error::BadMap)
97
+}
... ...
@@ -23,13 +23,12 @@
23 23
 use std::{collections::HashMap, ffi::CStr, mem::ManuallyDrop, os::raw::c_char};
24 24
 
25 25
 use log::{debug, error, warn};
26
-use thiserror::Error;
27 26
 
28 27
 use crate::{ffi_util::FFIError, rrf_call, sys, validate_str_param};
29 28
 
30
-/// CdiffError enumerates all possible errors returned by this library.
31
-#[derive(Error, Debug)]
32
-pub enum EvidenceError {
29
+/// Error enumerates all possible errors returned by this library.
30
+#[derive(thiserror::Error, Debug)]
31
+pub enum Error {
33 32
     #[error("Invalid format")]
34 33
     Format,
35 34
 
... ...
@@ -240,21 +239,21 @@ impl Evidence {
240 240
         name: &str,
241 241
         static_virname: *const c_char,
242 242
         indicator_type: IndicatorType,
243
-    ) -> Result<(), EvidenceError> {
243
+    ) -> Result<(), Error> {
244 244
         let meta: IndicatorMeta = IndicatorMeta { static_virname };
245 245
 
246 246
         match indicator_type {
247 247
             IndicatorType::Strong => {
248 248
                 self.strong
249 249
                     .entry(name.to_string())
250
-                    .or_insert_with(Vec::new)
250
+                    .or_default()
251 251
                     .push(meta);
252 252
             }
253 253
 
254 254
             IndicatorType::PotentiallyUnwanted => {
255 255
                 self.pua
256 256
                     .entry(name.to_string())
257
-                    .or_insert_with(Vec::new)
257
+                    .or_default()
258 258
                     .push(meta);
259 259
             }
260 260
 
... ...
@@ -265,7 +264,7 @@ impl Evidence {
265 265
             IndicatorType::Weak => {
266 266
                 self.weak
267 267
                     .entry(name.to_string())
268
-                    .or_insert_with(Vec::new)
268
+                    .or_default()
269 269
                     .push(meta);
270 270
             }
271 271
         }
272 272
new file mode 100644
... ...
@@ -0,0 +1,105 @@
0
+/*
1
+ *  Rust interface for libclamav FMap module
2
+ *
3
+ *  Copyright (C) 2023 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
4
+ *
5
+ *  Authors: Micah Snyder
6
+ *
7
+ *  This program is free software; you can redistribute it and/or modify
8
+ *  it under the terms of the GNU General Public License version 2 as
9
+ *  published by the Free Software Foundation.
10
+ *
11
+ *  This program is distributed in the hope that it will be useful,
12
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ *  GNU General Public License for more details.
15
+ *
16
+ *  You should have received a copy of the GNU General Public License
17
+ *  along with this program; if not, write to the Free Software
18
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19
+ *  MA 02110-1301, USA.
20
+ */
21
+
22
+use std::convert::TryFrom;
23
+
24
+use log::{debug, error};
25
+
26
+use crate::{sys, util::str_from_ptr};
27
+
28
+/// Error enumerates all possible errors returned by this library.
29
+#[derive(thiserror::Error, Debug)]
30
+pub enum Error {
31
+    #[error("Invalid parameter: {0}")]
32
+    InvalidParameter(String),
33
+
34
+    #[error("{0} parmeter is NULL")]
35
+    NullParam(&'static str),
36
+
37
+    #[error("Offset {0} and length {1} not contained in FMap of size {2}")]
38
+    NotContained(usize, usize, usize),
39
+
40
+    #[error("FMap pointer not initialized: {0}")]
41
+    UninitializedPtr(&'static str),
42
+
43
+    #[error("Attempted to create Rust FMap interface from NULL pointer")]
44
+    Null,
45
+}
46
+
47
+#[derive(PartialEq, Eq, Hash, Debug)]
48
+pub struct FMap {
49
+    fmap_ptr: *mut sys::cl_fmap_t,
50
+}
51
+
52
+impl TryFrom<*mut sys::cl_fmap_t> for FMap {
53
+    type Error = Error;
54
+
55
+    fn try_from(value: *mut sys::cl_fmap_t) -> Result<Self, Self::Error> {
56
+        if value.is_null() {
57
+            return Err(Error::Null);
58
+        }
59
+
60
+        Ok(FMap { fmap_ptr: value })
61
+    }
62
+}
63
+
64
+impl<'a> FMap {
65
+    /// Simple wrapper around C FMAP module's fmap.need() method.
66
+    pub fn need_off(&'a self, at: usize, len: usize) -> Result<&'a [u8], Error> {
67
+        // Get the need() method function pointer from the fmap.
68
+        let need_fn = match unsafe { *self.fmap_ptr }.need {
69
+            Some(ptr) => ptr,
70
+            None => return Err(Error::UninitializedPtr("need()")),
71
+        };
72
+
73
+        let ptr: *const u8 = unsafe { need_fn(self.fmap_ptr, at, len, 1) } as *const u8;
74
+
75
+        if ptr.is_null() {
76
+            let fmap_size = unsafe { *self.fmap_ptr }.len;
77
+            debug!(
78
+                "need_off at {:?} len {:?} for fmap size {:?} returned NULL",
79
+                at, len, fmap_size
80
+            );
81
+            return Err(Error::NotContained(at, len, fmap_size));
82
+        }
83
+
84
+        let slice: &[u8] = unsafe { std::slice::from_raw_parts(ptr, len) };
85
+
86
+        Ok(slice)
87
+    }
88
+
89
+    pub fn len(&self) -> usize {
90
+        unsafe { (*self.fmap_ptr).len }
91
+    }
92
+
93
+    pub fn is_empty(&self) -> bool {
94
+        unsafe { (*self.fmap_ptr).len == 0 }
95
+    }
96
+
97
+    pub fn name(&self) -> &'static str {
98
+        unsafe {
99
+            str_from_ptr((*self.fmap_ptr).name)
100
+                .unwrap_or(Some("<invalid-utf8>"))
101
+                .unwrap_or("<unnamed>")
102
+        }
103
+    }
104
+}
... ...
@@ -34,14 +34,13 @@ use image::{imageops::FilterType::Lanczos3, DynamicImage, ImageBuffer, Luma, Pix
34 34
 use log::{debug, error, warn};
35 35
 use num_traits::{NumCast, ToPrimitive, Zero};
36 36
 use rustdct::DctPlanner;
37
-use thiserror::Error;
38 37
 use transpose::transpose;
39 38
 
40 39
 use crate::{ffi_error, ffi_util::FFIError, rrf_call, sys, validate_str_param};
41 40
 
42
-/// CdiffError enumerates all possible errors returned by this library.
43
-#[derive(Error, Debug)]
44
-pub enum FuzzyHashError {
41
+/// Error enumerates all possible errors returned by this library.
42
+#[derive(thiserror::Error, Debug)]
43
+pub enum Error {
45 44
     #[error("Invalid format")]
46 45
     Format,
47 46
 
... ...
@@ -68,6 +67,9 @@ pub enum FuzzyHashError {
68 68
 
69 69
     #[error("{0} parmeter is NULL")]
70 70
     NullParam(&'static str),
71
+
72
+    #[error("{0} hash must be {1} characters in length")]
73
+    InvalidHashLength(&'static str, usize),
71 74
 }
72 75
 
73 76
 #[derive(PartialEq, Eq, Hash, Debug)]
... ...
@@ -81,18 +83,18 @@ pub enum FuzzyHash {
81 81
 }
82 82
 
83 83
 impl TryFrom<&str> for ImageFuzzyHash {
84
-    type Error = &'static str;
84
+    type Error = Error;
85 85
 
86 86
     fn try_from(value: &str) -> Result<Self, Self::Error> {
87 87
         if value.len() != 16 {
88
-            return Err("Image fuzzy hash must be 16 characters in length");
88
+            return Err(Error::InvalidHashLength("ImageFuzzyHash", 16));
89 89
         }
90 90
 
91 91
         let mut hashbytes = [0; 8];
92 92
         if hex::decode_to_slice(value, &mut hashbytes).is_ok() {
93 93
             Ok(ImageFuzzyHash { bytes: hashbytes })
94 94
         } else {
95
-            Err("Failed to decode image fuzzy hash bytes from hex to bytes")
95
+            Err(Error::FormatHashBytes(value.to_string()))
96 96
         }
97 97
     }
98 98
 }
... ...
@@ -205,11 +207,11 @@ pub unsafe extern "C" fn _fuzzy_hash_calculate_image(
205 205
     err: *mut *mut FFIError,
206 206
 ) -> bool {
207 207
     if hash_out.is_null() {
208
-        return ffi_error!(err = err, FuzzyHashError::NullParam("hash_out"));
208
+        return ffi_error!(err = err, Error::NullParam("hash_out"));
209 209
     }
210 210
 
211 211
     let buffer = if file_bytes.is_null() {
212
-        return ffi_error!(err = err, FuzzyHashError::NullParam("file_bytes"));
212
+        return ffi_error!(err = err, Error::NullParam("file_bytes"));
213 213
     } else {
214 214
         slice::from_raw_parts(file_bytes, file_size)
215 215
     };
... ...
@@ -223,7 +225,7 @@ pub unsafe extern "C" fn _fuzzy_hash_calculate_image(
223 223
     if hash_out_len < hash_bytes.len() {
224 224
         return ffi_error!(
225 225
             err = err,
226
-            FuzzyHashError::InvalidParameter(format!(
226
+            Error::InvalidParameter(format!(
227 227
                 "hash_bytes output parameter too small to hold the hash: {} < {}",
228 228
                 hash_out_len,
229 229
                 hash_bytes.len()
... ...
@@ -257,24 +259,24 @@ impl FuzzyHashMap {
257 257
         hexsig: &str,
258 258
         lsig_id: u32,
259 259
         subsig_id: u32,
260
-    ) -> Result<(), FuzzyHashError> {
260
+    ) -> Result<(), Error> {
261 261
         let mut hexsig_split = hexsig.split('#');
262 262
 
263 263
         let algorithm = match hexsig_split.next() {
264 264
             Some(x) => x,
265
-            None => return Err(FuzzyHashError::Format),
265
+            None => return Err(Error::Format),
266 266
         };
267 267
 
268 268
         let hash = match hexsig_split.next() {
269 269
             Some(x) => x,
270
-            None => return Err(FuzzyHashError::Format),
270
+            None => return Err(Error::Format),
271 271
         };
272 272
 
273 273
         let distance: u32 = match hexsig_split.next() {
274 274
             Some(x) => match x.parse::<u32>() {
275 275
                 Ok(n) => n,
276 276
                 Err(_) => {
277
-                    return Err(FuzzyHashError::FormatHammingDistance(x.to_string()));
277
+                    return Err(Error::FormatHammingDistance(x.to_string()));
278 278
                 }
279 279
             },
280 280
             None => 0,
... ...
@@ -285,7 +287,7 @@ impl FuzzyHashMap {
285 285
             error!(
286 286
             "Non-zero hamming distances for image fuzzy hashes are not supported in this version."
287 287
         );
288
-            return Err(FuzzyHashError::InvalidHammingDistance(distance));
288
+            return Err(Error::InvalidHammingDistance(distance));
289 289
         }
290 290
 
291 291
         match algorithm {
... ...
@@ -293,7 +295,7 @@ impl FuzzyHashMap {
293 293
                 // Convert the hash string to an image fuzzy hash bytes struct
294 294
                 let image_fuzzy_hash = hash
295 295
                     .try_into()
296
-                    .map_err(|e| FuzzyHashError::FormatHashBytes(format!("{}: {}", e, hash)))?;
296
+                    .map_err(|e| Error::FormatHashBytes(format!("{}: {}", e, hash)))?;
297 297
 
298 298
                 let fuzzy_hash = FuzzyHash::Image(image_fuzzy_hash);
299 299
 
... ...
@@ -308,14 +310,14 @@ impl FuzzyHashMap {
308 308
                 // Then add the current meta struct to the entry.
309 309
                 self.hashmap
310 310
                     .entry(fuzzy_hash)
311
-                    .or_insert_with(Vec::new)
311
+                    .or_default()
312 312
                     .push(meta);
313 313
 
314 314
                 Ok(())
315 315
             }
316 316
             _ => {
317 317
                 error!("Unknown fuzzy hash algorithm: {}", algorithm);
318
-                Err(FuzzyHashError::UnknownAlgorithm(algorithm.to_string()))
318
+                Err(Error::UnknownAlgorithm(algorithm.to_string()))
319 319
             }
320 320
         }
321 321
     }
... ...
@@ -412,17 +414,17 @@ impl FuzzyHashMap {
412 412
 ///
413 413
 /// param: hash_out is an output variable
414 414
 /// param: hash_out_len indicates the size of the hash_out buffer
415
-pub fn fuzzy_hash_calculate_image(buffer: &[u8]) -> Result<Vec<u8>, FuzzyHashError> {
415
+pub fn fuzzy_hash_calculate_image(buffer: &[u8]) -> Result<Vec<u8>, Error> {
416 416
 
417 417
     // Load image and attempt to catch panics in case the decoders encounter unexpected issues
418
-    let result = panic::catch_unwind(|| -> Result<DynamicImage, FuzzyHashError> {
419
-        let image = image::load_from_memory(buffer).map_err(FuzzyHashError::ImageLoad)?;
418
+    let result = panic::catch_unwind(|| -> Result<DynamicImage, Error> {
419
+        let image = image::load_from_memory(buffer).map_err(Error::ImageLoad)?;
420 420
         Ok(image)
421 421
     });
422 422
 
423 423
     let og_image = match result {
424 424
         Ok(image) => image?,
425
-        Err(_) => return Err(FuzzyHashError::ImageLoadPanic()),
425
+        Err(_) => return Err(Error::ImageLoadPanic()),
426 426
     };
427 427
 
428 428
     // Drop the alpha channel (if exists).
... ...
@@ -24,9 +24,13 @@
24 24
 pub mod sys;
25 25
 
26 26
 pub mod cdiff;
27
+pub mod css_image_extract;
28
+pub mod ctx;
27 29
 pub mod evidence;
28 30
 pub mod ffi_util;
31
+pub mod fmap;
29 32
 pub mod fuzzy_hash;
30 33
 pub mod logging;
34
+pub mod onenote;
35
+pub mod scanners;
31 36
 pub mod util;
32
-pub mod css_image_extract;
33 37
new file mode 100644
... ...
@@ -0,0 +1,285 @@
0
+/*
1
+ *  Onenote document parser to extract embedded files.
2
+ *
3
+ *  Copyright (C) 2023 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
4
+ *
5
+ *  Authors: Micah Snyder
6
+ *
7
+ *  This program is free software; you can redistribute it and/or modify
8
+ *  it under the terms of the GNU General Public License version 2 as
9
+ *  published by the Free Software Foundation.
10
+ *
11
+ *  This program is distributed in the hope that it will be useful,
12
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ *  GNU General Public License for more details.
15
+ *
16
+ *  You should have received a copy of the GNU General Public License
17
+ *  along with this program; if not, write to the Free Software
18
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19
+ *  MA 02110-1301, USA.
20
+ */
21
+
22
+use std::{
23
+    convert::TryInto,
24
+    mem, panic,
25
+    path::{Path, PathBuf},
26
+};
27
+
28
+use hex_literal::hex;
29
+use log::{debug, error};
30
+use onenote_parser;
31
+
32
+/// Error enumerates all possible errors returned by this library.
33
+#[derive(thiserror::Error, Debug)]
34
+pub enum Error {
35
+    #[error("Invalid format")]
36
+    Format,
37
+
38
+    #[error("Invalid parameter: {0}")]
39
+    InvalidParameter(String),
40
+
41
+    #[error("Failed to open file: {0}, {1}")]
42
+    FailedToOpen(PathBuf, String),
43
+
44
+    #[error("Failed to get size for file: {0}")]
45
+    FailedToGetFileSize(PathBuf),
46
+
47
+    #[error("{0} parameter is NULL")]
48
+    NullParam(&'static str),
49
+
50
+    #[error("No more files to extract")]
51
+    NoMoreFiles,
52
+
53
+    #[error("Unable to parse OneNote file")]
54
+    Parse,
55
+
56
+    #[error("Failed to parse OneNote file due to a panic in the onenote_parser library")]
57
+    OneNoteParserPanic,
58
+}
59
+
60
+fn find_bytes(haystack: &[u8], needle: &[u8]) -> Option<usize> {
61
+    haystack
62
+        .windows(needle.len())
63
+        .position(|window| window == needle)
64
+}
65
+
66
+/// Struct representing a file extracted from a OneNote document.
67
+/// This has the option of providing a file name, if one was found when extracting the file.
68
+pub struct ExtractedFile {
69
+    pub name: Option<String>,
70
+    pub data: Vec<u8>,
71
+}
72
+
73
+/// Struct used for a file handle for our OneNote parser.
74
+/// This struct is used to keep track of state for our iterator to work through the document extracting each file.
75
+/// There are three different ways we keep track of state depending on the file format and the way in which the file was opened.
76
+#[derive(Default)]
77
+pub struct OneNote<'a> {
78
+    embedded_files: Vec<ExtractedFile>,
79
+    remaining_vec: Option<Vec<u8>>,
80
+    remaining: Option<&'a [u8]>,
81
+}
82
+
83
+// https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-onestore/8806fd18-6735-4874-b111-227b83eaac26
84
+#[repr(packed)]
85
+#[allow(dead_code)]
86
+struct FileDataHeader {
87
+    guid_header: [u8; 16],
88
+    cb_length: u64,
89
+    unused: u32,
90
+    reserved: u64,
91
+}
92
+const SIZE_OF_FILE_DATA_HEADER: usize = mem::size_of::<FileDataHeader>();
93
+
94
+// Hex sequence identifying the start of a file data store object.
95
+const FILE_DATA_STORE_OBJECT: &[u8] = &hex!("e716e3bd65261145a4c48d4d0b7a9eac");
96
+
97
+// Hex sequence identifying the start of a OneNote file.
98
+const ONE_MAGIC: &[u8] = &hex!("e4525c7b8cd8a74daeb15378d02996d3");
99
+
100
+impl<'a> OneNote<'a> {
101
+    /// Open a OneNote document given a slice bytes.
102
+    pub fn from_bytes(data: &'a [u8], filename: &Path) -> Result<OneNote<'a>, Error> {
103
+        debug!(
104
+            "Inspecting OneNote file for attachments from in-memory buffer of size {}-bytes named {}\n",
105
+            data.len(), filename.to_string_lossy()
106
+        );
107
+
108
+        fn parse_section_buffer(data: &[u8], filename: &Path) -> Result<Vec<ExtractedFile>, Error> {
109
+            let mut embedded_files: Vec<ExtractedFile> = vec![];
110
+            let mut parser = onenote_parser::Parser::new();
111
+
112
+            if let Ok(section) = parser.parse_section_buffer(data, filename) {
113
+                // file appears to be OneStore 2.8 `.one` file.
114
+                section.page_series().iter().for_each(|page_series| {
115
+                    page_series.pages().iter().for_each(|page| {
116
+                        page.contents().iter().for_each(|page_content| {
117
+                            if let Some(page_outline) = page_content.outline() {
118
+                                page_outline.items().iter().for_each(|outline_item| {
119
+                                    outline_item.element().iter().for_each(|&outline_element| {
120
+                                        outline_element.contents().iter().for_each(|content| {
121
+                                            if let Some(embedded_file) = content.embedded_file() {
122
+                                                let data = embedded_file.data();
123
+                                                let name = embedded_file.filename();
124
+
125
+                                                // If name is empty, set to None.
126
+                                                let name = if name.is_empty() {
127
+                                                    debug!("Found unnamed attached file of size {}-bytes", data.len());
128
+                                                    None
129
+                                                } else {
130
+                                                    debug!("Found attached file '{}' of size {}-bytes", name, data.len());
131
+                                                    Some(name.to_string())
132
+                                                };
133
+
134
+                                                embedded_files.push(ExtractedFile {
135
+                                                    name,
136
+                                                    data: data.to_vec(),
137
+                                                });
138
+                                            }
139
+                                        });
140
+                                    });
141
+                                });
142
+                            }
143
+                        });
144
+                    });
145
+                });
146
+            } else {
147
+                return Err(Error::Parse);
148
+            }
149
+
150
+            Ok(embedded_files)
151
+        }
152
+
153
+        // Try to parse the section buffer using the onenote_parser crate.
154
+        // Attempt to catch panics in case the parser encounter unexpected issues.
155
+        let result_result = panic::catch_unwind(|| -> Result<Vec<ExtractedFile>, Error> {
156
+            parse_section_buffer(data, filename)
157
+        });
158
+
159
+        // Check if it panicked. If no panic, grab the parse result.
160
+        let result = result_result.map_err(|_| Error::OneNoteParserPanic)?;
161
+
162
+        if let Ok(embedded_files) = result {
163
+            // Successfully parsed the OneNote file with the onenote_parser crate.
164
+            Ok(OneNote {
165
+                embedded_files,
166
+                ..Default::default()
167
+            })
168
+        } else {
169
+            debug!("Unable to parse OneNote file with onenote_parser crate. Trying a different method known to work with older office 2010 OneNote files to extract attachments.");
170
+
171
+            let embedded_files: Vec<ExtractedFile> = vec![];
172
+
173
+            // Verify that the OneNote document file magic is correct.
174
+            // We don't check this for the onenote_parser crate because it does this for us, and may add support for newer OneNote file formats in the future.
175
+            let file_magic = data.get(0..16).ok_or(Error::Format)?;
176
+            if file_magic != ONE_MAGIC {
177
+                return Err(Error::Format);
178
+            }
179
+
180
+            Ok(OneNote {
181
+                embedded_files,
182
+                remaining: Some(data),
183
+                ..Default::default()
184
+            })
185
+        }
186
+    }
187
+
188
+    /// Open a OneNote document given the document was provided as a slice of bytes.
189
+    pub fn next_file(&mut self) -> Option<ExtractedFile> {
190
+        debug!("Looking to extract file from OneNote section...");
191
+
192
+        let mut file_data: Option<Vec<u8>> = None;
193
+
194
+        let remaining = if let Some(remaining_in) = self.remaining {
195
+            let remaining = if let Some(pos) = find_bytes(remaining_in, FILE_DATA_STORE_OBJECT) {
196
+                let (_, remaining) = remaining_in.split_at(pos);
197
+                // Found file data store object.
198
+                remaining
199
+            } else {
200
+                return None;
201
+            };
202
+
203
+            let data_length = if let Some(x) = remaining.get(16..20) {
204
+                u32::from_le_bytes(x.try_into().unwrap()) as u64
205
+            } else {
206
+                return None;
207
+            };
208
+
209
+            let data: &[u8] = remaining
210
+                .get(SIZE_OF_FILE_DATA_HEADER..SIZE_OF_FILE_DATA_HEADER + data_length as usize)?;
211
+
212
+            file_data = Some(data.to_vec());
213
+
214
+            Some(&remaining[SIZE_OF_FILE_DATA_HEADER + (data_length as usize)..remaining.len()])
215
+        } else {
216
+            None
217
+        };
218
+
219
+        self.remaining = remaining;
220
+
221
+        file_data.map(|data| ExtractedFile { data, name: None })
222
+    }
223
+
224
+    /// Get the next file from the OneNote document using the method required for when we've read the file into a Vec.
225
+    pub fn next_file_vec(&mut self) -> Option<ExtractedFile> {
226
+        debug!("Looking to extract file from OneNote section...");
227
+
228
+        let mut file_data: Option<Vec<u8>> = None;
229
+
230
+        self.remaining_vec = if let Some(ref remaining_vec) = self.remaining_vec {
231
+            let remaining = if let Some(pos) = find_bytes(remaining_vec, FILE_DATA_STORE_OBJECT) {
232
+                let (_, remaining) = remaining_vec.split_at(pos);
233
+                // Found file data store object.
234
+                remaining
235
+            } else {
236
+                return None;
237
+            };
238
+
239
+            let data_length = if let Some(x) = remaining.get(16..20) {
240
+                u32::from_le_bytes(x.try_into().unwrap()) as u64
241
+            } else {
242
+                return None;
243
+            };
244
+
245
+            let data: &[u8] = remaining
246
+                .get(SIZE_OF_FILE_DATA_HEADER..SIZE_OF_FILE_DATA_HEADER + data_length as usize)?;
247
+
248
+            file_data = Some(data.to_vec());
249
+
250
+            Some(Vec::from(
251
+                &remaining[SIZE_OF_FILE_DATA_HEADER + (data_length as usize)..remaining.len()],
252
+            ))
253
+        } else {
254
+            None
255
+        };
256
+
257
+        file_data.map(|data| ExtractedFile { data, name: None })
258
+    }
259
+
260
+    /// Get the next file from the OneNote document using the method required for the onenote_parser crate.
261
+    pub fn next_file_parser(&mut self) -> Option<ExtractedFile> {
262
+        self.embedded_files.pop()
263
+    }
264
+}
265
+
266
+impl<'a> Iterator for OneNote<'a> {
267
+    type Item = ExtractedFile;
268
+
269
+    fn next(&mut self) -> Option<ExtractedFile> {
270
+        // Find the next embedded file
271
+        if self.remaining.is_some() {
272
+            // Data stored in a slice.
273
+            self.next_file()
274
+        } else if self.remaining_vec.is_some() {
275
+            // Data stored in a Vec.
276
+            self.next_file_vec()
277
+        } else if !self.embedded_files.is_empty() {
278
+            // Data stored in a Vec.
279
+            self.next_file_parser()
280
+        } else {
281
+            None
282
+        }
283
+    }
284
+}
0 285
new file mode 100644
... ...
@@ -0,0 +1,129 @@
0
+/*
1
+ *  Rust equivalent of libclamav's scanners.c module
2
+ *
3
+ *  Copyright (C) 2023 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
4
+ *
5
+ *  Authors: Micah Snyder
6
+ *
7
+ *  This program is free software; you can redistribute it and/or modify
8
+ *  it under the terms of the GNU General Public License version 2 as
9
+ *  published by the Free Software Foundation.
10
+ *
11
+ *  This program is distributed in the hope that it will be useful,
12
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
+ *  GNU General Public License for more details.
15
+ *
16
+ *  You should have received a copy of the GNU General Public License
17
+ *  along with this program; if not, write to the Free Software
18
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19
+ *  MA 02110-1301, USA.
20
+ */
21
+
22
+use std::{
23
+    ffi::{c_char, CString},
24
+    path::Path,
25
+    ptr::null_mut,
26
+};
27
+
28
+use libc::c_void;
29
+use log::{debug, error, warn};
30
+
31
+use crate::{
32
+    ctx,
33
+    onenote::OneNote,
34
+    sys::{cl_error_t, cl_error_t_CL_ERROR, cl_error_t_CL_SUCCESS, cli_ctx, cli_magic_scan_buff},
35
+};
36
+
37
+/// Rust wrapper of libclamav's cli_magic_scan_buff() function.
38
+/// Use magic sigs to identify the file type and then scan it.
39
+fn magic_scan(ctx: *mut cli_ctx, buf: &[u8], name: Option<String>) -> cl_error_t {
40
+    let ptr = buf.as_ptr();
41
+    let len = buf.len();
42
+
43
+    match &name {
44
+        Some(name) => debug!("Scanning {}-byte file named {}.", len, name),
45
+        None => debug!("Scanning {}-byte unnamed file.", len),
46
+    }
47
+
48
+    // Convert name to a C string.
49
+    let name = match name {
50
+        Some(name) => name,
51
+        None => String::from(""),
52
+    };
53
+
54
+    let name_ptr: *mut c_char = match CString::new(name) {
55
+        Ok(name_cstr) => {
56
+            // into_raw() so name_cstr doesn't get dropped and
57
+            // we don't do an unsafe deref of the pointer.
58
+            name_cstr.into_raw()
59
+        }
60
+        Err(_) => null_mut(),
61
+    };
62
+
63
+    let ret = unsafe { cli_magic_scan_buff(ptr as *const c_void, len, ctx, name_ptr, 0) };
64
+
65
+    if ret != cl_error_t_CL_SUCCESS {
66
+        debug!("cli_magic_scan_buff returned error: {}", ret);
67
+    }
68
+
69
+    // Okay now safe to drop the name CString.
70
+    let _ = unsafe { CString::from_raw(name_ptr) };
71
+
72
+    ret
73
+}
74
+
75
+/// Scan a OneNote file for attachments
76
+///
77
+/// # Safety
78
+///
79
+/// Must be a valid ctx pointer.
80
+#[no_mangle]
81
+pub unsafe extern "C" fn scan_onenote(ctx: *mut cli_ctx) -> cl_error_t {
82
+    let fmap = match ctx::current_fmap(ctx) {
83
+        Ok(fmap) => fmap,
84
+        Err(e) => {
85
+            warn!("Error getting FMap from ctx: {e}");
86
+            return cl_error_t_CL_ERROR;
87
+        }
88
+    };
89
+
90
+    let file_bytes = match fmap.need_off(0, fmap.len()) {
91
+        Ok(bytes) => bytes,
92
+        Err(err) => {
93
+            error!(
94
+                "Failed to get file bytes for fmap of size {}: {err}",
95
+                fmap.len()
96
+            );
97
+            return cl_error_t_CL_ERROR;
98
+        }
99
+    };
100
+
101
+    let one = match OneNote::from_bytes(file_bytes, Path::new(fmap.name())) {
102
+        Ok(x) => x,
103
+        Err(err) => {
104
+            error!("Failed to parse OneNote file: {}", err.to_string());
105
+            return cl_error_t_CL_ERROR;
106
+        }
107
+    };
108
+
109
+    let mut scan_result = cl_error_t_CL_SUCCESS;
110
+
111
+    one.into_iter().all(|attachment| {
112
+        debug!(
113
+            "Extracted {}-byte attachment with name: {:?}",
114
+            attachment.data.len(),
115
+            attachment.name
116
+        );
117
+
118
+        let ret = magic_scan(ctx, &attachment.data, attachment.name);
119
+        if ret != cl_error_t_CL_SUCCESS {
120
+            scan_result = ret;
121
+            return false;
122
+        }
123
+
124
+        true
125
+    });
126
+
127
+    scan_result
128
+}
... ...
@@ -375,34 +375,36 @@ pub const cli_file_CL_TYPE_HWP3: cli_file = 550;
375 375
 pub const cli_file_CL_TYPE_OOXML_HWP: cli_file = 551;
376 376
 pub const cli_file_CL_TYPE_PS: cli_file = 552;
377 377
 pub const cli_file_CL_TYPE_EGG: cli_file = 553;
378
-pub const cli_file_CL_TYPE_PART_ANY: cli_file = 554;
379
-pub const cli_file_CL_TYPE_PART_HFSPLUS: cli_file = 555;
380
-pub const cli_file_CL_TYPE_MBR: cli_file = 556;
381
-pub const cli_file_CL_TYPE_HTML: cli_file = 557;
382
-pub const cli_file_CL_TYPE_MAIL: cli_file = 558;
383
-pub const cli_file_CL_TYPE_SFX: cli_file = 559;
384
-pub const cli_file_CL_TYPE_ZIPSFX: cli_file = 560;
385
-pub const cli_file_CL_TYPE_RARSFX: cli_file = 561;
386
-pub const cli_file_CL_TYPE_7ZSFX: cli_file = 562;
387
-pub const cli_file_CL_TYPE_CABSFX: cli_file = 563;
388
-pub const cli_file_CL_TYPE_ARJSFX: cli_file = 564;
389
-pub const cli_file_CL_TYPE_EGGSFX: cli_file = 565;
390
-pub const cli_file_CL_TYPE_NULSFT: cli_file = 566;
391
-pub const cli_file_CL_TYPE_AUTOIT: cli_file = 567;
392
-pub const cli_file_CL_TYPE_ISHIELD_MSI: cli_file = 568;
393
-pub const cli_file_CL_TYPE_ISO9660: cli_file = 569;
394
-pub const cli_file_CL_TYPE_DMG: cli_file = 570;
395
-pub const cli_file_CL_TYPE_GPT: cli_file = 571;
396
-pub const cli_file_CL_TYPE_APM: cli_file = 572;
397
-pub const cli_file_CL_TYPE_XDP: cli_file = 573;
398
-pub const cli_file_CL_TYPE_XML_WORD: cli_file = 574;
399
-pub const cli_file_CL_TYPE_XML_XL: cli_file = 575;
400
-pub const cli_file_CL_TYPE_XML_HWP: cli_file = 576;
401
-pub const cli_file_CL_TYPE_HWPOLE2: cli_file = 577;
402
-pub const cli_file_CL_TYPE_MHTML: cli_file = 578;
403
-pub const cli_file_CL_TYPE_LNK: cli_file = 579;
404
-pub const cli_file_CL_TYPE_OTHER: cli_file = 580;
405
-pub const cli_file_CL_TYPE_IGNORED: cli_file = 581;
378
+pub const cli_file_CL_TYPE_ONENOTE: cli_file = 554;
379
+pub const cli_file_CL_TYPE_PART_ANY: cli_file = 555;
380
+pub const cli_file_CL_TYPE_PART_HFSPLUS: cli_file = 556;
381
+pub const cli_file_CL_TYPE_MBR: cli_file = 557;
382
+pub const cli_file_CL_TYPE_HTML: cli_file = 558;
383
+pub const cli_file_CL_TYPE_MAIL: cli_file = 559;
384
+pub const cli_file_CL_TYPE_SFX: cli_file = 560;
385
+pub const cli_file_CL_TYPE_ZIPSFX: cli_file = 561;
386
+pub const cli_file_CL_TYPE_RARSFX: cli_file = 562;
387
+pub const cli_file_CL_TYPE_7ZSFX: cli_file = 563;
388
+pub const cli_file_CL_TYPE_CABSFX: cli_file = 564;
389
+pub const cli_file_CL_TYPE_ARJSFX: cli_file = 565;
390
+pub const cli_file_CL_TYPE_EGGSFX: cli_file = 566;
391
+pub const cli_file_CL_TYPE_NULSFT: cli_file = 567;
392
+pub const cli_file_CL_TYPE_AUTOIT: cli_file = 568;
393
+pub const cli_file_CL_TYPE_ISHIELD_MSI: cli_file = 569;
394
+pub const cli_file_CL_TYPE_ISO9660: cli_file = 570;
395
+pub const cli_file_CL_TYPE_DMG: cli_file = 571;
396
+pub const cli_file_CL_TYPE_GPT: cli_file = 572;
397
+pub const cli_file_CL_TYPE_APM: cli_file = 573;
398
+pub const cli_file_CL_TYPE_XDP: cli_file = 574;
399
+pub const cli_file_CL_TYPE_XML_WORD: cli_file = 575;
400
+pub const cli_file_CL_TYPE_XML_XL: cli_file = 576;
401
+pub const cli_file_CL_TYPE_XML_HWP: cli_file = 577;
402
+pub const cli_file_CL_TYPE_HWPOLE2: cli_file = 578;
403
+pub const cli_file_CL_TYPE_MHTML: cli_file = 579;
404
+pub const cli_file_CL_TYPE_LNK: cli_file = 580;
405
+pub const cli_file_CL_TYPE_UDF: cli_file = 581;
406
+pub const cli_file_CL_TYPE_OTHER: cli_file = 582;
407
+pub const cli_file_CL_TYPE_IGNORED: cli_file = 583;
406 408
 pub type cli_file = ::std::os::raw::c_uint;
407 409
 pub use self::cli_file as cli_file_t;
408 410
 #[repr(C)]
... ...
@@ -612,6 +614,7 @@ pub struct recursion_level_tag {
612 612
 }
613 613
 pub type recursion_level_t = recursion_level_tag;
614 614
 pub type evidence_t = *mut ::std::os::raw::c_void;
615
+pub type onedump_t = *mut ::std::os::raw::c_void;
615 616
 #[repr(C)]
616 617
 #[derive(Debug, Copy, Clone)]
617 618
 pub struct cli_ctx_tag {
... ...
@@ -706,6 +709,7 @@ pub struct cl_engine {
706 706
     pub tmpdir: *mut ::std::os::raw::c_char,
707 707
     pub keeptmp: u32,
708 708
     pub engine_options: u64,
709
+    pub cache_size: u32,
709 710
     pub maxscantime: u32,
710 711
     pub maxscansize: u64,
711 712
     pub maxfilesize: u64,
... ...
@@ -1170,6 +1174,16 @@ extern "C" {
1170 1170
 }
1171 1171
 pub type css_image_extractor_t = *mut ::std::os::raw::c_void;
1172 1172
 pub type css_image_handle_t = *mut ::std::os::raw::c_void;
1173
+extern "C" {
1174
+    #[doc = " @brief   Convenience wrapper for cli_magic_scan_nested_fmap_type().\n\n Creates an fmap and calls cli_magic_scan_nested_fmap_type() for you, with type CL_TYPE_ANY.\n\n @param buffer        Pointer to the buffer to be scanned.\n @param length        Size in bytes of the buffer being scanned.\n @param ctx           Scanning context structure.\n @param name          (optional) Original name of the file (to set fmap name metadata)\n @param attributes    Layer attributes of the file being scanned (is it normalized, decrypted, etc)\n @return int          CL_SUCCESS, or an error code."]
1175
+    pub fn cli_magic_scan_buff(
1176
+        buffer: *const ::std::os::raw::c_void,
1177
+        length: usize,
1178
+        ctx: *mut cli_ctx,
1179
+        name: *const ::std::os::raw::c_char,
1180
+        attributes: u32,
1181
+    ) -> cl_error_t;
1182
+}
1173 1183
 #[repr(C)]
1174 1184
 #[derive(Debug, Copy, Clone)]
1175 1185
 pub struct re_guts {
... ...
@@ -20,7 +20,7 @@
20 20
  *  MA 02110-1301, USA.
21 21
  */
22 22
 
23
-use std::fs::File;
23
+use std::{ffi::CStr, fs::File};
24 24
 
25 25
 /// Obtain a std::fs::File from an i32 in a platform-independent manner.
26 26
 ///
... ...
@@ -46,3 +46,19 @@ pub fn file_from_fd_or_handle(fd: i32) -> File {
46 46
     #[cfg(not(any(windows, unix)))]
47 47
     compile_error!("implemented only for unix and windows targets")
48 48
 }
49
+
50
+/// Get a string from a pointer
51
+///
52
+/// # Safety
53
+///
54
+/// The caller is responsible for making sure the lifetime of the pointer
55
+/// exceeds the lifetime of the output string.
56
+///
57
+/// ptr must be a valid pointer to a C string.
58
+pub unsafe fn str_from_ptr(ptr: *const i8) -> Result<Option<&'static str>, std::str::Utf8Error> {
59
+    if ptr.is_null() {
60
+        return Ok(None);
61
+    }
62
+
63
+    Some(unsafe { CStr::from_ptr(ptr) }.to_str()).transpose()
64
+}
... ...
@@ -82,7 +82,7 @@ class TC(testcase.TestCase):
82 82
 
83 83
         expected_stderr = [
84 84
             'LibClamAV Error: Failed to load',
85
-            'Invalid hash: Image fuzzy hash must be 16 characters in length: abcdef',
85
+            'Invalid hash: ImageFuzzyHash hash must be 16 characters in length: abcdef',
86 86
         ]
87 87
         unexpected_stdout = [
88 88
             'logo.png.bad.UNOFFICIAL FOUND',
... ...
@@ -398,6 +398,12 @@ TCPAddr localhost
398 398
 # Default: yes
399 399
 #ScanHWP3 yes
400 400
 
401
+# This option enables scanning of OneNote files.
402
+# If you turn off this option, the original files will still be scanned, but
403
+# without additional processing.
404
+# Default: yes
405
+#ScanOneNote yes
406
+
401 407
 
402 408
 ##
403 409
 ## Mail files