Skip to content

Commit 6f2d464

Browse files
author
Shaji
committed
feat: conduct source URL discovery using sw360 lookup, perform extensive GitLab deep search, and adapt search strategy based on different programming language
1 parent b113561 commit 6f2d464

File tree

7 files changed

+470
-35
lines changed

7 files changed

+470
-35
lines changed

capycli/bom/findsources.py

Lines changed: 341 additions & 28 deletions
Large diffs are not rendered by default.

capycli/common/script_base.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ def __init__(self):
3434
self.project = None
3535
self.sw360_url = os.environ.get("SW360ServerUrl", None)
3636

37-
def login(self, token: str = "", url: str = "", oauth2: bool = False) -> bool:
37+
def login(self, token: str = "", url: str = "", oauth2: bool = False, exit_no_login: bool = True) -> bool:
3838
"""Login to SW360"""
3939
self.sw360_url = os.environ.get("SW360ServerUrl", None)
4040
sw360_api_token = os.environ.get("SW360ProductionToken", None)
@@ -46,15 +46,21 @@ def login(self, token: str = "", url: str = "", oauth2: bool = False) -> bool:
4646
self.sw360_url = url
4747

4848
if not self.sw360_url:
49-
print_red(" No SW360 server URL specified!")
50-
sys.exit(ResultCode.RESULT_ERROR_ACCESSING_SW360)
49+
if exit_no_login:
50+
print_red(" No SW360 server URL specified!")
51+
sys.exit(ResultCode.RESULT_ERROR_ACCESSING_SW360)
52+
else:
53+
return False
5154

5255
if self.sw360_url[-1] != "/":
5356
self.sw360_url += "/"
5457

5558
if not sw360_api_token:
56-
print_red(" No SW360 API token specified!")
57-
sys.exit(ResultCode.RESULT_AUTH_ERROR)
59+
if exit_no_login:
60+
print_red(" No SW360 API token specified!")
61+
sys.exit(ResultCode.RESULT_AUTH_ERROR)
62+
else:
63+
return False
5864

5965
self.client = sw360.sw360_api.SW360(self.sw360_url, sw360_api_token, oauth2)
6066

capycli/main/options.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,13 @@ def register_options(self):
160160
help="use this token for access to SW360",
161161
)
162162

