diff --git a/src/packagedcode/__init__.py b/src/packagedcode/__init__.py
index 0887a1859da..0cf25adca00 100644
--- a/src/packagedcode/__init__.py
+++ b/src/packagedcode/__init__.py
@@ -148,6 +148,8 @@
nuget.NugetNupkgHandler,
nuget.NugetNuspecHandler,
+ nuget.NugetCsprojHandler,
+ nuget.NugetPackagesConfigHandler,
opam.OpamFileHandler,
diff --git a/src/packagedcode/nuget.py b/src/packagedcode/nuget.py
index dee1afb39d5..1553eb2fde7 100644
--- a/src/packagedcode/nuget.py
+++ b/src/packagedcode/nuget.py
@@ -178,3 +178,64 @@ def parse(cls, location):
**urls,
)
+class NugetCsprojHandler(models.DatafileHandler):
+ datasource_id = 'nuget_csproj'
+ path_patterns = ('*.csproj',)
+ default_package_type = 'nuget'
+ description = 'Csproj package manifest'
+ documentation_url = 'https://learn.microsoft.com/en-us/nuget/consume-packages/package-references-in-project-files'
+
+ @classmethod
+ def parse(cls, location):
+ with open(location, 'rb') as loc:
+ parsed = xmltodict.parse(loc)
+ if not parsed:
+ return
+
+ groups = parsed.get('Project', {}).get('ItemGroup')
+ if groups is None:
+ return
+ for group in groups if isinstance(groups, list) else [groups]:
+ pkgs = group.get('PackageReference')
+ if pkgs is None:
+ continue
+ for pkg in pkgs if isinstance(pkgs, list) else [pkgs]:
+ name = pkg.get('Include') or pkg.get('@Include')
+ version = pkg.get('Version') or pkg.get('@Version')
+ urls = get_urls(name, version)
+ yield models.PackageData(
+ datasource_id=cls.datasource_id,
+ type=cls.default_package_type,
+ name=name,
+ version=version,
+ **urls,
+ )
+
+class NugetPackagesConfigHandler(models.DatafileHandler):
+ datasource_id = 'nuget_packages_config'
+ path_patterns = ('*/packages.config',)
+ default_package_type = 'nuget'
+ description = 'NuGet packages config manifest'
+ documentation_url = 'https://learn.microsoft.com/en-us/nuget/reference/packages-config'
+
+ @classmethod
+ def parse(cls, location):
+ with open(location, 'rb') as loc:
+ parsed = xmltodict.parse(loc)
+ if not parsed:
+ return
+
+ pkgs = parsed.get('packages', {}).get('package')
+ if pkgs is None:
+ return
+ for pkg in pkgs if isinstance(pkgs, list) else [pkgs]:
+ name = pkg.get('@id')
+ version = pkg.get('@version')
+ urls = get_urls(name, version)
+ yield models.PackageData(
+ datasource_id=cls.datasource_id,
+ type=cls.default_package_type,
+ name=name,
+ version=version,
+ **urls,
+ )
diff --git a/tests/packagedcode/data/nuget/csproj/gvfs.csproj b/tests/packagedcode/data/nuget/csproj/gvfs.csproj
new file mode 100644
index 00000000000..33c574523b6
--- /dev/null
+++ b/tests/packagedcode/data/nuget/csproj/gvfs.csproj
@@ -0,0 +1,11 @@
+
+
+
+ net461
+
+
+
+
+
+
+
diff --git a/tests/packagedcode/data/nuget/csproj/gvfs.csproj.expected b/tests/packagedcode/data/nuget/csproj/gvfs.csproj.expected
new file mode 100644
index 00000000000..3ec525d0aa0
--- /dev/null
+++ b/tests/packagedcode/data/nuget/csproj/gvfs.csproj.expected
@@ -0,0 +1,44 @@
+[
+ {
+ "type": "nuget",
+ "namespace": null,
+ "name": "Newtonsoft.Json",
+ "version": "13.0.1",
+ "qualifiers": {},
+ "subpath": null,
+ "primary_language": null,
+ "description": null,
+ "release_date": null,
+ "parties": [],
+ "keywords": [],
+ "homepage_url": null,
+ "download_url": null,
+ "size": null,
+ "sha1": null,
+ "md5": null,
+ "sha256": null,
+ "sha512": null,
+ "bug_tracking_url": null,
+ "code_view_url": null,
+ "vcs_url": null,
+ "copyright": null,
+ "holder": null,
+ "declared_license_expression": null,
+ "declared_license_expression_spdx": null,
+ "license_detections": [],
+ "other_license_expression": null,
+ "other_license_expression_spdx": null,
+ "other_license_detections": [],
+ "extracted_license_statement": null,
+ "notice_text": null,
+ "source_packages": [],
+ "file_references": [],
+ "extra_data": {},
+ "dependencies": [],
+ "repository_homepage_url": "https://www.nuget.org/packages/Newtonsoft.Json/13.0.1",
+ "repository_download_url": "https://www.nuget.org/api/v2/package/Newtonsoft.Json/13.0.1",
+ "api_data_url": "https://api.nuget.org/v3/registration3/newtonsoft.json/13.0.1.json",
+ "datasource_id": "nuget_csproj",
+ "purl": "pkg:nuget/Newtonsoft.Json@13.0.1"
+ }
+]
diff --git a/tests/packagedcode/data/nuget/csproj/multiple_groups.csproj b/tests/packagedcode/data/nuget/csproj/multiple_groups.csproj
new file mode 100644
index 00000000000..b11aac9f488
--- /dev/null
+++ b/tests/packagedcode/data/nuget/csproj/multiple_groups.csproj
@@ -0,0 +1,24 @@
+
+
+
+
+
+ PreserveNewest
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/packagedcode/data/nuget/csproj/multiple_groups.csproj.expected b/tests/packagedcode/data/nuget/csproj/multiple_groups.csproj.expected
new file mode 100644
index 00000000000..98b328ad2a1
--- /dev/null
+++ b/tests/packagedcode/data/nuget/csproj/multiple_groups.csproj.expected
@@ -0,0 +1,86 @@
+[
+ {
+ "type": "nuget",
+ "namespace": null,
+ "name": "ByteSize",
+ "version": "2.1.1",
+ "qualifiers": {},
+ "subpath": null,
+ "primary_language": null,
+ "description": null,
+ "release_date": null,
+ "parties": [],
+ "keywords": [],
+ "homepage_url": null,
+ "download_url": null,
+ "size": null,
+ "sha1": null,
+ "md5": null,
+ "sha256": null,
+ "sha512": null,
+ "bug_tracking_url": null,
+ "code_view_url": null,
+ "vcs_url": null,
+ "copyright": null,
+ "holder": null,
+ "declared_license_expression": null,
+ "declared_license_expression_spdx": null,
+ "license_detections": [],
+ "other_license_expression": null,
+ "other_license_expression_spdx": null,
+ "other_license_detections": [],
+ "extracted_license_statement": null,
+ "notice_text": null,
+ "source_packages": [],
+ "file_references": [],
+ "extra_data": {},
+ "dependencies": [],
+ "repository_homepage_url": "https://www.nuget.org/packages/ByteSize/2.1.1",
+ "repository_download_url": "https://www.nuget.org/api/v2/package/ByteSize/2.1.1",
+ "api_data_url": "https://api.nuget.org/v3/registration3/bytesize/2.1.1.json",
+ "datasource_id": "nuget_csproj",
+ "purl": "pkg:nuget/ByteSize@2.1.1"
+ },
+ {
+ "type": "nuget",
+ "namespace": null,
+ "name": "CommunityToolkit.Mvvm",
+ "version": "8.2.2",
+ "qualifiers": {},
+ "subpath": null,
+ "primary_language": null,
+ "description": null,
+ "release_date": null,
+ "parties": [],
+ "keywords": [],
+ "homepage_url": null,
+ "download_url": null,
+ "size": null,
+ "sha1": null,
+ "md5": null,
+ "sha256": null,
+ "sha512": null,
+ "bug_tracking_url": null,
+ "code_view_url": null,
+ "vcs_url": null,
+ "copyright": null,
+ "holder": null,
+ "declared_license_expression": null,
+ "declared_license_expression_spdx": null,
+ "license_detections": [],
+ "other_license_expression": null,
+ "other_license_expression_spdx": null,
+ "other_license_detections": [],
+ "extracted_license_statement": null,
+ "notice_text": null,
+ "source_packages": [],
+ "file_references": [],
+ "extra_data": {},
+ "dependencies": [],
+ "repository_homepage_url": "https://www.nuget.org/packages/CommunityToolkit.Mvvm/8.2.2",
+ "repository_download_url": "https://www.nuget.org/api/v2/package/CommunityToolkit.Mvvm/8.2.2",
+ "api_data_url": "https://api.nuget.org/v3/registration3/communitytoolkit.mvvm/8.2.2.json",
+ "datasource_id": "nuget_csproj",
+ "purl": "pkg:nuget/CommunityToolkit.Mvvm@8.2.2"
+ }
+ ]
diff --git a/tests/packagedcode/data/nuget/packages-config/packages.config b/tests/packagedcode/data/nuget/packages-config/packages.config
new file mode 100644
index 00000000000..71c38ee9108
--- /dev/null
+++ b/tests/packagedcode/data/nuget/packages-config/packages.config
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/tests/packagedcode/data/nuget/packages-config/packages.config.expected b/tests/packagedcode/data/nuget/packages-config/packages.config.expected
new file mode 100644
index 00000000000..f2b9e8c0a42
--- /dev/null
+++ b/tests/packagedcode/data/nuget/packages-config/packages.config.expected
@@ -0,0 +1,86 @@
+[
+ {
+ "type": "nuget",
+ "namespace": null,
+ "name": "jQuery",
+ "version": "3.1.1",
+ "qualifiers": {},
+ "subpath": null,
+ "primary_language": null,
+ "description": null,
+ "release_date": null,
+ "parties": [],
+ "keywords": [],
+ "homepage_url": null,
+ "download_url": null,
+ "size": null,
+ "sha1": null,
+ "md5": null,
+ "sha256": null,
+ "sha512": null,
+ "bug_tracking_url": null,
+ "code_view_url": null,
+ "vcs_url": null,
+ "copyright": null,
+ "holder": null,
+ "declared_license_expression": null,
+ "declared_license_expression_spdx": null,
+ "license_detections": [],
+ "other_license_expression": null,
+ "other_license_expression_spdx": null,
+ "other_license_detections": [],
+ "extracted_license_statement": null,
+ "notice_text": null,
+ "source_packages": [],
+ "file_references": [],
+ "extra_data": {},
+ "dependencies": [],
+ "repository_homepage_url": "https://www.nuget.org/packages/jQuery/3.1.1",
+ "repository_download_url": "https://www.nuget.org/api/v2/package/jQuery/3.1.1",
+ "api_data_url": "https://api.nuget.org/v3/registration3/jquery/3.1.1.json",
+ "datasource_id": "nuget_packages_config",
+ "purl": "pkg:nuget/jQuery@3.1.1"
+ },
+ {
+ "type": "nuget",
+ "namespace": null,
+ "name": "NLog",
+ "version": "4.3.10",
+ "qualifiers": {},
+ "subpath": null,
+ "primary_language": null,
+ "description": null,
+ "release_date": null,
+ "parties": [],
+ "keywords": [],
+ "homepage_url": null,
+ "download_url": null,
+ "size": null,
+ "sha1": null,
+ "md5": null,
+ "sha256": null,
+ "sha512": null,
+ "bug_tracking_url": null,
+ "code_view_url": null,
+ "vcs_url": null,
+ "copyright": null,
+ "holder": null,
+ "declared_license_expression": null,
+ "declared_license_expression_spdx": null,
+ "license_detections": [],
+ "other_license_expression": null,
+ "other_license_expression_spdx": null,
+ "other_license_detections": [],
+ "extracted_license_statement": null,
+ "notice_text": null,
+ "source_packages": [],
+ "file_references": [],
+ "extra_data": {},
+ "dependencies": [],
+ "repository_homepage_url": "https://www.nuget.org/packages/NLog/4.3.10",
+ "repository_download_url": "https://www.nuget.org/api/v2/package/NLog/4.3.10",
+ "api_data_url": "https://api.nuget.org/v3/registration3/nlog/4.3.10.json",
+ "datasource_id": "nuget_packages_config",
+ "purl": "pkg:nuget/NLog@4.3.10"
+ }
+]
diff --git a/tests/packagedcode/test_nuget.py b/tests/packagedcode/test_nuget.py
index 37298a45c7e..3f7d6fb7472 100644
--- a/tests/packagedcode/test_nuget.py
+++ b/tests/packagedcode/test_nuget.py
@@ -56,3 +56,24 @@ def test_parse_as_package(self):
package = nuget.NugetNuspecHandler.parse(test_file)
expected_loc = self.get_test_loc('nuget/Castle.Core.nuspec.json.expected')
self.check_packages_data(package, expected_loc, regen=REGEN_TEST_FIXTURES)
+
+ def test_parse_csproj(self):
+ # See: https://github.com/microsoft/VFSForGit/blob/master/GVFS/GVFS.GVFlt/GVFS.GVFlt.csproj
+ test_file = self.get_test_loc('nuget/csproj/gvfs.csproj')
+ package = nuget.NugetCsprojHandler.parse(test_file)
+ expected_loc = self.get_test_loc('nuget/csproj/gvfs.csproj.expected')
+ self.check_packages_data(package, expected_loc, regen=REGEN_TEST_FIXTURES)
+
+ def test_parse_csproj_multiple_groups(self):
+ # See: https://github.com/files-community/Files/blob/main/src/Files.App/Files.App.csproj
+ test_file = self.get_test_loc('nuget/csproj/multiple_groups.csproj')
+ package = nuget.NugetCsprojHandler.parse(test_file)
+ expected_loc = self.get_test_loc('nuget/csproj/multiple_groups.csproj.expected')
+ self.check_packages_data(package, expected_loc, regen=REGEN_TEST_FIXTURES)
+
+ def test_parse_packages_config(self):
+ # See: https://learn.microsoft.com/en-us/nuget/reference/packages-config#examples
+ test_file = self.get_test_loc('nuget/packages-config/packages.config')
+ package = nuget.NugetPackagesConfigHandler.parse(test_file)
+ expected_loc = self.get_test_loc('nuget/packages-config/packages.config.expected')
+ self.check_packages_data(package, expected_loc, regen=REGEN_TEST_FIXTURES)