Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@

# CaPyCli - Clearing Automation Python Command Line Tool for SW360

## NEXT

* fix namespace handling in `bom componentcheck` and `project componentcheck`.

## 2.11.0

* Two new commands: `bom componentcheck` and `project componentcheck`. The first one
Expand Down
2 changes: 1 addition & 1 deletion capycli/bom/check_bom.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@
from colorama import Fore, Style
from cyclonedx.model.bom import Bom
from cyclonedx.model.component import Component
from sw360 import SW360Error

import capycli.common.script_base
from capycli.common.capycli_bom_support import CaPyCliBom, CycloneDxSupport
from capycli.common.print import print_green, print_red, print_text, print_yellow
from capycli.main.result_codes import ResultCode
from sw360 import SW360Error

LOG = capycli.get_logger(__name__)

Expand Down
2 changes: 1 addition & 1 deletion capycli/bom/check_bom_item_status.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@
from colorama import Fore, Style
from cyclonedx.model.bom import Bom
from cyclonedx.model.component import Component
from sw360 import SW360Error

import capycli.common.script_base
from capycli.common.capycli_bom_support import CaPyCliBom, CycloneDxSupport
from capycli.common.print import print_red, print_text
from capycli.main.result_codes import ResultCode
from sw360 import SW360Error

LOG = capycli.get_logger(__name__)

Expand Down
33 changes: 28 additions & 5 deletions capycli/bom/component_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
# SPDX-License-Identifier: MIT
# -------------------------------------------------------------------------------

import logging

try:
import importlib.resources as pkg_resources
except ImportError:
Expand Down Expand Up @@ -38,6 +40,7 @@ class ComponentCheck(capycli.common.script_base.ScriptBase):
def __init__(self) -> None:
self.component_check_list: Dict[str, Any] = {}
self.files_to_ignore: List[Dict[str, Any]] = []
self.verbose = False

