Skip to content

Commit 2c5566b

Browse files
committed
added official support for cryptographic reporting
1 parent f1f90a3 commit 2c5566b

File tree

7 files changed

+171
-47
lines changed

7 files changed

+171
-47
lines changed

.github/workflows/python-local-test.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ jobs:
4242
run: |
4343
which scanoss-py
4444
scanoss-py version
45-
scanoss-py fast
45+
scanoss-py utils fast
4646
scanoss-py scan tests > results.json
4747
id_count=$(cat results.json | grep '"id":' | wc -l)
4848
echo "ID Count: $id_count"
@@ -56,7 +56,7 @@ jobs:
5656
pip install scanoss_winnowing
5757
which scanoss-py
5858
scanoss-py version
59-
scanoss-py fast
59+
scanoss-py utils fast
6060
scanoss-py scan tests > results.json
6161
id_count=$(cat results.json | grep '"id":' | wc -l)
6262
echo "ID Count: $id_count"

.github/workflows/python-publish-pypi.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ jobs:
3636
run: |
3737
which scanoss-py
3838
scanoss-py version
39-
scanoss-py fast
39+
scanoss-py utils fast
4040
scanoss-py scan tests > results.json
4141
id_count=$(cat results.json | grep '"id":' | wc -l)
4242
echo "ID Count: $id_count"
@@ -85,7 +85,7 @@ jobs:
8585
run: |
8686
which scanoss-py
8787
scanoss-py version
88-
scanoss-py fast
88+
scanoss-py utils fast
8989
scanoss-py scan tests > results.json
9090
id_count=$(cat results.json | grep '"id":' | wc -l)
9191
echo "ID Count: $id_count"
@@ -99,7 +99,7 @@ jobs:
9999
pip install scanoss_winnowing
100100
which scanoss-py
101101
scanoss-py version
102-
scanoss-py fast
102+
scanoss-py utils fast
103103
scanoss-py scan tests > results.json
104104
id_count=$(cat results.json | grep '"id":' | wc -l)
105105
echo "ID Count: $id_count"

.github/workflows/python-publish-testpypi.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ jobs:
7979
run: |
8080
which scanoss-py
8181
scanoss-py version
82-
scanoss-py fast
82+
scanoss-py utils fast
8383
scanoss-py scan tests > results.json
8484
id_count=$(cat results.json | grep '"id":' | wc -l)
8585
echo "ID Count: $id_count"
@@ -93,7 +93,7 @@ jobs:
9393
pip install scanoss_winnowing
9494
which scanoss-py
9595
scanoss-py version
96-
scanoss-py fast
96+
scanoss-py utils fast
9797
scanoss-py scan tests > results.json
9898
id_count=$(cat results.json | grep '"id":' | wc -l)
9999
echo "ID Count: $id_count"

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99
### Added
1010
- Upcoming changes...
1111

12+
## [1.5.0] - 2023-03-21
13+
### Added
14+
- Added support for component cryptographic reporting
15+
- `scanoss-py component crypto ...`
16+
1217
## [1.4.2] - 2023-03-09
1318
### Fixed
1419
- Fixed issue with custom certificate when scanning (--ca-cert)
@@ -218,3 +223,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
218223
[1.3.7]: https://github.com/scanoss/scanoss.py/compare/v1.3.6...v1.3.7
219224
[1.4.0]: https://github.com/scanoss/scanoss.py/compare/v1.3.7...v1.4.0
220225
[1.4.2]: https://github.com/scanoss/scanoss.py/compare/v1.4.0...v1.4.2
226+
[1.5.0]: https://github.com/scanoss/scanoss.py/compare/v1.4.2...v1.5.0

