|
7 | 7 | # See https://aboutcode.org for more information about nexB OSS projects.
|
8 | 8 | #
|
9 | 9 |
|
| 10 | +import json |
10 | 11 | import xmltodict
|
11 | 12 | from packageurl import PackageURL
|
12 | 13 |
|
@@ -179,3 +180,88 @@ def parse(cls, location, package_only=False):
|
179 | 180 | )
|
180 | 181 | yield models.PackageData.from_data(package_data, package_only)
|
181 | 182 |
|
| 183 | + |
| 184 | +class NugetPackagesLockHandler(models.DatafileHandler): |
| 185 | + datasource_id = 'nuget_packages_lock' |
| 186 | + path_patterns = ('*packages.lock.json',) |
| 187 | + default_package_type = 'nuget' |
| 188 | + description = 'NuGet packages.lock.json file' |
| 189 | + documentation_url = 'https://learn.microsoft.com/en-us/nuget/reference/cli-reference/cli-ref-restore' |
| 190 | + |
| 191 | + @classmethod |
| 192 | + def get_dependencies(cls, package_info, scope): |
| 193 | + dependencies = [] |
| 194 | + dependencies_mapping = package_info.get("dependencies") or {} |
| 195 | + for name, version in dependencies_mapping.items(): |
| 196 | + dependency = models.DependentPackage( |
| 197 | + purl=str(PackageURL(type='nuget', name=name, version=version)), |
| 198 | + extracted_requirement=version, |
| 199 | + is_resolved=True, |
| 200 | + scope=scope, |
| 201 | + is_optional=False, |
| 202 | + is_runtime=True, |
| 203 | + is_direct=True, |
| 204 | + ) |
| 205 | + dependencies.append(dependency) |
| 206 | + return dependencies |
| 207 | + |
| 208 | + @classmethod |
| 209 | + def parse(cls, location, package_only=False): |
| 210 | + with open(location) as loc: |
| 211 | + parsed = json.load(loc) |
| 212 | + |
| 213 | + if not parsed: |
| 214 | + return |
| 215 | + |
| 216 | + top_dependencies = [] |
| 217 | + for target_framework, packages in parsed.get('dependencies', {}).items(): |
| 218 | + extra_data = dict( |
| 219 | + target_framework=target_framework, |
| 220 | + ) |
| 221 | + for package_name, package_info in packages.items(): |
| 222 | + dependencies = cls.get_dependencies(package_info=package_info, scope=target_framework) |
| 223 | + resolved_package_mapping = dict( |
| 224 | + datasource_id=cls.datasource_id, |
| 225 | + type=cls.default_package_type, |
| 226 | + primary_language=cls.default_primary_language, |
| 227 | + name=package_name, |
| 228 | + dependencies=[ |
| 229 | + dep.to_dict() for dep in dependencies |
| 230 | + ], |
| 231 | + is_virtual=True, |
| 232 | + version=package_info.get('resolved'), |
| 233 | + ) |
| 234 | + resolved_package = models.PackageData.from_data(resolved_package_mapping) |
| 235 | + package_type = package_info.get('type') |
| 236 | + if package_type == "Direct": |
| 237 | + is_direct = True |
| 238 | + elif package_type == "Transitive": |
| 239 | + is_direct = False |
| 240 | + else: |
| 241 | + raise Exception(f"Unknown package type: {package_type} for package {package_name} in {location}") |
| 242 | + |
| 243 | + |
| 244 | + version = package_info.get('resolved') |
| 245 | + requested = package_info.get('requested') |
| 246 | + dependency = models.DependentPackage( |
| 247 | + purl=str(PackageURL(type='nuget', name=package_name, version=version)), |
| 248 | + extracted_requirement=requested or version, |
| 249 | + is_resolved=True, |
| 250 | + resolved_package=resolved_package.to_dict(), |
| 251 | + # We use the target framework as scope since there is no concept of scope in .NET |
| 252 | + # and we may have different resolutions for different target frameworks |
| 253 | + scope=target_framework, |
| 254 | + is_optional=False, |
| 255 | + is_runtime=True, |
| 256 | + is_direct=is_direct, |
| 257 | + ) |
| 258 | + top_dependencies.append(dependency.to_dict()) |
| 259 | + package_data = dict( |
| 260 | + datasource_id=cls.datasource_id, |
| 261 | + type=cls.default_package_type, |
| 262 | + primary_language=cls.default_primary_language, |
| 263 | + extra_data=extra_data, |
| 264 | + dependencies=top_dependencies, |
| 265 | + ) |
| 266 | + yield models.PackageData.from_data(package_data, package_only) |
| 267 | + |
0 commit comments