Skip to content

Commit

Permalink
feat: SP-1855 Use scanoss.json as default settings file
Browse files Browse the repository at this point in the history
  • Loading branch information
matiasdaloia committed Nov 18, 2024
1 parent e96142f commit 5dff2e2
Show file tree
Hide file tree
Showing 6 changed files with 170 additions and 73 deletions.
69 changes: 34 additions & 35 deletions src/scanoss/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,13 @@
import sys
import pypac

from scanoss.utils.file import validate_json_file


from .inspection.copyleft import Copyleft
from .inspection.undeclared_component import UndeclaredComponent
from .threadeddependencies import SCOPE
from .scanoss_settings import ScanossSettings
from .scanoss_settings import DEFAULT_SCANOSS_JSON_FILE, ScanossSettings
from .scancodedeps import ScancodeDeps
from .scanner import Scanner
from .scantype import ScanType
Expand Down Expand Up @@ -113,6 +116,11 @@ def setup_args() -> None:
type=str,
help='Settings file to use for scanning (optional - default scanoss.json)',
)
p_scan.add_argument(
'--omit-settings-file',
action='store_true',
help='Omit default settings file (scanoss.json) if it exists',
)


# Sub-command: fingerprint
Expand Down Expand Up @@ -547,51 +555,42 @@ def scan(parser, args):
exit(1)

if args.identify and args.settings:
print_stderr(f'ERROR: Cannot specify both --identify and --settings options.')
print_stderr('ERROR: Cannot specify both --identify and --settings options.')
exit(1)

def is_valid_file(file_path: str) -> bool:
if not os.path.exists(file_path) or not os.path.isfile(file_path):
print_stderr(f'Specified file does not exist or is not a file: {file_path}')
return False
if not Scanner.valid_json_file(file_path):
return False
return True

scan_settings = ScanossSettings(
debug=args.debug, trace=args.trace, quiet=args.quiet
)

if args.identify:
if not is_valid_file(args.identify) or args.ignore:
exit(1)
scan_settings.load_json_file(args.identify).set_file_type(
'legacy'
).set_scan_type('identify')
elif args.ignore:
if not is_valid_file(args.ignore):
exit(1)
scan_settings.load_json_file(args.ignore).set_file_type('legacy').set_scan_type(
'blacklist'
)
elif args.settings:
if not is_valid_file(args.settings):

if args.settings and args.omit_settings_file:
print_stderr('ERROR: Cannot specify both --settings and --omit-file-settings options.')
exit(1)

scan_settings = None

if args.omit_settings_file:
print_stderr('Omit settings file is set. Skipping...')
else:
scan_settings = ScanossSettings(debug=args.debug, trace=args.trace, quiet=args.quiet)
try:
if args.identify:
scan_settings.load_json_file(args.identify).set_file_type('legacy').set_scan_type('identify')
elif args.ignore:
scan_settings.load_json_file(args.ignore).set_file_type('legacy').set_scan_type('blacklist')
else:
scan_settings.load_json_file(args.settings).set_file_type('new').set_scan_type('identify')
except Exception:
exit(1)
scan_settings.load_json_file(args.settings).set_file_type('new').set_scan_type(
'identify'
)

if args.dep:
if not os.path.exists(args.dep) or not os.path.isfile(args.dep):
print_stderr(
f'Specified --dep file does not exist or is not a file: {args.dep}'
)
exit(1)
if not Scanner.valid_json_file(args.dep): # Make sure it's a valid JSON file
try:
validate_json_file(args.dep)
except Exception:
exit(1)
if args.strip_hpsm and not args.hpsm and not args.quiet:
print_stderr(
f'Warning: --strip-hpsm option supplied without enabling HPSM (--hpsm). Ignoring.'
'Warning: --strip-hpsm option supplied without enabling HPSM (--hpsm). Ignoring.'
)

scan_output: str = None
Expand Down Expand Up @@ -684,7 +683,7 @@ def is_valid_file(file_path: str) -> bool:
skip_md5_ids=args.skip_md5,
strip_hpsm_ids=args.strip_hpsm,
strip_snippet_ids=args.strip_snippet,
scan_settings=scan_settings
scan_settings=scan_settings,
)

if args.wfp:
Expand Down
74 changes: 41 additions & 33 deletions src/scanoss/scanner.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
from progress.spinner import Spinner
from pypac.parser import PACFile

from scanoss.utils.file import validate_json_file

