fosslight-dependency 3.0.7__py3-none-any.whl → 4.1.30__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fosslight_dependency/LICENSES/LICENSE +201 -0
- fosslight_dependency/LICENSES/LicenseRef-3rd_party_licenses.txt +1254 -0
- fosslight_dependency/__init__.py +0 -1
- fosslight_dependency/_analyze_dependency.py +130 -0
- fosslight_dependency/_graph_convertor.py +67 -0
- fosslight_dependency/_help.py +79 -0
- fosslight_dependency/_package_manager.py +397 -0
- fosslight_dependency/cli.py +127 -0
- fosslight_dependency/constant.py +57 -0
- fosslight_dependency/dependency_item.py +103 -0
- fosslight_dependency/package_manager/Android.py +90 -0
- fosslight_dependency/package_manager/Cargo.py +144 -0
- fosslight_dependency/package_manager/Carthage.py +130 -0
- fosslight_dependency/package_manager/Cocoapods.py +194 -0
- fosslight_dependency/package_manager/Go.py +179 -0
- fosslight_dependency/package_manager/Gradle.py +123 -0
- fosslight_dependency/package_manager/Helm.py +106 -0
- fosslight_dependency/package_manager/Maven.py +274 -0
- fosslight_dependency/package_manager/Npm.py +296 -0
- fosslight_dependency/package_manager/Nuget.py +368 -0
- fosslight_dependency/package_manager/Pnpm.py +155 -0
- fosslight_dependency/package_manager/Pub.py +241 -0
- fosslight_dependency/package_manager/Pypi.py +395 -0
- fosslight_dependency/package_manager/Swift.py +159 -0
- fosslight_dependency/package_manager/Unity.py +118 -0
- fosslight_dependency/package_manager/Yarn.py +231 -0
- fosslight_dependency/package_manager/__init__.py +0 -0
- fosslight_dependency/run_dependency_scanner.py +393 -0
- fosslight_dependency-4.1.30.dist-info/METADATA +213 -0
- fosslight_dependency-4.1.30.dist-info/RECORD +37 -0
- {fosslight_dependency-3.0.7.dist-info → fosslight_dependency-4.1.30.dist-info}/WHEEL +1 -1
- fosslight_dependency-4.1.30.dist-info/entry_points.txt +2 -0
- fosslight_dependency-4.1.30.dist-info/licenses/LICENSES/Apache-2.0.txt +201 -0
- fosslight_dependency-4.1.30.dist-info/licenses/LICENSES/LicenseRef-3rd_party_licenses.txt +1254 -0
- fosslight_dependency-4.1.30.dist-info/licenses/LICENSES/MIT.txt +21 -0
- fosslight_dependency/_version.py +0 -1
- fosslight_dependency/analyze_dependency.py +0 -1090
- fosslight_dependency/third_party/askalono/askalono.exe +0 -0
- fosslight_dependency/third_party/askalono/askalono_macos +0 -0
- fosslight_dependency/third_party/nomos/nomossa +0 -0
- fosslight_dependency-3.0.7.dist-info/3rd_party_licenses.txt +0 -726
- fosslight_dependency-3.0.7.dist-info/METADATA +0 -51
- fosslight_dependency-3.0.7.dist-info/RECORD +0 -13
- fosslight_dependency-3.0.7.dist-info/entry_points.txt +0 -3
- {fosslight_dependency-3.0.7.dist-info → fosslight_dependency-4.1.30.dist-info/licenses}/LICENSE +0 -0
- {fosslight_dependency-3.0.7.dist-info → fosslight_dependency-4.1.30.dist-info}/top_level.txt +0 -0
fosslight_dependency/__init__.py
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
from ._version import __version__
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# Copyright (c) 2021 LG Electronics Inc.
|
|
4
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
import logging
|
|
8
|
+
import fosslight_dependency.constant as const
|
|
9
|
+
from fosslight_dependency._package_manager import deduplicate_dep_items
|
|
10
|
+
from fosslight_dependency.package_manager.Pypi import Pypi
|
|
11
|
+
from fosslight_dependency.package_manager.Npm import Npm
|
|
12
|
+
from fosslight_dependency.package_manager.Yarn import Yarn
|
|
13
|
+
from fosslight_dependency.package_manager.Maven import Maven
|
|
14
|
+
from fosslight_dependency.package_manager.Gradle import Gradle
|
|
15
|
+
from fosslight_dependency.package_manager.Pub import Pub
|
|
16
|
+
from fosslight_dependency.package_manager.Cocoapods import Cocoapods
|
|
17
|
+
from fosslight_dependency.package_manager.Android import Android
|
|
18
|
+
from fosslight_dependency.package_manager.Swift import Swift
|
|
19
|
+
from fosslight_dependency.package_manager.Carthage import Carthage
|
|
20
|
+
from fosslight_dependency.package_manager.Go import Go
|
|
21
|
+
from fosslight_dependency.package_manager.Nuget import Nuget
|
|
22
|
+
from fosslight_dependency.package_manager.Helm import Helm
|
|
23
|
+
from fosslight_dependency.package_manager.Unity import Unity
|
|
24
|
+
from fosslight_dependency.package_manager.Cargo import Cargo
|
|
25
|
+
from fosslight_dependency.package_manager.Pnpm import Pnpm
|
|
26
|
+
import fosslight_util.constant as constant
|
|
27
|
+
|
|
28
|
+
logger = logging.getLogger(constant.LOGGER_NAME)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def analyze_dependency(package_manager_name, input_dir, output_dir, pip_activate_cmd='', pip_deactivate_cmd='',
|
|
32
|
+
output_custom_dir='', app_name=const.default_app_name, github_token='', manifest_file_name=[],
|
|
33
|
+
direct=True):
|
|
34
|
+
ret = True
|
|
35
|
+
package_dep_item_list = []
|
|
36
|
+
cover_comment = ''
|
|
37
|
+
npm_fallback_to_yarn = False
|
|
38
|
+
|
|
39
|
+
if package_manager_name == const.PYPI:
|
|
40
|
+
package_manager = Pypi(input_dir, output_dir, pip_activate_cmd, pip_deactivate_cmd)
|
|
41
|
+
elif package_manager_name == const.NPM:
|
|
42
|
+
package_manager = Npm(input_dir, output_dir)
|
|
43
|
+
npm_fallback_to_yarn = True
|
|
44
|
+
elif package_manager_name == const.YARN:
|
|
45
|
+
package_manager = Yarn(input_dir, output_dir)
|
|
46
|
+
elif package_manager_name == const.MAVEN:
|
|
47
|
+
package_manager = Maven(input_dir, output_dir, output_custom_dir)
|
|
48
|
+
elif package_manager_name == const.GRADLE:
|
|
49
|
+
package_manager = Gradle(input_dir, output_dir, output_custom_dir)
|
|
50
|
+
elif package_manager_name == const.PUB:
|
|
51
|
+
package_manager = Pub(input_dir, output_dir)
|
|
52
|
+
elif package_manager_name == const.COCOAPODS:
|
|
53
|
+
package_manager = Cocoapods(input_dir, output_dir)
|
|
54
|
+
elif package_manager_name == const.ANDROID:
|
|
55
|
+
package_manager = Android(input_dir, output_dir, app_name)
|
|
56
|
+
elif package_manager_name == const.SWIFT:
|
|
57
|
+
package_manager = Swift(input_dir, output_dir, github_token)
|
|
58
|
+
elif package_manager_name == const.CARTHAGE:
|
|
59
|
+
package_manager = Carthage(input_dir, output_dir, github_token)
|
|
60
|
+
elif package_manager_name == const.GO:
|
|
61
|
+
package_manager = Go(input_dir, output_dir)
|
|
62
|
+
elif package_manager_name == const.NUGET:
|
|
63
|
+
package_manager = Nuget(input_dir, output_dir)
|
|
64
|
+
elif package_manager_name == const.HELM:
|
|
65
|
+
package_manager = Helm(input_dir, output_dir)
|
|
66
|
+
elif package_manager_name == const.UNITY:
|
|
67
|
+
package_manager = Unity(input_dir, output_dir)
|
|
68
|
+
elif package_manager_name == const.CARGO:
|
|
69
|
+
package_manager = Cargo(input_dir, output_dir)
|
|
70
|
+
elif package_manager_name == const.PNPM:
|
|
71
|
+
package_manager = Pnpm(input_dir, output_dir)
|
|
72
|
+
else:
|
|
73
|
+
logger.error(f"Not supported package manager name: {package_manager_name}")
|
|
74
|
+
ret = False
|
|
75
|
+
return ret, package_dep_item_list, cover_comment, package_manager_name
|
|
76
|
+
|
|
77
|
+
if manifest_file_name:
|
|
78
|
+
package_manager.set_manifest_file(manifest_file_name)
|
|
79
|
+
|
|
80
|
+
if direct:
|
|
81
|
+
package_manager.set_direct_dependencies(direct)
|
|
82
|
+
ret = package_manager.run_plugin()
|
|
83
|
+
|
|
84
|
+
if not ret and npm_fallback_to_yarn:
|
|
85
|
+
logger.warning("Npm analysis failed. Attempting to use Yarn as fallback...")
|
|
86
|
+
del package_manager
|
|
87
|
+
package_manager = Yarn(input_dir, output_dir)
|
|
88
|
+
package_manager_name = const.YARN
|
|
89
|
+
|
|
90
|
+
if manifest_file_name:
|
|
91
|
+
package_manager.set_manifest_file(manifest_file_name)
|
|
92
|
+
if direct:
|
|
93
|
+
package_manager.set_direct_dependencies(direct)
|
|
94
|
+
|
|
95
|
+
ret = package_manager.run_plugin()
|
|
96
|
+
if ret:
|
|
97
|
+
logger.info("Successfully switched to Yarn")
|
|
98
|
+
else:
|
|
99
|
+
logger.error("Yarn also failed")
|
|
100
|
+
|
|
101
|
+
if ret:
|
|
102
|
+
if direct:
|
|
103
|
+
package_manager.parse_direct_dependencies()
|
|
104
|
+
|
|
105
|
+
for f_name in package_manager.input_package_list_file:
|
|
106
|
+
logger.info(f"Parse oss information with file: {f_name}")
|
|
107
|
+
|
|
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):
|
|
110
|
+
package_manager.parse_oss_information(f_name)
|
|
111
|
+
package_dep_item_list.extend(package_manager.dep_items)
|
|
112
|
+
else:
|
|
113
|
+
logger.error(f"Failed to open input file: {file_path}")
|
|
114
|
+
ret = False
|
|
115
|
+
if package_manager_name == const.PNPM:
|
|
116
|
+
logger.info("Parse oss information for pnpm")
|
|
117
|
+
package_manager.parse_oss_information_for_pnpm()
|
|
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)
|
|
121
|
+
if ret:
|
|
122
|
+
logger.warning(f"### Complete to analyze: {package_manager_name}({input_dir}: {','.join(manifest_file_name)})")
|
|
123
|
+
if package_manager.cover_comment:
|
|
124
|
+
cover_comment = package_manager.cover_comment
|
|
125
|
+
else:
|
|
126
|
+
logger.error(f"### Fail to analyze: {package_manager_name}({input_dir}: {','.join(manifest_file_name)})")
|
|
127
|
+
|
|
128
|
+
del package_manager
|
|
129
|
+
|
|
130
|
+
return ret, package_dep_item_list, cover_comment, package_manager_name
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# Copyright (c) 2021 LG Electronics Inc.
|
|
4
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
from typing import Optional, Tuple
|
|
6
|
+
import igraph as ig
|
|
7
|
+
import matplotlib.pyplot as plt
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class GraphConvertor:
|
|
11
|
+
def __init__(self, dep_items: Optional[list] = None):
|
|
12
|
+
self._verticies = {}
|
|
13
|
+
self._edges = []
|
|
14
|
+
if dep_items:
|
|
15
|
+
self.init_list(dep_items)
|
|
16
|
+
|
|
17
|
+
def init_list(self, dep_items: list):
|
|
18
|
+
"""
|
|
19
|
+
Initialize dep_items to self._verticies and self._edges
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
dep_items : List containing package information
|
|
23
|
+
"""
|
|
24
|
+
depend_on_package_dict = {}
|
|
25
|
+
for idx, file_item in enumerate(dep_items):
|
|
26
|
+
package_name = file_item.purl
|
|
27
|
+
depend_on_packages = file_item.depends_on
|
|
28
|
+
self._verticies[package_name] = idx
|
|
29
|
+
depend_on_package_dict[package_name] = depend_on_packages
|
|
30
|
+
else:
|
|
31
|
+
for package_name, depend_on_packages in depend_on_package_dict.items():
|
|
32
|
+
if not package_name:
|
|
33
|
+
pass
|
|
34
|
+
else:
|
|
35
|
+
package_idx = self._verticies[package_name]
|
|
36
|
+
for depend_on_package in depend_on_packages:
|
|
37
|
+
if not depend_on_package:
|
|
38
|
+
pass
|
|
39
|
+
else:
|
|
40
|
+
depend_on_package_idx = self._verticies[depend_on_package]
|
|
41
|
+
self._edges.append((package_idx, depend_on_package_idx))
|
|
42
|
+
|
|
43
|
+
def save(self, path: str, size: Tuple[(int, int)]):
|
|
44
|
+
g = ig.Graph((len(self._verticies)), (self._edges), directed=True)
|
|
45
|
+
|
|
46
|
+
g["title"] = "Dependency Graph"
|
|
47
|
+
g.vs["name"] = list(self._verticies.keys())
|
|
48
|
+
|
|
49
|
+
fig, ax = plt.subplots(figsize=(tuple(map((lambda x: x / 100), size))))
|
|
50
|
+
fig.tight_layout()
|
|
51
|
+
|
|
52
|
+
ig.plot(
|
|
53
|
+
g,
|
|
54
|
+
target=ax,
|
|
55
|
+
layout="kk",
|
|
56
|
+
vertex_size=15,
|
|
57
|
+
vertex_color=["#FFD2D2"],
|
|
58
|
+
vertex_label=(g.vs["name"]),
|
|
59
|
+
vertex_label_dist=1.5,
|
|
60
|
+
vertex_label_size=7.0,
|
|
61
|
+
edge_width=0.5,
|
|
62
|
+
edge_color=["#FFD2D2"],
|
|
63
|
+
edge_arrow_size=5,
|
|
64
|
+
edge_arrow_width=5,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
fig.savefig(path)
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# Copyright (c) 2021 LG Electronics Inc.
|
|
4
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
from fosslight_util.help import PrintHelpMsg, print_package_version
|
|
6
|
+
from fosslight_util.output_format import SUPPORT_FORMAT
|
|
7
|
+
|
|
8
|
+
_HELP_MESSAGE_DEPENDENCY = f"""
|
|
9
|
+
Usage: fosslight_dependency [option1] <arg1> [option2] <arg2>...
|
|
10
|
+
|
|
11
|
+
FOSSLight Dependency Scanner is the tool that supports the analysis of dependencies for multiple package managers.
|
|
12
|
+
It detects the manifest file of package managers automatically and analyzes the dependencies with using open source tools.
|
|
13
|
+
Then, it generates the report file that contains OSS information of dependencies.
|
|
14
|
+
|
|
15
|
+
Currently, it supports the following package managers:
|
|
16
|
+
Gradle (Java)
|
|
17
|
+
Maven (Java)
|
|
18
|
+
NPM (Node.js)
|
|
19
|
+
PNPM (Node.js)
|
|
20
|
+
Yarn (Node.js)
|
|
21
|
+
PIP (Python)
|
|
22
|
+
Pub (Dart with flutter)
|
|
23
|
+
Cocoapods (Swift/Obj-C)
|
|
24
|
+
Swift (Swift)
|
|
25
|
+
Carthage (Swift/Obj-C)
|
|
26
|
+
Go (Go)
|
|
27
|
+
Nuget (.NET)
|
|
28
|
+
Helm (Kubernetes)
|
|
29
|
+
Unity (Unity)
|
|
30
|
+
Cargo (Rust)
|
|
31
|
+
|
|
32
|
+
Options:
|
|
33
|
+
Optional
|
|
34
|
+
-h\t\t\t\t Print help message.
|
|
35
|
+
-v\t\t\t\t Print the version of the script.
|
|
36
|
+
-m <package_manager>\t Enter the package manager.
|
|
37
|
+
\t(npm, maven, gradle, pypi, pub, cocoapods, android, swift, carthage,
|
|
38
|
+
\t go, nuget, helm, unity, cargo, pnpm, yarn)
|
|
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 (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"
|
|
43
|
+
-o <output_path>\t\t Output path
|
|
44
|
+
\t\t\t\t\t(If you want to generate the specific file name, add the output path with file name.)
|
|
45
|
+
-f <format> [<format> ...]\t Output formats
|
|
46
|
+
\t\t\t\t \t({', '.join(SUPPORT_FORMAT)})
|
|
47
|
+
\t\t\t\t Multiple formats can be specified separated by space.
|
|
48
|
+
--graph-path <save_path> \t Enter the path where the graph image will be saved
|
|
49
|
+
\t\t\t\t\t(ex. /your/directory/path/filename.[pdf, jpg, png]) (recommend pdf extension)
|
|
50
|
+
--graph-size <width> <height> Enter the size of the graph image (The size unit is pixels)
|
|
51
|
+
\t\t\t\t\t--graph-path option is required
|
|
52
|
+
--direct\t\t\t Print the direct/transitive dependency type in comment.
|
|
53
|
+
\t\tChoice 'True' or 'False'. (default:True)
|
|
54
|
+
-r\t\t\t\t Recursive mode. Scan all subdirectories for manifest files.
|
|
55
|
+
--notice\t\t\t Print the open source license notice text.
|
|
56
|
+
|
|
57
|
+
Required only for swift, carthage
|
|
58
|
+
-t <token>\t\t\t Enter the github personal access token.
|
|
59
|
+
|
|
60
|
+
Optional only for pypi
|
|
61
|
+
-a <activate_cmd>\t\t Virtual environment activate command(ex, 'conda activate (venv name)')
|
|
62
|
+
-d <deactivate_cmd>\t\t Virtual environment deactivate command(ex, 'conda deactivate')
|
|
63
|
+
|
|
64
|
+
Optional only for gradle, maven
|
|
65
|
+
-c <dir_name>\t\t Enter the customized build output directory name
|
|
66
|
+
\t\t-Default name : 'build' for gradle, 'target' for maven
|
|
67
|
+
|
|
68
|
+
Optional only for android
|
|
69
|
+
-n <app_name>\t\t Enter the application directory name where the plugin output file is located(default: app)
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def print_version(pkg_name: str) -> None:
|
|
74
|
+
print_package_version(pkg_name, "FOSSLight Dependency Scanner Version:")
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def print_help_msg():
|
|
78
|
+
helpMsg = PrintHelpMsg(_HELP_MESSAGE_DEPENDENCY)
|
|
79
|
+
helpMsg.print_help_msg(True)
|
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# Copyright (c) 2021 LG Electronics Inc.
|
|
4
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
import logging
|
|
8
|
+
import platform
|
|
9
|
+
import re
|
|
10
|
+
import base64
|
|
11
|
+
import subprocess
|
|
12
|
+
import shutil
|
|
13
|
+
import stat
|
|
14
|
+
from packageurl.contrib import url2purl
|
|
15
|
+
from askalono import identify
|
|
16
|
+
import fosslight_util.constant as constant
|
|
17
|
+
import fosslight_dependency.constant as const
|
|
18
|
+
|
|
19
|
+
try:
|
|
20
|
+
from github import Github
|
|
21
|
+
except Exception:
|
|
22
|
+
pass
|
|
23
|
+
|
|
24
|
+
logger = logging.getLogger(constant.LOGGER_NAME)
|
|
25
|
+
|
|
26
|
+
gradle_config = ['runtimeClasspath', 'runtime']
|
|
27
|
+
android_config = ['releaseRuntimeClasspath']
|
|
28
|
+
ASKALONO_THRESHOLD = 0.7
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class PackageManager:
|
|
32
|
+
input_package_list_file = []
|
|
33
|
+
direct_dep = False
|
|
34
|
+
total_dep_list = []
|
|
35
|
+
direct_dep_list = []
|
|
36
|
+
|
|
37
|
+
def __init__(self, package_manager_name, dn_url, input_dir, output_dir):
|
|
38
|
+
self.input_package_list_file = []
|
|
39
|
+
self.direct_dep = False
|
|
40
|
+
self.total_dep_list = []
|
|
41
|
+
self.direct_dep_list = []
|
|
42
|
+
self.package_manager_name = package_manager_name
|
|
43
|
+
self.input_dir = input_dir
|
|
44
|
+
self.output_dir = output_dir
|
|
45
|
+
self.dn_url = dn_url
|
|
46
|
+
self.manifest_file_name = []
|
|
47
|
+
self.relation_tree = {}
|
|
48
|
+
self.package_name = ''
|
|
49
|
+
self.cover_comment = ''
|
|
50
|
+
self.dep_items = []
|
|
51
|
+
|
|
52
|
+
self.platform = platform.system()
|
|
53
|
+
|
|
54
|
+
def __del__(self):
|
|
55
|
+
self.input_package_list_file = []
|
|
56
|
+
self.direct_dep = False
|
|
57
|
+
self.total_dep_list = []
|
|
58
|
+
self.direct_dep_list = []
|
|
59
|
+
self.package_manager_name = ''
|
|
60
|
+
self.input_dir = ''
|
|
61
|
+
self.output_dir = ''
|
|
62
|
+
self.dn_url = ''
|
|
63
|
+
self.manifest_file_name = []
|
|
64
|
+
self.relation_tree = {}
|
|
65
|
+
self.package_name = ''
|
|
66
|
+
self.dep_items = []
|
|
67
|
+
|
|
68
|
+
def run_plugin(self):
|
|
69
|
+
ret = True
|
|
70
|
+
if self.package_manager_name == const.GRADLE or self.package_manager_name == const.ANDROID:
|
|
71
|
+
ret = self.run_gradle_task()
|
|
72
|
+
else:
|
|
73
|
+
logger.info(f"This package manager({self.package_manager_name}) skips the step to run plugin.")
|
|
74
|
+
return ret
|
|
75
|
+
|
|
76
|
+
def append_input_package_list_file(self, input_package_file):
|
|
77
|
+
self.input_package_list_file.append(input_package_file)
|
|
78
|
+
|
|
79
|
+
def set_manifest_file(self, manifest_file_name):
|
|
80
|
+
self.manifest_file_name = manifest_file_name
|
|
81
|
+
|
|
82
|
+
def set_direct_dependencies(self, direct):
|
|
83
|
+
self.direct_dep = direct
|
|
84
|
+
|
|
85
|
+
def parse_direct_dependencies(self):
|
|
86
|
+
pass
|
|
87
|
+
|
|
88
|
+
def run_gradle_task(self):
|
|
89
|
+
ret_task = True
|
|
90
|
+
if os.path.isfile(const.SUPPORT_PACKAE.get(self.package_manager_name)):
|
|
91
|
+
gradle_backup = f'{const.SUPPORT_PACKAE.get(self.package_manager_name)}_bk'
|
|
92
|
+
|
|
93
|
+
shutil.copy(const.SUPPORT_PACKAE.get(self.package_manager_name), gradle_backup)
|
|
94
|
+
ret_alldeps = self.add_allDeps_in_gradle()
|
|
95
|
+
|
|
96
|
+
ret_plugin = False
|
|
97
|
+
if (self.package_manager_name == const.ANDROID):
|
|
98
|
+
module_build_gradle = os.path.join(self.app_name, const.SUPPORT_PACKAE.get(self.package_manager_name))
|
|
99
|
+
module_gradle_backup = f'{module_build_gradle}_bk'
|
|
100
|
+
if os.path.isfile(module_build_gradle) and (not os.path.isfile(self.input_file_name)):
|
|
101
|
+
shutil.copy(module_build_gradle, module_gradle_backup)
|
|
102
|
+
ret_plugin = self.add_android_plugin_in_gradle(module_build_gradle)
|
|
103
|
+
|
|
104
|
+
if os.path.isfile('gradlew') or os.path.isfile('gradlew.bat'):
|
|
105
|
+
if self.platform == const.WINDOWS:
|
|
106
|
+
cmd_gradle = "gradlew.bat"
|
|
107
|
+
else:
|
|
108
|
+
cmd_gradle = "./gradlew"
|
|
109
|
+
else:
|
|
110
|
+
ret_task = False
|
|
111
|
+
self.set_direct_dependencies(False)
|
|
112
|
+
logger.warning('No gradlew file exists (Skip to find dependencies relationship.).')
|
|
113
|
+
if ret_plugin:
|
|
114
|
+
logger.warning('Also it cannot run android-dependency-scanning plugin.')
|
|
115
|
+
if ret_task:
|
|
116
|
+
current_mode = change_file_mode(cmd_gradle)
|
|
117
|
+
if ret_alldeps:
|
|
118
|
+
cmd = f"{cmd_gradle} allDeps"
|
|
119
|
+
try:
|
|
120
|
+
ret = subprocess.check_output(cmd, shell=True, encoding='utf-8')
|
|
121
|
+
if ret != 0:
|
|
122
|
+
self.parse_dependency_tree(ret)
|
|
123
|
+
else:
|
|
124
|
+
self.set_direct_dependencies(False)
|
|
125
|
+
logger.warning(f"Fail to run {cmd}")
|
|
126
|
+
except Exception as e:
|
|
127
|
+
self.set_direct_dependencies(False)
|
|
128
|
+
logger.warning(f"Cannot print 'depends on' information. (fail {cmd}: {e})")
|
|
129
|
+
|
|
130
|
+
if ret_plugin:
|
|
131
|
+
cmd = f"{cmd_gradle} generateLicenseTxt"
|
|
132
|
+
try:
|
|
133
|
+
ret = subprocess.check_output(cmd, shell=True, encoding='utf-8')
|
|
134
|
+
if ret == 0:
|
|
135
|
+
ret_task = False
|
|
136
|
+
logger.error(f'Fail to run {cmd}')
|
|
137
|
+
if os.path.isfile(self.input_file_name):
|
|
138
|
+
logger.info('Automatically run android-dependency-scanning plugin and generate output.')
|
|
139
|
+
self.plugin_auto_run = True
|
|
140
|
+
else:
|
|
141
|
+
logger.warning('Automatically run android-dependency-scanning plugin, but fail to generate output.')
|
|
142
|
+
except Exception as e:
|
|
143
|
+
logger.error(f'Fail to run {cmd}: {e}')
|
|
144
|
+
ret_task = False
|
|
145
|
+
change_file_mode(cmd_gradle, current_mode)
|
|
146
|
+
|
|
147
|
+
if os.path.isfile(gradle_backup):
|
|
148
|
+
os.remove(const.SUPPORT_PACKAE.get(self.package_manager_name))
|
|
149
|
+
shutil.move(gradle_backup, const.SUPPORT_PACKAE.get(self.package_manager_name))
|
|
150
|
+
|
|
151
|
+
if (self.package_manager_name == const.ANDROID):
|
|
152
|
+
if os.path.isfile(module_gradle_backup):
|
|
153
|
+
os.remove(module_build_gradle)
|
|
154
|
+
shutil.move(module_gradle_backup, module_build_gradle)
|
|
155
|
+
if os.path.isfile(self.input_file_name):
|
|
156
|
+
logger.info(f'Found {self.input_file_name}, skip to run plugin.')
|
|
157
|
+
self.set_direct_dependencies(False)
|
|
158
|
+
ret_task = True
|
|
159
|
+
return ret_task
|
|
160
|
+
|
|
161
|
+
def add_android_plugin_in_gradle(self, module_build_gradle):
|
|
162
|
+
ret = False
|
|
163
|
+
build_script = '''buildscript {
|
|
164
|
+
repositories {
|
|
165
|
+
mavenCentral()
|
|
166
|
+
}
|
|
167
|
+
dependencies {
|
|
168
|
+
//Android dependency scanning Plugin
|
|
169
|
+
classpath 'org.fosslight:android-dependency-scanning:+'
|
|
170
|
+
}
|
|
171
|
+
}'''
|
|
172
|
+
apply = "apply plugin: 'org.fosslight'\n"
|
|
173
|
+
try:
|
|
174
|
+
with open(const.SUPPORT_PACKAE.get(self.package_manager_name), 'r', encoding='utf-8') as original:
|
|
175
|
+
data = original.read()
|
|
176
|
+
with open(const.SUPPORT_PACKAE.get(self.package_manager_name), 'w', encoding='utf-8') as modified:
|
|
177
|
+
modified.write(f"{build_script}\n{data}")
|
|
178
|
+
ret = True
|
|
179
|
+
except Exception as e:
|
|
180
|
+
logging.warning(f"Cannot add the buildscript task in build.gradle: {e}")
|
|
181
|
+
|
|
182
|
+
try:
|
|
183
|
+
with open(module_build_gradle, 'a', encoding='utf-8') as modified:
|
|
184
|
+
modified.write(f'\n{apply}\n')
|
|
185
|
+
ret = True
|
|
186
|
+
except Exception as e:
|
|
187
|
+
logging.warning(f"Cannot add the apply plugin in {module_build_gradle}: {e}")
|
|
188
|
+
return ret
|
|
189
|
+
|
|
190
|
+
def add_allDeps_in_gradle(self):
|
|
191
|
+
ret = False
|
|
192
|
+
config = android_config if self.package_manager_name == 'android' else gradle_config
|
|
193
|
+
configuration = ','.join([f'project.configurations.{c}' for c in config])
|
|
194
|
+
|
|
195
|
+
allDeps = f'''allprojects {{
|
|
196
|
+
task allDeps(type: DependencyReportTask) {{
|
|
197
|
+
doFirst{{
|
|
198
|
+
try {{
|
|
199
|
+
configurations = [{configuration}] as Set }}
|
|
200
|
+
catch(UnknownConfigurationException) {{}}
|
|
201
|
+
}}
|
|
202
|
+
}}
|
|
203
|
+
}}'''
|
|
204
|
+
try:
|
|
205
|
+
with open(const.SUPPORT_PACKAE.get(self.package_manager_name), 'a', encoding='utf8') as f:
|
|
206
|
+
f.write(f'\n{allDeps}\n')
|
|
207
|
+
ret = True
|
|
208
|
+
except Exception as e:
|
|
209
|
+
logging.warning(f"Cannot add the allDeps task in build.gradle: {e}")
|
|
210
|
+
|
|
211
|
+
return ret
|
|
212
|
+
|
|
213
|
+
def create_dep_stack(self, dep_line, config):
|
|
214
|
+
packages_in_config = False
|
|
215
|
+
dep_stack = []
|
|
216
|
+
cur_flag = ''
|
|
217
|
+
dep_level = -1
|
|
218
|
+
dep_level_plus = False
|
|
219
|
+
for line in dep_line.split('\n'):
|
|
220
|
+
try:
|
|
221
|
+
if not packages_in_config:
|
|
222
|
+
filtered = next(filter(lambda c: re.findall(rf'^{c}\s\-', line), config), None)
|
|
223
|
+
if filtered:
|
|
224
|
+
packages_in_config = True
|
|
225
|
+
else:
|
|
226
|
+
if line == '':
|
|
227
|
+
packages_in_config = False
|
|
228
|
+
prev_flag = cur_flag
|
|
229
|
+
prev_dep_level = dep_level
|
|
230
|
+
dep_level = line.count("|")
|
|
231
|
+
|
|
232
|
+
re_result = re.findall(r'([\+|\\])\-\-\-\s([^\:\s]+\:[^\:\s]+)\:([^\:\s]+)', line)
|
|
233
|
+
if re_result:
|
|
234
|
+
cur_flag = re_result[0][0]
|
|
235
|
+
if (prev_flag == '\\') and (prev_dep_level == dep_level):
|
|
236
|
+
dep_level_plus = True
|
|
237
|
+
if dep_level_plus and (prev_flag == '\\') and (prev_dep_level != dep_level):
|
|
238
|
+
dep_level_plus = False
|
|
239
|
+
if dep_level_plus:
|
|
240
|
+
dep_level += 1
|
|
241
|
+
dep_name = f'{re_result[0][1]}({re_result[0][2]})'
|
|
242
|
+
dep_stack = dep_stack[:dep_level] + [dep_name]
|
|
243
|
+
yield dep_stack[:dep_level], dep_name
|
|
244
|
+
else:
|
|
245
|
+
cur_flag = ''
|
|
246
|
+
except Exception as e:
|
|
247
|
+
logger.warning(f"Failed to parse dependency tree: {e}")
|
|
248
|
+
|
|
249
|
+
def parse_dependency_tree(self, f_name):
|
|
250
|
+
config = android_config if self.package_manager_name == 'android' else gradle_config
|
|
251
|
+
try:
|
|
252
|
+
for stack, name in self.create_dep_stack(f_name, config):
|
|
253
|
+
self.total_dep_list.append(name)
|
|
254
|
+
if len(stack) == 0:
|
|
255
|
+
self.direct_dep_list.append(name)
|
|
256
|
+
else:
|
|
257
|
+
if stack[-1] not in self.relation_tree:
|
|
258
|
+
self.relation_tree[stack[-1]] = []
|
|
259
|
+
self.relation_tree[stack[-1]].append(name)
|
|
260
|
+
except Exception as e:
|
|
261
|
+
logger.warning(f'Fail to parse gradle dependency tree:{e}')
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def get_url_to_purl(url, pkg_manager, oss_name='', oss_version=''):
|
|
265
|
+
purl_prefix = f'pkg:{pkg_manager}'
|
|
266
|
+
purl = str(url2purl.get_purl(url))
|
|
267
|
+
if not re.match(purl_prefix, purl):
|
|
268
|
+
match = re.match(constant.PKG_PATTERN.get(pkg_manager, 'not_support'), url)
|
|
269
|
+
try:
|
|
270
|
+
if match and (match != ''):
|
|
271
|
+
if pkg_manager == 'maven':
|
|
272
|
+
purl = f'{purl_prefix}/{match.group(1)}/{match.group(2)}@{match.group(3)}'
|
|
273
|
+
elif pkg_manager == 'pub':
|
|
274
|
+
purl = f'{purl_prefix}/{match.group(1)}@{match.group(2)}'
|
|
275
|
+
elif pkg_manager == 'cocoapods':
|
|
276
|
+
match = re.match(r'([^\/]+)\/?([^\/]*)', oss_name) # ex, GoogleUtilities/NSData+zlib
|
|
277
|
+
purl = f'{purl_prefix}/{match.group(1)}@{oss_version}'
|
|
278
|
+
if match.group(2):
|
|
279
|
+
purl = f'{purl}#{match.group(2)}'
|
|
280
|
+
elif pkg_manager == 'go':
|
|
281
|
+
purl = f'{purl_prefix}lang/{match.group(1)}@{match.group(2)}'
|
|
282
|
+
elif pkg_manager == 'cargo':
|
|
283
|
+
purl = f'{purl_prefix}/{oss_name}@{oss_version}'
|
|
284
|
+
else:
|
|
285
|
+
if pkg_manager == 'swift':
|
|
286
|
+
if oss_version:
|
|
287
|
+
purl = f'{purl_prefix}/{oss_name}@{oss_version}'
|
|
288
|
+
else:
|
|
289
|
+
purl = f'{purl_prefix}/{oss_name}'
|
|
290
|
+
elif pkg_manager == 'carthage':
|
|
291
|
+
if oss_version:
|
|
292
|
+
purl = f'{purl}@{oss_version}'
|
|
293
|
+
except Exception:
|
|
294
|
+
logger.debug('Fail to get purl. So use the link purl({purl}).')
|
|
295
|
+
return purl
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
def version_refine(oss_version):
|
|
299
|
+
version_cmp = oss_version.upper()
|
|
300
|
+
|
|
301
|
+
if version_cmp.find(".RELEASE") != -1:
|
|
302
|
+
oss_version = version_cmp.rstrip(".RELEASE")
|
|
303
|
+
elif version_cmp.find(".FINAL") != -1:
|
|
304
|
+
oss_version = version_cmp.rstrip(".FINAL")
|
|
305
|
+
|
|
306
|
+
return oss_version
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def connect_github(github_token):
|
|
310
|
+
if len(github_token) > 0:
|
|
311
|
+
g = Github(github_token)
|
|
312
|
+
else:
|
|
313
|
+
g = Github()
|
|
314
|
+
|
|
315
|
+
return g
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
def get_github_license(g, github_repo):
|
|
319
|
+
license_name = ''
|
|
320
|
+
|
|
321
|
+
try:
|
|
322
|
+
repository = g.get_repo(github_repo)
|
|
323
|
+
except Exception:
|
|
324
|
+
logger.info("It cannot find the license name. Please use '-t' option with github token.")
|
|
325
|
+
logger.info("{0}{1}".format("refer:https://docs.github.com/en/github/authenticating-to-github/",
|
|
326
|
+
"keeping-your-account-and-data-secure/creating-a-personal-access-token"))
|
|
327
|
+
repository = ''
|
|
328
|
+
|
|
329
|
+
if repository != '':
|
|
330
|
+
try:
|
|
331
|
+
license_name = repository.get_license().license.spdx_id
|
|
332
|
+
if license_name == "" or license_name == "NOASSERTION":
|
|
333
|
+
try:
|
|
334
|
+
license_txt_data = base64.b64decode(repository.get_license().content).decode('utf-8')
|
|
335
|
+
license_name = check_license_name(license_txt_data)
|
|
336
|
+
except Exception:
|
|
337
|
+
logger.info("Cannot find the license name with askalono.")
|
|
338
|
+
except Exception:
|
|
339
|
+
logger.info("Cannot find the license name with github api.")
|
|
340
|
+
|
|
341
|
+
return license_name
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
def check_license_name(license_txt, is_filepath=False):
|
|
345
|
+
license_name = ''
|
|
346
|
+
if is_filepath:
|
|
347
|
+
with open(license_txt, 'r', encoding='utf-8') as f:
|
|
348
|
+
license_content = f.read()
|
|
349
|
+
else:
|
|
350
|
+
license_content = license_txt
|
|
351
|
+
|
|
352
|
+
detect_askalono = identify(license_content)
|
|
353
|
+
if detect_askalono.score > ASKALONO_THRESHOLD:
|
|
354
|
+
license_name = detect_askalono.name
|
|
355
|
+
return license_name
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
def change_file_mode(filepath, mode=''):
|
|
359
|
+
current_mode = ''
|
|
360
|
+
|
|
361
|
+
if not os.path.exists(filepath):
|
|
362
|
+
logger.debug(f"The file{filepath} does not exist.")
|
|
363
|
+
else:
|
|
364
|
+
current_mode = os.stat(filepath).st_mode
|
|
365
|
+
if not mode:
|
|
366
|
+
new_mode = current_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
|
|
367
|
+
else:
|
|
368
|
+
new_mode = mode
|
|
369
|
+
os.chmod(filepath, new_mode)
|
|
370
|
+
logger.debug(f"File mode of {filepath} has been changed to {oct(new_mode)}.")
|
|
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
|