fosslight-dependency 4.1.25__tar.gz → 4.1.27__tar.gz

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 (44) hide show
  1. {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/PKG-INFO +1 -1
  2. {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/setup.py +1 -1
  3. {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency/_analyze_dependency.py +6 -2
  4. {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency/_help.py +3 -1
  5. {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency/_package_manager.py +26 -0
  6. {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency/constant.py +1 -1
  7. {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency/package_manager/Maven.py +5 -4
  8. {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency/package_manager/Npm.py +43 -12
  9. fosslight_dependency-4.1.27/src/fosslight_dependency/package_manager/Nuget.py +368 -0
  10. {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency/package_manager/Yarn.py +21 -12
  11. {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency/run_dependency_scanner.py +48 -24
  12. {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency.egg-info/PKG-INFO +1 -1
  13. fosslight_dependency-4.1.25/src/fosslight_dependency/package_manager/Nuget.py +0 -196
  14. {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/LICENSE +0 -0
  15. {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/LICENSES/Apache-2.0.txt +0 -0
  16. {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/LICENSES/LicenseRef-3rd_party_licenses.txt +0 -0
  17. {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/LICENSES/MIT.txt +0 -0
  18. {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/MANIFEST.in +0 -0
  19. {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/README.md +0 -0
  20. {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/requirements.txt +0 -0
  21. {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/setup.cfg +0 -0
  22. {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency/LICENSES/LICENSE +0 -0
  23. {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency/LICENSES/LicenseRef-3rd_party_licenses.txt +0 -0
  24. {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency/__init__.py +0 -0
  25. {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency/_graph_convertor.py +0 -0
  26. {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency/dependency_item.py +0 -0
  27. {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency/package_manager/Android.py +0 -0
  28. {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency/package_manager/Cargo.py +0 -0
  29. {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency/package_manager/Carthage.py +0 -0
  30. {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency/package_manager/Cocoapods.py +0 -0
  31. {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency/package_manager/Go.py +0 -0
  32. {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency/package_manager/Gradle.py +0 -0
  33. {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency/package_manager/Helm.py +0 -0
  34. {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency/package_manager/Pnpm.py +0 -0
  35. {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency/package_manager/Pub.py +0 -0
  36. {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency/package_manager/Pypi.py +0 -0
  37. {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency/package_manager/Swift.py +0 -0
  38. {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency/package_manager/Unity.py +0 -0
  39. {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency/package_manager/__init__.py +0 -0
  40. {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency.egg-info/SOURCES.txt +0 -0
  41. {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency.egg-info/dependency_links.txt +0 -0
  42. {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency.egg-info/entry_points.txt +0 -0
  43. {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency.egg-info/requires.txt +0 -0
  44. {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fosslight_dependency
3
- Version: 4.1.25
3
+ Version: 4.1.27
4
4
  Summary: FOSSLight Dependency Scanner
5
5
  Home-page: https://github.com/fosslight/fosslight_dependency_scanner
6
6
  Download-URL: https://github.com/fosslight/fosslight_dependency_scanner
@@ -35,7 +35,7 @@ if __name__ == "__main__":
35
35
 
36
36
  setup(
37
37
  name=_PACKAEG_NAME,
38
- version='4.1.25',
38
+ version='4.1.27',
39
39
  package_dir={"": "src"},
40
40
  packages=find_namespace_packages(where='src'),
41
41
  description='FOSSLight Dependency Scanner',
@@ -6,6 +6,7 @@
6
6
  import os
7
7
  import logging
8
8
  import fosslight_dependency.constant as const
9
+ from fosslight_dependency._package_manager import deduplicate_dep_items
9
10
  from fosslight_dependency.package_manager.Pypi import Pypi
10
11
  from fosslight_dependency.package_manager.Npm import Npm
11
12
  from fosslight_dependency.package_manager.Yarn import Yarn
@@ -104,16 +105,19 @@ def analyze_dependency(package_manager_name, input_dir, output_dir, pip_activate
104
105
  for f_name in package_manager.input_package_list_file:
105
106
  logger.info(f"Parse oss information with file: {f_name}")
106
107
 
107
- if os.path.isfile(f_name):
108
+ file_path = os.path.join(input_dir, f_name) if not os.path.isabs(f_name) else f_name
109
+ if os.path.isfile(file_path):
108
110
  package_manager.parse_oss_information(f_name)
109
111
  package_dep_item_list.extend(package_manager.dep_items)
110
112
  else:
111
- logger.error(f"Failed to open input file: {f_name}")
113
+ logger.error(f"Failed to open input file: {file_path}")
112
114
  ret = False
113
115
  if package_manager_name == const.PNPM:
114
116
  logger.info("Parse oss information for pnpm")
115
117
  package_manager.parse_oss_information_for_pnpm()
116
118
  package_dep_item_list.extend(package_manager.dep_items)
119
+ if package_dep_item_list:
120
+ package_dep_item_list = deduplicate_dep_items(package_dep_item_list)
117
121
  if ret:
118
122
  logger.warning(f"### Complete to analyze: {package_manager_name}({input_dir}: {','.join(manifest_file_name)})")
119
123
  if package_manager.cover_comment:
@@ -37,7 +37,9 @@ _HELP_MESSAGE_DEPENDENCY = f"""
37
37
  \t(npm, maven, gradle, pypi, pub, cocoapods, android, swift, carthage,
38
38
  \t go, nuget, helm, unity, cargo, pnpm, yarn)
39
39
  -p <input_path>\t\t Enter the path where the script will be run.
40
- -e <exclude_path>\t\t Enter the path where the analysis will not be performed.
40
+ -e <exclude_path>\t\t Enter the path where the analysis will not be performed (files and directories).
41
+ \t\t\t\t * IMPORTANT: Always wrap patterns in double quotes ("") to avoid shell expansion.
42
+ \t\t\t\t Example) fosslight_dependency -e "test/abc.py" "*.jar"
41
43
  -o <output_path>\t\t Output path
42
44
  \t\t\t\t\t(If you want to generate the specific file name, add the output path with file name.)
43
45
  -f <format> [<format> ...]\t Output formats
@@ -369,3 +369,29 @@ def change_file_mode(filepath, mode=''):
369
369
  os.chmod(filepath, new_mode)
370
370
  logger.debug(f"File mode of {filepath} has been changed to {oct(new_mode)}.")
371
371
  return current_mode
372
+
373
+
374
+ def deduplicate_dep_items(dep_items):
375
+ if not dep_items:
376
+ return dep_items
377
+
378
+ unique_items = []
379
+ seen = set()
380
+
381
+ for item in dep_items:
382
+ first_oss = item.oss_items[0] if getattr(item, "oss_items", None) else None
383
+ oss_name = getattr(first_oss, "name", None) if first_oss else None
384
+ oss_ver = getattr(first_oss, "version", None) if first_oss else None
385
+ comment = getattr(first_oss, "comment", None) if first_oss else None
386
+
387
+ depends_on = None
388
+ if getattr(item, "depends_on", None):
389
+ depends_on = tuple(sorted(item.depends_on))
390
+
391
+ key = (getattr(item, "purl", None), oss_name, oss_ver, comment, depends_on)
392
+ if key in seen:
393
+ continue
394
+ seen.add(key)
395
+ unique_items.append(item)
396
+
397
+ return unique_items
@@ -41,7 +41,7 @@ SUPPORT_PACKAE = {
41
41
  SWIFT: 'Package.resolved',
42
42
  CARTHAGE: 'Cartfile.resolved',
43
43
  GO: 'go.mod',
44
- NUGET: ['packages.config', os.path.join('obj', 'project.assets.json')],
44
+ NUGET: ['packages.config', os.path.join('obj', 'project.assets.json'), 'Directory.Packages.props'],
45
45
  HELM: 'Chart.yaml',
46
46
  UNITY: os.path.join('Library', 'PackageManager', 'ProjectCache'),
47
47
  CARGO: 'Cargo.toml'
@@ -255,10 +255,11 @@ class Maven(PackageManager):
255
255
  dep_key = f"{oss_item.name}({version})"
256
256
 
257
257
  if self.direct_dep:
258
- if dep_key in self.direct_dep_list:
259
- oss_item.comment = 'direct'
260
- else:
261
- oss_item.comment = 'transitive'
258
+ if len(self.direct_dep_list) > 0:
259
+ if dep_key in self.direct_dep_list:
260
+ oss_item.comment = 'direct'
261
+ else:
262
+ oss_item.comment = 'transitive'
262
263
  try:
263
264
  if dep_key in self.relation_tree:
264
265
  dep_item.depends_on_raw = self.relation_tree[dep_key]
@@ -9,6 +9,7 @@ import subprocess
9
9
  import json
10
10
  import shutil
11
11
  import re
12
+ import requests
12
13
  import fosslight_util.constant as constant
13
14
  import fosslight_dependency.constant as const
14
15
  from fosslight_dependency._package_manager import PackageManager, get_url_to_purl
@@ -26,9 +27,11 @@ class Npm(PackageManager):
26
27
  input_file_name = 'tmp_npm_license_output.json'
27
28
  tmp_custom_json = 'custom.json'
28
29
  flag_tmp_node_modules = False
30
+ _network_available = False
29
31
 
30
32
  def __init__(self, input_dir, output_dir):
31
33
  super().__init__(self.package_manager_name, self.dn_url, input_dir, output_dir)
34
+ self._check_network_available()
32
35
 
33
36
  def __del__(self):
34
37
  if os.path.isfile(os.path.join(self.input_dir, self.input_file_name)):
@@ -174,24 +177,33 @@ class Npm(PackageManager):
174
177
  package_path = d['path']
175
178
 
176
179
  private_pkg = False
177
- if _private in d:
178
- if d[_private]:
179
- private_pkg = True
180
+ if _private in d and d[_private]:
181
+ private_pkg = True
180
182
 
181
- oss_item.download_location = f"{self.dn_url}{oss_init_name}/v/{oss_item.version}"
182
- dn_loc = f"{self.dn_url}{oss_init_name}"
183
- dep_item.purl = get_url_to_purl(oss_item.download_location, self.package_manager_name)
184
- purl_dict[f'{oss_init_name}({oss_item.version})'] = dep_item.purl
185
- if d[_repository]:
186
- dn_loc = d[_repository]
187
- elif private_pkg:
188
- dn_loc = ''
183
+ oss_item.download_location = ''
189
184
 
190
- oss_item.homepage = dn_loc
185
+ npm_dl_url = f"{self.dn_url}{oss_init_name}/v/{oss_item.version}"
186
+ npm_home_url = f"{self.dn_url}{oss_init_name}"
187
+ dep_item.purl = get_url_to_purl(npm_dl_url, self.package_manager_name)
188
+ purl_dict[f'{oss_init_name}({oss_item.version})'] = dep_item.purl
191
189
 
190
+ repo_url = d[_repository] if d[_repository] else ''
192
191
  if private_pkg:
192
+ oss_item.homepage = repo_url or ''
193
193
  oss_item.download_location = oss_item.homepage
194
194
  oss_item.comment = 'private'
195
+ else:
196
+ npm_url_exists = False
197
+ if self._network_available is True:
198
+ npm_url_exists = self._npm_url_exists(oss_init_name)
199
+
200
+ if self._network_available and not npm_url_exists:
201
+ oss_item.homepage = repo_url or ""
202
+ oss_item.download_location = oss_item.homepage
203
+ else:
204
+ oss_item.homepage = repo_url or npm_home_url
205
+ oss_item.download_location = npm_dl_url
206
+
195
207
  if self.package_name == f'{oss_init_name}({oss_item.version})':
196
208
  oss_item.comment = 'root package'
197
209
  elif self.direct_dep and len(self.relation_tree) > 0:
@@ -221,6 +233,25 @@ class Npm(PackageManager):
221
233
  self.dep_items = change_dependson_to_purl(purl_dict, self.dep_items)
222
234
  return
223
235
 
236
+ def _check_network_available(self) -> bool:
237
+ test_url = 'https://registry.npmjs.org/'
238
+ try:
239
+ resp = requests.head(test_url, timeout=3, allow_redirects=True)
240
+ self._network_available = resp.status_code < 400
241
+ except Exception:
242
+ self._network_available = False
243
+ return self._network_available
244
+
245
+ def _npm_url_exists(self, package_name: str) -> bool:
246
+ url = f"https://registry.npmjs.org/{package_name}"
247
+ try:
248
+ resp = requests.head(url, timeout=3, allow_redirects=True)
249
+ if resp.status_code == 405:
250
+ resp = requests.get(url, timeout=3, allow_redirects=True)
251
+ return resp.status_code < 400
252
+ except Exception:
253
+ return False
254
+
224
255
 
225
256
  def check_multi_license(license_name, manifest_file_path):
226
257
  multi_license_list = []
@@ -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
@@ -121,24 +121,33 @@ class Yarn(Npm):
121
121
  package_path = d['path']
122
122
 
123
123
  private_pkg = False
124
- if _private in d:
125
- if d[_private]:
126
- private_pkg = True
124
+ if _private in d and d[_private]:
125
+ private_pkg = True
127
126
 
128
- oss_item.download_location = f"{self.dn_url}{oss_init_name}/v/{oss_item.version}"
129
- dn_loc = f"{self.dn_url}{oss_init_name}"
130
- dep_item.purl = get_url_to_purl(oss_item.download_location, self.package_manager_name)
131
- purl_dict[f'{oss_init_name}({oss_item.version})'] = dep_item.purl
132
- if d[_repository]:
133
- dn_loc = d[_repository]
134
- elif private_pkg:
135
- dn_loc = ''
127
+ oss_item.download_location = ''
136
128
 
137
- oss_item.homepage = dn_loc
129
+ npm_dl_url = f"{self.dn_url}{oss_init_name}/v/{oss_item.version}"
130
+ npm_home_url = f"{self.dn_url}{oss_init_name}"
131
+ dep_item.purl = get_url_to_purl(npm_dl_url, self.package_manager_name)
132
+ purl_dict[f'{oss_init_name}({oss_item.version})'] = dep_item.purl
138
133
 
134
+ repo_url = d[_repository] if d[_repository] else ''
139
135
  if private_pkg:
136
+ oss_item.homepage = repo_url or ''
140
137
  oss_item.download_location = oss_item.homepage
141
138
  oss_item.comment = 'private'
139
+ else:
140
+ npm_url_exists = False
141
+ if self._network_available is True:
142
+ npm_url_exists = self._npm_url_exists(oss_init_name, oss_item.version)
143
+
144
+ if self._network_available and not npm_url_exists:
145
+ oss_item.homepage = repo_url or ""
146
+ oss_item.download_location = oss_item.homepage
147
+ else:
148
+ oss_item.homepage = repo_url or npm_home_url
149
+ oss_item.download_location = npm_dl_url
150
+
142
151
  if self.package_name == f'{oss_init_name}({oss_item.version})':
143
152
  oss_item.comment = 'root package'
144
153
  elif self.direct_dep and len(self.relation_tree) > 0:
@@ -60,6 +60,7 @@ def find_package_manager(input_dir, abs_path_to_exclude=[], manifest_file_name=[
60
60
  manifest_file_name.append(value)
61
61
 
62
62
  found_manifest_file = []
63
+ found_manifest_set = set()
63
64
  suggested_files = []
64
65
  for parent, dirs, files in os.walk(input_dir):
65
66
  parent_parts = parent.split(os.sep)
@@ -74,17 +75,30 @@ def find_package_manager(input_dir, abs_path_to_exclude=[], manifest_file_name=[
74
75
  for exclude_path in abs_path_to_exclude):
75
76
  continue
76
77
  if file in manifest_file_name:
77
- path_with_filename = os.path.join(parent, file)
78
- found_manifest_file.append(path_with_filename)
78
+ candidate = os.path.join(parent, file)
79
+ norm_candidate = os.path.normpath(candidate)
80
+ if norm_candidate not in found_manifest_set:
81
+ found_manifest_set.add(norm_candidate)
82
+ found_manifest_file.append(candidate)
83
+ for manifest_f in manifest_file_name:
84
+ candidate = os.path.join(parent, manifest_f)
85
+ norm_candidate = os.path.normpath(candidate)
86
+ if os.path.exists(candidate) and norm_candidate not in found_manifest_set:
87
+ found_manifest_set.add(norm_candidate)
88
+ found_manifest_file.append(candidate)
79
89
  if file in const.SUGGESTED_PACKAGE.keys():
80
90
  suggested_files.append(os.path.join(parent, file))
91
+
81
92
  for dir in dirs:
82
93
  for manifest_f in manifest_file_name:
83
94
  manifest_l = manifest_f.split(os.path.sep)
84
- if len(manifest_l) > 1:
85
- if manifest_l[0] == dir:
86
- if os.path.exists(os.path.join(parent, manifest_f)):
87
- found_manifest_file.append(os.path.join(parent, manifest_f))
95
+ if len(manifest_l) > 1 and manifest_l[0] == dir:
96
+ candidate = os.path.join(parent, manifest_f)
97
+ norm_candidate = os.path.normpath(candidate)
98
+ if os.path.exists(candidate) and norm_candidate not in found_manifest_set:
99
+ found_manifest_set.add(norm_candidate)
100
+ found_manifest_file.append(candidate)
101
+
88
102
  if not recursive:
89
103
  if len(found_manifest_file) > 0:
90
104
  input_dir = parent
@@ -95,12 +109,22 @@ def find_package_manager(input_dir, abs_path_to_exclude=[], manifest_file_name=[
95
109
  f_name = os.path.basename(f_with_path)
96
110
  dir_path = os.path.dirname(f_with_path)
97
111
  for key, value in const.SUPPORT_PACKAE.items():
98
- if isinstance(value, list):
99
- if f_name in value:
100
- found_package_manager[key][dir_path].append(f_name)
101
- else:
102
- if f_name == value:
103
- found_package_manager[key][dir_path].append(f_name)
112
+ manifest_patterns = value if isinstance(value, list) else [value]
113
+
114
+ for pattern in manifest_patterns:
115
+ if os.path.sep not in pattern:
116
+ if f_name == pattern:
117
+ if pattern not in found_package_manager[key][dir_path]:
118
+ found_package_manager[key][dir_path].append(pattern)
119
+ else:
120
+ rel_dir, rel_file = os.path.split(pattern)
121
+ expected_path = os.path.join(dir_path, rel_file)
122
+
123
+ if f_name == rel_file:
124
+ candidate = os.path.join(os.path.dirname(dir_path), rel_dir, rel_file) if rel_dir else expected_path
125
+ if os.path.normpath(candidate) == os.path.normpath(f_with_path):
126
+ if pattern not in found_package_manager[key][dir_path]:
127
+ found_package_manager[key][dir_path].append(pattern)
104
128
  found_package_manager = {k: dict(v) for k, v in found_package_manager.items()}
105
129
 
106
130
  # both npm and pnpm are detected, remove npm.
@@ -133,10 +157,12 @@ def find_package_manager(input_dir, abs_path_to_exclude=[], manifest_file_name=[
133
157
  return ret, found_package_manager, input_dir, suggested_files
134
158
 
135
159
 
136
- def print_package_info(success_pm, log_lines):
137
- if success_pm:
138
- for pm, dir_dict in success_pm.items():
139
- log_lines.append(f"- {pm}:")
160
+ def print_package_info(pm, log_lines, status=''):
161
+ if pm:
162
+ if status:
163
+ status = f"[{status}] "
164
+ for pm, dir_dict in pm.items():
165
+ log_lines.append(f"- {status} {pm}:")
140
166
  for path, files in dir_dict.items():
141
167
  file_list = ', '.join(files)
142
168
  log_lines.append(f" {path}: {file_list}")
@@ -318,16 +344,14 @@ def run_dependency_scanner(package_manager='', input_dir='', output_dir_file='',
318
344
  success_pm = {k: dict(v) for k, v in success_pm.items()}
319
345
  fail_pm = {k: dict(v) for k, v in fail_pm.items()}
320
346
  if len(found_package_manager.keys()) > 0:
347
+ log_lines = ["Dependency Analysis Summary"]
321
348
  if len(success_pm) > 0:
322
- log_lines = ["Success to analyze:"]
323
- log_lines = print_package_info(success_pm, log_lines)
324
- scan_item.set_cover_comment('\n'.join(log_lines))
349
+ log_lines = print_package_info(success_pm, log_lines, 'Success')
325
350
  if len(fail_pm) > 0:
326
- log_lines = ["Fail to analyze:"]
327
- log_lines = print_package_info(fail_pm, log_lines)
328
- scan_item.set_cover_comment('\n'.join(log_lines))
329
- scan_item.set_cover_comment('Check log file(fosslight_log*.txt) '
330
- 'and https://fosslight.org/fosslight-guide-en/scanner/3_dependency.html#-prerequisite.')
351
+ log_lines = print_package_info(fail_pm, log_lines, 'Fail')
352
+ log_lines.append('If analysis fails, see fosslight_log*.txt and the prerequisite guide: '
353
+ 'https://fosslight.org/fosslight-guide-en/scanner/3_dependency.html#-prerequisite.')
354
+ scan_item.set_cover_comment('\n'.join(log_lines))
331
355
 
332
356
  if ret and graph_path:
333
357
  graph_path = os.path.abspath(graph_path)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fosslight_dependency
3
- Version: 4.1.25
3
+ Version: 4.1.27
4
4
  Summary: FOSSLight Dependency Scanner
5
5
  Home-page: https://github.com/fosslight/fosslight_dependency_scanner
6
6
  Download-URL: https://github.com/fosslight/fosslight_dependency_scanner
@@ -1,196 +0,0 @@
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
- from defusedxml.ElementTree import parse, fromstring
10
- import json
11
- import requests
12
- import fosslight_util.constant as constant
13
- import fosslight_dependency.constant as const
14
- from fosslight_dependency._package_manager import PackageManager
15
- from fosslight_dependency._package_manager import check_license_name, get_url_to_purl
16
- from fosslight_dependency.dependency_item import DependencyItem, change_dependson_to_purl
17
- from fosslight_util.oss_item import OssItem
18
-
19
- logger = logging.getLogger(constant.LOGGER_NAME)
20
-
21
-
22
- class Nuget(PackageManager):
23
- package_manager_name = const.NUGET
24
-
25
- dn_url = "https://nuget.org/packages/"
26
- packageReference = False
27
- nuget_api_url = 'https://api.nuget.org/v3-flatcontainer/'
28
- dotnet_ver = []
29
-
30
- def __init__(self, input_dir, output_dir):
31
- super().__init__(self.package_manager_name, self.dn_url, input_dir, output_dir)
32
-
33
- for manifest_i in const.SUPPORT_PACKAE.get(self.package_manager_name):
34
- if os.path.isfile(manifest_i):
35
- self.append_input_package_list_file(manifest_i)
36
- if manifest_i != 'packages.config':
37
- self.packageReference = True
38
-
39
- def parse_oss_information(self, f_name):
40
- tmp_license_txt_file_name = 'tmp_license.txt'
41
- with open(f_name, 'r', encoding='utf8') as input_fp:
42
- purl_dict = {}
43
- package_list = []
44
- if self.packageReference:
45
- package_list = self.get_package_info_in_packagereference(input_fp)
46
- else:
47
- package_list = self.get_package_list_in_packages_config(input_fp)
48
-
49
- for oss_origin_name, oss_version in package_list:
50
- try:
51
- dep_item = DependencyItem()
52
- oss_item = OssItem()
53
- oss_item.name = f'{self.package_manager_name}:{oss_origin_name}'
54
- oss_item.version = oss_version
55
-
56
- license_name = ''
57
- response = requests.get(f'{self.nuget_api_url.lower()}{oss_origin_name.lower()}/'
58
- f'{oss_item.version.lower()}/{oss_origin_name.lower()}.nuspec')
59
- if response.status_code == 200:
60
- root = fromstring(response.text)
61
- xmlns = ''
62
- m = re.search('{.*}', root.tag)
63
- if m:
64
- xmlns = m.group(0)
65
- nupkg_metadata = root.find(f'{xmlns}metadata')
66
-
67
- license_name_id = nupkg_metadata.find(f'{xmlns}license')
68
- if license_name_id is not None:
69
- license_name, license_comment = self.check_multi_license(license_name_id.text)
70
- if license_comment != '':
71
- oss_item.comment = license_comment
72
- else:
73
- license_url = nupkg_metadata.find(f'{xmlns}licenseUrl')
74
- if license_url is not None:
75
- url_res = requests.get(license_url.text)
76
- if url_res.status_code == 200:
77
- license_name_with_scanner = check_license_name(url_res.text)
78
- if license_name_with_scanner != "":
79
- license_name = license_name_with_scanner
80
- else:
81
- license_name = license_url.text
82
- oss_item.license = license_name
83
- repo_id = nupkg_metadata.find(f'{xmlns}repository')
84
- if repo_id is not None:
85
- oss_item.download_location = repo_id.get("url")
86
- else:
87
- proj_url_id = nupkg_metadata.find(f'{xmlns}projectUrl')
88
- if proj_url_id is not None:
89
- oss_item.download_location = proj_url_id.text
90
- oss_item.homepage = f'{self.dn_url}{oss_origin_name}'
91
- if oss_item.download_location == '':
92
- oss_item.download_location = f'{oss_item.homepage}/{oss_item.version}'
93
- else:
94
- if oss_item.download_location.endswith('.git'):
95
- oss_item.download_location = oss_item.download_location[:-4]
96
- dep_item.purl = get_url_to_purl(f'{oss_item.homepage}/{oss_item.version}', self.package_manager_name)
97
- else:
98
- oss_item.comment = 'Fail to response for nuget api'
99
- dep_item.purl = f'pkg:nuget/{oss_origin_name}@{oss_item.version}'
100
- purl_dict[f'{oss_origin_name}({oss_item.version})'] = dep_item.purl
101
-
102
- if self.direct_dep and self.packageReference:
103
- if oss_origin_name in self.direct_dep_list:
104
- oss_item.comment = 'direct'
105
- else:
106
- oss_item.comment = 'transitive'
107
-
108
- if f'{oss_origin_name}({oss_item.version})' in self.relation_tree:
109
- dep_item.depends_on_raw = self.relation_tree[f'{oss_origin_name}({oss_item.version})']
110
-
111
- dep_item.oss_items.append(oss_item)
112
- self.dep_items.append(dep_item)
113
-
114
- except Exception as e:
115
- logger.warning(f"Failed to parse oss information: {e}")
116
- if self.direct_dep:
117
- self.dep_items = change_dependson_to_purl(purl_dict, self.dep_items)
118
-
119
- if os.path.isfile(tmp_license_txt_file_name):
120
- os.remove(tmp_license_txt_file_name)
121
-
122
- return
123
-
124
- def get_package_list_in_packages_config(self, input_fp):
125
- package_list = []
126
- root = parse(input_fp).getroot()
127
- for p in root.findall("package"):
128
- package_list.append([p.get("id"), p.get("version")])
129
- return package_list
130
-
131
- def get_package_info_in_packagereference(self, input_fp):
132
- json_f = json.load(input_fp)
133
-
134
- self.get_dotnet_ver_list(json_f)
135
- package_list = self.get_package_list_in_packages_assets(json_f)
136
- self.get_dependency_tree(json_f)
137
- self.get_direct_package_in_packagereference()
138
-
139
- return package_list
140
-
141
- def get_package_list_in_packages_assets(self, json_f):
142
- package_list = []
143
- for item in json_f['libraries']:
144
- if json_f['libraries'][item]['type'] == 'package':
145
- oss_info = item.split('/')
146
- package_list.append([oss_info[0], oss_info[1]])
147
- return package_list
148
-
149
- def get_dotnet_ver_list(self, json_f):
150
- json_project_group = json_f['projectFileDependencyGroups']
151
- for dotnet_ver in json_project_group:
152
- self.dotnet_ver.append(dotnet_ver)
153
-
154
- def get_dependency_tree(self, json_f):
155
- json_target = json_f['targets']
156
- for item in json_target:
157
- if item not in self.dotnet_ver:
158
- continue
159
- json_item = json_target[item]
160
- for pkg in json_item:
161
- json_pkg = json_item[pkg]
162
- if 'type' not in json_pkg:
163
- continue
164
- if 'dependencies' not in json_pkg:
165
- continue
166
- if json_pkg['type'] != 'package':
167
- continue
168
- oss_info = pkg.split('/')
169
- self.relation_tree[f'{oss_info[0]}({oss_info[1]})'] = []
170
- for dep in json_pkg['dependencies']:
171
- oss_name = dep
172
- oss_ver = json_pkg['dependencies'][dep]
173
- self.relation_tree[f'{oss_info[0]}({oss_info[1]})'].append(f'{oss_name}({oss_ver})')
174
-
175
- def get_direct_package_in_packagereference(self):
176
- for f in os.listdir(self.input_dir):
177
- if os.path.isfile(f) and ((f.split('.')[-1] == 'csproj') or (f.split('.')[-1] == 'xproj')):
178
- with open(f, 'r', encoding='utf8') as input_fp:
179
- root = parse(input_fp).getroot()
180
- itemgroups = root.findall('ItemGroup')
181
- for itemgroup in itemgroups:
182
- for item in itemgroup.findall('PackageReference'):
183
- self.direct_dep_list.append(item.get('Include'))
184
-
185
- def check_multi_license(self, license_name):
186
- multi_license = license_name
187
- license_comment = ''
188
- try:
189
- if license_name.startswith('(') and license_name.endswith(')'):
190
- license_name = license_name.lstrip('(').rstrip(')')
191
- license_comment = license_name
192
- multi_license = ','.join(re.split(r'OR|AND', license_name))
193
- except Exception as e:
194
- logger.warning(f'Fail to parse multi license in npm: {e}')
195
-
196
- return multi_license, license_comment