scanoss 1.40.0__tar.gz → 1.41.0__tar.gz
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.
- {scanoss-1.40.0/src/scanoss.egg-info → scanoss-1.41.0}/PKG-INFO +1 -1
- {scanoss-1.40.0 → scanoss-1.41.0}/README.md +5 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/__init__.py +1 -1
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/cli.py +22 -9
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/constants.py +3 -0
- scanoss-1.41.0/src/scanoss/data/build_date.txt +1 -0
- scanoss-1.41.0/src/scanoss/data/osadl-copyleft.json +133 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/filecount.py +37 -38
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/gitlabqualityreport.py +33 -4
- scanoss-1.41.0/src/scanoss/inspection/policy_check/dependency_track/__init__.py +0 -0
- {scanoss-1.40.0/src/scanoss/inspection → scanoss-1.41.0/src/scanoss/inspection/policy_check}/dependency_track/project_violation.py +24 -24
- {scanoss-1.40.0/src/scanoss/inspection → scanoss-1.41.0/src/scanoss/inspection/policy_check}/policy_check.py +22 -18
- scanoss-1.41.0/src/scanoss/inspection/policy_check/scanoss/__init__.py +0 -0
- {scanoss-1.40.0/src/scanoss/inspection/raw → scanoss-1.41.0/src/scanoss/inspection/policy_check/scanoss}/copyleft.py +42 -36
- {scanoss-1.40.0/src/scanoss/inspection/raw → scanoss-1.41.0/src/scanoss/inspection/policy_check/scanoss}/undeclared_component.py +30 -29
- scanoss-1.41.0/src/scanoss/inspection/summary/__init__.py +0 -0
- {scanoss-1.40.0/src/scanoss/inspection/raw → scanoss-1.41.0/src/scanoss/inspection/summary}/component_summary.py +34 -9
- {scanoss-1.40.0/src/scanoss/inspection/raw → scanoss-1.41.0/src/scanoss/inspection/summary}/license_summary.py +46 -44
- {scanoss-1.40.0/src/scanoss/inspection/raw → scanoss-1.41.0/src/scanoss/inspection/summary}/match_summary.py +51 -0
- scanoss-1.41.0/src/scanoss/inspection/utils/license_utils.py +123 -0
- scanoss-1.40.0/src/scanoss/inspection/raw/raw_base.py → scanoss-1.41.0/src/scanoss/inspection/utils/scan_result_processor.py +47 -59
- scanoss-1.41.0/src/scanoss/osadl.py +125 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/scanner.py +191 -189
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/scanners/folder_hasher.py +24 -24
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/scanners/scanner_hfh.py +20 -15
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/threadedscanning.py +10 -0
- {scanoss-1.40.0 → scanoss-1.41.0/src/scanoss.egg-info}/PKG-INFO +1 -1
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss.egg-info/SOURCES.txt +15 -9
- scanoss-1.41.0/tests/test_osadl.py +102 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/tests/test_policy_inspect.py +316 -63
- scanoss-1.40.0/src/scanoss/data/build_date.txt +0 -1
- scanoss-1.40.0/src/scanoss/inspection/utils/license_utils.py +0 -137
- {scanoss-1.40.0 → scanoss-1.41.0}/LICENSE +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/PACKAGE.md +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/pyproject.toml +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/setup.cfg +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/protoc_gen_swagger/__init__.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/protoc_gen_swagger/options/__init__.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/protoc_gen_swagger/options/annotations_pb2.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/protoc_gen_swagger/options/annotations_pb2.pyi +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/protoc_gen_swagger/options/annotations_pb2_grpc.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/protoc_gen_swagger/options/openapiv2_pb2.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/protoc_gen_swagger/options/openapiv2_pb2.pyi +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/protoc_gen_swagger/options/openapiv2_pb2_grpc.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/__init__.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/common/__init__.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/common/v2/__init__.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/common/v2/scanoss_common_pb2.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/common/v2/scanoss_common_pb2_grpc.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/components/__init__.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/components/v2/__init__.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/components/v2/scanoss_components_pb2.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/components/v2/scanoss_components_pb2_grpc.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/cryptography/v2/scanoss_cryptography_pb2.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/cryptography/v2/scanoss_cryptography_pb2_grpc.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/dependencies/__init__.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/dependencies/v2/__init__.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/dependencies/v2/scanoss_dependencies_pb2.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/dependencies/v2/scanoss_dependencies_pb2_grpc.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/geoprovenance/__init__.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/geoprovenance/v2/__init__.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/geoprovenance/v2/scanoss_geoprovenance_pb2.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/geoprovenance/v2/scanoss_geoprovenance_pb2_grpc.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/licenses/__init__.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/licenses/v2/__init__.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/licenses/v2/scanoss_licenses_pb2.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/licenses/v2/scanoss_licenses_pb2_grpc.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/scanning/__init__.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/scanning/v2/__init__.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/scanning/v2/scanoss_scanning_pb2.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/scanning/v2/scanoss_scanning_pb2_grpc.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/semgrep/__init__.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/semgrep/v2/__init__.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/semgrep/v2/scanoss_semgrep_pb2.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/semgrep/v2/scanoss_semgrep_pb2_grpc.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/vulnerabilities/__init__.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/vulnerabilities/v2/__init__.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/vulnerabilities/v2/scanoss_vulnerabilities_pb2.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/api/vulnerabilities/v2/scanoss_vulnerabilities_pb2_grpc.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/components.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/cryptography.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/csvoutput.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/cyclonedx.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/data/scanoss-settings-schema.json +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/data/spdx-exceptions.json +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/data/spdx-licenses.json +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/delta.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/export/__init__.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/export/dependency_track.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/file_filters.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/inspection/__init__.py +0 -0
- {scanoss-1.40.0/src/scanoss/inspection/raw → scanoss-1.41.0/src/scanoss/inspection/policy_check}/__init__.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/inspection/utils/file_utils.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/inspection/utils/markdown_utils.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/results.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/scancodedeps.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/scanners/__init__.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/scanners/container_scanner.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/scanners/scanner_config.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/scanoss_settings.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/scanossapi.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/scanossbase.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/scanossgrpc.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/scanpostprocessor.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/scantype.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/services/dependency_track_service.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/spdxlite.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/threadeddependencies.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/utils/__init__.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/utils/abstract_presenter.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/utils/crc64.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/utils/file.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/utils/scanoss_scan_results_utils.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/utils/simhash.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss/winnowing.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss.egg-info/dependency_links.txt +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss.egg-info/entry_points.txt +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss.egg-info/requires.txt +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/src/scanoss.egg-info/top_level.txt +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/tests/test_csv_output.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/tests/test_file_filters.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/tests/test_scan_post_processor.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/tests/test_spdxlite.py +0 -0
- {scanoss-1.40.0 → scanoss-1.41.0}/tests/test_winnowing.py +0 -0
|
@@ -135,3 +135,8 @@ Details of major changes to the library can be found in [CHANGELOG.md](CHANGELOG
|
|
|
135
135
|
|
|
136
136
|
## Background
|
|
137
137
|
Details about the Winnowing algorithm used for scanning can be found [here](WINNOWING.md).
|
|
138
|
+
|
|
139
|
+
## Dataset License Notice
|
|
140
|
+
This application is licensed under the MIT License. In addition, it includes an unmodified copy of the OSADL copyleft license dataset ([osadl-copyleft.json](src/scanoss/data/osadl-copyleft.json)) which is licensed under the [Creative Commons Attribution 4.0 International license (CC-BY-4.0)](https://creativecommons.org/licenses/by/4.0/) by the [Open Source Automation Development Lab (OSADL) eG](https://www.osadl.org/).
|
|
141
|
+
|
|
142
|
+
**Attribution:** A project by the Open Source Automation Development Lab (OSADL) eG. Original source: [https://www.osadl.org/fileadmin/checklists/copyleft.json](https://www.osadl.org/fileadmin/checklists/copyleft.json)
|
|
@@ -35,12 +35,6 @@ import pypac
|
|
|
35
35
|
from scanoss.cryptography import Cryptography, create_cryptography_config_from_args
|
|
36
36
|
from scanoss.delta import Delta
|
|
37
37
|
from scanoss.export.dependency_track import DependencyTrackExporter
|
|
38
|
-
from scanoss.inspection.dependency_track.project_violation import (
|
|
39
|
-
DependencyTrackProjectViolationPolicyCheck,
|
|
40
|
-
)
|
|
41
|
-
from scanoss.inspection.raw.component_summary import ComponentSummary
|
|
42
|
-
from scanoss.inspection.raw.license_summary import LicenseSummary
|
|
43
|
-
from scanoss.inspection.raw.match_summary import MatchSummary
|
|
44
38
|
from scanoss.scanners.container_scanner import (
|
|
45
39
|
DEFAULT_SYFT_COMMAND,
|
|
46
40
|
DEFAULT_SYFT_TIMEOUT,
|
|
@@ -61,6 +55,7 @@ from . import __version__
|
|
|
61
55
|
from .components import Components
|
|
62
56
|
from .constants import (
|
|
63
57
|
DEFAULT_API_TIMEOUT,
|
|
58
|
+
DEFAULT_COPYLEFT_LICENSE_SOURCES,
|
|
64
59
|
DEFAULT_HFH_DEPTH,
|
|
65
60
|
DEFAULT_HFH_MIN_ACCEPTED_SCORE,
|
|
66
61
|
DEFAULT_HFH_RANK_THRESHOLD,
|
|
@@ -70,13 +65,20 @@ from .constants import (
|
|
|
70
65
|
DEFAULT_TIMEOUT,
|
|
71
66
|
MIN_TIMEOUT,
|
|
72
67
|
PYTHON_MAJOR_VERSION,
|
|
68
|
+
VALID_LICENSE_SOURCES,
|
|
73
69
|
)
|
|
74
70
|
from .csvoutput import CsvOutput
|
|
75
71
|
from .cyclonedx import CycloneDx
|
|
76
72
|
from .filecount import FileCount
|
|
77
73
|
from .gitlabqualityreport import GitLabQualityReport
|
|
78
|
-
from .inspection.
|
|
79
|
-
|
|
74
|
+
from .inspection.policy_check.dependency_track.project_violation import (
|
|
75
|
+
DependencyTrackProjectViolationPolicyCheck,
|
|
76
|
+
)
|
|
77
|
+
from .inspection.policy_check.scanoss.copyleft import Copyleft
|
|
78
|
+
from .inspection.policy_check.scanoss.undeclared_component import UndeclaredComponent
|
|
79
|
+
from .inspection.summary.component_summary import ComponentSummary
|
|
80
|
+
from .inspection.summary.license_summary import LicenseSummary
|
|
81
|
+
from .inspection.summary.match_summary import MatchSummary
|
|
80
82
|
from .results import Results
|
|
81
83
|
from .scancodedeps import ScancodeDeps
|
|
82
84
|
from .scanner import FAST_WINNOWING, Scanner
|
|
@@ -699,6 +701,17 @@ def setup_args() -> None: # noqa: PLR0912, PLR0915
|
|
|
699
701
|
p.add_argument('--exclude', help='Licenses to exclude from analysis (comma-separated list)')
|
|
700
702
|
p.add_argument('--explicit', help='Use only these specific licenses for analysis (comma-separated list)')
|
|
701
703
|
|
|
704
|
+
# License source filtering
|
|
705
|
+
for p in [p_inspect_raw_copyleft, p_inspect_legacy_copyleft]:
|
|
706
|
+
p.add_argument(
|
|
707
|
+
'-ls', '--license-sources',
|
|
708
|
+
action='extend',
|
|
709
|
+
nargs='+',
|
|
710
|
+
choices=VALID_LICENSE_SOURCES,
|
|
711
|
+
help=f'Specify which license sources to check for copyleft violations. Each license object in scan results '
|
|
712
|
+
f'has a source field indicating its origin. Default: {", ".join(DEFAULT_COPYLEFT_LICENSE_SOURCES)}',
|
|
713
|
+
)
|
|
714
|
+
|
|
702
715
|
# Common options for (legacy) copyleft and undeclared component inspection
|
|
703
716
|
for p in [p_inspect_raw_copyleft, p_inspect_raw_undeclared, p_inspect_legacy_copyleft, p_inspect_legacy_undeclared]:
|
|
704
717
|
p.add_argument('-i', '--input', nargs='?', help='Path to scan results file to analyse')
|
|
@@ -1752,8 +1765,8 @@ def inspect_copyleft(parser, args):
|
|
|
1752
1765
|
include=args.include, # Additional licenses to check
|
|
1753
1766
|
exclude=args.exclude, # Licenses to ignore
|
|
1754
1767
|
explicit=args.explicit, # Explicit license list
|
|
1768
|
+
license_sources=args.license_sources, # License sources to check (list)
|
|
1755
1769
|
)
|
|
1756
|
-
|
|
1757
1770
|
# Execute inspection and exit with appropriate status code
|
|
1758
1771
|
status, _ = i_copyleft.run()
|
|
1759
1772
|
sys.exit(status)
|
|
@@ -17,3 +17,6 @@ DEFAULT_HFH_RANK_THRESHOLD = 5
|
|
|
17
17
|
DEFAULT_HFH_DEPTH = 1
|
|
18
18
|
DEFAULT_HFH_RECURSIVE_THRESHOLD = 0.8
|
|
19
19
|
DEFAULT_HFH_MIN_ACCEPTED_SCORE = 0.15
|
|
20
|
+
|
|
21
|
+
VALID_LICENSE_SOURCES = ['component_declared', 'license_file', 'file_header', 'file_spdx_tag', 'scancode']
|
|
22
|
+
DEFAULT_COPYLEFT_LICENSE_SOURCES = ['component_declared', 'license_file']
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
date: 20251117160705, utime: 1763395625
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
{
|
|
2
|
+
"title": "OSADL Open Source License Obligations Checklist (https:\/\/www.osadl.org\/Checklists)",
|
|
3
|
+
"license": "Creative Commons Attribution 4.0 International license (CC-BY-4.0)",
|
|
4
|
+
"attribution": "A project by the Open Source Automation Development Lab (OSADL) eG. For further information about the project see the description at www.osadl.org\/checklists.",
|
|
5
|
+
"copyright": "(C) 2017 - 2024 Open Source Automation Development Lab (OSADL) eG and contributors, info@osadl.org",
|
|
6
|
+
"disclaimer": "The checklists and particularly the copyleft data have been assembled with maximum diligence and care; however, the authors do not warrant nor can be held liable in any way for its correctness, usefulness, merchantibility or fitness for a particular purpose as far as permissible by applicable law. Anyone who uses the information does this on his or her sole responsibility. For any individual legal advice, it is recommended to contact a lawyer.",
|
|
7
|
+
"timeformat": "%Y-%m-%dT%H:%M:%S%z",
|
|
8
|
+
"timestamp": "2025-10-30T11:23:00+0000",
|
|
9
|
+
"copyleft":
|
|
10
|
+
{
|
|
11
|
+
"0BSD": "No",
|
|
12
|
+
"AFL-2.0": "No",
|
|
13
|
+
"AFL-2.1": "No",
|
|
14
|
+
"AFL-3.0": "No",
|
|
15
|
+
"AGPL-3.0-only": "Yes",
|
|
16
|
+
"AGPL-3.0-or-later": "Yes",
|
|
17
|
+
"Apache-1.0": "No",
|
|
18
|
+
"Apache-1.1": "No",
|
|
19
|
+
"Apache-2.0": "No",
|
|
20
|
+
"APSL-2.0": "Yes (restricted)",
|
|
21
|
+
"Artistic-1.0": "No",
|
|
22
|
+
"Artistic-1.0-Perl": "No",
|
|
23
|
+
"Artistic-2.0": "No",
|
|
24
|
+
"Bitstream-Vera": "No",
|
|
25
|
+
"blessing": "No",
|
|
26
|
+
"BlueOak-1.0.0": "No",
|
|
27
|
+
"BSD-1-Clause": "No",
|
|
28
|
+
"BSD-2-Clause": "No",
|
|
29
|
+
"BSD-2-Clause-Patent": "No",
|
|
30
|
+
"BSD-3-Clause": "No",
|
|
31
|
+
"BSD-3-Clause-Open-MPI": "No",
|
|
32
|
+
"BSD-4-Clause": "No",
|
|
33
|
+
"BSD-4-Clause-UC": "No",
|
|
34
|
+
"BSD-4.3TAHOE": "No",
|
|
35
|
+
"BSD-Source-Code": "No",
|
|
36
|
+
"BSL-1.0": "No",
|
|
37
|
+
"bzip2-1.0.5": "No",
|
|
38
|
+
"bzip2-1.0.6": "No",
|
|
39
|
+
"CC-BY-2.5": "No",
|
|
40
|
+
"CC-BY-3.0": "No",
|
|
41
|
+
"CDDL-1.0": "Yes (restricted)",
|
|
42
|
+
"CDDL-1.1": "Yes (restricted)",
|
|
43
|
+
"CPL-1.0": "Yes",
|
|
44
|
+
"curl": "No",
|
|
45
|
+
"ECL-1.0": "No",
|
|
46
|
+
"ECL-2.0": "No",
|
|
47
|
+
"EFL-2.0": "No",
|
|
48
|
+
"EPL-1.0": "Yes",
|
|
49
|
+
"EPL-2.0": "Yes (restricted)",
|
|
50
|
+
"EUPL-1.1": "Yes",
|
|
51
|
+
"EUPL-1.2": "Yes",
|
|
52
|
+
"FSFAP": "No",
|
|
53
|
+
"FSFUL": "No",
|
|
54
|
+
"FSFULLR": "No",
|
|
55
|
+
"FSFULLRWD": "No",
|
|
56
|
+
"FTL": "No",
|
|
57
|
+
"GPL-1.0-only": "Yes",
|
|
58
|
+
"GPL-1.0-or-later": "Yes",
|
|
59
|
+
"GPL-2.0-only": "Yes",
|
|
60
|
+
"GPL-2.0-only WITH Classpath-exception-2.0": "Yes (restricted)",
|
|
61
|
+
"GPL-2.0-or-later": "Yes",
|
|
62
|
+
"GPL-3.0-only": "Yes",
|
|
63
|
+
"GPL-3.0-or-later": "Yes",
|
|
64
|
+
"HPND": "No",
|
|
65
|
+
"IBM-pibs": "No",
|
|
66
|
+
"ICU": "No",
|
|
67
|
+
"IJG": "No",
|
|
68
|
+
"ImageMagick": "No",
|
|
69
|
+
"Info-ZIP": "No",
|
|
70
|
+
"IPL-1.0": "Yes",
|
|
71
|
+
"ISC": "No",
|
|
72
|
+
"JasPer-2.0": "No",
|
|
73
|
+
"LGPL-2.0-only": "Yes (restricted)",
|
|
74
|
+
"LGPL-2.0-or-later": "Yes (restricted)",
|
|
75
|
+
"LGPL-2.1-only": "Yes (restricted)",
|
|
76
|
+
"LGPL-2.1-or-later": "Yes (restricted)",
|
|
77
|
+
"LGPL-3.0-only": "Yes (restricted)",
|
|
78
|
+
"LGPL-3.0-or-later": "Yes (restricted)",
|
|
79
|
+
"Libpng": "No",
|
|
80
|
+
"libpng-2.0": "No",
|
|
81
|
+
"libtiff": "No",
|
|
82
|
+
"LicenseRef-scancode-bsla-no-advert": "No",
|
|
83
|
+
"LicenseRef-scancode-info-zip-2003-05": "No",
|
|
84
|
+
"LicenseRef-scancode-ppp": "No",
|
|
85
|
+
"Minpack": "No",
|
|
86
|
+
"MirOS": "No",
|
|
87
|
+
"MIT": "No",
|
|
88
|
+
"MIT-0": "No",
|
|
89
|
+
"MIT-CMU": "No",
|
|
90
|
+
"MPL-1.1": "Yes (restricted)",
|
|
91
|
+
"MPL-2.0": "Yes (restricted)",
|
|
92
|
+
"MPL-2.0-no-copyleft-exception": "Yes (restricted)",
|
|
93
|
+
"MS-PL": "Questionable",
|
|
94
|
+
"MS-RL": "Yes (restricted)",
|
|
95
|
+
"NBPL-1.0": "No",
|
|
96
|
+
"NCSA": "No",
|
|
97
|
+
"NTP": "No",
|
|
98
|
+
"OFL-1.1": "Yes (restricted)",
|
|
99
|
+
"OGC-1.0": "No",
|
|
100
|
+
"OLDAP-2.8": "No",
|
|
101
|
+
"OpenSSL": "Questionable",
|
|
102
|
+
"OSL-3.0": "Yes",
|
|
103
|
+
"PHP-3.01": "No",
|
|
104
|
+
"PostgreSQL": "No",
|
|
105
|
+
"PSF-2.0": "No",
|
|
106
|
+
"Python-2.0": "No",
|
|
107
|
+
"Qhull": "No",
|
|
108
|
+
"RSA-MD": "No",
|
|
109
|
+
"Saxpath": "No",
|
|
110
|
+
"SGI-B-2.0": "No",
|
|
111
|
+
"Sleepycat": "Yes",
|
|
112
|
+
"SMLNJ": "No",
|
|
113
|
+
"Spencer-86": "No",
|
|
114
|
+
"SSH-OpenSSH": "No",
|
|
115
|
+
"SSH-short": "No",
|
|
116
|
+
"SunPro": "No",
|
|
117
|
+
"Ubuntu-font-1.0": "Yes (restricted)",
|
|
118
|
+
"Unicode-3.0": "No",
|
|
119
|
+
"Unicode-DFS-2015": "No",
|
|
120
|
+
"Unicode-DFS-2016": "No",
|
|
121
|
+
"Unlicense": "No",
|
|
122
|
+
"UPL-1.0": "No",
|
|
123
|
+
"W3C": "No",
|
|
124
|
+
"W3C-19980720": "No",
|
|
125
|
+
"W3C-20150513": "No",
|
|
126
|
+
"WTFPL": "No",
|
|
127
|
+
"X11": "No",
|
|
128
|
+
"XFree86-1.1": "No",
|
|
129
|
+
"Zlib": "No",
|
|
130
|
+
"zlib-acknowledgement": "No",
|
|
131
|
+
"ZPL-2.0": "No"
|
|
132
|
+
}
|
|
133
|
+
}
|
|
@@ -26,6 +26,7 @@ import csv
|
|
|
26
26
|
import os
|
|
27
27
|
import pathlib
|
|
28
28
|
import sys
|
|
29
|
+
from contextlib import nullcontext
|
|
29
30
|
|
|
30
31
|
from progress.spinner import Spinner
|
|
31
32
|
|
|
@@ -105,48 +106,46 @@ class FileCount(ScanossBase):
|
|
|
105
106
|
"""
|
|
106
107
|
success = True
|
|
107
108
|
if not scan_dir:
|
|
108
|
-
raise Exception(
|
|
109
|
+
raise Exception('ERROR: Please specify a folder to scan')
|
|
109
110
|
if not os.path.exists(scan_dir) or not os.path.isdir(scan_dir):
|
|
110
111
|
raise Exception(f'ERROR: Specified folder does not exist or is not a folder: {scan_dir}')
|
|
111
112
|
|
|
112
113
|
self.print_msg(f'Searching {scan_dir} for files to count...')
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
if spinner:
|
|
149
|
-
spinner.finish()
|
|
114
|
+
spinner_ctx = Spinner('Searching ') if (not self.quiet and self.isatty) else nullcontext()
|
|
115
|
+
|
|
116
|
+
with spinner_ctx as spinner:
|
|
117
|
+
file_types = {}
|
|
118
|
+
file_count = 0
|
|
119
|
+
file_size = 0
|
|
120
|
+
for root, dirs, files in os.walk(scan_dir):
|
|
121
|
+
self.print_trace(f'U Root: {root}, Dirs: {dirs}, Files {files}')
|
|
122
|
+
dirs[:] = self.__filter_dirs(dirs) # Strip out unwanted directories
|
|
123
|
+
filtered_files = self.__filter_files(files) # Strip out unwanted files
|
|
124
|
+
self.print_trace(f'F Root: {root}, Dirs: {dirs}, Files {filtered_files}')
|
|
125
|
+
for file in filtered_files: # Cycle through each filtered file
|
|
126
|
+
path = os.path.join(root, file)
|
|
127
|
+
f_size = 0
|
|
128
|
+
try:
|
|
129
|
+
f_size = os.stat(path).st_size
|
|
130
|
+
except Exception as e:
|
|
131
|
+
self.print_trace(f'Ignoring missing symlink file: {file} ({e})') # broken symlink
|
|
132
|
+
if f_size > 0: # Ignore broken links and empty files
|
|
133
|
+
file_count = file_count + 1
|
|
134
|
+
file_size = file_size + f_size
|
|
135
|
+
f_suffix = pathlib.Path(file).suffix
|
|
136
|
+
if not f_suffix or f_suffix == '':
|
|
137
|
+
f_suffix = 'no_suffix'
|
|
138
|
+
self.print_trace(f'Counting {path} ({f_suffix} - {f_size})..')
|
|
139
|
+
fc = file_types.get(f_suffix)
|
|
140
|
+
if not fc:
|
|
141
|
+
fc = [1, f_size]
|
|
142
|
+
else:
|
|
143
|
+
fc[0] = fc[0] + 1
|
|
144
|
+
fc[1] = fc[1] + f_size
|
|
145
|
+
file_types[f_suffix] = fc
|
|
146
|
+
if spinner:
|
|
147
|
+
spinner.next()
|
|
148
|
+
# End for loop
|
|
150
149
|
self.print_stderr(f'Found {file_count:,.0f} files with a total size of {file_size / (1 << 20):,.2f} MB.')
|
|
151
150
|
if file_types:
|
|
152
151
|
csv_dict = []
|
|
@@ -74,16 +74,21 @@ class GitLabQualityReport(ScanossBase):
|
|
|
74
74
|
Initialise the GitLabCodeQuality class
|
|
75
75
|
"""
|
|
76
76
|
super().__init__(debug, trace, quiet)
|
|
77
|
+
self.print_trace(f"GitLabQualityReport initialized with debug={debug}, trace={trace}, quiet={quiet}")
|
|
77
78
|
|
|
78
79
|
|
|
79
80
|
def _get_code_quality(self, file_name: str, result: dict) -> CodeQuality or None:
|
|
81
|
+
self.print_trace(f"_get_code_quality called for file: {file_name}")
|
|
82
|
+
self.print_trace(f"Processing result: {result}")
|
|
83
|
+
|
|
80
84
|
if not result.get('file_hash'):
|
|
81
85
|
self.print_debug(f"Warning: no hash found for result: {result}")
|
|
82
86
|
return None
|
|
83
87
|
|
|
84
88
|
if result.get('id') == 'file':
|
|
89
|
+
self.print_debug(f"Processing file match for: {file_name}")
|
|
85
90
|
description = f"File match found in: {file_name}"
|
|
86
|
-
|
|
91
|
+
code_quality = CodeQuality(
|
|
87
92
|
description=description,
|
|
88
93
|
check_name=file_name,
|
|
89
94
|
fingerprint=result.get('file_hash'),
|
|
@@ -95,17 +100,21 @@ class GitLabQualityReport(ScanossBase):
|
|
|
95
100
|
)
|
|
96
101
|
)
|
|
97
102
|
)
|
|
103
|
+
self.print_trace(f"Created file CodeQuality object: {code_quality}")
|
|
104
|
+
return code_quality
|
|
98
105
|
|
|
99
106
|
if not result.get('lines'):
|
|
100
107
|
self.print_debug(f"Warning: No lines found for result: {result}")
|
|
101
108
|
return None
|
|
102
109
|
lines = scanoss_scan_results_utils.get_lines(result.get('lines'))
|
|
110
|
+
self.print_trace(f"Extracted lines: {lines}")
|
|
103
111
|
if len(lines) == 0:
|
|
104
112
|
self.print_debug(f"Warning: empty lines for result: {result}")
|
|
105
113
|
return None
|
|
106
114
|
end_line = lines[len(lines) - 1] if len(lines) > 1 else lines[0]
|
|
107
115
|
description = f"Snippet found in: {file_name} - lines {lines[0]}-{end_line}"
|
|
108
|
-
|
|
116
|
+
self.print_debug(f"Processing snippet match for: {file_name}, lines: {lines[0]}-{end_line}")
|
|
117
|
+
code_quality = CodeQuality(
|
|
109
118
|
description=description,
|
|
110
119
|
check_name=file_name,
|
|
111
120
|
fingerprint=result.get('file_hash'),
|
|
@@ -117,35 +126,47 @@ class GitLabQualityReport(ScanossBase):
|
|
|
117
126
|
)
|
|
118
127
|
)
|
|
119
128
|
)
|
|
129
|
+
self.print_trace(f"Created snippet CodeQuality object: {code_quality}")
|
|
130
|
+
return code_quality
|
|
120
131
|
|
|
121
132
|
def _write_output(self, data: list[CodeQuality], output_file: str = None) -> bool:
|
|
122
133
|
"""Write the Gitlab Code Quality Report to output."""
|
|
134
|
+
self.print_trace(f"_write_output called with {len(data)} items, output_file: {output_file}")
|
|
123
135
|
try:
|
|
124
136
|
json_data = [item.to_dict() for item in data]
|
|
137
|
+
self.print_trace(f"JSON data: {json_data}")
|
|
125
138
|
file = open(output_file, 'w') if output_file else sys.stdout
|
|
126
139
|
print(json.dumps(json_data, indent=2), file=file)
|
|
127
140
|
if output_file:
|
|
128
141
|
file.close()
|
|
142
|
+
self.print_debug(f"Wrote output to file: {output_file}")
|
|
143
|
+
else:
|
|
144
|
+
self.print_debug("Wrote output to 'stdout'")
|
|
129
145
|
return True
|
|
130
146
|
except Exception as e:
|
|
131
147
|
self.print_stderr(f'Error writing output: {str(e)}')
|
|
132
148
|
return False
|
|
133
149
|
|
|
134
150
|
def _produce_from_json(self, data: dict, output_file: str = None) -> bool:
|
|
151
|
+
self.print_trace(f"_produce_from_json called with output_file: {output_file}")
|
|
152
|
+
self.print_debug(f"Processing {len(data)} files from JSON data")
|
|
135
153
|
code_quality = []
|
|
136
154
|
for file_name, results in data.items():
|
|
155
|
+
self.print_trace(f"Processing file: {file_name} with {len(results)} results")
|
|
137
156
|
for result in results:
|
|
138
157
|
if not result.get('id'):
|
|
139
158
|
self.print_debug(f"Warning: No ID found for result: {result}")
|
|
140
159
|
continue
|
|
141
160
|
if result.get('id') != 'snippet' and result.get('id') != 'file':
|
|
142
|
-
self.print_debug(f"Skipping non-snippet/file match: {result}")
|
|
161
|
+
self.print_debug(f"Skipping non-snippet/file match: {file_name}, id: '{result['id']}'")
|
|
143
162
|
continue
|
|
144
163
|
code_quality_item = self._get_code_quality(file_name, result)
|
|
145
164
|
if code_quality_item:
|
|
146
165
|
code_quality.append(code_quality_item)
|
|
166
|
+
self.print_trace(f"Added code quality item for {file_name}")
|
|
147
167
|
else:
|
|
148
168
|
self.print_debug(f"Warning: No Code Quality found for result: {result}")
|
|
169
|
+
self.print_debug(f"Generated {len(code_quality)} code quality items")
|
|
149
170
|
self._write_output(data=code_quality,output_file=output_file)
|
|
150
171
|
return True
|
|
151
172
|
|
|
@@ -156,11 +177,15 @@ class GitLabQualityReport(ScanossBase):
|
|
|
156
177
|
:param output_file: Output file (optional)
|
|
157
178
|
:return: True if successful, False otherwise
|
|
158
179
|
"""
|
|
180
|
+
self.print_trace(f"_produce_from_str called with output_file: {output_file}")
|
|
159
181
|
if not json_str:
|
|
160
182
|
self.print_stderr('ERROR: No JSON string provided to parse.')
|
|
161
183
|
return False
|
|
184
|
+
self.print_debug(f"Parsing JSON string of length: {len(json_str)}")
|
|
162
185
|
try:
|
|
163
186
|
data = json.loads(json_str)
|
|
187
|
+
self.print_debug("Successfully parsed JSON data")
|
|
188
|
+
self.print_trace(f"Parsed data structure: {type(data)}")
|
|
164
189
|
except Exception as e:
|
|
165
190
|
self.print_stderr(f'ERROR: Problem parsing input JSON: {e}')
|
|
166
191
|
return False
|
|
@@ -174,12 +199,16 @@ class GitLabQualityReport(ScanossBase):
|
|
|
174
199
|
:param output_file:
|
|
175
200
|
:return: True if successful, False otherwise
|
|
176
201
|
"""
|
|
202
|
+
self.print_trace(f"produce_from_file called with json_file: {json_file}, output_file: {output_file}")
|
|
203
|
+
self.print_debug(f"Input JSON file: {json_file}, output_file: {output_file}")
|
|
177
204
|
if not json_file:
|
|
178
205
|
self.print_stderr('ERROR: No JSON file provided to parse.')
|
|
179
206
|
return False
|
|
180
207
|
if not os.path.isfile(json_file):
|
|
181
208
|
self.print_stderr(f'ERROR: JSON file does not exist or is not a file: {json_file}')
|
|
182
209
|
return False
|
|
210
|
+
self.print_debug(f"Reading JSON file: {json_file}")
|
|
183
211
|
with open(json_file, 'r') as f:
|
|
184
|
-
|
|
212
|
+
json_content = f.read()
|
|
213
|
+
success = self._produce_from_str(json_content, output_file)
|
|
185
214
|
return success
|
|
File without changes
|
|
@@ -26,9 +26,9 @@ import time
|
|
|
26
26
|
from datetime import datetime
|
|
27
27
|
from typing import Any, Dict, List, Optional, TypedDict
|
|
28
28
|
|
|
29
|
-
from
|
|
30
|
-
from
|
|
31
|
-
from ..
|
|
29
|
+
from ....services.dependency_track_service import DependencyTrackService
|
|
30
|
+
from ...utils.markdown_utils import generate_jira_table, generate_table
|
|
31
|
+
from ..policy_check import PolicyCheck, PolicyOutput, PolicyStatus
|
|
32
32
|
|
|
33
33
|
# Constants
|
|
34
34
|
PROCESSING_RETRY_DELAY = 5 # seconds
|
|
@@ -171,7 +171,7 @@ class DependencyTrackProjectViolationPolicyCheck(PolicyCheck[PolicyViolationDict
|
|
|
171
171
|
self.url = url.strip().rstrip('/') if url else None
|
|
172
172
|
self.dep_track_service = DependencyTrackService(self.api_key, self.url, debug=debug, trace=trace, quiet=quiet)
|
|
173
173
|
|
|
174
|
-
def _json(self, project_violations: list[PolicyViolationDict]) ->
|
|
174
|
+
def _json(self, project_violations: list[PolicyViolationDict]) -> PolicyOutput:
|
|
175
175
|
"""
|
|
176
176
|
Format project violations as JSON.
|
|
177
177
|
|
|
@@ -181,12 +181,12 @@ class DependencyTrackProjectViolationPolicyCheck(PolicyCheck[PolicyViolationDict
|
|
|
181
181
|
Returns:
|
|
182
182
|
Dictionary containing JSON formatted results and summary
|
|
183
183
|
"""
|
|
184
|
-
return
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
184
|
+
return PolicyOutput(
|
|
185
|
+
details= json.dumps(project_violations, indent=2),
|
|
186
|
+
summary= f'{len(project_violations)} policy violations were found.\n',
|
|
187
|
+
)
|
|
188
188
|
|
|
189
|
-
def _markdown(self, project_violations: list[PolicyViolationDict]) ->
|
|
189
|
+
def _markdown(self, project_violations: list[PolicyViolationDict]) -> PolicyOutput:
|
|
190
190
|
"""
|
|
191
191
|
Format Dependency Track violations to Markdown format.
|
|
192
192
|
|
|
@@ -198,7 +198,7 @@ class DependencyTrackProjectViolationPolicyCheck(PolicyCheck[PolicyViolationDict
|
|
|
198
198
|
"""
|
|
199
199
|
return self._md_summary_generator(project_violations, generate_table)
|
|
200
200
|
|
|
201
|
-
def _jira_markdown(self, data: list[PolicyViolationDict]) ->
|
|
201
|
+
def _jira_markdown(self, data: list[PolicyViolationDict]) -> PolicyOutput:
|
|
202
202
|
"""
|
|
203
203
|
Format project violations for Jira Markdown.
|
|
204
204
|
|
|
@@ -357,8 +357,7 @@ class DependencyTrackProjectViolationPolicyCheck(PolicyCheck[PolicyViolationDict
|
|
|
357
357
|
self.print_stderr(f'Error: Failed to get project uuid from: {dt_project}')
|
|
358
358
|
raise ValueError(f'Error: Project {self.project_name}@{self.project_version} does not have a valid UUID')
|
|
359
359
|
|
|
360
|
-
|
|
361
|
-
def _sort_project_violations(violations: List[PolicyViolationDict]) -> List[PolicyViolationDict]:
|
|
360
|
+
def _sort_project_violations(self,violations: List[PolicyViolationDict]) -> List[PolicyViolationDict]:
|
|
362
361
|
"""
|
|
363
362
|
Sort project violations by priority.
|
|
364
363
|
|
|
@@ -377,7 +376,7 @@ class DependencyTrackProjectViolationPolicyCheck(PolicyCheck[PolicyViolationDict
|
|
|
377
376
|
key=lambda x: -type_priority.get(x.get('type', 'OTHER'), 1)
|
|
378
377
|
)
|
|
379
378
|
|
|
380
|
-
def _md_summary_generator(self, project_violations: list[PolicyViolationDict], table_generator):
|
|
379
|
+
def _md_summary_generator(self, project_violations: list[PolicyViolationDict], table_generator) -> PolicyOutput:
|
|
381
380
|
"""
|
|
382
381
|
Generates a Markdown summary of project policy violations.
|
|
383
382
|
|
|
@@ -396,10 +395,10 @@ class DependencyTrackProjectViolationPolicyCheck(PolicyCheck[PolicyViolationDict
|
|
|
396
395
|
"""
|
|
397
396
|
if project_violations is None:
|
|
398
397
|
self.print_stderr('Warning: No project violations found. Returning empty results.')
|
|
399
|
-
return
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
398
|
+
return PolicyOutput(
|
|
399
|
+
details= "h3. Dependency Track Project Violations\n\nNo policy violations found.\n",
|
|
400
|
+
summary= "0 policy violations were found.\n",
|
|
401
|
+
)
|
|
403
402
|
headers = ['State', 'Risk Type', 'Policy Name', 'Component', 'Date']
|
|
404
403
|
c_cols = [0, 1]
|
|
405
404
|
rows: List[List[str]] = []
|
|
@@ -424,11 +423,11 @@ class DependencyTrackProjectViolationPolicyCheck(PolicyCheck[PolicyViolationDict
|
|
|
424
423
|
]
|
|
425
424
|
rows.append(row)
|
|
426
425
|
# End for loop
|
|
427
|
-
return
|
|
428
|
-
|
|
426
|
+
return PolicyOutput(
|
|
427
|
+
details= f'### Dependency Track Project Violations\n{table_generator(headers, rows, c_cols)}\n\n'
|
|
429
428
|
f'View project in Dependency Track [here]({self.url}/projects/{self.project_id}).\n',
|
|
430
|
-
|
|
431
|
-
|
|
429
|
+
summary= f'{len(project_violations)} policy violations were found.\n'
|
|
430
|
+
)
|
|
432
431
|
|
|
433
432
|
def run(self) -> int:
|
|
434
433
|
"""
|
|
@@ -470,10 +469,11 @@ class DependencyTrackProjectViolationPolicyCheck(PolicyCheck[PolicyViolationDict
|
|
|
470
469
|
self.print_stderr('Error: Invalid format specified.')
|
|
471
470
|
return PolicyStatus.ERROR.value
|
|
472
471
|
# Format and output data - handle empty results gracefully
|
|
473
|
-
|
|
474
|
-
self.print_to_file_or_stdout(
|
|
475
|
-
self.print_to_file_or_stderr(
|
|
472
|
+
policy_output = formatter(self._sort_project_violations(dt_project_violations))
|
|
473
|
+
self.print_to_file_or_stdout(policy_output.details, self.output)
|
|
474
|
+
self.print_to_file_or_stderr(policy_output.summary, self.status)
|
|
476
475
|
# Return appropriate status based on violation count
|
|
477
476
|
if len(dt_project_violations) > 0:
|
|
478
477
|
return PolicyStatus.POLICY_FAIL.value
|
|
479
478
|
return PolicyStatus.POLICY_SUCCESS.value
|
|
479
|
+
|