163+
self.parser.add_argument(
164+
"-gt",
165+
"--github_token",
166+
dest="github_token",
167+
help="use this token for access to github",
168+
)
169+
163170
self.parser.add_argument(
164171
"-oa",
165172
"--oauth2",

poetry.lock

Lines changed: 39 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ capycli = "capycli.main.cli:main"
3737
python = "^3.8" # drop support for 3.6 and 3.7 because of wheel and cli-support
3838
colorama = "^0.4.3"
3939
requests = "^2.31.0" # fix CVE-2023-32681
40+
semver = "^2.9.1"
4041
packageurl-python = ">0.8, <1.0"
4142
pyjwt = "^1.7.1"
4243
openpyxl = "^3.0.3"
@@ -51,6 +52,7 @@ tomli = "^2.0.1"
5152
dateparser = "^1.1.8"
5253
urllib3 = "*"
5354
importlib-resources = "^5.12.0"
55+
beautifulsoup4 = "^4.11.1"
5456

5557
[tool.poetry.dev-dependencies]
5658
flake8 = ">=3.7.8"

tests/test_find_sources.py

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@
77
# -------------------------------------------------------------------------------
88

99
import os
10+
import responses
1011

1112
import capycli.common.json_support
1213
import capycli.common.script_base
1314
from capycli.bom.findsources import FindSources
1415
from capycli.common.capycli_bom_support import CaPyCliBom, CycloneDxSupport
1516
from capycli.main.result_codes import ResultCode
1617
from tests.test_base import AppArguments, TestBase
18+
from unittest.mock import MagicMock, patch
1719

1820

1921
class TestFindSources(TestBase):
@@ -100,7 +102,7 @@ def test_find_sources(self) -> None:
100102
self.assertTrue("Using anonymous GitHub access" in out)
101103
self.assertTrue("8 components read from SBOM" in out)
102104
self.assertTrue("1 source files were already available" in out)
103-
self.assertTrue("5 source file URLs were found" in out)
105+
self.assertTrue("6 source file URLs were found" in out)
104106

105107
sbom = CaPyCliBom.read_sbom(args.outputfile)
106108
self.assertIsNotNone(sbom)
@@ -191,3 +193,69 @@ def test_normalize_version(self):
191193
actual = sut.to_semver_string(version)
192194
self.assertEqual(actual, expected)
193195
self.assertTrue(actual == expected, 'version %s is %s' % (actual, expected))
196+
197+
@responses.activate
198+
def test_get_release_component_id(self):
199+
# Mock the sw360 client
200+
mock_client = MagicMock()
201+
mock_client.get_release.return_value = {"_links": {"sw360:component": {"href": self.MYURL + 'components/123'}}}
202+
203+
# Call the method and assert the result
204+
find_sources = FindSources()
205+
find_sources.client = mock_client
206+
component_id = find_sources.get_release_component_id("some_release_id")
207+
self.assertEqual(component_id, "123")
208+
209+
@responses.activate
210+
def test_find_source_url_from_component(self):
211+
# Mock the client
212+
mock_client = MagicMock()
213+
mock_client.get_component.return_value = {"_embedded": {"sw360:releases": [{"_links": {"self": {"href": self.MYURL + 'releases/456'}}}]}}
214+
mock_client.get_release.return_value = {"_links": {"sw360:component": {"href": self.MYURL + 'components/123'}}, "sourceCodeDownloadurl": "http://github.com/some/repo/0.0.0"}
215+
216+
# Call the method and assert the result
217+
find_sources = FindSources()
218+
find_sources.client = mock_client # Inject the mocked client
219+
source_url = find_sources.find_source_url_from_component(component_id="some_component_id")
220+
self.assertEqual(source_url, "http://github.com/some/repo/0.0.0")
221+
222+
@patch('requests.get')
223+
@patch('bs4.BeautifulSoup')
224+
def test_get_pkg_go_repo_url_success(self, mock_beautifulsoup, mock_requests_get):
225+
# Mocking successful response
226+
mock_requests_get.return_value.text = '<div class="UnitMeta-repo"><a href="https://github.com/example/repo/1.0.0">Repo Link</a></div>'
227+
mock_beautifulsoup.return_value.find.return_value = MagicMock(get=lambda x: 'https://github.com/example/repo/1.0.0')
228+
find_sources = FindSources()
229+
repo_url = find_sources.get_pkg_go_repo_url('example/package')
230+
self.assertEqual(repo_url, 'https://github.com/example/repo/1.0.0')
231+
232+
@patch('requests.get', side_effect=Exception('Some error'))
233+
def test_get_pkg_go_repo_url_error(self, mock_requests_get):
234+
# Mocking an exception during the request
235+
find_sources = FindSources()
236+
repo_url = find_sources.get_pkg_go_repo_url('some/package')
237+
self.assertEqual(repo_url, 'https://pkg.go.dev/some/package')
238+
239+
@patch('capycli.bom.findsources.FindSources.get_github_info')
240+
@patch('capycli.bom.findsources.FindSources.get_matching_tag')
241+
def test_find_golang_url_github(self, mock_get_github_info, mock_get_matching_tag):
242+
# Mocking a GitHub scenario
243+
mock_get_github_info.return_value = 'https://pkg.go.dev/github.com/opencontainers/runc'
244+
mock_get_matching_tag.return_value = 'https://github.com/opencontainers/runc/archive/refs/tags/v1.0.1.zip'
245+
find_sources = FindSources()
246+
component = MagicMock()
247+
component.name = 'github.com/opencontainers/runc'
248+
component.version = 'v1.0.1'
249+
source_url = find_sources.find_golang_url(component)
250+
251+
self.assertEqual(source_url, 'https://pkg.go.dev/github.com/opencontainers/runc')
252+
253+
def test_find_golang_url_non_github(self):
254+
# Mocking a non-GitHub scenario
255+
find_sources = FindSources()
256+
component = MagicMock()
257+
component.name = 'example/package'
258+
component.version = 'v1.0.0'
259+
source_url = find_sources.find_golang_url(component)
260+
261+
self.assertEqual(source_url, '')

tests/test_get_dependencies_python.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,7 @@ def test_process_poetry_lock_v2(self):
405405
self.assertTrue("Checking meta-data:" in out)
406406
self.assertTrue("cli-support" in out)
407407
self.assertTrue(self.OUTPUTFILE2 in out)
408-
self.assertTrue("34 components items written to file." in out)
408+
self.assertTrue("37 components items written to file." in out)
409409

410410
# ensure that dev dependencies are NOT listed
411411
self.assertTrue("flake8" not in out)

0 commit comments

Comments
 (0)