fosslight-dependency 3.0.7__py3-none-any.whl → 4.1.30__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. fosslight_dependency/LICENSES/LICENSE +201 -0
  2. fosslight_dependency/LICENSES/LicenseRef-3rd_party_licenses.txt +1254 -0
  3. fosslight_dependency/__init__.py +0 -1
  4. fosslight_dependency/_analyze_dependency.py +130 -0
  5. fosslight_dependency/_graph_convertor.py +67 -0
  6. fosslight_dependency/_help.py +79 -0
  7. fosslight_dependency/_package_manager.py +397 -0
  8. fosslight_dependency/cli.py +127 -0
  9. fosslight_dependency/constant.py +57 -0
  10. fosslight_dependency/dependency_item.py +103 -0
  11. fosslight_dependency/package_manager/Android.py +90 -0
  12. fosslight_dependency/package_manager/Cargo.py +144 -0
  13. fosslight_dependency/package_manager/Carthage.py +130 -0
  14. fosslight_dependency/package_manager/Cocoapods.py +194 -0
  15. fosslight_dependency/package_manager/Go.py +179 -0
  16. fosslight_dependency/package_manager/Gradle.py +123 -0
  17. fosslight_dependency/package_manager/Helm.py +106 -0
  18. fosslight_dependency/package_manager/Maven.py +274 -0
  19. fosslight_dependency/package_manager/Npm.py +296 -0
  20. fosslight_dependency/package_manager/Nuget.py +368 -0
  21. fosslight_dependency/package_manager/Pnpm.py +155 -0
  22. fosslight_dependency/package_manager/Pub.py +241 -0
  23. fosslight_dependency/package_manager/Pypi.py +395 -0
  24. fosslight_dependency/package_manager/Swift.py +159 -0
  25. fosslight_dependency/package_manager/Unity.py +118 -0
  26. fosslight_dependency/package_manager/Yarn.py +231 -0
  27. fosslight_dependency/package_manager/__init__.py +0 -0
  28. fosslight_dependency/run_dependency_scanner.py +393 -0
  29. fosslight_dependency-4.1.30.dist-info/METADATA +213 -0
  30. fosslight_dependency-4.1.30.dist-info/RECORD +37 -0
  31. {fosslight_dependency-3.0.7.dist-info → fosslight_dependency-4.1.30.dist-info}/WHEEL +1 -1
  32. fosslight_dependency-4.1.30.dist-info/entry_points.txt +2 -0
  33. fosslight_dependency-4.1.30.dist-info/licenses/LICENSES/Apache-2.0.txt +201 -0
  34. fosslight_dependency-4.1.30.dist-info/licenses/LICENSES/LicenseRef-3rd_party_licenses.txt +1254 -0
  35. fosslight_dependency-4.1.30.dist-info/licenses/LICENSES/MIT.txt +21 -0
  36. fosslight_dependency/_version.py +0 -1
  37. fosslight_dependency/analyze_dependency.py +0 -1090
  38. fosslight_dependency/third_party/askalono/askalono.exe +0 -0
  39. fosslight_dependency/third_party/askalono/askalono_macos +0 -0
  40. fosslight_dependency/third_party/nomos/nomossa +0 -0
  41. fosslight_dependency-3.0.7.dist-info/3rd_party_licenses.txt +0 -726
  42. fosslight_dependency-3.0.7.dist-info/METADATA +0 -51
  43. fosslight_dependency-3.0.7.dist-info/RECORD +0 -13
  44. fosslight_dependency-3.0.7.dist-info/entry_points.txt +0 -3
  45. {fosslight_dependency-3.0.7.dist-info → fosslight_dependency-4.1.30.dist-info/licenses}/LICENSE +0 -0
  46. {fosslight_dependency-3.0.7.dist-info → fosslight_dependency-4.1.30.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,368 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ # Copyright (c) 2022 LG Electronics Inc.
4
+ # SPDX-License-Identifier: Apache-2.0
5
+
6
+ import logging
7
+ import re
8
+ import os
9
+ import subprocess
10
+ from defusedxml.ElementTree import parse, fromstring
11
+ import json
12
+ import requests
13
+ import fosslight_util.constant as constant
14
+ import fosslight_dependency.constant as const
15
+ from fosslight_dependency._package_manager import PackageManager
16
+ from fosslight_dependency._package_manager import check_license_name, get_url_to_purl
17
+ from fosslight_dependency.dependency_item import DependencyItem, change_dependson_to_purl
18
+ from fosslight_util.oss_item import OssItem
19
+
20
+ logger = logging.getLogger(constant.LOGGER_NAME)
21
+
22
+
23
+ class Nuget(PackageManager):
24
+ package_manager_name = const.NUGET
25
+
26
+ dn_url = "https://nuget.org/packages/"
27
+ packageReference = False
28
+ directory_packages_props = 'Directory.Packages.props'
29
+ nuget_api_url = 'https://api.nuget.org/v3-flatcontainer/'
30
+ dotnet_ver = []
31
+ _exclude_dirs = {"test", "tests", "sample", "samples", "example", "examples"}
32
+
33
+ def __init__(self, input_dir, output_dir):
34
+ super().__init__(self.package_manager_name, self.dn_url, input_dir, output_dir)
35
+
36
+ for manifest_i in const.SUPPORT_PACKAE.get(self.package_manager_name):
37
+ if os.path.exists(os.path.basename(manifest_i)):
38
+ self.append_input_package_list_file(os.path.basename(manifest_i))
39
+ if manifest_i != 'packages.config':
40
+ self.packageReference = True
41
+
42
+ def run_plugin(self):
43
+ ret = True
44
+ directory_packages_props_path = os.path.join(self.input_dir, self.directory_packages_props)
45
+ if not os.path.isfile(directory_packages_props_path):
46
+ return ret
47
+
48
+ logger.info(f"Found {self.directory_packages_props}. Using NuGet CPM flow.")
49
+ self.packageReference = True
50
+
51
+ restore_targets = self._find_restore_targets()
52
+ if restore_targets:
53
+ logger.info("Found .sln or .csproj files. Running 'dotnet restore'...")
54
+ for target_path, target_file in restore_targets:
55
+ logger.info(f"Restoring: {os.path.relpath(target_file, self.input_dir)}")
56
+ try:
57
+ result = subprocess.run(
58
+ ['dotnet', 'restore', target_file, '/p:EnableWindowsTargeting=true'],
59
+ cwd=target_path,
60
+ capture_output=True,
61
+ text=True,
62
+ timeout=300
63
+ )
64
+ if result.returncode == 0:
65
+ logger.info(f"Successfully restored {os.path.relpath(target_file, self.input_dir)}")
66
+ else:
67
+ logger.warning(f"'dotnet restore' failed for {target_file} with return code {result.returncode}")
68
+ if result.stderr:
69
+ logger.warning(result.stderr)
70
+ except FileNotFoundError:
71
+ logger.error("'dotnet' command not found. Please install .NET SDK.")
72
+ except subprocess.TimeoutExpired:
73
+ logger.warning(f"'dotnet restore' timed out for {target_file}.")
74
+ except Exception as e:
75
+ logger.warning(f"Failed to run 'dotnet restore' for {target_file}: {e}")
76
+ else:
77
+ logger.warning("No .sln or .csproj files found to restore.")
78
+
79
+ self.project_dirs = []
80
+ found_projects = False
81
+
82
+ for root, dirs, files in os.walk(self.input_dir):
83
+ rel_root = os.path.relpath(root, self.input_dir)
84
+ parts = rel_root.split(os.sep) if rel_root != os.curdir else []
85
+ if any(p.lower() in self._exclude_dirs for p in parts):
86
+ continue
87
+ assets_json = os.path.join(root, 'obj', 'project.assets.json')
88
+ if os.path.isfile(assets_json):
89
+ found_projects = True
90
+
91
+ rel_path = os.path.relpath(assets_json, self.input_dir)
92
+ logger.info(f"Found project.assets.json at: {rel_path}")
93
+
94
+ if rel_path not in self.input_package_list_file:
95
+ self.append_input_package_list_file(rel_path)
96
+
97
+ project_dir = os.path.dirname(assets_json)
98
+ if project_dir.endswith(os.sep + 'obj'):
99
+ project_dir = project_dir[: -len(os.sep + 'obj')]
100
+
101
+ if project_dir and project_dir not in self.project_dirs:
102
+ self.project_dirs.append(project_dir)
103
+
104
+ if not found_projects:
105
+ logger.warning(
106
+ "Directory.Packages.props found and 'dotnet restore' completed, "
107
+ "but no obj/project.assets.json files were discovered."
108
+ )
109
+
110
+ return ret
111
+
112
+ def parse_oss_information(self, f_name):
113
+ tmp_license_txt_file_name = 'tmp_license.txt'
114
+ if f_name == self.directory_packages_props:
115
+ return
116
+
117
+ relation_tree = {}
118
+ direct_dep_list = []
119
+ if not hasattr(self, 'global_purl_dict'):
120
+ self.global_purl_dict = {}
121
+ if not hasattr(self, 'processed_packages'):
122
+ self.processed_packages = {}
123
+
124
+ file_path = os.path.join(self.input_dir, f_name) if not os.path.isabs(f_name) else f_name
125
+ with open(file_path, 'r', encoding='utf8') as input_fp:
126
+ package_list = []
127
+ if self.packageReference:
128
+ package_list = self.get_package_info_in_packagereference(input_fp, relation_tree, direct_dep_list)
129
+ else:
130
+ package_list = self.get_package_list_in_packages_config(input_fp)
131
+
132
+ for oss_origin_name, oss_version in package_list:
133
+ try:
134
+ pkg_key = f'{oss_origin_name}({oss_version})'
135
+ if pkg_key in self.processed_packages:
136
+ existing_idx = self.processed_packages[pkg_key]
137
+ existing_dep_item = self.dep_items[existing_idx]
138
+
139
+ if pkg_key in relation_tree:
140
+ new_deps = relation_tree[pkg_key]
141
+ if existing_dep_item.depends_on_raw:
142
+ existing_deps_set = set(existing_dep_item.depends_on_raw)
143
+ new_deps_set = set(new_deps)
144
+ merged_deps = sorted(existing_deps_set | new_deps_set)
145
+ existing_dep_item.depends_on_raw = merged_deps
146
+ else:
147
+ existing_dep_item.depends_on_raw = new_deps
148
+ if self.direct_dep and self.packageReference:
149
+ if oss_origin_name in direct_dep_list:
150
+ if 'direct' not in existing_dep_item.oss_items[0].comment:
151
+ existing_dep_item.oss_items[0].comment = 'direct'
152
+ continue
153
+
154
+ dep_item = DependencyItem()
155
+ oss_item = OssItem()
156
+ oss_item.name = f'{self.package_manager_name}:{oss_origin_name}'
157
+ oss_item.version = oss_version
158
+
159
+ license_name = ''
160
+ response = requests.get(f'{self.nuget_api_url.lower()}{oss_origin_name.lower()}/'
161
+ f'{oss_item.version.lower()}/{oss_origin_name.lower()}.nuspec')
162
+ if response.status_code == 200:
163
+ root = fromstring(response.text)
164
+ xmlns = ''
165
+ m = re.search('{.*}', root.tag)
166
+ if m:
167
+ xmlns = m.group(0)
168
+ nupkg_metadata = root.find(f'{xmlns}metadata')
169
+
170
+ license_name_id = nupkg_metadata.find(f'{xmlns}license')
171
+ if license_name_id is not None:
172
+ license_name, license_comment = self.check_multi_license(license_name_id.text)
173
+ if license_comment != '':
174
+ oss_item.comment = license_comment
175
+ else:
176
+ license_url = nupkg_metadata.find(f'{xmlns}licenseUrl')
177
+ if license_url is not None:
178
+ url_res = requests.get(license_url.text)
179
+ if url_res.status_code == 200:
180
+ license_name_with_scanner = check_license_name(url_res.text)
181
+ if license_name_with_scanner != "":
182
+ license_name = license_name_with_scanner
183
+ else:
184
+ license_name = license_url.text
185
+ oss_item.license = license_name
186
+ repo_id = nupkg_metadata.find(f'{xmlns}repository')
187
+ if repo_id is not None:
188
+ oss_item.download_location = repo_id.get("url")
189
+ else:
190
+ proj_url_id = nupkg_metadata.find(f'{xmlns}projectUrl')
191
+ if proj_url_id is not None:
192
+ oss_item.download_location = proj_url_id.text
193
+ oss_item.homepage = f'{self.dn_url}{oss_origin_name}'
194
+ if not oss_item.download_location:
195
+ oss_item.download_location = f'{oss_item.homepage}/{oss_item.version}'
196
+ else:
197
+ if oss_item.download_location.endswith('.git'):
198
+ oss_item.download_location = oss_item.download_location[:-4]
199
+ dep_item.purl = get_url_to_purl(f'{oss_item.homepage}/{oss_item.version}', self.package_manager_name)
200
+ else:
201
+ oss_item.comment = 'Fail to response for nuget api'
202
+ dep_item.purl = f'pkg:nuget/{oss_origin_name}@{oss_item.version}'
203
+ self.global_purl_dict[f'{oss_origin_name}({oss_item.version})'] = dep_item.purl
204
+
205
+ if self.direct_dep and self.packageReference:
206
+ if oss_origin_name in direct_dep_list:
207
+ oss_item.comment = 'direct'
208
+ else:
209
+ oss_item.comment = 'transitive'
210
+
211
+ key = f'{oss_origin_name}({oss_item.version})'
212
+ if key in relation_tree:
213
+ dep_item.depends_on_raw = relation_tree[key]
214
+
215
+ dep_item.oss_items.append(oss_item)
216
+ self.dep_items.append(dep_item)
217
+ self.processed_packages[pkg_key] = len(self.dep_items) - 1
218
+
219
+ except Exception as e:
220
+ logger.warning(f"Failed to parse oss information: {e}")
221
+ if self.direct_dep:
222
+ self.dep_items = change_dependson_to_purl(self.global_purl_dict, self.dep_items)
223
+
224
+ if os.path.isfile(tmp_license_txt_file_name):
225
+ os.remove(tmp_license_txt_file_name)
226
+
227
+ return
228
+
229
+ def get_package_list_in_packages_config(self, input_fp):
230
+ package_list = []
231
+ root = parse(input_fp).getroot()
232
+ for p in root.findall("package"):
233
+ package_list.append([p.get("id"), p.get("version")])
234
+ return package_list
235
+
236
+ def get_package_info_in_packagereference(self, input_fp, relation_tree, direct_dep_list):
237
+ json_f = json.load(input_fp)
238
+
239
+ dotnet_ver = self.get_dotnet_ver_list(json_f)
240
+ package_list = self.get_package_list_in_packages_assets(json_f)
241
+ self.get_dependency_tree(json_f, relation_tree, dotnet_ver)
242
+ self.get_direct_dependencies_from_assets_json(json_f, direct_dep_list)
243
+ self.get_direct_package_in_packagereference(direct_dep_list)
244
+
245
+ return package_list
246
+
247
+ def get_package_list_in_packages_assets(self, json_f):
248
+ package_list = []
249
+ for item in json_f['libraries']:
250
+ if json_f['libraries'][item]['type'] == 'package':
251
+ oss_info = item.split('/')
252
+ package_list.append([oss_info[0], oss_info[1]])
253
+ return package_list
254
+
255
+ def get_dotnet_ver_list(self, json_f):
256
+ dotnet_ver = []
257
+ json_project_group = json_f['projectFileDependencyGroups']
258
+ for ver in json_project_group:
259
+ dotnet_ver.append(ver)
260
+ return dotnet_ver
261
+
262
+ def get_direct_dependencies_from_assets_json(self, json_f, direct_dep_list):
263
+ try:
264
+ json_project_group = json_f.get('projectFileDependencyGroups', {})
265
+ for _, dependencies in json_project_group.items():
266
+ if not dependencies:
267
+ continue
268
+ for dep_string in dependencies:
269
+ package_name = dep_string.split()[0] if dep_string else ''
270
+ if package_name and package_name not in direct_dep_list:
271
+ direct_dep_list.append(package_name)
272
+ except Exception as e:
273
+ logger.warning(f"Failed to extract direct dependencies from project.assets.json: {e}")
274
+
275
+ def get_dependency_tree(self, json_f, relation_tree, dotnet_ver):
276
+ actual_versions = {}
277
+ for lib_key in json_f.get('libraries', {}):
278
+ if '/' in lib_key:
279
+ lib_name, lib_version = lib_key.split('/', 1)
280
+ actual_versions[lib_name.lower()] = lib_version
281
+
282
+ json_target = json_f['targets']
283
+ for item in json_target:
284
+ if item not in dotnet_ver:
285
+ continue
286
+ json_item = json_target[item]
287
+ for pkg in json_item:
288
+ json_pkg = json_item[pkg]
289
+ if 'type' not in json_pkg:
290
+ continue
291
+ if 'dependencies' not in json_pkg:
292
+ continue
293
+ if json_pkg['type'] != 'package':
294
+ continue
295
+ oss_info = pkg.split('/')
296
+ relation_tree[f'{oss_info[0]}({oss_info[1]})'] = []
297
+ for dep in json_pkg['dependencies']:
298
+ oss_name = dep
299
+ dep_ver_in_spec = json_pkg['dependencies'][dep]
300
+ actual_ver = actual_versions.get(oss_name.lower(), dep_ver_in_spec)
301
+ relation_tree[f'{oss_info[0]}({oss_info[1]})'].append(f'{oss_name}({actual_ver})')
302
+
303
+ def get_direct_package_in_packagereference(self, direct_dep_list):
304
+ if hasattr(self, 'project_dirs') and self.project_dirs:
305
+ for project_dir in self.project_dirs:
306
+ for f in os.listdir(project_dir):
307
+ f_path = os.path.join(project_dir, f)
308
+ if os.path.isfile(f_path) and ((f.split('.')[-1] == 'csproj') or (f.split('.')[-1] == 'xproj')):
309
+ with open(f_path, 'r', encoding='utf8') as input_fp:
310
+ root = parse(input_fp).getroot()
311
+ itemgroups = root.findall('ItemGroup')
312
+ for itemgroup in itemgroups:
313
+ for item in itemgroup.findall('PackageReference'):
314
+ pkg_name = item.get('Include')
315
+ if pkg_name and pkg_name not in direct_dep_list:
316
+ direct_dep_list.append(pkg_name)
317
+ else:
318
+ for f in os.listdir(self.input_dir):
319
+ f_path = os.path.join(self.input_dir, f)
320
+ if os.path.isfile(f_path) and ((f.split('.')[-1] == 'csproj') or (f.split('.')[-1] == 'xproj')):
321
+ with open(f_path, 'r', encoding='utf8') as input_fp:
322
+ root = parse(input_fp).getroot()
323
+ itemgroups = root.findall('ItemGroup')
324
+ for itemgroup in itemgroups:
325
+ for item in itemgroup.findall('PackageReference'):
326
+ pkg_name = item.get('Include')
327
+ if pkg_name and pkg_name not in direct_dep_list:
328
+ direct_dep_list.append(pkg_name)
329
+
330
+ def check_multi_license(self, license_name):
331
+ multi_license = license_name
332
+ license_comment = ''
333
+ try:
334
+ if license_name.startswith('(') and license_name.endswith(')'):
335
+ license_name = license_name.lstrip('(').rstrip(')')
336
+ license_comment = license_name
337
+ multi_license = ','.join(re.split(r'OR|AND', license_name))
338
+ except Exception as e:
339
+ logger.warning(f'Fail to parse multi license in npm: {e}')
340
+
341
+ return multi_license, license_comment
342
+
343
+ def _find_restore_targets(self):
344
+ sln_files = []
345
+ csproj_files = []
346
+
347
+ for root, dirs, files in os.walk(self.input_dir):
348
+ rel_root = os.path.relpath(root, self.input_dir)
349
+ parts = rel_root.split(os.sep) if rel_root != os.curdir else []
350
+ if any(p.lower() in self._exclude_dirs for p in parts):
351
+ continue
352
+
353
+ depth = len(parts) if parts and parts[0] != '.' else 0
354
+
355
+ for f in files:
356
+ if f.endswith('.sln'):
357
+ sln_files.append((depth, root, os.path.join(root, f)))
358
+ elif f.endswith('.csproj'):
359
+ csproj_files.append((depth, root, os.path.join(root, f)))
360
+
361
+ result = []
362
+ if sln_files:
363
+ result.extend([(d, f) for _, d, f in sln_files])
364
+
365
+ if csproj_files:
366
+ result.extend([(d, f) for _, d, f in csproj_files])
367
+
368
+ return result
@@ -0,0 +1,155 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ # Copyright (c) 2025 LG Electronics Inc.
4
+ # SPDX-License-Identifier: Apache-2.0
5
+
6
+ import os
7
+ import logging
8
+ import subprocess
9
+ import json
10
+ import shutil
11
+ import fosslight_util.constant as constant
12
+ import fosslight_dependency.constant as const
13
+ from fosslight_dependency._package_manager import PackageManager, get_url_to_purl
14
+ from fosslight_dependency.dependency_item import DependencyItem, change_dependson_to_purl
15
+ from fosslight_dependency.package_manager.Npm import check_multi_license
16
+ from fosslight_util.oss_item import OssItem
17
+
18
+ logger = logging.getLogger(constant.LOGGER_NAME)
19
+ node_modules = 'node_modules'
20
+
21
+
22
+ class Pnpm(PackageManager):
23
+ package_manager_name = const.PNPM
24
+
25
+ dn_url = 'https://www.npmjs.com/package/'
26
+ input_file_name = 'tmp_pnpm_license_output.json'
27
+ flag_tmp_node_modules = False
28
+ project_name_list = []
29
+ pkg_list = {}
30
+
31
+ def __init__(self, input_dir, output_dir):
32
+ super().__init__(self.package_manager_name, self.dn_url, input_dir, output_dir)
33
+
34
+ def __del__(self):
35
+ if os.path.isfile(self.input_file_name):
36
+ os.remove(self.input_file_name)
37
+ if self.flag_tmp_node_modules:
38
+ shutil.rmtree(node_modules, ignore_errors=True)
39
+
40
+ def run_plugin(self):
41
+ ret = True
42
+
43
+ pnpm_install_cmd = 'pnpm install --prod --ignore-scripts --ignore-pnpmfile'
44
+ if os.path.isdir(node_modules) != 1:
45
+ logger.info(f"node_modules directory is not existed. So it executes '{pnpm_install_cmd}'.")
46
+ self.flag_tmp_node_modules = True
47
+ cmd_ret = subprocess.call(pnpm_install_cmd, shell=True)
48
+ if cmd_ret != 0:
49
+ logger.error(f"{pnpm_install_cmd} returns an error")
50
+ ret = False
51
+ if ret:
52
+ project_cmd = 'pnpm ls -r --depth -1 -P --json'
53
+ ret_txt = subprocess.check_output(project_cmd, text=True, shell=True)
54
+ if ret_txt is not None:
55
+ deps_l = json.loads(ret_txt)
56
+ for items in deps_l:
57
+ self.project_name_list.append(items["name"])
58
+ return ret
59
+
60
+ def parse_direct_dependencies(self):
61
+ if not self.direct_dep:
62
+ return
63
+ try:
64
+ direct_cmd = 'pnpm ls -r --depth 0 -P --json'
65
+ ret_txt = subprocess.check_output(direct_cmd, text=True, shell=True)
66
+ if ret_txt is not None:
67
+ deps_l = json.loads(ret_txt)
68
+ for item in deps_l:
69
+ if 'dependencies' in item and isinstance(item['dependencies'], dict):
70
+ self.direct_dep_list.extend(item['dependencies'].keys())
71
+ else:
72
+ self.direct_dep = False
73
+ logger.warning('Cannot print direct/transitive dependency')
74
+ except Exception as e:
75
+ logger.warning(f'Fail to print direct/transitive dependency: {e}')
76
+ self.direct_dep = False
77
+ if self.direct_dep:
78
+ self.direct_dep_list = list(filter(lambda dep: dep not in self.project_name_list, self.direct_dep_list))
79
+
80
+ def extract_dependencies(self, dependencies, purl_dict):
81
+ dep_item_list = []
82
+ for dep_name, dep_info in dependencies.items():
83
+ if dep_name not in self.project_name_list:
84
+ if dep_name in self.pkg_list.keys():
85
+ if dep_info.get('version') in self.pkg_list[dep_name]:
86
+ continue
87
+ self.pkg_list.setdefault(dep_name, []).append(dep_info.get('version'))
88
+ dep_item = DependencyItem()
89
+ oss_item = OssItem()
90
+ oss_item.name = f'npm:{dep_name}'
91
+ oss_item.version = dep_info.get('version')
92
+
93
+ license_name = dep_info.get('license')
94
+ if license_name:
95
+ multi_license, license_comment, multi_flag = check_multi_license(license_name, '')
96
+ if multi_flag:
97
+ oss_item.comment = license_comment
98
+ license_name = multi_license
99
+ else:
100
+ license_name = license_name.replace(",", "")
101
+ oss_item.license = license_name
102
+
103
+ oss_item.homepage = f'{self.dn_url}{dep_name}'
104
+ oss_item.download_location = dep_info.get('repository')
105
+ if oss_item.download_location:
106
+ if oss_item.download_location.endswith('.git'):
107
+ oss_item.download_location = oss_item.download_location[:-4]
108
+ if oss_item.download_location.startswith('git://'):
109
+ oss_item.download_location = 'https://' + oss_item.download_location[6:]
110
+ elif oss_item.download_location.startswith('git+https://'):
111
+ oss_item.download_location = 'https://' + oss_item.download_location[12:]
112
+ elif oss_item.download_location.startswith('git+ssh://git@'):
113
+ oss_item.download_location = 'https://' + oss_item.download_location[14:]
114
+ else:
115
+ oss_item.download_location = f'{self.dn_url}{dep_name}/v/{oss_item.version}'
116
+
117
+ dn_loc = f'{oss_item.homepage}/v/{oss_item.version}'
118
+ dep_item.purl = get_url_to_purl(dn_loc, 'npm')
119
+ purl_dict[f'{dep_name}({oss_item.version})'] = dep_item.purl
120
+
121
+ if dep_name in self.direct_dep_list:
122
+ oss_item.comment = 'direct'
123
+ else:
124
+ oss_item.comment = 'transitive'
125
+
126
+ if 'dependencies' in dep_info:
127
+ for dn, di in dep_info.get('dependencies').items():
128
+ if dn not in self.project_name_list:
129
+ dep_item.depends_on_raw.append(f"{dn}({di['version']})")
130
+
131
+ dep_item.oss_items.append(oss_item)
132
+ dep_item_list.append(dep_item)
133
+
134
+ if 'dependencies' in dep_info:
135
+ dep_item_list_inner, purl_dict_inner = self.extract_dependencies(dep_info['dependencies'], purl_dict)
136
+ dep_item_list.extend(dep_item_list_inner)
137
+ purl_dict.update(purl_dict_inner)
138
+
139
+ return dep_item_list, purl_dict
140
+
141
+ def parse_oss_information_for_pnpm(self):
142
+ project_cmd = 'pnpm ls --json -r --depth Infinity -P --long'
143
+ ret_txt = subprocess.check_output(project_cmd, text=True, shell=True)
144
+ if ret_txt is not None:
145
+ deps_l = json.loads(ret_txt)
146
+ purl_dict = {}
147
+ for items in deps_l:
148
+ if 'dependencies' in items:
149
+ dep_item_list_inner, purl_dict_inner = self.extract_dependencies(items['dependencies'], purl_dict)
150
+ self.dep_items.extend(dep_item_list_inner)
151
+ purl_dict.update(purl_dict_inner)
152
+ if self.direct_dep:
153
+ self.dep_items = change_dependson_to_purl(purl_dict, self.dep_items)
154
+ else:
155
+ logger.warning(f'No output for {project_cmd}')