@staticmethod
def get_component_check_list(download_url: str) -> None:
Expand All @@ -54,6 +57,8 @@ def read_component_check_list(self, download_url: str = "", local_check_list_fil
try:
with open(local_check_list_file, "r", encoding="utf-8") as fin:
self.component_check_list = json.load(fin)
if self.verbose:
print_text(f" Reading component checklist from {local_check_list_file}...")
except FileNotFoundError as e:
print_yellow(f"File not found: {e} \n Reading the default component check list")
except Exception as e:
Expand All @@ -63,6 +68,8 @@ def read_component_check_list(self, download_url: str = "", local_check_list_fil
ComponentCheck.get_component_check_list(download_url)
with open("component_checks.json", "r", encoding="utf-8") as fin:
self.component_check_list = json.load(fin)
if self.verbose:
print_text(f" Reading component checklist from {download_url}...")
except FileNotFoundError as e:
print_yellow(f"File not found: {e} \n Reading the default component check list")
except Exception as e:
Expand Down Expand Up @@ -92,11 +99,20 @@ def is_dev_dependency(self, comp: Component) -> bool:
pd = comp.purl.to_dict()
ecosystem = pd.get("type", "").lower()
for entry in self.get_dev_dependencies(ecosystem):
if comp.group:
if comp.group.lower() != entry.get("namespace", ""):
name_to_compare = entry.get("name", "")
if comp.purl.namespace and entry.get("namespace", ""):
if comp.purl.namespace.lower() != entry.get("namespace", ""):
continue

if comp.name.lower() == entry.get("name", ""):
if comp.name.lower() == name_to_compare.lower():
return True

# it can happen that comp.purl.namespace is empty, but the namespace
# ...is included in the name
if entry.get("namespace", ""):
name_to_compare = entry.get("namespace", "") + "/" + name_to_compare

if comp.name.lower() == name_to_compare.lower():
return True
else:
# fallback: only check by name
Expand Down Expand Up @@ -154,6 +170,11 @@ def run(self, args: Any) -> None:
if args.debug:
global LOG
LOG = capycli.get_logger(__name__)
else:
# suppress (debug) log output from requests and urllib
logging.getLogger("requests").setLevel(logging.WARNING)
logging.getLogger("urllib3").setLevel(logging.WARNING)
logging.getLogger("urllib3.connectionpool").setLevel(logging.WARNING)

print_text(
"\n" + capycli.get_app_signature() +
Expand All @@ -179,11 +200,13 @@ def run(self, args: Any) -> None:
print_red("Input file not found!")
sys.exit(ResultCode.RESULT_FILE_NOT_FOUND)

print_text("Reading component check list from component_checks.json...")
self.verbose = args.verbose

print_text("Reading component checklist...")
try:
self.read_component_check_list(args.remote_check_list, args.local_checklist_list)
except Exception as ex:
print_red("Error reading component check list " + repr(ex))
print_red("Error reading component checklist " + repr(ex))
sys.exit(ResultCode.RESULT_GENERAL_ERROR)
if len(self.component_check_list) > 0:
print_text(" Got component checklist.")
Expand Down
2 changes: 1 addition & 1 deletion capycli/bom/create_components.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
from cyclonedx.model.bom import Bom
from cyclonedx.model.component import Component
from cyclonedx.model.license import DisjunctiveLicense, LicenseExpression
from sw360 import SW360Error

import capycli.common.json_support
import capycli.common.script_base
Expand All @@ -30,6 +29,7 @@
from capycli.common.purl_utils import PurlUtils
from capycli.common.script_support import ScriptSupport
from capycli.main.result_codes import ResultCode
from sw360 import SW360Error

LOG = capycli.get_logger(__name__)

Expand Down
2 changes: 1 addition & 1 deletion capycli/bom/findsources.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@
from cyclonedx.model import ExternalReferenceType, XsUri
from cyclonedx.model.bom import Bom
from cyclonedx.model.component import Component
from sw360 import SW360Error

import capycli.common.script_base
from capycli import get_logger
from capycli.common.capycli_bom_support import CaPyCliBom, CycloneDxSupport, SbomWriter
from capycli.common.github_support import GitHubSupport
from capycli.common.print import print_green, print_red, print_text, print_yellow
from capycli.main.result_codes import ResultCode
from sw360 import SW360Error

LOG = get_logger(__name__)

Expand Down
2 changes: 1 addition & 1 deletion capycli/bom/map_bom.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
from cyclonedx.model.bom import Bom
from cyclonedx.model.component import Component
from packageurl import PackageURL
from sw360 import SW360

import capycli.common.file_support
import capycli.common.script_base
Expand All @@ -34,6 +33,7 @@
from capycli.common.print import print_green, print_red, print_text, print_yellow
from capycli.common.purl_service import PurlService
from capycli.main.result_codes import ResultCode
from sw360 import SW360

LOG = get_logger(__name__)

Expand Down
1 change: 0 additions & 1 deletion capycli/common/component_cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
from typing import Any, Dict, List, Optional

import sw360

from capycli import get_logger
from capycli.common.print import print_red, print_text, print_yellow
from capycli.common.script_support import ScriptSupport
Expand Down
2 changes: 1 addition & 1 deletion capycli/common/purl_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
from typing import Any, Dict, List, Optional

import packageurl
from sw360 import SW360

from capycli.common.print import print_green, print_text, print_yellow
from capycli.common.purl_store import PurlStore
from capycli.common.purl_utils import PurlUtils
from sw360 import SW360


class PurlService:
Expand Down
2 changes: 1 addition & 1 deletion capycli/common/script_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@
import jwt
import requests
from cyclonedx.model.bom import Bom
from sw360 import SW360, SW360Error

from capycli.common.print import print_red, print_text, print_yellow
from capycli.main.result_codes import ResultCode
from sw360 import SW360, SW360Error


class ScriptBase:
Expand Down
2 changes: 2 additions & 0 deletions capycli/main/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ def __init__(self) -> None:
Findsources determine the source code for SBOM items
Validate validate an SBOM
BomPackage create a single archive that contains the SBOM and all source and binary files
ComponentCheck Check the SBOM for special components

mapping
ToHtml create a HTML page showing the mapping result
Expand All @@ -72,6 +73,7 @@ def __init__(self) -> None:
CreateReadme create a Readme_OSS
Vulnerabilities show security vulnerabilities of a project
ECC show export control status of a project
ComponentCheck Check the project for special components

Note that each command has also its own help display, i.e. if you enter
`capycli project vulnerabilities -h` you will get a help that only shows the options
Expand Down
2 changes: 1 addition & 1 deletion capycli/project/create_bom.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@
from cyclonedx.model.bom import Bom
from cyclonedx.model.component import Component
from packageurl import PackageURL
from sw360 import SW360Error

import capycli.common.script_base
from capycli import get_logger
from capycli.common.capycli_bom_support import CaPyCliBom, CycloneDxSupport, SbomCreator
from capycli.common.print import print_red, print_text, print_yellow
from capycli.common.purl_utils import PurlUtils
from capycli.main.result_codes import ResultCode
from sw360 import SW360Error

LOG = get_logger(__name__)

Expand Down
2 changes: 1 addition & 1 deletion capycli/project/create_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@

import requests
from cyclonedx.model.bom import Bom
from sw360 import SW360Error

import capycli.common.script_base
from capycli import get_logger
from capycli.common.capycli_bom_support import CaPyCliBom, CycloneDxSupport
from capycli.common.print import print_red, print_text, print_yellow
from capycli.main.result_codes import ResultCode
from sw360 import SW360Error

LOG = get_logger(__name__)

Expand Down
2 changes: 1 addition & 1 deletion capycli/project/find_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
from typing import Any, Dict, Optional

import requests
import sw360

import capycli.common.script_base
import sw360
from capycli.common.print import print_red, print_text, print_yellow
from capycli.main.result_codes import ResultCode

Expand Down
3 changes: 1 addition & 2 deletions capycli/project/get_license_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,12 @@
import sys
from typing import Any, Dict, List

from sw360 import SW360Error

import capycli.common.script_base
from capycli.common.json_support import load_json_file
from capycli.common.print import print_red, print_text, print_yellow
from capycli.common.script_support import ScriptSupport
from capycli.main.result_codes import ResultCode
from sw360 import SW360Error

LOG = capycli.get_logger(__name__)

Expand Down
1 change: 1 addition & 0 deletions capycli/project/handle_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ def run_project_command(args: Any) -> None:
print(" CreateReadme create a Readme_OSS")
print(" Vulnerabilities show security vulnerabilities of a project")
print(" ECC Show export control status of a project")
print(" ComponentCheck Check the project for special components")
return

subcommand = args.command[1].lower()
Expand Down
16 changes: 11 additions & 5 deletions capycli/project/project_component_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@
import sys
from typing import Any

import sw360

import capycli.common.script_base
import sw360
from capycli.bom.component_check import ComponentCheck
from capycli.common.print import print_red, print_text, print_yellow
from capycli.main.result_codes import ResultCode
Expand All @@ -26,13 +25,17 @@ class ProjectComponentCheck(capycli.common.script_base.ScriptBase):
"""
def __init__(self) -> None:
self.component_check = ComponentCheck()
self.verbose = False

def is_dev_dependency(self, name: str) -> bool:
"""Check whether the given component matches any known development dependency."""
dd = self.component_check.component_check_list.get("dev_dependencies", [])
for ecosystem in dd:
for entry in dd.get(ecosystem, []):
if name.lower() == entry.get("name", ""):
to_compare = entry.get("name", "")
if entry.get("namespace", ""):
to_compare = entry.get("namespace", "") + "/" + to_compare
if name.lower() == to_compare.lower():
return True

return False
Expand Down Expand Up @@ -129,11 +132,14 @@ def run(self, args: Any) -> None:
print_text(" --forceerror force an error exit code in case of validation errors or warnings")
return

print_text("Reading component check list from component_checks.json...")
self.verbose = args.verbose
self.component_check.verbose = args.verbose

print_text("Reading component checklist...")
try:
self.component_check.read_component_check_list(args.remote_check_list, args.local_checklist_list)
except Exception as ex:
print_red("Error reading component check list " + repr(ex))
print_red("Error reading component checklist " + repr(ex))
sys.exit(ResultCode.RESULT_GENERAL_ERROR)
if len(self.component_check.component_check_list) > 0:
print_text(" Got component checklist.")
Expand Down
3 changes: 1 addition & 2 deletions capycli/project/show_ecc.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@
import sys
from typing import Any, Dict

import sw360

import capycli.common.script_base
import sw360
from capycli.common.json_support import write_json_to_file
from capycli.common.print import print_green, print_red, print_text, print_yellow
from capycli.main.result_codes import ResultCode
Expand Down
2 changes: 1 addition & 1 deletion capycli/project/show_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@
import sys
from typing import Any, Dict, Optional

import sw360
from colorama import Fore

import capycli.common.json_support
import capycli.common.script_base
import sw360
from capycli.common.print import print_red, print_text, print_yellow
from capycli.main.result_codes import ResultCode

Expand Down
2 changes: 1 addition & 1 deletion capycli/project/show_vulnerabilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@

import requests
from colorama import Fore, Style
from sw360 import SW360Error

import capycli.common.json_support
import capycli.common.script_base
from capycli.common.print import print_green, print_red, print_text, print_yellow
from capycli.main.result_codes import ResultCode
from sw360 import SW360Error

LOG = capycli.get_logger(__name__)

Expand Down
16 changes: 15 additions & 1 deletion tests/fixtures/sbom_for_component_check.json
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,21 @@
],
"purl": "pkg:maven/org.eclipse.jdt/junit@1.2.3",
"type": "library",
"version": "123"
"version": "1.2.3"
},
{
"bom-ref": "pkg:maven/org.eclipse.jdt/junit@1.2.4",
"name": "junit",
"group": "org.eclipse.jdt",
"properties": [
{
"name": "siemens:primaryLanguage",
"value": "Java"
}
],
"purl": "pkg:maven/org.eclipse.jdt/junit@1.2.4",
"type": "library",
"version": "1.2.4"
},
{
"bom-ref": "pkg:npm/gulp@1.2.3",
Expand Down
Loading
Loading