use super::*;
use crate::models::{FileReference, FileType, PackageData, PackageType};
use strum::IntoEnumIterator;
#[test]
fn test_every_file_reference_resolver_kind_is_registered_once() {
let registered: std::collections::HashSet<FileReferenceResolverKind> =
FILE_REFERENCE_RESOLVER_CONFIGS
.iter()
.map(|config| config.kind)
.collect();
let missing: Vec<_> = FileReferenceResolverKind::iter()
.filter(|kind| !registered.contains(kind))
.collect();
assert!(
missing.is_empty(),
"File-reference resolver kinds not registered: {missing:?}"
);
for kind in FileReferenceResolverKind::iter() {
let count = FILE_REFERENCE_RESOLVER_CONFIGS
.iter()
.filter(|config| config.kind == kind)
.count();
assert_eq!(
count, 1,
"File-reference resolver kind {kind:?} should be registered exactly once"
);
}
}
#[test]
fn test_file_reference_resolver_datasource_ids_are_unique() {
let mut seen = std::collections::HashSet::new();
let mut duplicates = Vec::new();
for config in FILE_REFERENCE_RESOLVER_CONFIGS {
for datasource_id in config.datasource_ids {
if !seen.insert(*datasource_id) {
duplicates.push(*datasource_id);
}
}
}
assert!(
duplicates.is_empty(),
"Datasource IDs registered in multiple file-reference resolvers: {duplicates:?}"
);
}
#[test]
fn test_find_root_from_path() {
assert_eq!(
compute_root("rootfs/lib/apk/db/installed", "lib/apk/db/installed"),
"rootfs/"
);
assert_eq!(
compute_root("lib/apk/db/installed", "lib/apk/db/installed"),
""
);
assert_eq!(
compute_root("container/var/lib/rpm/Packages", "var/lib/rpm/Packages"),
"container/"
);
assert_eq!(
compute_root("var/lib/rpm/Packages", "var/lib/rpm/Packages"),
""
);
}
#[test]
fn test_resolve_basic_alpine() {
let mut files = vec![
FileInfo {
name: "installed".to_string(),
base_name: "installed".to_string(),
extension: String::new(),
path: "lib/apk/db/installed".to_string(),
file_type: FileType::File,
mime_type: None,
size: 100,
date: None,
sha1: None,
md5: None,
sha256: None,
programming_language: None,
package_data: vec![PackageData {
datasource_id: Some(DatasourceId::AlpineInstalledDb),
purl: Some("pkg:alpine/musl@1.2.3".to_string()),
name: Some("musl".to_string()),
file_references: vec![
FileReference {
path: "lib/libc.so".to_string(),
size: None,
sha1: None,
md5: None,
sha256: None,
sha512: None,
extra_data: None,
},
FileReference {
path: "usr/bin/ldconfig".to_string(),
size: None,
sha1: None,
md5: None,
sha256: None,
sha512: None,
extra_data: None,
},
],
..Default::default()
}],
license_expression: None,
license_detections: vec![],
license_clues: vec![],
percentage_of_license_text: None,
copyrights: vec![],
holders: vec![],
authors: vec![],
emails: vec![],
urls: vec![],
for_packages: vec![],
scan_errors: vec![],
is_source: None,
source_count: None,
is_legal: false,
is_manifest: false,
is_readme: false,
is_top_level: false,
is_key_file: false,
is_community: false,
is_generated: None,
facets: vec![],
tallies: None,
},
FileInfo {
name: "libc.so".to_string(),
base_name: "libc".to_string(),
extension: "so".to_string(),
path: "lib/libc.so".to_string(),
file_type: FileType::File,
mime_type: None,
size: 200,
date: None,
sha1: None,
md5: None,
sha256: None,
programming_language: None,
package_data: vec![],
license_expression: None,
license_detections: vec![],
license_clues: vec![],
percentage_of_license_text: None,
copyrights: vec![],
holders: vec![],
authors: vec![],
emails: vec![],
urls: vec![],
for_packages: vec![],
scan_errors: vec![],
is_source: None,
source_count: None,
is_legal: false,
is_manifest: false,
is_readme: false,
is_top_level: false,
is_key_file: false,
is_community: false,
is_generated: None,
facets: vec![],
tallies: None,
},
FileInfo {
name: "ldconfig".to_string(),
base_name: "ldconfig".to_string(),
extension: String::new(),
path: "usr/bin/ldconfig".to_string(),
file_type: FileType::File,
mime_type: None,
size: 300,
date: None,
sha1: None,
md5: None,
sha256: None,
programming_language: None,
package_data: vec![],
license_expression: None,
license_detections: vec![],
license_clues: vec![],
percentage_of_license_text: None,
copyrights: vec![],
holders: vec![],
authors: vec![],
emails: vec![],
urls: vec![],
for_packages: vec![],
scan_errors: vec![],
is_source: None,
source_count: None,
is_legal: false,
is_manifest: false,
is_readme: false,
is_top_level: false,
is_key_file: false,
is_community: false,
is_generated: None,
facets: vec![],
tallies: None,
},
];
let mut packages = vec![Package {
package_type: Some(PackageType::Alpine),
namespace: None,
name: Some("musl".to_string()),
version: Some("1.2.3".to_string()),
qualifiers: None,
subpath: None,
primary_language: None,
description: None,
release_date: None,
parties: vec![],
keywords: vec![],
homepage_url: None,
download_url: None,
size: None,
sha1: None,
md5: None,
sha256: None,
sha512: None,
bug_tracking_url: None,
code_view_url: None,
vcs_url: None,
copyright: None,
holder: None,
declared_license_expression: None,
declared_license_expression_spdx: None,
license_detections: vec![],
other_license_expression: None,
other_license_expression_spdx: None,
other_license_detections: vec![],
extracted_license_statement: None,
notice_text: None,
source_packages: vec![],
is_private: false,
is_virtual: false,
extra_data: None,
repository_homepage_url: None,
repository_download_url: None,
api_data_url: None,
purl: Some("pkg:alpine/musl@1.2.3".to_string()),
package_uid: "pkg:alpine/musl@1.2.3?uuid=test-uuid".to_string(),
datafile_paths: vec!["lib/apk/db/installed".to_string()],
datasource_ids: vec![DatasourceId::AlpineInstalledDb],
}];
let mut dependencies = vec![];
resolve_file_references(&mut files, &mut packages, &mut dependencies);
assert_eq!(files[1].for_packages.len(), 1);
assert_eq!(
files[1].for_packages[0],
"pkg:alpine/musl@1.2.3?uuid=test-uuid"
);
assert_eq!(files[2].for_packages.len(), 1);
assert_eq!(
files[2].for_packages[0],
"pkg:alpine/musl@1.2.3?uuid=test-uuid"
);
}
#[test]
fn test_resolve_missing_refs() {
let mut files = vec![FileInfo {
name: "installed".to_string(),
base_name: "installed".to_string(),
extension: String::new(),
path: "lib/apk/db/installed".to_string(),
file_type: FileType::File,
mime_type: None,
size: 100,
date: None,
sha1: None,
md5: None,
sha256: None,
programming_language: None,
package_data: vec![PackageData {
datasource_id: Some(DatasourceId::AlpineInstalledDb),
purl: Some("pkg:alpine/test@1.0".to_string()),
name: Some("test".to_string()),
file_references: vec![
FileReference {
path: "missing/file1.txt".to_string(),
size: None,
sha1: None,
md5: None,
sha256: None,
sha512: None,
extra_data: None,
},
FileReference {
path: "another/missing.so".to_string(),
size: None,
sha1: None,
md5: None,
sha256: None,
sha512: None,
extra_data: None,
},
],
..Default::default()
}],
license_expression: None,
license_detections: vec![],
license_clues: vec![],
percentage_of_license_text: None,
copyrights: vec![],
holders: vec![],
authors: vec![],
emails: vec![],
urls: vec![],
for_packages: vec![],
scan_errors: vec![],
is_source: None,
source_count: None,
is_legal: false,
is_manifest: false,
is_readme: false,
is_top_level: false,
is_key_file: false,
is_community: false,
is_generated: None,
facets: vec![],
tallies: None,
}];
let mut packages = vec![Package {
package_type: Some(PackageType::Alpine),
namespace: None,
name: Some("test".to_string()),
version: Some("1.0".to_string()),
qualifiers: None,
subpath: None,
primary_language: None,
description: None,
release_date: None,
parties: vec![],
keywords: vec![],
homepage_url: None,
download_url: None,
size: None,
sha1: None,
md5: None,
sha256: None,
sha512: None,
bug_tracking_url: None,
code_view_url: None,
vcs_url: None,
copyright: None,
holder: None,
declared_license_expression: None,
declared_license_expression_spdx: None,
license_detections: vec![],
other_license_expression: None,
other_license_expression_spdx: None,
other_license_detections: vec![],
extracted_license_statement: None,
notice_text: None,
source_packages: vec![],
is_private: false,
is_virtual: false,
extra_data: None,
repository_homepage_url: None,
repository_download_url: None,
api_data_url: None,
purl: Some("pkg:alpine/test@1.0".to_string()),
package_uid: "pkg:alpine/test@1.0?uuid=test-uuid".to_string(),
datafile_paths: vec!["lib/apk/db/installed".to_string()],
datasource_ids: vec![DatasourceId::AlpineInstalledDb],
}];
let mut dependencies = vec![];
resolve_file_references(&mut files, &mut packages, &mut dependencies);
assert!(packages[0].extra_data.is_some());
let extra_data = packages[0].extra_data.as_ref().unwrap();
assert!(extra_data.contains_key("missing_file_references"));
let missing = extra_data.get("missing_file_references").unwrap();
assert!(missing.is_array());
let missing_array = missing.as_array().unwrap();
assert_eq!(missing_array.len(), 2);
assert_eq!(missing_array[0]["path"], "another/missing.so");
assert_eq!(missing_array[1]["path"], "missing/file1.txt");
}
#[test]
fn test_resolve_rpm_namespace() {
let mut files = vec![
FileInfo {
name: "Packages".to_string(),
base_name: "Packages".to_string(),
extension: String::new(),
path: "rootfs/var/lib/rpm/Packages".to_string(),
file_type: FileType::File,
mime_type: None,
size: 100,
date: None,
sha1: None,
md5: None,
sha256: None,
programming_language: None,
package_data: vec![PackageData {
datasource_id: Some(DatasourceId::RpmInstalledDatabaseBdb),
purl: Some("pkg:rpm/bash@5.0".to_string()),
name: Some("bash".to_string()),
file_references: vec![],
..Default::default()
}],
license_expression: None,
license_detections: vec![],
license_clues: vec![],
percentage_of_license_text: None,
copyrights: vec![],
holders: vec![],
authors: vec![],
emails: vec![],
urls: vec![],
for_packages: vec![],
scan_errors: vec![],
is_source: None,
source_count: None,
is_legal: false,
is_manifest: false,
is_readme: false,
is_top_level: false,
is_key_file: false,
is_community: false,
is_generated: None,
facets: vec![],
tallies: None,
},
FileInfo {
name: "os-release".to_string(),
base_name: "os-release".to_string(),
extension: String::new(),
path: "rootfs/etc/os-release".to_string(),
file_type: FileType::File,
mime_type: None,
size: 50,
date: None,
sha1: None,
md5: None,
sha256: None,
programming_language: None,
package_data: vec![PackageData {
datasource_id: Some(DatasourceId::EtcOsRelease),
namespace: Some("fedora".to_string()),
name: Some("fedora".to_string()),
..Default::default()
}],
license_expression: None,
license_detections: vec![],
license_clues: vec![],
percentage_of_license_text: None,
copyrights: vec![],
holders: vec![],
authors: vec![],
emails: vec![],
urls: vec![],
for_packages: vec![],
scan_errors: vec![],
is_source: None,
source_count: None,
is_legal: false,
is_manifest: false,
is_readme: false,
is_top_level: false,
is_key_file: false,
is_community: false,
is_generated: None,
facets: vec![],
tallies: None,
},
];
let mut packages = vec![Package {
package_type: Some(PackageType::Rpm),
namespace: None,
name: Some("bash".to_string()),
version: Some("5.0".to_string()),
qualifiers: None,
subpath: None,
primary_language: None,
description: None,
release_date: None,
parties: vec![],
keywords: vec![],
homepage_url: None,
download_url: None,
size: None,
sha1: None,
md5: None,
sha256: None,
sha512: None,
bug_tracking_url: None,
code_view_url: None,
vcs_url: None,
copyright: None,
holder: None,
declared_license_expression: None,
declared_license_expression_spdx: None,
license_detections: vec![],
other_license_expression: None,
other_license_expression_spdx: None,
other_license_detections: vec![],
extracted_license_statement: None,
notice_text: None,
source_packages: vec![],
is_private: false,
is_virtual: false,
extra_data: None,
repository_homepage_url: None,
repository_download_url: None,
api_data_url: None,
purl: Some("pkg:rpm/bash@5.0".to_string()),
package_uid: "pkg:rpm/bash@5.0?uuid=test-uuid".to_string(),
datafile_paths: vec!["rootfs/var/lib/rpm/Packages".to_string()],
datasource_ids: vec![DatasourceId::RpmInstalledDatabaseBdb],
}];
let mut dependencies = vec![TopLevelDependency {
purl: Some("pkg:rpm/readline@8.0".to_string()),
extracted_requirement: None,
scope: None,
is_runtime: Some(true),
is_optional: None,
is_pinned: None,
is_direct: None,
resolved_package: None,
extra_data: None,
dependency_uid: "pkg:rpm/readline@8.0?uuid=dep-uuid".to_string(),
for_package_uid: Some("pkg:rpm/bash@5.0?uuid=test-uuid".to_string()),
datafile_path: "rootfs/var/lib/rpm/Packages".to_string(),
datasource_id: DatasourceId::RpmInstalledDatabaseBdb,
namespace: None,
}];
resolve_file_references(&mut files, &mut packages, &mut dependencies);
assert_eq!(packages[0].namespace, Some("fedora".to_string()));
assert_eq!(packages[0].purl.as_deref(), Some("pkg:rpm/fedora/bash@5.0"));
assert!(
packages[0]
.package_uid
.starts_with("pkg:rpm/fedora/bash@5.0?uuid=")
);
assert_eq!(dependencies[0].namespace, Some("fedora".to_string()));
assert_eq!(
dependencies[0].purl.as_deref(),
Some("pkg:rpm/fedora/readline@8.0")
);
assert_eq!(
dependencies[0].for_package_uid.as_deref(),
Some(packages[0].package_uid.as_str())
);
}
#[test]
fn test_merge_rpm_yumdb_metadata() {
let mut files = vec![
FileInfo {
name: "Packages".to_string(),
base_name: "Packages".to_string(),
extension: String::new(),
path: "rootfs/var/lib/rpm/Packages".to_string(),
file_type: FileType::File,
mime_type: None,
size: 1,
date: None,
sha1: None,
md5: None,
sha256: None,
programming_language: None,
package_data: vec![],
license_expression: None,
license_detections: vec![],
license_clues: vec![],
percentage_of_license_text: None,
copyrights: vec![],
holders: vec![],
authors: vec![],
emails: vec![],
urls: vec![],
for_packages: vec!["pkg:rpm/bash@5.0-1.el8?uuid=rpm-uuid".to_string()],
scan_errors: vec![],
is_source: None,
source_count: None,
is_legal: false,
is_manifest: false,
is_readme: false,
is_top_level: false,
is_key_file: false,
is_community: false,
is_generated: None,
facets: vec![],
tallies: None,
},
FileInfo {
name: "from_repo".to_string(),
base_name: "from_repo".to_string(),
extension: String::new(),
path: "rootfs/var/lib/yum/yumdb/p/abc123-bash-5.0-1.el8.x86_64/from_repo".to_string(),
file_type: FileType::File,
mime_type: None,
size: 1,
date: None,
sha1: None,
md5: None,
sha256: None,
programming_language: None,
package_data: vec![],
license_expression: None,
license_detections: vec![],
license_clues: vec![],
percentage_of_license_text: None,
copyrights: vec![],
holders: vec![],
authors: vec![],
emails: vec![],
urls: vec![],
for_packages: vec!["pkg:rpm/bash@5.0-1.el8?uuid=yumdb-uuid".to_string()],
scan_errors: vec![],
is_source: None,
source_count: None,
is_legal: false,
is_manifest: false,
is_readme: false,
is_top_level: false,
is_key_file: false,
is_community: false,
is_generated: None,
facets: vec![],
tallies: None,
},
];
let mut packages = vec![
Package {
package_type: Some(PackageType::Rpm),
namespace: None,
name: Some("bash".to_string()),
version: Some("5.0-1.el8".to_string()),
qualifiers: Some(std::iter::once(("arch".to_string(), "x86_64".to_string())).collect()),
subpath: None,
primary_language: None,
description: None,
release_date: None,
parties: vec![],
keywords: vec![],
homepage_url: None,
download_url: None,
size: None,
sha1: None,
md5: None,
sha256: None,
sha512: None,
bug_tracking_url: None,
code_view_url: None,
vcs_url: None,
copyright: None,
holder: None,
declared_license_expression: None,
declared_license_expression_spdx: None,
license_detections: vec![],
other_license_expression: None,
other_license_expression_spdx: None,
other_license_detections: vec![],
extracted_license_statement: None,
notice_text: None,
source_packages: vec![],
is_private: false,
is_virtual: false,
extra_data: None,
repository_homepage_url: None,
repository_download_url: None,
api_data_url: None,
purl: Some("pkg:rpm/bash@5.0-1.el8?arch=x86_64".to_string()),
package_uid: "pkg:rpm/bash@5.0-1.el8?uuid=rpm-uuid".to_string(),
datafile_paths: vec!["rootfs/var/lib/rpm/Packages".to_string()],
datasource_ids: vec![DatasourceId::RpmInstalledDatabaseBdb],
},
Package {
package_type: Some(PackageType::Rpm),
namespace: None,
name: Some("bash".to_string()),
version: Some("5.0-1.el8".to_string()),
qualifiers: Some(std::iter::once(("arch".to_string(), "x86_64".to_string())).collect()),
subpath: None,
primary_language: None,
description: None,
release_date: None,
parties: vec![],
keywords: vec![],
homepage_url: None,
download_url: None,
size: None,
sha1: None,
md5: None,
sha256: None,
sha512: None,
bug_tracking_url: None,
code_view_url: None,
vcs_url: None,
copyright: None,
holder: None,
declared_license_expression: None,
declared_license_expression_spdx: None,
license_detections: vec![],
other_license_expression: None,
other_license_expression_spdx: None,
other_license_detections: vec![],
extracted_license_statement: None,
notice_text: None,
source_packages: vec![],
is_private: false,
is_virtual: true,
extra_data: Some(
[
(
"from_repo".to_string(),
serde_json::Value::String("baseos".to_string()),
),
(
"releasever".to_string(),
serde_json::Value::String("8".to_string()),
),
]
.into_iter()
.collect(),
),
repository_homepage_url: None,
repository_download_url: None,
api_data_url: None,
purl: Some("pkg:rpm/bash@5.0-1.el8?arch=x86_64".to_string()),
package_uid: "pkg:rpm/bash@5.0-1.el8?uuid=yumdb-uuid".to_string(),
datafile_paths: vec![
"rootfs/var/lib/yum/yumdb/p/abc123-bash-5.0-1.el8.x86_64/from_repo".to_string(),
],
datasource_ids: vec![DatasourceId::RpmYumdb],
},
];
merge_rpm_yumdb_metadata(&mut files, &mut packages);
assert_eq!(packages.len(), 1);
assert!(packages[0].datasource_ids.contains(&DatasourceId::RpmYumdb));
assert!(
packages[0]
.datafile_paths
.iter()
.any(|path| path.contains("var/lib/yum/yumdb"))
);
let yumdb = packages[0]
.extra_data
.as_ref()
.and_then(|extra| extra.get("yumdb"))
.and_then(|value| value.as_object())
.unwrap();
assert_eq!(yumdb["from_repo"], "baseos");
assert_eq!(yumdb["releasever"], "8");
assert_eq!(
files[1].for_packages,
vec!["pkg:rpm/bash@5.0-1.el8?uuid=rpm-uuid".to_string()]
);
}
#[test]
fn test_strip_leading_slash() {
let mut files = vec![
FileInfo {
name: "installed".to_string(),
base_name: "installed".to_string(),
extension: String::new(),
path: "lib/apk/db/installed".to_string(),
file_type: FileType::File,
mime_type: None,
size: 100,
date: None,
sha1: None,
md5: None,
sha256: None,
programming_language: None,
package_data: vec![PackageData {
datasource_id: Some(DatasourceId::AlpineInstalledDb),
purl: Some("pkg:alpine/test@1.0".to_string()),
name: Some("test".to_string()),
file_references: vec![FileReference {
path: "/lib/test.so".to_string(),
size: None,
sha1: None,
md5: None,
sha256: None,
sha512: None,
extra_data: None,
}],
..Default::default()
}],
license_expression: None,
license_detections: vec![],
license_clues: vec![],
percentage_of_license_text: None,
copyrights: vec![],
holders: vec![],
authors: vec![],
emails: vec![],
urls: vec![],
for_packages: vec![],
scan_errors: vec![],
is_source: None,
source_count: None,
is_legal: false,
is_manifest: false,
is_readme: false,
is_top_level: false,
is_key_file: false,
is_community: false,
is_generated: None,
facets: vec![],
tallies: None,
},
FileInfo {
name: "test.so".to_string(),
base_name: "test".to_string(),
extension: "so".to_string(),
path: "lib/test.so".to_string(),
file_type: FileType::File,
mime_type: None,
size: 200,
date: None,
sha1: None,
md5: None,
sha256: None,
programming_language: None,
package_data: vec![],
license_expression: None,
license_detections: vec![],
license_clues: vec![],
percentage_of_license_text: None,
copyrights: vec![],
holders: vec![],
authors: vec![],
emails: vec![],
urls: vec![],
for_packages: vec![],
scan_errors: vec![],
is_source: None,
source_count: None,
is_legal: false,
is_manifest: false,
is_readme: false,
is_top_level: false,
is_key_file: false,
is_community: false,
is_generated: None,
facets: vec![],
tallies: None,
},
];
let mut packages = vec![Package {
package_type: Some(PackageType::Alpine),
namespace: None,
name: Some("test".to_string()),
version: Some("1.0".to_string()),
qualifiers: None,
subpath: None,
primary_language: None,
description: None,
release_date: None,
parties: vec![],
keywords: vec![],
homepage_url: None,
download_url: None,
size: None,
sha1: None,
md5: None,
sha256: None,
sha512: None,
bug_tracking_url: None,
code_view_url: None,
vcs_url: None,
copyright: None,
holder: None,
declared_license_expression: None,
declared_license_expression_spdx: None,
license_detections: vec![],
other_license_expression: None,
other_license_expression_spdx: None,
other_license_detections: vec![],
extracted_license_statement: None,
notice_text: None,
source_packages: vec![],
is_private: false,
is_virtual: false,
extra_data: None,
repository_homepage_url: None,
repository_download_url: None,
api_data_url: None,
purl: Some("pkg:alpine/test@1.0".to_string()),
package_uid: "pkg:alpine/test@1.0?uuid=test-uuid".to_string(),
datafile_paths: vec!["lib/apk/db/installed".to_string()],
datasource_ids: vec![DatasourceId::AlpineInstalledDb],
}];
let mut dependencies = vec![];
resolve_file_references(&mut files, &mut packages, &mut dependencies);
assert_eq!(files[1].for_packages.len(), 1);
assert_eq!(
files[1].for_packages[0],
"pkg:alpine/test@1.0?uuid=test-uuid"
);
}
#[test]
fn test_resolve_python_metadata_file_references() {
let mut files = vec![
FileInfo {
name: "METADATA".to_string(),
base_name: "METADATA".to_string(),
extension: String::new(),
path: "venv/lib/python3.11/site-packages/click-8.0.4.dist-info/METADATA".to_string(),
file_type: FileType::File,
mime_type: None,
size: 100,
date: None,
sha1: None,
md5: None,
sha256: None,
programming_language: None,
package_data: vec![PackageData {
datasource_id: Some(DatasourceId::PypiWheelMetadata),
purl: Some("pkg:pypi/click@8.0.4".to_string()),
name: Some("click".to_string()),
version: Some("8.0.4".to_string()),
file_references: vec![
FileReference {
path: "click/__init__.py".to_string(),
size: None,
sha1: None,
md5: None,
sha256: None,
sha512: None,
extra_data: None,
},
FileReference {
path: "click/core.py".to_string(),
size: None,
sha1: None,
md5: None,
sha256: None,
sha512: None,
extra_data: None,
},
FileReference {
path: "click-8.0.4.dist-info/LICENSE.rst".to_string(),
size: None,
sha1: None,
md5: None,
sha256: None,
sha512: None,
extra_data: None,
},
],
..Default::default()
}],
license_expression: None,
license_detections: vec![],
license_clues: vec![],
percentage_of_license_text: None,
copyrights: vec![],
holders: vec![],
authors: vec![],
emails: vec![],
urls: vec![],
for_packages: vec![],
scan_errors: vec![],
is_source: None,
source_count: None,
is_legal: false,
is_manifest: false,
is_readme: false,
is_top_level: false,
is_key_file: false,
is_community: false,
is_generated: None,
facets: vec![],
tallies: None,
},
FileInfo {
name: "__init__.py".to_string(),
base_name: "__init__".to_string(),
extension: "py".to_string(),
path: "venv/lib/python3.11/site-packages/click/__init__.py".to_string(),
file_type: FileType::File,
mime_type: None,
size: 5,
date: None,
sha1: None,
md5: None,
sha256: None,
programming_language: None,
package_data: vec![],
license_expression: None,
license_detections: vec![],
license_clues: vec![],
percentage_of_license_text: None,
copyrights: vec![],
holders: vec![],
authors: vec![],
emails: vec![],
urls: vec![],
for_packages: vec![],
scan_errors: vec![],
is_source: None,
source_count: None,
is_legal: false,
is_manifest: false,
is_readme: false,
is_top_level: false,
is_key_file: false,
is_community: false,
is_generated: None,
facets: vec![],
tallies: None,
},
FileInfo {
name: "core.py".to_string(),
base_name: "core".to_string(),
extension: "py".to_string(),
path: "venv/lib/python3.11/site-packages/click/core.py".to_string(),
file_type: FileType::File,
mime_type: None,
size: 10,
date: None,
sha1: None,
md5: None,
sha256: None,
programming_language: None,
package_data: vec![],
license_expression: None,
license_detections: vec![],
license_clues: vec![],
percentage_of_license_text: None,
copyrights: vec![],
holders: vec![],
authors: vec![],
emails: vec![],
urls: vec![],
for_packages: vec![],
scan_errors: vec![],
is_source: None,
source_count: None,
is_legal: false,
is_manifest: false,
is_readme: false,
is_top_level: false,
is_key_file: false,
is_community: false,
is_generated: None,
facets: vec![],
tallies: None,
},
FileInfo {
name: "LICENSE.rst".to_string(),
base_name: "LICENSE".to_string(),
extension: "rst".to_string(),
path: "venv/lib/python3.11/site-packages/click-8.0.4.dist-info/LICENSE.rst".to_string(),
file_type: FileType::File,
mime_type: None,
size: 20,
date: None,
sha1: None,
md5: None,
sha256: None,
programming_language: None,
package_data: vec![],
license_expression: None,
license_detections: vec![],
license_clues: vec![],
percentage_of_license_text: None,
copyrights: vec![],
holders: vec![],
authors: vec![],
emails: vec![],
urls: vec![],
for_packages: vec![],
scan_errors: vec![],
is_source: None,
source_count: None,
is_legal: false,
is_manifest: false,
is_readme: false,
is_top_level: false,
is_key_file: false,
is_community: false,
is_generated: None,
facets: vec![],
tallies: None,
},
];
let mut packages = vec![Package {
package_type: Some(PackageType::Pypi),
namespace: None,
name: Some("click".to_string()),
version: Some("8.0.4".to_string()),
qualifiers: None,
subpath: None,
primary_language: None,
description: None,
release_date: None,
parties: vec![],
keywords: vec![],
homepage_url: None,
download_url: None,
size: None,
sha1: None,
md5: None,
sha256: None,
sha512: None,
bug_tracking_url: None,
code_view_url: None,
vcs_url: None,
copyright: None,
holder: None,
declared_license_expression: None,
declared_license_expression_spdx: None,
license_detections: vec![],
other_license_expression: None,
other_license_expression_spdx: None,
other_license_detections: vec![],
extracted_license_statement: None,
notice_text: None,
source_packages: vec![],
is_private: false,
is_virtual: false,
extra_data: None,
repository_homepage_url: None,
repository_download_url: None,
api_data_url: None,
purl: Some("pkg:pypi/click@8.0.4".to_string()),
package_uid: "pkg:pypi/click@8.0.4?uuid=test-uuid".to_string(),
datafile_paths: vec![
"venv/lib/python3.11/site-packages/click-8.0.4.dist-info/METADATA".to_string(),
],
datasource_ids: vec![DatasourceId::PypiWheelMetadata],
}];
let mut dependencies = vec![];
resolve_file_references(&mut files, &mut packages, &mut dependencies);
assert_eq!(files[1].for_packages.len(), 1);
assert_eq!(files[2].for_packages.len(), 1);
assert_eq!(files[3].for_packages.len(), 1);
assert_eq!(
files[2].for_packages[0],
"pkg:pypi/click@8.0.4?uuid=test-uuid"
);
}
#[test]
fn test_resolve_python_pkg_info_installed_files_references() {
let mut files = vec![
FileInfo {
name: "PKG-INFO".to_string(),
base_name: "PKG-INFO".to_string(),
extension: String::new(),
path: "venv/lib/python3.11/site-packages/examplepkg.egg-info/PKG-INFO".to_string(),
file_type: FileType::File,
mime_type: None,
size: 100,
date: None,
sha1: None,
md5: None,
sha256: None,
programming_language: None,
package_data: vec![PackageData {
datasource_id: Some(DatasourceId::PypiSdistPkginfo),
purl: Some("pkg:pypi/examplepkg@1.0.0".to_string()),
name: Some("examplepkg".to_string()),
version: Some("1.0.0".to_string()),
file_references: vec![FileReference {
path: "../examplepkg/core.py".to_string(),
size: None,
sha1: None,
md5: None,
sha256: None,
sha512: None,
extra_data: None,
}],
..Default::default()
}],
license_expression: None,
license_detections: vec![],
license_clues: vec![],
percentage_of_license_text: None,
copyrights: vec![],
holders: vec![],
authors: vec![],
emails: vec![],
urls: vec![],
for_packages: vec![],
scan_errors: vec![],
is_source: None,
source_count: None,
is_legal: false,
is_manifest: false,
is_readme: false,
is_top_level: false,
is_key_file: false,
is_community: false,
is_generated: None,
facets: vec![],
tallies: None,
},
FileInfo {
name: "core.py".to_string(),
base_name: "core".to_string(),
extension: "py".to_string(),
path: "venv/lib/python3.11/site-packages/examplepkg/core.py".to_string(),
file_type: FileType::File,
mime_type: None,
size: 10,
date: None,
sha1: None,
md5: None,
sha256: None,
programming_language: None,
package_data: vec![],
license_expression: None,
license_detections: vec![],
license_clues: vec![],
percentage_of_license_text: None,
copyrights: vec![],
holders: vec![],
authors: vec![],
emails: vec![],
urls: vec![],
for_packages: vec![],
scan_errors: vec![],
is_source: None,
source_count: None,
is_legal: false,
is_manifest: false,
is_readme: false,
is_top_level: false,
is_key_file: false,
is_community: false,
is_generated: None,
facets: vec![],
tallies: None,
},
];
let mut packages = vec![Package {
package_type: Some(PackageType::Pypi),
namespace: None,
name: Some("examplepkg".to_string()),
version: Some("1.0.0".to_string()),
qualifiers: None,
subpath: None,
primary_language: None,
description: None,
release_date: None,
parties: vec![],
keywords: vec![],
homepage_url: None,
download_url: None,
size: None,
sha1: None,
md5: None,
sha256: None,
sha512: None,
bug_tracking_url: None,
code_view_url: None,
vcs_url: None,
copyright: None,
holder: None,
declared_license_expression: None,
declared_license_expression_spdx: None,
license_detections: vec![],
other_license_expression: None,
other_license_expression_spdx: None,
other_license_detections: vec![],
extracted_license_statement: None,
notice_text: None,
source_packages: vec![],
is_private: false,
is_virtual: false,
extra_data: None,
repository_homepage_url: None,
repository_download_url: None,
api_data_url: None,
purl: Some("pkg:pypi/examplepkg@1.0.0".to_string()),
package_uid: "pkg:pypi/examplepkg@1.0.0?uuid=test-uuid".to_string(),
datafile_paths: vec![
"venv/lib/python3.11/site-packages/examplepkg.egg-info/PKG-INFO".to_string(),
],
datasource_ids: vec![DatasourceId::PypiSdistPkginfo],
}];
let mut dependencies = vec![];
resolve_file_references(&mut files, &mut packages, &mut dependencies);
assert_eq!(
files[1].for_packages,
vec!["pkg:pypi/examplepkg@1.0.0?uuid=test-uuid".to_string()]
);
}
#[test]
fn test_resolve_python_metadata_file_references_in_dist_packages() {
let mut files = vec![
FileInfo {
name: "METADATA".to_string(),
base_name: "METADATA".to_string(),
extension: String::new(),
path: "usr/lib/python3/dist-packages/click-8.0.4.dist-info/METADATA".to_string(),
file_type: FileType::File,
mime_type: None,
size: 100,
date: None,
sha1: None,
md5: None,
sha256: None,
programming_language: None,
package_data: vec![PackageData {
datasource_id: Some(DatasourceId::PypiWheelMetadata),
purl: Some("pkg:pypi/click@8.0.4".to_string()),
name: Some("click".to_string()),
version: Some("8.0.4".to_string()),
file_references: vec![FileReference {
path: "click/core.py".to_string(),
size: None,
sha1: None,
md5: None,
sha256: None,
sha512: None,
extra_data: None,
}],
..Default::default()
}],
license_expression: None,
license_detections: vec![],
license_clues: vec![],
percentage_of_license_text: None,
copyrights: vec![],
holders: vec![],
authors: vec![],
emails: vec![],
urls: vec![],
for_packages: vec![],
scan_errors: vec![],
is_source: None,
source_count: None,
is_legal: false,
is_manifest: false,
is_readme: false,
is_top_level: false,
is_key_file: false,
is_community: false,
is_generated: None,
facets: vec![],
tallies: None,
},
FileInfo {
name: "core.py".to_string(),
base_name: "core".to_string(),
extension: "py".to_string(),
path: "usr/lib/python3/dist-packages/click/core.py".to_string(),
file_type: FileType::File,
mime_type: None,
size: 10,
date: None,
sha1: None,
md5: None,
sha256: None,
programming_language: None,
package_data: vec![],
license_expression: None,
license_detections: vec![],
license_clues: vec![],
percentage_of_license_text: None,
copyrights: vec![],
holders: vec![],
authors: vec![],
emails: vec![],
urls: vec![],
for_packages: vec![],
scan_errors: vec![],
is_source: None,
source_count: None,
is_legal: false,
is_manifest: false,
is_readme: false,
is_top_level: false,
is_key_file: false,
is_community: false,
is_generated: None,
facets: vec![],
tallies: None,
},
];
let mut packages = vec![Package {
package_type: Some(PackageType::Pypi),
namespace: None,
name: Some("click".to_string()),
version: Some("8.0.4".to_string()),
qualifiers: None,
subpath: None,
primary_language: None,
description: None,
release_date: None,
parties: vec![],
keywords: vec![],
homepage_url: None,
download_url: None,
size: None,
sha1: None,
md5: None,
sha256: None,
sha512: None,
bug_tracking_url: None,
code_view_url: None,
vcs_url: None,
copyright: None,
holder: None,
declared_license_expression: None,
declared_license_expression_spdx: None,
license_detections: vec![],
other_license_expression: None,
other_license_expression_spdx: None,
other_license_detections: vec![],
extracted_license_statement: None,
notice_text: None,
source_packages: vec![],
is_private: false,
is_virtual: false,
extra_data: None,
repository_homepage_url: None,
repository_download_url: None,
api_data_url: None,
purl: Some("pkg:pypi/click@8.0.4".to_string()),
package_uid: "pkg:pypi/click@8.0.4?uuid=test-uuid".to_string(),
datafile_paths: vec![
"usr/lib/python3/dist-packages/click-8.0.4.dist-info/METADATA".to_string(),
],
datasource_ids: vec![DatasourceId::PypiWheelMetadata],
}];
let mut dependencies = vec![];
resolve_file_references(&mut files, &mut packages, &mut dependencies);
assert_eq!(
files[1].for_packages,
vec!["pkg:pypi/click@8.0.4?uuid=test-uuid".to_string()]
);
}
#[test]
fn test_python_metadata_file_references_do_not_assign_outside_packages_dirs() {
let mut files = vec![
FileInfo {
name: "METADATA".to_string(),
base_name: "METADATA".to_string(),
extension: String::new(),
path: "project/metadata/METADATA".to_string(),
file_type: FileType::File,
mime_type: None,
size: 100,
date: None,
sha1: None,
md5: None,
sha256: None,
programming_language: None,
package_data: vec![PackageData {
datasource_id: Some(DatasourceId::PypiWheelMetadata),
purl: Some("pkg:pypi/examplepkg@1.0.0".to_string()),
name: Some("examplepkg".to_string()),
version: Some("1.0.0".to_string()),
file_references: vec![FileReference {
path: "examplepkg/core.py".to_string(),
size: None,
sha1: None,
md5: None,
sha256: None,
sha512: None,
extra_data: None,
}],
..Default::default()
}],
license_expression: None,
license_detections: vec![],
license_clues: vec![],
percentage_of_license_text: None,
copyrights: vec![],
holders: vec![],
authors: vec![],
emails: vec![],
urls: vec![],
for_packages: vec![],
scan_errors: vec![],
is_source: None,
source_count: None,
is_legal: false,
is_manifest: false,
is_readme: false,
is_top_level: false,
is_key_file: false,
is_community: false,
is_generated: None,
facets: vec![],
tallies: None,
},
FileInfo {
name: "core.py".to_string(),
base_name: "core".to_string(),
extension: "py".to_string(),
path: "project/examplepkg/core.py".to_string(),
file_type: FileType::File,
mime_type: None,
size: 10,
date: None,
sha1: None,
md5: None,
sha256: None,
programming_language: None,
package_data: vec![],
license_expression: None,
license_detections: vec![],
license_clues: vec![],
percentage_of_license_text: None,
copyrights: vec![],
holders: vec![],
authors: vec![],
emails: vec![],
urls: vec![],
for_packages: vec![],
scan_errors: vec![],
is_source: None,
source_count: None,
is_legal: false,
is_manifest: false,
is_readme: false,
is_top_level: false,
is_key_file: false,
is_community: false,
is_generated: None,
facets: vec![],
tallies: None,
},
];
let mut packages = vec![Package {
package_type: Some(PackageType::Pypi),
namespace: None,
name: Some("examplepkg".to_string()),
version: Some("1.0.0".to_string()),
qualifiers: None,
subpath: None,
primary_language: None,
description: None,
release_date: None,
parties: vec![],
keywords: vec![],
homepage_url: None,
download_url: None,
size: None,
sha1: None,
md5: None,
sha256: None,
sha512: None,
bug_tracking_url: None,
code_view_url: None,
vcs_url: None,
copyright: None,
holder: None,
declared_license_expression: None,
declared_license_expression_spdx: None,
license_detections: vec![],
other_license_expression: None,
other_license_expression_spdx: None,
other_license_detections: vec![],
extracted_license_statement: None,
notice_text: None,
source_packages: vec![],
is_private: false,
is_virtual: false,
extra_data: None,
repository_homepage_url: None,
repository_download_url: None,
api_data_url: None,
purl: Some("pkg:pypi/examplepkg@1.0.0".to_string()),
package_uid: "pkg:pypi/examplepkg@1.0.0?uuid=test-uuid".to_string(),
datafile_paths: vec!["project/metadata/METADATA".to_string()],
datasource_ids: vec![DatasourceId::PypiWheelMetadata],
}];
let mut dependencies = vec![];
resolve_file_references(&mut files, &mut packages, &mut dependencies);
assert!(files[1].for_packages.is_empty());
}
#[test]
fn test_python_sources_file_references_do_not_escape_project_root() {
let mut files = vec![
FileInfo {
name: "PKG-INFO".to_string(),
base_name: "PKG-INFO".to_string(),
extension: String::new(),
path: "project/PyJPString.egg-info/PKG-INFO".to_string(),
file_type: FileType::File,
mime_type: None,
size: 100,
date: None,
sha1: None,
md5: None,
sha256: None,
programming_language: None,
package_data: vec![PackageData {
datasource_id: Some(DatasourceId::PypiSdistPkginfo),
purl: Some("pkg:pypi/PyJPString@0.0.3".to_string()),
name: Some("PyJPString".to_string()),
version: Some("0.0.3".to_string()),
file_references: vec![FileReference {
path: "../../outside.py".to_string(),
size: None,
sha1: None,
md5: None,
sha256: None,
sha512: None,
extra_data: None,
}],
..Default::default()
}],
license_expression: None,
license_detections: vec![],
license_clues: vec![],
percentage_of_license_text: None,
copyrights: vec![],
holders: vec![],
authors: vec![],
emails: vec![],
urls: vec![],
for_packages: vec![],
scan_errors: vec![],
is_source: None,
source_count: None,
is_legal: false,
is_manifest: false,
is_readme: false,
is_top_level: false,
is_key_file: false,
is_community: false,
is_generated: None,
facets: vec![],
tallies: None,
},
FileInfo {
name: "outside.py".to_string(),
base_name: "outside".to_string(),
extension: "py".to_string(),
path: "outside.py".to_string(),
file_type: FileType::File,
mime_type: None,
size: 10,
date: None,
sha1: None,
md5: None,
sha256: None,
programming_language: None,
package_data: vec![],
license_expression: None,
license_detections: vec![],
license_clues: vec![],
percentage_of_license_text: None,
copyrights: vec![],
holders: vec![],
authors: vec![],
emails: vec![],
urls: vec![],
for_packages: vec![],
scan_errors: vec![],
is_source: None,
source_count: None,
is_legal: false,
is_manifest: false,
is_readme: false,
is_top_level: false,
is_key_file: false,
is_community: false,
is_generated: None,
facets: vec![],
tallies: None,
},
];
let mut packages = vec![Package {
package_type: Some(PackageType::Pypi),
namespace: None,
name: Some("PyJPString".to_string()),
version: Some("0.0.3".to_string()),
qualifiers: None,
subpath: None,
primary_language: None,
description: None,
release_date: None,
parties: vec![],
keywords: vec![],
homepage_url: None,
download_url: None,
size: None,
sha1: None,
md5: None,
sha256: None,
sha512: None,
bug_tracking_url: None,
code_view_url: None,
vcs_url: None,
copyright: None,
holder: None,
declared_license_expression: None,
declared_license_expression_spdx: None,
license_detections: vec![],
other_license_expression: None,
other_license_expression_spdx: None,
other_license_detections: vec![],
extracted_license_statement: None,
notice_text: None,
source_packages: vec![],
is_private: false,
is_virtual: false,
extra_data: None,
repository_homepage_url: None,
repository_download_url: None,
api_data_url: None,
purl: Some("pkg:pypi/PyJPString@0.0.3".to_string()),
package_uid: "pkg:pypi/PyJPString@0.0.3?uuid=test-uuid".to_string(),
datafile_paths: vec!["project/PyJPString.egg-info/PKG-INFO".to_string()],
datasource_ids: vec![DatasourceId::PypiSdistPkginfo],
}];
let mut dependencies = vec![];
resolve_file_references(&mut files, &mut packages, &mut dependencies);
assert!(files[1].for_packages.is_empty());
let missing = packages[0]
.extra_data
.as_ref()
.and_then(|extra| extra.get("missing_file_references"))
.and_then(|value| value.as_array())
.expect("missing_file_references should be recorded");
assert_eq!(missing.len(), 1);
}
#[test]
fn test_resolve_debian_installed_file_references_from_status_db() {
let mut files = vec![
FileInfo {
name: "status".to_string(),
base_name: "status".to_string(),
extension: String::new(),
path: "rootfs/var/lib/dpkg/status".to_string(),
file_type: FileType::File,
mime_type: None,
size: 100,
date: None,
sha1: None,
md5: None,
sha256: None,
programming_language: None,
package_data: vec![PackageData {
datasource_id: Some(DatasourceId::DebianInstalledStatusDb),
package_type: Some(PackageType::Deb),
namespace: Some("debian".to_string()),
name: Some("bash".to_string()),
version: Some("5.2-1".to_string()),
purl: Some("pkg:deb/debian/bash@5.2-1?arch=amd64".to_string()),
..Default::default()
}],
license_expression: None,
license_detections: vec![],
license_clues: vec![],
percentage_of_license_text: None,
copyrights: vec![],
holders: vec![],
authors: vec![],
emails: vec![],
urls: vec![],
for_packages: vec![],
scan_errors: vec![],
is_source: None,
source_count: None,
is_legal: false,
is_manifest: false,
is_readme: false,
is_top_level: false,
is_key_file: false,
is_community: false,
is_generated: None,
facets: vec![],
tallies: None,
},
FileInfo {
name: "bash.list".to_string(),
base_name: "bash".to_string(),
extension: "list".to_string(),
path: "rootfs/var/lib/dpkg/info/bash.list".to_string(),
file_type: FileType::File,
mime_type: None,
size: 40,
date: None,
sha1: None,
md5: None,
sha256: None,
programming_language: None,
package_data: vec![PackageData {
datasource_id: Some(DatasourceId::DebianInstalledFilesList),
package_type: Some(PackageType::Deb),
namespace: Some("debian".to_string()),
name: Some("bash".to_string()),
purl: Some("pkg:deb/debian/bash".to_string()),
file_references: vec![
FileReference {
path: "/bin/bash".to_string(),
size: None,
sha1: None,
md5: None,
sha256: None,
sha512: None,
extra_data: None,
},
FileReference {
path: "/usr/share/doc/bash/copyright".to_string(),
size: None,
sha1: None,
md5: None,
sha256: None,
sha512: None,
extra_data: None,
},
],
..Default::default()
}],
license_expression: None,
license_detections: vec![],
license_clues: vec![],
percentage_of_license_text: None,
copyrights: vec![],
holders: vec![],
authors: vec![],
emails: vec![],
urls: vec![],
for_packages: vec![],
scan_errors: vec![],
is_source: None,
source_count: None,
is_legal: false,
is_manifest: false,
is_readme: false,
is_top_level: false,
is_key_file: false,
is_community: false,
is_generated: None,
facets: vec![],
tallies: None,
},
FileInfo {
name: "bash.md5sums".to_string(),
base_name: "bash".to_string(),
extension: "md5sums".to_string(),
path: "rootfs/var/lib/dpkg/info/bash.md5sums".to_string(),
file_type: FileType::File,
mime_type: None,
size: 40,
date: None,
sha1: None,
md5: None,
sha256: None,
programming_language: None,
package_data: vec![PackageData {
datasource_id: Some(DatasourceId::DebianInstalledMd5Sums),
package_type: Some(PackageType::Deb),
namespace: Some("debian".to_string()),
name: Some("bash".to_string()),
purl: Some("pkg:deb/debian/bash".to_string()),
file_references: vec![FileReference {
path: "bin/bash".to_string(),
size: None,
sha1: None,
md5: Some("77506afebd3b7e19e937a678a185b62e".to_string()),
sha256: None,
sha512: None,
extra_data: None,
}],
..Default::default()
}],
license_expression: None,
license_detections: vec![],
license_clues: vec![],
percentage_of_license_text: None,
copyrights: vec![],
holders: vec![],
authors: vec![],
emails: vec![],
urls: vec![],
for_packages: vec![],
scan_errors: vec![],
is_source: None,
source_count: None,
is_legal: false,
is_manifest: false,
is_readme: false,
is_top_level: false,
is_key_file: false,
is_community: false,
is_generated: None,
facets: vec![],
tallies: None,
},
FileInfo {
name: "bash".to_string(),
base_name: "bash".to_string(),
extension: String::new(),
path: "rootfs/bin/bash".to_string(),
file_type: FileType::File,
mime_type: None,
size: 20,
date: None,
sha1: None,
md5: None,
sha256: None,
programming_language: None,
package_data: vec![],
license_expression: None,
license_detections: vec![],
license_clues: vec![],
percentage_of_license_text: None,
copyrights: vec![],
holders: vec![],
authors: vec![],
emails: vec![],
urls: vec![],
for_packages: vec![],
scan_errors: vec![],
is_source: None,
source_count: None,
is_legal: false,
is_manifest: false,
is_readme: false,
is_top_level: false,
is_key_file: false,
is_community: false,
is_generated: None,
facets: vec![],
tallies: None,
},
FileInfo {
name: "copyright".to_string(),
base_name: "copyright".to_string(),
extension: String::new(),
path: "rootfs/usr/share/doc/bash/copyright".to_string(),
file_type: FileType::File,
mime_type: None,
size: 20,
date: None,
sha1: None,
md5: None,
sha256: None,
programming_language: None,
package_data: vec![],
license_expression: None,
license_detections: vec![],
license_clues: vec![],
percentage_of_license_text: None,
copyrights: vec![],
holders: vec![],
authors: vec![],
emails: vec![],
urls: vec![],
for_packages: vec![],
scan_errors: vec![],
is_source: None,
source_count: None,
is_legal: false,
is_manifest: false,
is_readme: false,
is_top_level: false,
is_key_file: false,
is_community: false,
is_generated: None,
facets: vec![],
tallies: None,
},
];
let mut packages = vec![Package {
package_type: Some(PackageType::Deb),
namespace: Some("debian".to_string()),
name: Some("bash".to_string()),
version: Some("5.2-1".to_string()),
qualifiers: Some(HashMap::from([("arch".to_string(), "amd64".to_string())])),
subpath: None,
primary_language: None,
description: None,
release_date: None,
parties: vec![],
keywords: vec![],
homepage_url: None,
download_url: None,
size: None,
sha1: None,
md5: None,
sha256: None,
sha512: None,
bug_tracking_url: None,
code_view_url: None,
vcs_url: None,
copyright: None,
holder: None,
declared_license_expression: None,
declared_license_expression_spdx: None,
license_detections: vec![],
other_license_expression: None,
other_license_expression_spdx: None,
other_license_detections: vec![],
extracted_license_statement: None,
notice_text: None,
source_packages: vec![],
is_private: false,
is_virtual: false,
extra_data: None,
repository_homepage_url: None,
repository_download_url: None,
api_data_url: None,
purl: Some("pkg:deb/debian/bash@5.2-1?arch=amd64".to_string()),
package_uid: "pkg:deb/debian/bash@5.2-1?arch=amd64&uuid=test-uuid".to_string(),
datafile_paths: vec!["rootfs/var/lib/dpkg/status".to_string()],
datasource_ids: vec![DatasourceId::DebianInstalledStatusDb],
}];
let mut dependencies = vec![];
resolve_file_references(&mut files, &mut packages, &mut dependencies);
assert_eq!(
files[3].for_packages,
vec!["pkg:deb/debian/bash@5.2-1?arch=amd64&uuid=test-uuid".to_string()]
);
assert_eq!(
files[4].for_packages,
vec!["pkg:deb/debian/bash@5.2-1?arch=amd64&uuid=test-uuid".to_string()]
);
}
#[test]
fn test_resolve_debian_installed_file_references_matches_ubuntu_package_namespace() {
let mut files = vec![
FileInfo {
name: "status".to_string(),
base_name: "status".to_string(),
extension: String::new(),
path: "rootfs/var/lib/dpkg/status".to_string(),
file_type: FileType::File,
mime_type: None,
size: 100,
date: None,
sha1: None,
md5: None,
sha256: None,
programming_language: None,
package_data: vec![PackageData {
datasource_id: Some(DatasourceId::DebianInstalledStatusDb),
package_type: Some(PackageType::Deb),
namespace: Some("ubuntu".to_string()),
name: Some("bash".to_string()),
version: Some("5.2-1ubuntu1".to_string()),
purl: Some("pkg:deb/ubuntu/bash@5.2-1ubuntu1?arch=amd64".to_string()),
..Default::default()
}],
license_expression: None,
license_detections: vec![],
license_clues: vec![],
percentage_of_license_text: None,
copyrights: vec![],
holders: vec![],
authors: vec![],
emails: vec![],
urls: vec![],
for_packages: vec![],
scan_errors: vec![],
is_source: None,
source_count: None,
is_legal: false,
is_manifest: false,
is_readme: false,
is_top_level: false,
is_key_file: false,
is_community: false,
is_generated: None,
facets: vec![],
tallies: None,
},
FileInfo {
name: "bash.list".to_string(),
base_name: "bash".to_string(),
extension: "list".to_string(),
path: "rootfs/var/lib/dpkg/info/bash.list".to_string(),
file_type: FileType::File,
mime_type: None,
size: 40,
date: None,
sha1: None,
md5: None,
sha256: None,
programming_language: None,
package_data: vec![PackageData {
datasource_id: Some(DatasourceId::DebianInstalledFilesList),
package_type: Some(PackageType::Deb),
namespace: Some("debian".to_string()),
name: Some("bash".to_string()),
purl: Some("pkg:deb/debian/bash".to_string()),
file_references: vec![FileReference {
path: "/bin/bash".to_string(),
size: None,
sha1: None,
md5: None,
sha256: None,
sha512: None,
extra_data: None,
}],
..Default::default()
}],
license_expression: None,
license_detections: vec![],
license_clues: vec![],
percentage_of_license_text: None,
copyrights: vec![],
holders: vec![],
authors: vec![],
emails: vec![],
urls: vec![],
for_packages: vec![],
scan_errors: vec![],
is_source: None,
source_count: None,
is_legal: false,
is_manifest: false,
is_readme: false,
is_top_level: false,
is_key_file: false,
is_community: false,
is_generated: None,
facets: vec![],
tallies: None,
},
FileInfo {
name: "bash".to_string(),
base_name: "bash".to_string(),
extension: String::new(),
path: "rootfs/bin/bash".to_string(),
file_type: FileType::File,
mime_type: None,
size: 20,
date: None,
sha1: None,
md5: None,
sha256: None,
programming_language: None,
package_data: vec![],
license_expression: None,
license_detections: vec![],
license_clues: vec![],
percentage_of_license_text: None,
copyrights: vec![],
holders: vec![],
authors: vec![],
emails: vec![],
urls: vec![],
for_packages: vec![],
scan_errors: vec![],
is_source: None,
source_count: None,
is_legal: false,
is_manifest: false,
is_readme: false,
is_top_level: false,
is_key_file: false,
is_community: false,
is_generated: None,
facets: vec![],
tallies: None,
},
];
let mut packages = vec![Package {
package_type: Some(PackageType::Deb),
namespace: Some("ubuntu".to_string()),
name: Some("bash".to_string()),
version: Some("5.2-1ubuntu1".to_string()),
qualifiers: Some(HashMap::from([("arch".to_string(), "amd64".to_string())])),
subpath: None,
primary_language: None,
description: None,
release_date: None,
parties: vec![],
keywords: vec![],
homepage_url: None,
download_url: None,
size: None,
sha1: None,
md5: None,
sha256: None,
sha512: None,
bug_tracking_url: None,
code_view_url: None,
vcs_url: None,
copyright: None,
holder: None,
declared_license_expression: None,
declared_license_expression_spdx: None,
license_detections: vec![],
other_license_expression: None,
other_license_expression_spdx: None,
other_license_detections: vec![],
extracted_license_statement: None,
notice_text: None,
source_packages: vec![],
is_private: false,
is_virtual: false,
extra_data: None,
repository_homepage_url: None,
repository_download_url: None,
api_data_url: None,
purl: Some("pkg:deb/ubuntu/bash@5.2-1ubuntu1?arch=amd64".to_string()),
package_uid: "pkg:deb/ubuntu/bash@5.2-1ubuntu1?arch=amd64&uuid=test-uuid".to_string(),
datafile_paths: vec!["rootfs/var/lib/dpkg/status".to_string()],
datasource_ids: vec![DatasourceId::DebianInstalledStatusDb],
}];
let mut dependencies = vec![];
resolve_file_references(&mut files, &mut packages, &mut dependencies);
assert_eq!(
files[2].for_packages,
vec!["pkg:deb/ubuntu/bash@5.2-1ubuntu1?arch=amd64&uuid=test-uuid".to_string()]
);
}
#[test]
fn test_resolve_debian_installed_file_references_respects_arch_qualifier() {
let mut files = vec![
FileInfo {
name: "status".to_string(),
base_name: "status".to_string(),
extension: String::new(),
path: "rootfs/var/lib/dpkg/status".to_string(),
file_type: FileType::File,
mime_type: None,
size: 100,
date: None,
sha1: None,
md5: None,
sha256: None,
programming_language: None,
package_data: vec![PackageData {
datasource_id: Some(DatasourceId::DebianInstalledStatusDb),
package_type: Some(PackageType::Deb),
namespace: Some("debian".to_string()),
name: Some("libc6".to_string()),
version: Some("2.36-1".to_string()),
purl: Some("pkg:deb/debian/libc6@2.36-1?arch=amd64".to_string()),
qualifiers: Some(HashMap::from([("arch".to_string(), "amd64".to_string())])),
..Default::default()
}],
license_expression: None,
license_detections: vec![],
license_clues: vec![],
percentage_of_license_text: None,
copyrights: vec![],
holders: vec![],
authors: vec![],
emails: vec![],
urls: vec![],
for_packages: vec![],
scan_errors: vec![],
is_source: None,
source_count: None,
is_legal: false,
is_manifest: false,
is_readme: false,
is_top_level: false,
is_key_file: false,
is_community: false,
is_generated: None,
facets: vec![],
tallies: None,
},
FileInfo {
name: "libc6:amd64.list".to_string(),
base_name: "libc6:amd64".to_string(),
extension: "list".to_string(),
path: "rootfs/var/lib/dpkg/info/libc6:amd64.list".to_string(),
file_type: FileType::File,
mime_type: None,
size: 20,
date: None,
sha1: None,
md5: None,
sha256: None,
programming_language: None,
package_data: vec![PackageData {
datasource_id: Some(DatasourceId::DebianInstalledFilesList),
package_type: Some(PackageType::Deb),
namespace: Some("debian".to_string()),
name: Some("libc6".to_string()),
qualifiers: Some(HashMap::from([("arch".to_string(), "amd64".to_string())])),
purl: Some("pkg:deb/debian/libc6?arch=amd64".to_string()),
file_references: vec![FileReference {
path: "/lib/x86_64-linux-gnu/libc.so.6".to_string(),
size: None,
sha1: None,
md5: None,
sha256: None,
sha512: None,
extra_data: None,
}],
..Default::default()
}],
license_expression: None,
license_detections: vec![],
license_clues: vec![],
percentage_of_license_text: None,
copyrights: vec![],
holders: vec![],
authors: vec![],
emails: vec![],
urls: vec![],
for_packages: vec![],
scan_errors: vec![],
is_source: None,
source_count: None,
is_legal: false,
is_manifest: false,
is_readme: false,
is_top_level: false,
is_key_file: false,
is_community: false,
is_generated: None,
facets: vec![],
tallies: None,
},
FileInfo {
name: "libc6:i386.list".to_string(),
base_name: "libc6:i386".to_string(),
extension: "list".to_string(),
path: "rootfs/var/lib/dpkg/info/libc6:i386.list".to_string(),
file_type: FileType::File,
mime_type: None,
size: 20,
date: None,
sha1: None,
md5: None,
sha256: None,
programming_language: None,
package_data: vec![PackageData {
datasource_id: Some(DatasourceId::DebianInstalledFilesList),
package_type: Some(PackageType::Deb),
namespace: Some("debian".to_string()),
name: Some("libc6".to_string()),
qualifiers: Some(HashMap::from([("arch".to_string(), "i386".to_string())])),
purl: Some("pkg:deb/debian/libc6?arch=i386".to_string()),
file_references: vec![FileReference {
path: "/lib/i386-linux-gnu/libc.so.6".to_string(),
size: None,
sha1: None,
md5: None,
sha256: None,
sha512: None,
extra_data: None,
}],
..Default::default()
}],
license_expression: None,
license_detections: vec![],
license_clues: vec![],
percentage_of_license_text: None,
copyrights: vec![],
holders: vec![],
authors: vec![],
emails: vec![],
urls: vec![],
for_packages: vec![],
scan_errors: vec![],
is_source: None,
source_count: None,
is_legal: false,
is_manifest: false,
is_readme: false,
is_top_level: false,
is_key_file: false,
is_community: false,
is_generated: None,
facets: vec![],
tallies: None,
},
FileInfo {
name: "libc.so.6".to_string(),
base_name: "libc.so".to_string(),
extension: "6".to_string(),
path: "rootfs/lib/x86_64-linux-gnu/libc.so.6".to_string(),
file_type: FileType::File,
mime_type: None,
size: 10,
date: None,
sha1: None,
md5: None,
sha256: None,
programming_language: None,
package_data: vec![],
license_expression: None,
license_detections: vec![],
license_clues: vec![],
percentage_of_license_text: None,
copyrights: vec![],
holders: vec![],
authors: vec![],
emails: vec![],
urls: vec![],
for_packages: vec![],
scan_errors: vec![],
is_source: None,
source_count: None,
is_legal: false,
is_manifest: false,
is_readme: false,
is_top_level: false,
is_key_file: false,
is_community: false,
is_generated: None,
facets: vec![],
tallies: None,
},
FileInfo {
name: "libc.so.6".to_string(),
base_name: "libc.so".to_string(),
extension: "6".to_string(),
path: "rootfs/lib/i386-linux-gnu/libc.so.6".to_string(),
file_type: FileType::File,
mime_type: None,
size: 10,
date: None,
sha1: None,
md5: None,
sha256: None,
programming_language: None,
package_data: vec![],
license_expression: None,
license_detections: vec![],
license_clues: vec![],
percentage_of_license_text: None,
copyrights: vec![],
holders: vec![],
authors: vec![],
emails: vec![],
urls: vec![],
for_packages: vec![],
scan_errors: vec![],
is_source: None,
source_count: None,
is_legal: false,
is_manifest: false,
is_readme: false,
is_top_level: false,
is_key_file: false,
is_community: false,
is_generated: None,
facets: vec![],
tallies: None,
},
];
let mut packages = vec![Package {
package_type: Some(PackageType::Deb),
namespace: Some("debian".to_string()),
name: Some("libc6".to_string()),
version: Some("2.36-1".to_string()),
qualifiers: Some(HashMap::from([("arch".to_string(), "amd64".to_string())])),
subpath: None,
primary_language: None,
description: None,
release_date: None,
parties: vec![],
keywords: vec![],
homepage_url: None,
download_url: None,
size: None,
sha1: None,
md5: None,
sha256: None,
sha512: None,
bug_tracking_url: None,
code_view_url: None,
vcs_url: None,
copyright: None,
holder: None,
declared_license_expression: None,
declared_license_expression_spdx: None,
license_detections: vec![],
other_license_expression: None,
other_license_expression_spdx: None,
other_license_detections: vec![],
extracted_license_statement: None,
notice_text: None,
source_packages: vec![],
is_private: false,
is_virtual: false,
extra_data: None,
repository_homepage_url: None,
repository_download_url: None,
api_data_url: None,
purl: Some("pkg:deb/debian/libc6@2.36-1?arch=amd64".to_string()),
package_uid: "pkg:deb/debian/libc6@2.36-1?arch=amd64&uuid=test-uuid".to_string(),
datafile_paths: vec!["rootfs/var/lib/dpkg/status".to_string()],
datasource_ids: vec![DatasourceId::DebianInstalledStatusDb],
}];
let mut dependencies = vec![];
resolve_file_references(&mut files, &mut packages, &mut dependencies);
assert_eq!(
files[3].for_packages,
vec!["pkg:deb/debian/libc6@2.36-1?arch=amd64&uuid=test-uuid".to_string()]
);
assert!(files[4].for_packages.is_empty());
}