1mod about;
2#[cfg(test)]
3mod about_scan_test;
4#[cfg(test)]
5mod about_test;
6mod alpine;
7#[cfg(test)]
8mod alpine_scan_test;
9mod arch;
10#[cfg(test)]
11mod arch_scan_test;
12#[cfg(test)]
13mod arch_test;
14mod autotools;
15#[cfg(test)]
16mod autotools_test;
17mod bazel;
18#[cfg(test)]
19mod bazel_module_test;
20#[cfg(test)]
21mod bazel_test;
22mod bower;
23#[cfg(test)]
24mod bower_scan_test;
25#[cfg(test)]
26mod bower_test;
27mod buck;
28#[cfg(test)]
29mod buck_test;
30mod bun_lock;
31#[cfg(test)]
32mod bun_lock_test;
33mod bun_lockb;
34#[cfg(test)]
35mod bun_lockb_test;
36mod cargo;
37mod cargo_lock;
38#[cfg(test)]
39mod cargo_lock_test;
40#[cfg(test)]
41mod cargo_scan_test;
42#[cfg(test)]
43mod cargo_test;
44mod chef;
45#[cfg(test)]
46mod chef_scan_test;
47#[cfg(test)]
48mod chef_test;
49mod citation;
50#[cfg(test)]
51mod citation_test;
52mod clojure;
53#[cfg(test)]
54mod clojure_test;
55#[cfg(test)]
56mod cocoapods_scan_test;
57pub(crate) mod compiled_binary;
58mod composer;
59#[cfg(test)]
60mod composer_scan_test;
61#[cfg(test)]
62mod composer_test;
63mod conan;
64mod conan_data;
65#[cfg(test)]
66mod conan_data_test;
67#[cfg(test)]
68mod conan_scan_test;
69#[cfg(test)]
70mod conan_test;
71mod conda;
72mod conda_meta_json;
73#[cfg(test)]
74mod conda_meta_json_test;
75#[cfg(test)]
76mod conda_scan_test;
77#[cfg(test)]
78mod conda_test;
79mod cpan;
80mod cpan_dist_ini;
81#[cfg(test)]
82mod cpan_dist_ini_test;
83mod cpan_makefile_pl;
84#[cfg(test)]
85mod cpan_makefile_pl_test;
86#[cfg(test)]
87mod cpan_scan_test;
88#[cfg(test)]
89mod cpan_test;
90mod cran;
91#[cfg(test)]
92mod cran_scan_test;
93#[cfg(test)]
94mod cran_test;
95mod dart;
96#[cfg(test)]
97mod dart_scan_test;
98#[cfg(test)]
99mod dart_test;
100mod debian;
101#[cfg(test)]
102mod debian_scan_test;
103#[cfg(test)]
104mod debian_test;
105mod deno;
106mod deno_lock;
107#[cfg(test)]
108mod deno_lock_test;
109#[cfg(test)]
110mod deno_scan_test;
111#[cfg(test)]
112mod deno_test;
113mod docker;
114#[cfg(test)]
115mod docker_scan_test;
116#[cfg(test)]
117mod docker_test;
118mod freebsd;
119#[cfg(test)]
120mod freebsd_scan_test;
121#[cfg(test)]
122mod freebsd_test;
123mod gitmodules;
124#[cfg(test)]
125mod gitmodules_scan_test;
126mod go;
127mod go_mod_graph;
128#[cfg(test)]
129mod go_scan_test;
130#[cfg(test)]
131mod go_test;
132#[cfg(test)]
133mod go_work_test;
134#[cfg(all(test, feature = "golden-tests"))]
135pub(crate) mod golden_test_utils;
136mod gradle;
137mod gradle_lock;
138#[cfg(test)]
139mod gradle_lock_test;
140mod gradle_module;
141#[cfg(test)]
142mod gradle_module_scan_test;
143#[cfg(test)]
144mod gradle_module_test;
145#[cfg(test)]
146mod gradle_scan_test;
147mod hackage;
148#[cfg(test)]
149mod hackage_scan_test;
150#[cfg(test)]
151mod hackage_test;
152mod haxe;
153#[cfg(test)]
154mod haxe_scan_test;
155#[cfg(test)]
156mod haxe_test;
157mod helm;
158#[cfg(test)]
159mod helm_scan_test;
160#[cfg(test)]
161mod helm_test;
162mod hex_lock;
163#[cfg(test)]
164mod hex_lock_test;
165mod julia;
166#[cfg(test)]
167mod julia_test;
168mod license_normalization;
169mod maven;
170#[cfg(test)]
171mod maven_scan_test;
172#[cfg(test)]
173mod maven_test;
174mod meson;
175#[cfg(test)]
176mod meson_scan_test;
177#[cfg(test)]
178mod meson_test;
179pub mod metadata;
180mod microsoft_update_manifest;
181#[cfg(test)]
182mod microsoft_update_manifest_test;
183mod misc;
184#[cfg(test)]
185mod misc_test;
186mod nix;
187#[cfg(test)]
188mod nix_scan_test;
189#[cfg(test)]
190mod nix_test;
191mod npm;
192mod npm_lock;
193#[cfg(test)]
194mod npm_lock_test;
195#[cfg(test)]
196mod npm_scan_test;
197#[cfg(test)]
198mod npm_test;
199mod npm_workspace;
200#[cfg(test)]
201mod npm_workspace_test;
202mod nuget;
203#[cfg(test)]
204mod nuget_scan_test;
205#[cfg(test)]
206mod nuget_test;
207mod opam;
208#[cfg(test)]
209mod opam_scan_test;
210mod os_release;
211#[cfg(test)]
212mod os_release_test;
213#[cfg(test)]
214mod osgi_test;
215mod pep508;
216mod pip_inspect_deplock;
217#[cfg(test)]
218mod pip_inspect_deplock_test;
219mod pipfile_lock;
220#[cfg(test)]
221mod pipfile_lock_test;
222mod pixi;
223#[cfg(test)]
224mod pixi_scan_test;
225#[cfg(test)]
226mod pixi_test;
227mod pnpm_lock;
228#[cfg(test)]
229mod pnpm_lock_test;
230mod podfile;
231mod podfile_lock;
232#[cfg(test)]
233mod podfile_lock_test;
234mod podspec;
235mod podspec_json;
236#[cfg(test)]
237mod podspec_json_test;
238mod poetry_lock;
239#[cfg(test)]
240mod poetry_lock_test;
241mod publiccode;
242#[cfg(test)]
243mod publiccode_test;
244mod pylock_toml;
245#[cfg(test)]
246mod pylock_toml_test;
247mod python;
248#[cfg(test)]
249mod python_scan_test;
250#[cfg(test)]
251mod python_test;
252mod readme;
253#[cfg(test)]
254mod readme_test;
255mod requirements_txt;
256#[cfg(test)]
257mod requirements_txt_test;
258pub(crate) mod rfc822;
259mod rpm_db;
260mod rpm_db_native;
261#[cfg(test)]
262mod rpm_db_scan_test;
263mod rpm_license_files;
264#[cfg(test)]
265mod rpm_license_files_test;
266mod rpm_mariner_manifest;
267#[cfg(test)]
268mod rpm_mariner_manifest_test;
269mod rpm_parser;
270#[cfg(test)]
271mod rpm_scan_test;
272mod rpm_specfile;
273#[cfg(test)]
274mod rpm_specfile_test;
275mod rpm_yumdb;
276mod ruby;
277#[cfg(test)]
278mod ruby_scan_test;
279#[cfg(test)]
280mod ruby_test;
281mod sbt;
282#[cfg(test)]
283mod sbt_test;
284#[cfg(test)]
285mod scan_test_utils;
286mod swift_manifest_json;
287#[cfg(test)]
288mod swift_manifest_json_test;
289mod swift_resolved;
290#[cfg(test)]
291mod swift_resolved_test;
292#[cfg(test)]
293mod swift_scan_test;
294mod swift_show_dependencies;
295#[cfg(test)]
296mod swift_show_dependencies_test;
297pub mod utils;
298mod uv_lock;
299#[cfg(test)]
300mod uv_lock_test;
301mod vcpkg;
302#[cfg(test)]
303mod vcpkg_scan_test;
304#[cfg(test)]
305mod vcpkg_test;
306pub(crate) mod windows_executable;
307#[cfg(test)]
308mod windows_executable_golden_test;
309mod yarn_lock;
310#[cfg(test)]
311mod yarn_lock_test;
312mod yarn_pnp;
313#[cfg(test)]
314mod yarn_pnp_test;
315
316#[cfg(all(test, feature = "golden-tests"))]
317mod golden_test;
318
319use std::cell::RefCell;
320use std::panic::{AssertUnwindSafe, catch_unwind};
321use std::path::Path;
322use std::sync::Arc;
323
324use crate::license_detection::LicenseDetectionEngine;
325use crate::models::{PackageData, PackageType};
326use crate::parsers::license_normalization::finalize_package_declared_license_references;
327use crate::parsers::utils::MAX_ITERATION_COUNT;
328
329thread_local! {
330 static PARSER_DIAGNOSTIC_STACK: RefCell<Vec<Vec<String>>> = const { RefCell::new(Vec::new()) };
331 static PARSER_LICENSE_ENGINE_STACK: RefCell<Vec<Option<Arc<LicenseDetectionEngine>>>> = const { RefCell::new(Vec::new()) };
332}
333
334#[derive(Debug, Default)]
335pub struct ParsePackagesResult {
336 pub packages: Vec<PackageData>,
337 pub scan_errors: Vec<String>,
338}
339
340fn panic_payload_to_string(payload: &(dyn std::any::Any + Send)) -> String {
341 if let Some(message) = payload.downcast_ref::<&str>() {
342 (*message).to_string()
343 } else if let Some(message) = payload.downcast_ref::<String>() {
344 message.clone()
345 } else {
346 "unknown panic payload".to_string()
347 }
348}
349
350pub(crate) fn capture_parser_diagnostics<F>(
351 extract: F,
352 handler_name: &str,
353 path: &Path,
354 license_engine: Option<Arc<LicenseDetectionEngine>>,
355) -> ParsePackagesResult
356where
357 F: FnOnce() -> Vec<PackageData>,
358{
359 PARSER_DIAGNOSTIC_STACK.with(|stack| {
360 stack.borrow_mut().push(Vec::new());
361 });
362 PARSER_LICENSE_ENGINE_STACK.with(|stack| {
363 stack.borrow_mut().push(license_engine);
364 });
365
366 let extract_result = catch_unwind(AssertUnwindSafe(|| {
367 extract()
368 .into_iter()
369 .map(|mut package| {
370 finalize_package_declared_license_references(&mut package);
371 package
372 })
373 .take(MAX_ITERATION_COUNT)
374 .collect::<Vec<_>>()
375 }));
376 PARSER_LICENSE_ENGINE_STACK.with(|stack| {
377 stack.borrow_mut().pop();
378 });
379 let mut scan_errors =
380 PARSER_DIAGNOSTIC_STACK.with(|stack| stack.borrow_mut().pop().unwrap_or_default());
381
382 match extract_result {
383 Ok(packages) => ParsePackagesResult {
384 packages,
385 scan_errors,
386 },
387 Err(payload) => {
388 scan_errors.push(format!(
389 "{} panicked while parsing {}: {}",
390 handler_name,
391 path.display(),
392 panic_payload_to_string(payload.as_ref())
393 ));
394 ParsePackagesResult {
395 packages: Vec::new(),
396 scan_errors,
397 }
398 }
399 }
400}
401
402pub(crate) fn active_parser_license_engine() -> Option<Arc<LicenseDetectionEngine>> {
403 PARSER_LICENSE_ENGINE_STACK.with(|stack| stack.borrow().last().cloned().flatten())
404}
405
406pub(crate) fn record_parser_diagnostic(message: String) -> bool {
407 PARSER_DIAGNOSTIC_STACK.with(|stack| {
408 let mut stack = stack.borrow_mut();
409 let Some(active) = stack.last_mut() else {
410 return false;
411 };
412 active.push(message);
413 true
414 })
415}
416
417#[macro_export]
418macro_rules! parser_warn {
419 ($($arg:tt)*) => {{
420 let message = format!($($arg)*);
421 if !$crate::parsers::record_parser_diagnostic(message.clone()) {
422 log::warn!("{message}");
423 }
424 }};
425}
426
427pub trait PackageParser {
471 const PACKAGE_TYPE: PackageType;
473
474 fn extract_packages(path: &Path) -> Vec<PackageData>;
484
485 fn is_match(path: &Path) -> bool;
490
491 fn extract_first_package(path: &Path) -> PackageData {
494 Self::extract_packages(path)
495 .into_iter()
496 .map(|mut package| {
497 finalize_package_declared_license_references(&mut package);
498 package
499 })
500 .next()
501 .unwrap_or_default()
502 }
503}
504
505pub use self::about::AboutFileParser;
506pub use self::alpine::{AlpineApkParser, AlpineApkbuildParser, AlpineInstalledParser};
507pub use self::arch::{ArchPkginfoParser, ArchSrcinfoParser};
508pub use self::autotools::AutotoolsConfigureParser;
509pub use self::bazel::{BazelBuildParser, BazelModuleParser};
510pub use self::bower::BowerJsonParser;
511pub use self::buck::{BuckBuildParser, BuckMetadataBzlParser};
512pub use self::bun_lock::BunLockParser;
513pub use self::bun_lockb::BunLockbParser;
514pub use self::cargo::CargoParser;
515#[cfg_attr(not(test), allow(unused_imports))]
516pub use self::cargo_lock::CargoLockParser;
517pub use self::chef::{ChefMetadataJsonParser, ChefMetadataRbParser};
518pub use self::citation::CitationCffParser;
519pub use self::clojure::{ClojureDepsEdnParser, ClojureProjectCljParser};
520pub use self::composer::{ComposerJsonParser, ComposerLockParser};
521pub use self::conan::{ConanFilePyParser, ConanLockParser, ConanfileTxtParser};
522pub use self::conan_data::ConanDataParser;
523pub use self::conda::{CondaEnvironmentYmlParser, CondaMetaYamlParser};
524pub use self::conda_meta_json::CondaMetaJsonParser;
525pub use self::cpan::{CpanManifestParser, CpanMetaJsonParser, CpanMetaYmlParser};
526pub use self::cpan_dist_ini::CpanDistIniParser;
527pub use self::cpan_makefile_pl::CpanMakefilePlParser;
528pub use self::cran::CranParser;
529pub use self::dart::{PubspecLockParser, PubspecYamlParser};
530pub use self::debian::{
531 DebianControlInExtractedDebParser, DebianControlParser, DebianCopyrightParser, DebianDebParser,
532 DebianDebianTarParser, DebianDistrolessInstalledParser, DebianDscParser,
533 DebianInstalledListParser, DebianInstalledMd5sumsParser, DebianInstalledParser,
534 DebianMd5sumInPackageParser, DebianOrigTarParser,
535};
536pub use self::deno::DenoParser;
537pub use self::deno_lock::DenoLockParser;
538pub use self::docker::DockerfileParser;
539pub use self::freebsd::FreebsdCompactManifestParser;
540pub use self::gitmodules::GitmodulesParser;
541pub use self::go::{GoModParser, GoSumParser, GoWorkParser, GodepsParser};
542pub use self::go_mod_graph::GoModGraphParser;
543pub use self::gradle::GradleParser;
544pub use self::gradle_lock::GradleLockfileParser;
545pub use self::gradle_module::GradleModuleParser;
546pub use self::hackage::{HackageCabalParser, HackageCabalProjectParser, HackageStackYamlParser};
547pub use self::haxe::HaxeParser;
548pub use self::helm::{HelmChartLockParser, HelmChartYamlParser};
549pub use self::hex_lock::HexLockParser;
550pub use self::julia::{JuliaManifestTomlParser, JuliaProjectTomlParser};
551pub use self::maven::MavenParser;
552pub use self::meson::MesonParser;
553pub use self::microsoft_update_manifest::MicrosoftUpdateManifestParser;
554pub use self::misc::{
555 AndroidApkRecognizer, AndroidLibraryRecognizer, AppleDmgRecognizer, Axis2MarRecognizer,
556 Axis2ModuleXmlRecognizer, CabArchiveRecognizer, ChromeCrxRecognizer, InstallShieldRecognizer,
557 IosIpaRecognizer, IsoImageRecognizer, IvyXmlRecognizer, JBossSarRecognizer,
558 JBossServiceXmlRecognizer, JavaEarAppXmlRecognizer, JavaEarRecognizer, JavaJarRecognizer,
559 JavaWarRecognizer, JavaWarWebXmlRecognizer, MeteorPackageRecognizer, MozillaXpiRecognizer,
560 NsisRecognizer, SharArchiveRecognizer, SquashfsRecognizer,
561};
562pub use self::nix::{NixDefaultParser, NixFlakeLockParser, NixFlakeParser};
563pub use self::npm::NpmParser;
564pub use self::npm_lock::NpmLockParser;
565pub use self::npm_workspace::NpmWorkspaceParser;
566pub use self::nuget::{
567 CentralPackageManagementPropsParser, DirectoryBuildPropsParser, DotNetDepsJsonParser,
568 NupkgParser, NuspecParser, PackageReferenceProjectParser, PackagesConfigParser,
569 PackagesLockParser, ProjectJsonParser, ProjectLockJsonParser,
570};
571pub use self::opam::OpamParser;
572pub use self::os_release::OsReleaseParser;
573pub use self::pip_inspect_deplock::PipInspectDeplockParser;
574pub use self::pipfile_lock::PipfileLockParser;
575pub use self::pixi::{PixiLockParser, PixiTomlParser};
576pub use self::pnpm_lock::PnpmLockParser;
577pub use self::podfile::PodfileParser;
578pub use self::podfile_lock::PodfileLockParser;
579pub use self::podspec::PodspecParser;
580pub use self::podspec_json::PodspecJsonParser;
581pub use self::poetry_lock::PoetryLockParser;
582pub use self::publiccode::PubliccodeParser;
583pub use self::pylock_toml::PylockTomlParser;
584pub use self::python::PythonParser;
585pub use self::readme::ReadmeParser;
586pub use self::requirements_txt::RequirementsTxtParser;
587#[cfg(feature = "rpm-sqlite")]
588pub use self::rpm_db::RpmSqliteDatabaseParser;
589pub use self::rpm_db::{RpmBdbDatabaseParser, RpmNdbDatabaseParser};
590pub use self::rpm_license_files::RpmLicenseFilesParser;
591pub use self::rpm_mariner_manifest::RpmMarinerManifestParser;
592pub use self::rpm_parser::RpmParser;
593pub use self::rpm_specfile::RpmSpecfileParser;
594pub use self::rpm_yumdb::RpmYumdbParser;
595pub use self::ruby::{
596 GemArchiveParser, GemMetadataExtractedParser, GemfileLockParser, GemfileParser, GemspecParser,
597};
598pub use self::sbt::SbtParser;
599pub use self::swift_manifest_json::SwiftManifestJsonParser;
600pub use self::swift_resolved::SwiftPackageResolvedParser;
601pub use self::swift_show_dependencies::SwiftShowDependenciesParser;
602pub use self::uv_lock::UvLockParser;
603pub use self::vcpkg::VcpkgManifestParser;
604pub use self::yarn_lock::YarnLockParser;
605pub use self::yarn_pnp::YarnPnpParser;
606
607macro_rules! register_package_handlers {
613 (
614 parsers: [$($(#[$parser_meta:meta])* $parser:ty),* $(,)?],
615 recognizers: [$($recognizer:ty),* $(,)?] $(,)?
616 ) => {
617 pub fn try_parse_file_with_license_engine(
618 path: &Path,
619 license_engine: Option<Arc<LicenseDetectionEngine>>,
620 ) -> Option<ParsePackagesResult> {
621 $(
622 $(#[$parser_meta])*
623 if <$parser>::is_match(path) {
624 return Some(capture_parser_diagnostics(
625 || <$parser>::extract_packages(path),
626 stringify!($parser),
627 path,
628 license_engine.clone(),
629 ));
630 }
631 )*
632 $(
633 if <$recognizer>::is_match(path) {
634 return Some(capture_parser_diagnostics(
635 || <$recognizer>::extract_packages(path),
636 stringify!($recognizer),
637 path,
638 license_engine.clone(),
639 ));
640 }
641 )*
642 None
643 }
644
645 pub fn try_parse_file(path: &Path) -> Option<ParsePackagesResult> {
646 try_parse_file_with_license_engine(path, None)
647 }
648
649 #[allow(dead_code)]
652 pub fn parse_by_type_name(type_name: &str, path: &Path) -> Option<PackageData> {
653 match type_name {
654 $(
655 $(#[$parser_meta])*
656 stringify!($parser) => Some(<$parser>::extract_first_package(path)),
657 )*
658 $(
659 stringify!($recognizer) => Some(<$recognizer>::extract_first_package(path)),
660 )*
661 _ => None
662 }
663 }
664
665 #[allow(dead_code)]
668 pub fn list_parser_types() -> Vec<&'static str> {
669 vec![
670 $(
671 $(#[$parser_meta])*
672 stringify!($parser),
673 )*
674 $(
675 stringify!($recognizer),
676 )*
677 ]
678 }
679 };
680}
681
682#[cfg(test)]
683mod tests {
684 use std::collections::HashMap;
685
686 use super::{active_parser_license_engine, capture_parser_diagnostics};
687 use crate::license_detection::LicenseDetectionEngine;
688 use crate::models::PackageData;
689 use crate::parsers::license_normalization::{
690 clear_last_parser_license_engine_ptr, last_parser_license_engine_ptr,
691 };
692 use std::path::Path;
693 use std::sync::Arc;
694
695 #[test]
696 fn test_capture_parser_diagnostics_exposes_active_license_engine() {
697 let engine =
698 Arc::new(LicenseDetectionEngine::from_embedded().expect("embedded engine should load"));
699
700 let result = capture_parser_diagnostics(
701 || {
702 assert!(active_parser_license_engine().is_some());
703 vec![PackageData::default()]
704 },
705 "TestParser",
706 Path::new("testdata/package.json"),
707 Some(engine),
708 );
709
710 assert_eq!(result.packages.len(), 1);
711 assert!(active_parser_license_engine().is_none());
712 }
713
714 #[test]
715 fn test_capture_parser_diagnostics_keeps_active_license_engine_for_finalization() {
716 let engine =
717 Arc::new(LicenseDetectionEngine::from_embedded().expect("embedded engine should load"));
718 clear_last_parser_license_engine_ptr();
719
720 let result = capture_parser_diagnostics(
721 || {
722 vec![PackageData {
723 declared_license_expression: Some("mit".to_string()),
724 declared_license_expression_spdx: Some("MIT".to_string()),
725 extracted_license_statement: Some("MIT".to_string()),
726 extra_data: Some(HashMap::from([(
727 "license_file".to_string(),
728 serde_json::Value::String("LICENSE".to_string()),
729 )])),
730 ..Default::default()
731 }]
732 },
733 "TestParser",
734 Path::new("testdata/package.json"),
735 Some(Arc::clone(&engine)),
736 );
737
738 assert_eq!(result.packages.len(), 1);
739 assert_eq!(
740 last_parser_license_engine_ptr(),
741 Some(Arc::as_ptr(&engine) as usize)
742 );
743 assert_eq!(
744 result.packages[0].license_detections[0].matches[0]
745 .referenced_filenames
746 .as_ref(),
747 Some(&vec!["LICENSE".to_string()])
748 );
749 assert!(active_parser_license_engine().is_none());
750 }
751}
752
753register_package_handlers! {
754 parsers: [
755 AboutFileParser,
756 AlpineApkParser,
757 AlpineApkbuildParser,
758 AlpineInstalledParser,
759 ArchPkginfoParser,
760 ArchSrcinfoParser,
761 AutotoolsConfigureParser,
762 BazelBuildParser,
763 BazelModuleParser,
764 BowerJsonParser,
765 BunLockParser,
766 BunLockbParser,
767 BuckBuildParser,
768 BuckMetadataBzlParser,
769 CargoLockParser,
770 CargoParser,
771 ChefMetadataJsonParser,
772 ChefMetadataRbParser,
773 CitationCffParser,
774 ClojureDepsEdnParser,
775 ClojureProjectCljParser,
776 ComposerJsonParser,
777 ComposerLockParser,
778 ConanDataParser,
779 ConanFilePyParser,
780 ConanfileTxtParser,
781 ConanLockParser,
782 CondaEnvironmentYmlParser,
783 CondaMetaJsonParser,
784 CondaMetaYamlParser,
785 CpanDistIniParser,
786 CpanMakefilePlParser,
787 CpanManifestParser,
788 CpanMetaJsonParser,
789 CpanMetaYmlParser,
790 CranParser,
791 DebianControlInExtractedDebParser,
792 DebianControlParser,
793 DebianCopyrightParser,
794 DebianDebianTarParser,
795 DebianDebParser,
796 DebianDistrolessInstalledParser,
797 DebianDscParser,
798 DebianInstalledListParser,
799 DebianInstalledMd5sumsParser,
800 DebianInstalledParser,
801 DebianMd5sumInPackageParser,
802 DebianOrigTarParser,
803 DenoParser,
804 DenoLockParser,
805 DockerfileParser,
806 FreebsdCompactManifestParser,
807 GemArchiveParser,
808 GemfileLockParser,
809 GemfileParser,
810 GemMetadataExtractedParser,
811 GemspecParser,
812 GitmodulesParser,
813 GodepsParser,
814 GoModParser,
815 GoModGraphParser,
816 GoSumParser,
817 GoWorkParser,
818 GradleLockfileParser,
819 GradleParser,
820 GradleModuleParser,
821 HackageCabalParser,
822 HackageCabalProjectParser,
823 HackageStackYamlParser,
824 HelmChartYamlParser,
825 HelmChartLockParser,
826 HaxeParser,
827 HexLockParser,
828 JuliaManifestTomlParser,
829 JuliaProjectTomlParser,
830 MavenParser,
831 MesonParser,
832 MicrosoftUpdateManifestParser,
833 NixDefaultParser,
834 NixFlakeLockParser,
835 NixFlakeParser,
836 NpmLockParser,
837 NpmParser,
838 NpmWorkspaceParser,
839 DotNetDepsJsonParser,
840 CentralPackageManagementPropsParser,
841 DirectoryBuildPropsParser,
842 NupkgParser,
843 NuspecParser,
844 PackageReferenceProjectParser,
845 OpamParser,
846 OsReleaseParser,
847 PackagesConfigParser,
848 PackagesLockParser,
849 ProjectJsonParser,
850 ProjectLockJsonParser,
851 PipfileLockParser,
852 PipInspectDeplockParser,
853 PixiTomlParser,
854 PixiLockParser,
855 PnpmLockParser,
856 PodfileLockParser,
857 PodfileParser,
858 PodspecJsonParser,
859 PodspecParser,
860 PoetryLockParser,
861 PubliccodeParser,
862 PylockTomlParser,
863 PubspecLockParser,
864 PubspecYamlParser,
865 PythonParser,
866 UvLockParser,
867 VcpkgManifestParser,
868 ReadmeParser,
869 RequirementsTxtParser,
870 RpmBdbDatabaseParser,
871 RpmLicenseFilesParser,
872 RpmMarinerManifestParser,
873 RpmNdbDatabaseParser,
874 RpmParser,
875 RpmSpecfileParser,
876 #[cfg(feature = "rpm-sqlite")]
877 RpmSqliteDatabaseParser,
878 RpmYumdbParser,
879 SbtParser,
880 SwiftManifestJsonParser,
881 SwiftPackageResolvedParser,
882 SwiftShowDependenciesParser,
883 YarnLockParser,
884 YarnPnpParser,
885 ],
886 recognizers: [
887 AndroidApkRecognizer,
888 AndroidLibraryRecognizer,
889 AppleDmgRecognizer,
890 Axis2MarRecognizer,
891 Axis2ModuleXmlRecognizer,
892 CabArchiveRecognizer,
893 ChromeCrxRecognizer,
894 InstallShieldRecognizer,
895 IosIpaRecognizer,
896 IsoImageRecognizer,
897 IvyXmlRecognizer,
898 JavaEarAppXmlRecognizer,
899 JavaEarRecognizer,
900 JavaJarRecognizer,
901 JavaWarRecognizer,
902 JavaWarWebXmlRecognizer,
903 JBossSarRecognizer,
904 JBossServiceXmlRecognizer,
905 MeteorPackageRecognizer,
906 MozillaXpiRecognizer,
907 NsisRecognizer,
908 SharArchiveRecognizer,
909 SquashfsRecognizer,
910 ],
911}
912
913#[cfg(test)]
914mod panic_isolation_tests {
915 use super::*;
916
917 #[test]
918 fn capture_parser_diagnostics_turns_panics_into_scan_errors() {
919 let path = Path::new("fixtures/panic-package.json");
920 let result = capture_parser_diagnostics(
921 || -> Vec<PackageData> { panic!("panic boom") },
922 "PanicParser",
923 path,
924 None,
925 );
926
927 assert!(result.packages.is_empty());
928 assert_eq!(result.scan_errors.len(), 1);
929 assert!(result.scan_errors[0].contains("PanicParser"));
930 assert!(result.scan_errors[0].contains("fixtures/panic-package.json"));
931 assert!(result.scan_errors[0].contains("panic boom"));
932 }
933
934 #[test]
935 fn capture_parser_diagnostics_recovers_after_panic() {
936 let panic_path = Path::new("fixtures/panic-package.json");
937 let _ = capture_parser_diagnostics(
938 || -> Vec<PackageData> { panic!("panic boom") },
939 "PanicParser",
940 panic_path,
941 None,
942 );
943
944 let ok_path = Path::new("fixtures/recovered-package.json");
945 let result = capture_parser_diagnostics(
946 || {
947 crate::parser_warn!("recoverable parser warning");
948 vec![PackageData {
949 package_type: Some(PackageType::Npm),
950 ..Default::default()
951 }]
952 },
953 "RecoveringParser",
954 ok_path,
955 None,
956 );
957
958 assert_eq!(result.packages.len(), 1);
959 assert_eq!(result.scan_errors, vec!["recoverable parser warning"]);
960 }
961}