src/scanoss/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,4 @@
2222
THE SOFTWARE.
2323
"""
2424

25-
__version__ = '1.4.2'
25+
__version__ = '1.5.0'

src/scanoss/cli.py

Lines changed: 7 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
from .cyclonedx import CycloneDx
3535
from .spdxlite import SpdxLite
3636
from .csvoutput import CsvOutput
37+
from .components import Components
3738
from . import __version__
3839
from .scanner import FAST_WINNOWING
3940

@@ -173,6 +174,8 @@ def setup_args() -> None:
173174
c_crypto.add_argument('--purl', '-p', type=str, nargs="*", help='Package URL - PURL to process.')
174175
c_crypto.add_argument('--input', '-i', type=str, help='Input file name')
175176
c_crypto.add_argument('--output','-o', type=str, help='Output result file name (optional - default stdout).')
177+
c_crypto.add_argument('--timeout', '-M', type=int, default=600,
178+
help='Timeout (in seconds) for API communication (optional - default 600)')
176179

177180
# Sub-command: utils
178181
p_util = subparsers.add_parser('utils', aliases=['ut'],
@@ -687,9 +690,6 @@ def comp_crypto(parser, args):
687690
args: Namespace
688691
Parsed arguments
689692
"""
690-
from .scanossgrpc import ScanossGrpc
691-
import json
692-
693693
if (not args.purl and not args.input) or (args.purl and args.input):
694694
print_stderr('Please specify an input file or purl to decorate (--purl or --input)')
695695
parser.parse_args([args.subparser, args.subparsercmd, '-h'])
@@ -698,43 +698,11 @@ def comp_crypto(parser, args):
698698
print_stderr(f'Error: Certificate file does not exist: {args.ca_cert}.')
699699
exit(1)
700700
pac_file = get_pac_file(args.pac)
701-
file = sys.stdout
702-
if args.output:
703-
file = open(args.output, 'w')
704-
success = False
705-
purl_request = {}
706-
if args.input:
707-
if not os.path.isfile(args.input):
708-
print_stderr(f'ERROR: JSON file does not exist or is not a file: {json_file}')
709-
exit(1)
710-
with open(args.input, 'r') as f:
711-
try:
712-
purl_request = json.loads(f.read())
713-
except Exception as e:
714-
print_stderr(f'ERROR: Problem parsing input JSON: {e}')
715-
elif args.purl:
716-
purls = []
717-
for p in args.purl:
718-
purls.append({'purl': p})
719-
purl_request = {'purls': purls}
720-
721-
purl_count = len(purl_request.get('purls'))
722-
if purl_count == 0:
723-
print_stderr('No PURLs supplied to get cryptographic algorithms.')
724-
725-
grpc_api = ScanossGrpc(url=args.api2url, debug=args.debug, trace=args.trace, quiet=args.quiet, api_key=args.key,
726-
ca_cert=args.ca_cert, proxy=args.proxy, grpc_proxy=args.grpc_proxy, pac=pac_file
727-
)
728-
resp = grpc_api.get_crypto_json(purl_request)
729-
# print_stderr(f'Crypto Algo Resp: {resp}')
730-
if resp:
731-
print(json.dumps(resp, indent=2, sort_keys=True), file=file)
732-
success = True
733-
if args.output:
734-
file.close()
735-
if not success:
701+
comps = Components(debug=args.debug, trace=args.trace, quiet=args.quiet, grpc_url=args.api2url, api_key=args.key,
702+
ca_cert=args.ca_cert, proxy=args.proxy, grpc_proxy=args.grpc_proxy, pac=pac_file,
703+
timeout=args.timeout)
704+
if not comps.get_crypto_details(args.input, args.purl, args.output):
736705
exit(1)
737-
738706
def main():
739707
"""
740708
Run the ScanOSS CLI

src/scanoss/components.py

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
"""
2+
SPDX-License-Identifier: MIT
3+
4+
Copyright (c) 2023, SCANOSS
5+
6+
Permission is hereby granted, free of charge, to any person obtaining a copy
7+
of this software and associated documentation files (the "Software"), to deal
8+
in the Software without restriction, including without limitation the rights
9+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
copies of the Software, and to permit persons to whom the Software is
11+
furnished to do so, subject to the following conditions:
12+
13+
The above copyright notice and this permission notice shall be included in
14+
all copies or substantial portions of the Software.
15+
16+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22+
THE SOFTWARE.
23+
"""
24+
import json
25+
import os
26+
import sys
27+
from typing import TextIO
28+
29+
from pypac.parser import PACFile
30+
31+
from .scanner import Scanner
32+
from .scanossbase import ScanossBase
33+
from .scanossgrpc import ScanossGrpc
34+
35+
36+
class Components(ScanossBase):
37+
"""
38+
Class for Component functionality
39+
"""
40+
41+
def __init__(self, debug: bool = False, trace: bool = False, quiet: bool = False,
42+
grpc_url: str = None, api_key: str = None, timeout: int = 600,
43+
proxy: str = None, grpc_proxy: str = None, ca_cert: str = None, pac: PACFile = None
44+
):
45+
"""
46+
Handle all component style requests
47+
48+
:param debug: Debug
49+
:param trace: Trace
50+
:param quiet: Quiet
51+
:param grpc_url: gRPC URL
52+
:param api_key: API Key
53+
:param timeout: Timeout for requests (default 600)
54+
:param proxy: Proxy to use (optional)
55+
:param grpc_proxy: Specific gRPC proxy (optional)
56+
:param ca_cert: TLS client certificate (optional)
57+
:param pac: Proxy Auto-Config file (optional)
58+
"""
59+
super().__init__(debug, trace, quiet)
60+
ver_details = Scanner.version_details()
61+
self.grpc_api = ScanossGrpc(url=grpc_url, debug=debug, quiet=quiet, trace=trace, api_key=api_key,
62+
ver_details=ver_details, ca_cert=ca_cert, proxy=proxy, pac=pac,
63+
grpc_proxy=grpc_proxy, timeout=timeout)
64+
65+
def load_purls(self, json_file: str = None, purls: [] = None) -> dict:
66+
"""
67+
Load the specified purls and return a dictionary
68+
69+
:param json_file: JSON PURL file (optional)
70+
:param purls: list of PURLs (optional)
71+
:return: PURL Request dictionary
72+
"""
73+
if json_file:
74+
if not os.path.isfile(json_file) or not os.access(json_file, os.R_OK):
75+
self.print_stderr(f'ERROR: JSON file does not exist, is not a file, or is not readable: {json_file}')
76+
return None
77+
with open(json_file, 'r') as f:
78+
try:
79+
purl_request = json.loads(f.read())
80+
except Exception as e:
81+
self.print_stderr(f'ERROR: Problem parsing input JSON: {e}')
82+
return None
83+
elif purls:
84+
parsed_purls = []
85+
for p in purls:
86+
parsed_purls.append({'purl': p})
87+
purl_request = {'purls': parsed_purls}
88+
else:
89+
self.print_stderr('ERROR: No purls specified to process.')
90+
return None
91+
purl_count = len(purl_request.get('purls', []))
92+
self.print_debug(f'Parsed Purls ({purl_count}): {purl_request}')
93+
if purl_count == 0:
94+
self.print_stderr('ERROR: No PURLs parsed from request.')
95+
return None
96+
return purl_request
97+
98+
def _open_file_or_sdtout(self, filename):
99+
"""
100+
Open the given filename if requested, otherwise return STDOUT
101+
102+
:param filename:
103+
:return:
104+
"""
105+
file = sys.stdout
106+
if filename:
107+
try:
108+
file = open(filename, 'w')
109+
except OSError as e:
110+
self.print_stderr(f'ERROR: Failed to open output file {filename}: {e}')
111+
return None
112+
return file
113+
114+
def _close_file(self, filename: str = None, file: TextIO = None) -> None:
115+
"""
116+
Close the file descriptor if its defined
117+
118+
:param filename: filename
119+
:param file: file IO object
120+
:return: None
121+
"""
122+
if filename and file:
123+
self.print_trace(f'Closing file: {filename}')
124+
file.close()
125+
126+
def get_crypto_details(self, json_file: str = None, purls: [] = None, output_file: str = None) -> bool:
127+
"""
128+
Retrieve the cryptographic details for the supplied PURLs
129+
130+
:param json_file: PURL JSON request file (optional)
131+
:param purls: PURL request array (optional)
132+
:param output_file: output filename (optional). Default: STDOUT
133+
:return: True on success, False otherwise
134+
"""
135+
success = False
136+
purls_request = self.load_purls(json_file, purls)
137+
if purls_request is None or len(purls_request) == 0:
138+
return False
139+
file = self._open_file_or_sdtout(output_file)
140+
if file is None:
141+
return False
142+
self.print_msg('Sending PURLs to Crypto API for decoration...')
143+
response = self.grpc_api.get_crypto_json(purls_request)
144+
if response:
145+
print(json.dumps(response, indent=2, sort_keys=True), file=file)
146+
success = True
147+
if output_file:
148+
self.print_msg(f'Results written to: {output_file}')
149+
self._close_file(output_file, file)
150+
return success

0 commit comments

Comments
 (0)