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.
- {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/PKG-INFO +1 -1
- {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/setup.py +1 -1
- {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency/_analyze_dependency.py +6 -2
- {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency/_help.py +3 -1
- {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency/_package_manager.py +26 -0
- {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency/constant.py +1 -1
- {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency/package_manager/Maven.py +5 -4
- {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency/package_manager/Npm.py +43 -12
- fosslight_dependency-4.1.27/src/fosslight_dependency/package_manager/Nuget.py +368 -0
- {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency/package_manager/Yarn.py +21 -12
- {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency/run_dependency_scanner.py +48 -24
- {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency.egg-info/PKG-INFO +1 -1
- fosslight_dependency-4.1.25/src/fosslight_dependency/package_manager/Nuget.py +0 -196
- {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/LICENSE +0 -0
- {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/LICENSES/Apache-2.0.txt +0 -0
- {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/LICENSES/LicenseRef-3rd_party_licenses.txt +0 -0
- {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/LICENSES/MIT.txt +0 -0
- {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/MANIFEST.in +0 -0
- {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/README.md +0 -0
- {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/requirements.txt +0 -0
- {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/setup.cfg +0 -0
- {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency/LICENSES/LICENSE +0 -0
- {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency/LICENSES/LicenseRef-3rd_party_licenses.txt +0 -0
- {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency/__init__.py +0 -0
- {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency/_graph_convertor.py +0 -0
- {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency/dependency_item.py +0 -0
- {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency/package_manager/Android.py +0 -0
- {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency/package_manager/Cargo.py +0 -0
- {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency/package_manager/Carthage.py +0 -0
- {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency/package_manager/Cocoapods.py +0 -0
- {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency/package_manager/Go.py +0 -0
- {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency/package_manager/Gradle.py +0 -0
- {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency/package_manager/Helm.py +0 -0
- {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency/package_manager/Pnpm.py +0 -0
- {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency/package_manager/Pub.py +0 -0
- {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency/package_manager/Pypi.py +0 -0
- {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency/package_manager/Swift.py +0 -0
- {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency/package_manager/Unity.py +0 -0
- {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency/package_manager/__init__.py +0 -0
- {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency.egg-info/SOURCES.txt +0 -0
- {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency.egg-info/dependency_links.txt +0 -0
- {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency.egg-info/entry_points.txt +0 -0
- {fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency.egg-info/requires.txt +0 -0
- {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.
|
|
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
|
|
@@ -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.
|
|
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: {
|
|
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:
|
{fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency/_help.py
RENAMED
|
@@ -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
|
{fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency/constant.py
RENAMED
|
@@ -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
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
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
|
-
|
|
179
|
-
private_pkg = True
|
|
180
|
+
if _private in d and d[_private]:
|
|
181
|
+
private_pkg = True
|
|
180
182
|
|
|
181
|
-
oss_item.download_location =
|
|
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
|
-
|
|
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
|
-
|
|
126
|
-
private_pkg = True
|
|
124
|
+
if _private in d and d[_private]:
|
|
125
|
+
private_pkg = True
|
|
127
126
|
|
|
128
|
-
oss_item.download_location =
|
|
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
|
-
|
|
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
|
-
|
|
78
|
-
|
|
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
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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(
|
|
137
|
-
if
|
|
138
|
-
|
|
139
|
-
|
|
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 =
|
|
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 =
|
|
327
|
-
log_lines
|
|
328
|
-
|
|
329
|
-
|
|
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.
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fosslight_dependency-4.1.25 → fosslight_dependency-4.1.27}/src/fosslight_dependency/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|