from .scanossapi import ScanossApi
from .cyclonedx import CycloneDx
from .spdxlite import SpdxLite
Expand Down Expand Up @@ -96,18 +98,44 @@ class Scanner(ScanossBase):
Handle the scanning of files, snippets and dependencies
"""

def __init__(self, wfp: str = None, scan_output: str = None, output_format: str = 'plain',
debug: bool = False, trace: bool = False, quiet: bool = False, api_key: str = None, url: str = None,
flags: str = None, nb_threads: int = 5,
post_size: int = 32, timeout: int = 180, no_wfp_file: bool = False,
all_extensions: bool = False, all_folders: bool = False, hidden_files_folders: bool = False,
scan_options: int = 7, sc_timeout: int = 600, sc_command: str = None, grpc_url: str = None,
obfuscate: bool = False, ignore_cert_errors: bool = False, proxy: str = None, grpc_proxy: str = None,
ca_cert: str = None, pac: PACFile = None, retry: int = 5, hpsm: bool = False,
skip_size: int = 0, skip_extensions=None, skip_folders=None,
strip_hpsm_ids=None, strip_snippet_ids=None, skip_md5_ids=None,
scan_settings: ScanossSettings = None
):
def __init__(
self,
wfp: str = None,
scan_output: str = None,
output_format: str = 'plain',
debug: bool = False,
trace: bool = False,
quiet: bool = False,
api_key: str = None,
url: str = None,
flags: str = None,
nb_threads: int = 5,
post_size: int = 32,
timeout: int = 180,
no_wfp_file: bool = False,
all_extensions: bool = False,
all_folders: bool = False,
hidden_files_folders: bool = False,
scan_options: int = 7,
sc_timeout: int = 600,
sc_command: str = None,
grpc_url: str = None,
obfuscate: bool = False,
ignore_cert_errors: bool = False,
proxy: str = None,
grpc_proxy: str = None,
ca_cert: str = None,
pac: PACFile = None,
retry: int = 5,
hpsm: bool = False,
skip_size: int = 0,
skip_extensions=None,
skip_folders=None,
strip_hpsm_ids=None,
strip_snippet_ids=None,
skip_md5_ids=None,
scan_settings: ScanossSettings = None
):
"""
Initialise scanning class, including Winnowing, ScanossApi, ThreadedScanning
"""
Expand Down Expand Up @@ -255,27 +283,7 @@ def __count_files_in_wfp_file(wfp_file: str):
if WFP_FILE_START in line:
count += 1
return count

@staticmethod
def valid_json_file(json_file: str) -> bool:
"""
Validate if the specified file is indeed valid JSON
:param: str JSON file to load
:return bool True if valid, False otherwise
"""
if not json_file:
Scanner.print_stderr('ERROR: No JSON file provided to parse.')
return False
if not os.path.isfile(json_file):
Scanner.print_stderr(f'ERROR: JSON file does not exist or is not a file: {json_file}')
return False
try:
with open(json_file) as f:
json.load(f)
except Exception as e:
Scanner.print_stderr(f'Problem parsing JSON file "{json_file}": {e}')
return False
return True


@staticmethod
def version_details() -> str:
Expand Down
27 changes: 22 additions & 5 deletions src/scanoss/scanoss_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,12 @@
from pathlib import Path
from typing import List, TypedDict

from scanoss.utils.file import validate_json_file

from .scanossbase import ScanossBase

DEFAULT_SCANOSS_JSON_FILE = 'scanoss.json'


class BomEntry(TypedDict, total=False):
purl: str
Expand Down Expand Up @@ -61,23 +65,32 @@ def __init__(
self.load_json_file(filepath)

def load_json_file(self, filepath: str):
"""Load the scan settings file
"""Load the scan settings file. If no filepath is provided, scanoss.json will be used as default.
Args:
filepath (str): Path to the SCANOSS settings file
"""

if not filepath:
filepath = DEFAULT_SCANOSS_JSON_FILE

json_file = Path(filepath).resolve()

if not json_file.exists():
self.print_stderr(f'Scan settings file not found: {filepath}')
self.data = {}
if filepath == DEFAULT_SCANOSS_JSON_FILE and not json_file.exists():
self.print_debug(f'Default settings file not found: {filepath}. Skipping...')
return self

try:
validate_json_file(json_file)
except ValueError as e:
return self.print_stderr(f'ERROR: Problem with settings file. {e}')

with open(json_file, 'r') as jsonfile:
self.print_debug(f'Loading scan settings from: {filepath}')
try:
self.data = json.load(jsonfile)
except Exception as e:
self.print_stderr(f'ERROR: Problem parsing input JSON: {e}')
self.print_stderr(f'ERROR: Problem parsing settings file. {e}')
return self

def set_file_type(self, file_type: str):
Expand Down Expand Up @@ -226,3 +239,7 @@ def _remove_duplicates(bom_entries: List[BomEntry]) -> List[BomEntry]:
already_added.add(entry_tuple)
unique_entries.append(entry)
return unique_entries

def is_legacy(self):
"""Check if the settings file is legacy"""
return self.settings_file_type == 'legacy'
5 changes: 5 additions & 0 deletions src/scanoss/scanpostprocessor.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@ def post_process(self):
Returns:
dict: Processed results
"""
if self.scan_settings.is_legacy():
self.print_stderr(
'Legacy settings file detected. Post-processing is not supported for legacy settings file.'
)
return self.results
self._remove_dismissed_files()
self._replace_purls()
return self.results
Expand Down
23 changes: 23 additions & 0 deletions src/scanoss/utils/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"""
SPDX-License-Identifier: MIT
Copyright (c) 2024, SCANOSS
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
"""
45 changes: 45 additions & 0 deletions src/scanoss/utils/file.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import json
import os
import sys


def print_stderr(*args, **kwargs):
"""
Print the given message to STDERR
"""
print(*args, file=sys.stderr, **kwargs)


def is_valid_file(file_path: str) -> bool:
"""Check if the specified file exists and is a file
Args:
file_path (str): The file path
Returns:
bool: True if valid, False otherwise
"""
if not os.path.exists(file_path) or not os.path.isfile(file_path):
print_stderr(f'Specified file does not exist or is not a file: {file_path}')
return False
return True


def validate_json_file(json_file_path: str) -> None:
"""Validate if the specified file is indeed a valid JSON file
Args:
json_file_path (str): The JSON file to validate
Raises:
ValueError: If the JSON file is not valid
"""
if not json_file_path:
raise ValueError('No JSON file provided to parse.')
if not os.path.isfile(json_file_path):
raise ValueError(f'JSON file does not exist or is not a file: {json_file_path}')
try:
with open(json_file_path) as f:
json.load(f)
except Exception as e:
raise ValueError(f'Problem parsing JSON file "{json_file_path}": {e}') from e

0 comments on commit 5dff2e2

Please sign in to comment.