Skip to content

Commit

Permalink
.
Browse files Browse the repository at this point in the history
  • Loading branch information
pazbechor committed Oct 26, 2023
2 parents bde1d81 + 3ec90e7 commit 361f251
Show file tree
Hide file tree
Showing 22 changed files with 196 additions and 114 deletions.
12 changes: 6 additions & 6 deletions .github/workflows/pr-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,8 @@ jobs:
strategy:
fail-fast: true
matrix:
python: [ "3.8" ]
os: [ ubuntu-latest, macos-latest]
python: ["3.8", "3.11"]
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v3
Expand All @@ -170,8 +170,8 @@ jobs:
bash -c 'pipenv run pip install dist/checkov-*.whl'
- name: Clone flask - Python repo for SAST
run: git clone https://github.com/pallets/flask
- name: Clone jenkins - Java repo for SAST
run: git clone https://github.com/jenkinsci/jenkins
- name: Clone WebGoat - Java repo for SAST
run: git clone https://github.com/WebGoat/WebGoat
- name: Clone axios - JavaScript repo for SAST
run: git clone https://github.com/axios/axios
- name: Create checkov reports
Expand All @@ -192,8 +192,8 @@ jobs:
strategy:
fail-fast: true
matrix:
python: [ "3.8" ]
os: [ ubuntu-latest, macos-latest]
python: ["3.8", "3.11"]
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v3
Expand Down
13 changes: 12 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
# CHANGELOG

## [Unreleased](https://github.com/bridgecrewio/checkov/compare/3.0.4...HEAD)
## [Unreleased](https://github.com/bridgecrewio/checkov/compare/3.0.7...HEAD)

## [3.0.7](https://github.com/bridgecrewio/checkov/compare/3.0.4...3.0.7) - 2023-10-25

### Bug Fix

- **secrets:** fix secret FP of client_secret_setting_name - [#5679](https://github.com/bridgecrewio/checkov/pull/5679)

### Platform

- **general:** Add SAST enforcement rules and check severity thresholds - [#5684](https://github.com/bridgecrewio/checkov/pull/5684)
- **general:** do not get fixes for on prem integrations - [#5668](https://github.com/bridgecrewio/checkov/pull/5668)

## [3.0.4](https://github.com/bridgecrewio/checkov/compare/2.5.18...3.0.4) - 2023-10-24

Expand Down
3 changes: 2 additions & 1 deletion cdk_integration_tests/prepare_data.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ for file in "checkov/cdk/checks/python"/*; do
# create a report for this check
echo "creating report for check: $filename, id: $check_id"
pipenv run checkov -s --framework cdk --repo-id cli/cdk -o json --check $check_id \
-d "cdk_integration_tests/src/python/$filename" > "checkov_report_cdk_python_$filename.json"
-d "cdk_integration_tests/src/python/$filename" --external-checks-dir "checkov/cdk/checks/python" \
> "checkov_report_cdk_python_$filename.json"
fi
done

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ def is_valid(self) -> bool:
return (
self.bc_integration.is_integration_configured()
and not self.bc_integration.skip_fixes
and not self.bc_integration.on_prem
and not self.bc_integration.skip_download
and not self.integration_feature_failures
)
Expand Down
63 changes: 38 additions & 25 deletions checkov/common/bridgecrew/platform_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@
REQUEST_RETRIES,
)
from checkov.common.util.type_forcers import convert_prisma_policy_filter_to_dict, convert_str_to_bool
from checkov.sast.consts import SastLanguages
from checkov.version import version as checkov_version

if TYPE_CHECKING:
Expand Down Expand Up @@ -115,6 +114,7 @@ def __init__(self) -> None:
self.prisma_policies_response = None
self.public_metadata_response = None
self.use_s3_integration = False
self.s3_setup_failed = False
self.platform_integration_configured = False
self.http: urllib3.PoolManager | urllib3.ProxyManager | None = None
self.http_timeout = urllib3.Timeout(connect=REQUEST_CONNECT_TIMEOUT, read=REQUEST_READ_TIMEOUT)
Expand Down Expand Up @@ -315,6 +315,9 @@ def set_s3_integration(self) -> None:
try:
self.skip_fixes = True # no need to run fixes on CI integration
repo_full_path, support_path, response = self.get_s3_role(self.repo_id) # type: ignore
if not repo_full_path: # happens if the setup fails with something other than an auth error - we continue locally
return

self.bucket, self.repo_path = repo_full_path.split("/", 1)

self.timestamp = self.repo_path.split("/")[-2]
Expand Down Expand Up @@ -359,7 +362,7 @@ def set_s3_integration(self) -> None:
logging.error("Received an error response during authentication")
raise

def get_s3_role(self, repo_id: str) -> tuple[str, str | None, dict[str, Any]]:
def get_s3_role(self, repo_id: str) -> tuple[str, str, dict[str, Any]] | tuple[None, None, dict[str, Any]]:
token = self.get_auth_token()

if not self.http:
Expand All @@ -370,28 +373,34 @@ def get_s3_role(self, repo_id: str) -> tuple[str, str | None, dict[str, Any]]:
while ('Message' in response or 'message' in response):
if response.get('Message') and response['Message'] == UNAUTHORIZED_MESSAGE:
raise BridgecrewAuthError()
if response.get('message') and ASSUME_ROLE_UNUATHORIZED_MESSAGE in response['message']:
elif response.get('message') and ASSUME_ROLE_UNUATHORIZED_MESSAGE in response['message']:
raise BridgecrewAuthError(
"Checkov got an unexpected authorization error that may not be due to your credentials. Please contact support.")
if response.get('message') and "cannot be found" in response['message']:
elif response.get('message') and "cannot be found" in response['message']:
self.loading_output("creating role")
response = self._get_s3_creds(repo_id, token)
if response.get('message') is None and response.get('Message') is None:
else:
if tries < 3:
tries += 1
response = self._get_s3_creds(repo_id, token)
else:
raise BridgecrewAuthError(
"Checkov got an unexpected error that may be due to backend issues. Please contact support.")
logging.error('Checkov got an unexpected error that may be due to backend issues. The scan will continue, '
'but results will not be sent to the platform. Please contact support for assistance.')
logging.error(f'Error from platform: {response.get("message") or response.get("Message")}')
self.s3_setup_failed = True
return None, None, response
repo_full_path = response["path"]
support_path = response.get("supportPath")
return repo_full_path, support_path, response

def _get_s3_creds(self, repo_id: str, token: str) -> dict[str, Any]:
logging.debug(f'Getting S3 upload credentials from {self.integrations_api_url}')
request = self.http.request("POST", self.integrations_api_url, # type:ignore[union-attr]
body=json.dumps({"repoId": repo_id, "support": self.support_flag_enabled}),
headers=merge_dicts({"Authorization": token, "Content-Type": "application/json"},
get_user_agent_header()))
logging.debug(f'Request ID: {request.headers.get("x-amzn-requestid")}')
logging.debug(f'Trace ID: {request.headers.get("x-amzn-trace-id")}')
if request.status == 403:
error_message = get_auth_error_message(request.status, self.is_prisma_integration(), True)
raise BridgecrewAuthError(error_message)
Expand Down Expand Up @@ -422,7 +431,7 @@ def persist_repository(
"""
excluded_paths = excluded_paths if excluded_paths is not None else []

if not self.use_s3_integration:
if not self.use_s3_integration or self.s3_setup_failed:
return
files_to_persist: List[FileToPersist] = []
if files:
Expand Down Expand Up @@ -451,7 +460,7 @@ def persist_repository(
self.persist_files(files_to_persist)

def persist_git_configuration(self, root_dir: str | Path, git_config_folders: list[str]) -> None:
if not self.use_s3_integration:
if not self.use_s3_integration or self.s3_setup_failed:
return
files_to_persist: list[FileToPersist] = []

Expand All @@ -476,7 +485,7 @@ def persist_scan_results(self, scan_reports: list[Report]) -> None:
Persist checkov's scan result into bridgecrew's platform.
:param scan_reports: List of checkov scan reports
"""
if not self.use_s3_integration or not self.s3_client:
if not self.use_s3_integration or not self.s3_client or self.s3_setup_failed:
return
if not self.bucket or not self.repo_path:
logging.error(f"Something went wrong: bucket {self.bucket}, repo path {self.repo_path}")
Expand All @@ -492,7 +501,7 @@ def persist_scan_results(self, scan_reports: list[Report]) -> None:
persist_checks_results(reduced_scan_reports, self.s3_client, self.bucket, self.repo_path)

async def persist_reachability_alias_mapping(self, alias_mapping: Dict[str, Any]) -> None:
if not self.use_s3_integration or not self.s3_client:
if not self.use_s3_integration or not self.s3_client or self.s3_setup_failed:
return
if not self.bucket or not self.repo_path:
logging.error(f"Something went wrong: bucket {self.bucket}, repo path {self.repo_path}")
Expand All @@ -508,11 +517,11 @@ def persist_assets_scan_results(self, assets_report: Optional[Dict[str, Any]]) -
new_report = {'imports': {lang.value: assets}}
persist_assets_results(f'sast_{lang.value}', new_report, self.s3_client, self.bucket, self.repo_path)

def persist_reachability_scan_results(self, reachability_report: Optional[Dict[SastLanguages, Any]]) -> None:
def persist_reachability_scan_results(self, reachability_report: Optional[Dict[str, Any]]) -> None:
if not reachability_report:
return
for lang, report in reachability_report.items():
persist_reachability_results(f'sast_{lang.value}', report, self.s3_client, self.bucket, self.repo_path)
persist_reachability_results(f'sast_{lang}', {lang: report}, self.s3_client, self.bucket, self.repo_path)

def persist_image_scan_results(self, report: dict[str, Any] | None, file_path: str, image_name: str, branch: str) -> None:
if not self.s3_client:
Expand Down Expand Up @@ -559,7 +568,7 @@ def persist_enriched_secrets(self, enriched_secrets: list[EnrichedSecret]) -> st
return s3_path

def persist_run_metadata(self, run_metadata: dict[str, str | list[str]]) -> None:
if not self.use_s3_integration or not self.s3_client:
if not self.use_s3_integration or not self.s3_client or self.s3_setup_failed:
return
if not self.bucket or not self.repo_path:
logging.error(f"Something went wrong: bucket {self.bucket}, repo path {self.repo_path}")
Expand All @@ -571,7 +580,7 @@ def persist_run_metadata(self, run_metadata: dict[str, str | list[str]]) -> None
persist_run_metadata(run_metadata, self.s3_client, self.support_bucket, self.support_repo_path, False)

def persist_logs_stream(self, logs_stream: StringIO) -> None:
if not self.use_s3_integration or not self.s3_client:
if not self.use_s3_integration or not self.s3_client or self.s3_setup_failed:
return
if not self.support_bucket or not self.support_repo_path:
logging.error(
Expand All @@ -582,7 +591,7 @@ def persist_logs_stream(self, logs_stream: StringIO) -> None:
persist_logs_stream(logs_stream, self.s3_client, self.support_bucket, log_path)

def persist_graphs(self, graphs: dict[str, list[tuple[LibraryGraph, Optional[str]]]], absolute_root_folder: str = '') -> None:
if not self.use_s3_integration or not self.s3_client:
if not self.use_s3_integration or not self.s3_client or self.s3_setup_failed:
return
if not self.bucket or not self.repo_path:
logging.error(f"Something went wrong: bucket {self.bucket}, repo path {self.repo_path}")
Expand All @@ -591,7 +600,7 @@ def persist_graphs(self, graphs: dict[str, list[tuple[LibraryGraph, Optional[str
absolute_root_folder=absolute_root_folder)

def persist_resource_subgraph_maps(self, resource_subgraph_maps: dict[str, dict[str, str]]) -> None:
if not self.use_s3_integration or not self.s3_client:
if not self.use_s3_integration or not self.s3_client or self.s3_setup_failed:
return
if not self.bucket or not self.repo_path:
logging.error(f"Something went wrong: bucket {self.bucket}, repo path {self.repo_path}")
Expand All @@ -605,7 +614,7 @@ def commit_repository(self, branch: str) -> str | None:
"""
try_num = 0
while try_num < MAX_RETRIES:
if not self.use_s3_integration:
if not self.use_s3_integration or self.s3_setup_failed:
return None

request = None
Expand All @@ -621,6 +630,7 @@ def commit_repository(self, branch: str) -> str | None:
# no need to upload something
return None

logging.debug(f'Submitting finalize upload request to {self.integrations_api_url}')
request = self.http.request("PUT", f"{self.integrations_api_url}?source={self.bc_source.name}", # type:ignore[no-untyped-call]
body=json.dumps(
{"path": self.repo_path, "branch": branch,
Expand All @@ -641,20 +651,21 @@ def commit_repository(self, branch: str) -> str | None:
get_user_agent_header()
))
response = json.loads(request.data.decode("utf8"))
logging.debug(f'Request ID: {request.headers.get("x-amzn-requestid")}')
logging.debug(f'Trace ID: {request.headers.get("x-amzn-trace-id")}')
url: str = self.get_sso_prismacloud_url(response.get("url", None))
return url
except HTTPError:
logging.error(f"Failed to commit repository {self.repo_path}", exc_info=True)
raise
self.s3_setup_failed = True
except JSONDecodeError:
if request:
logging.warning(
f"Response (status: {request.status}) of {self.integrations_api_url}: {request.data.decode('utf8')}")
logging.warning(f"Response (status: {request.status}) of {self.integrations_api_url}: {request.data.decode('utf8')}") # danger:ignore - we won't be here if the response contains valid data
logging.error(f"Response of {self.integrations_api_url} is not a valid JSON", exc_info=True)
raise
self.s3_setup_failed = True
finally:
if request and request.status == 201 and response and response.get("result") == "Success":
logging.info(f"Finalize repository {self.repo_id} in bridgecrew's platform")
logging.info(f"Finalize repository {self.repo_id} in the platform")
elif (
response
and try_num < MAX_RETRIES
Expand All @@ -665,8 +676,8 @@ def commit_repository(self, branch: str) -> str | None:
try_num += 1
sleep(SLEEP_SECONDS)
else:
raise Exception(
f"Failed to finalize repository {self.repo_id} in bridgecrew's platform\n{response}")
logging.error(f"Failed to finalize repository {self.repo_id} in the platform with the following error:\n{response}")
self.s3_setup_failed = True

return None

Expand Down Expand Up @@ -754,6 +765,8 @@ def get_customer_run_config(self) -> None:
url = self.get_run_config_url()
logging.debug(f'Platform run config URL: {url}')
request = self.http.request("GET", url, headers=headers) # type:ignore[no-untyped-call]
logging.debug(f'Request ID: {request.headers.get("x-amzn-requestid")}')
logging.debug(f'Trace ID: {request.headers.get("x-amzn-trace-id")}')
if request.status != 200:
error_message = get_auth_error_message(request.status, self.is_prisma_integration(), False)
logging.error(error_message)
Expand Down
8 changes: 5 additions & 3 deletions checkov/common/output/report.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from checkov.common.output.ai import OpenAi
from checkov.common.typing import _ExitCodeThresholds, _ScaExitCodeThresholds
from checkov.common.output.record import Record, SCA_PACKAGE_SCAN_CHECK_NAME
from checkov.common.util.consts import PARSE_ERROR_FAIL_FLAG, CHECKOV_RUN_SCA_PACKAGE_SCAN_V2
from checkov.common.util.consts import PARSE_ERROR_FAIL_FLAG, CHECKOV_RUN_SCA_PACKAGE_SCAN_V2, S3_UPLOAD_DETAILS_MESSAGE
from checkov.common.util.json_utils import CustomJSONEncoder
from checkov.runner_filter import RunnerFilter
from checkov.sast.consts import POLICIES_ERRORS, POLICIES_ERRORS_COUNT, ENGINE_NAME, SOURCE_FILES_COUNT, POLICY_COUNT
Expand Down Expand Up @@ -97,9 +97,11 @@ def get_json(self) -> str:
def get_all_records(self) -> List[Record]:
return self.failed_checks + self.passed_checks + self.skipped_checks

def get_dict(self, is_quiet: bool = False, url: str | None = None, full_report: bool = False) -> dict[str, Any]:
if not url:
def get_dict(self, is_quiet: bool = False, url: str | None = None, full_report: bool = False, s3_setup_failed: bool = False) -> dict[str, Any]:
if not url and not s3_setup_failed:
url = "Add an api key '--bc-api-key <api-key>' to see more detailed insights via https://bridgecrew.cloud"
elif s3_setup_failed:
url = S3_UPLOAD_DETAILS_MESSAGE
if is_quiet:
return {
"check_type": self.check_type,
Expand Down
12 changes: 9 additions & 3 deletions checkov/common/runners/runner_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
from checkov.common.typing import _ExitCodeThresholds, _BaseRunner, _ScaExitCodeThresholds, LibraryGraph
from checkov.common.util import data_structures_utils
from checkov.common.util.banner import tool as tool_name
from checkov.common.util.consts import S3_UPLOAD_DETAILS_MESSAGE
from checkov.common.util.data_structures_utils import pickle_deepcopy
from checkov.common.util.json_utils import CustomJSONEncoder
from checkov.common.util.secrets_omitter import SecretsOmitter
Expand Down Expand Up @@ -390,7 +391,7 @@ def print_reports(
for report in scan_reports:
if not report.is_empty():
if "json" in config.output:
report_jsons.append(report.get_dict(is_quiet=config.quiet, url=url))
report_jsons.append(report.get_dict(is_quiet=config.quiet, url=url, s3_setup_failed=bc_integration.s3_setup_failed))
if "junitxml" in config.output:
junit_reports.append(report)
if "github_failed_only" in config.output:
Expand Down Expand Up @@ -477,8 +478,11 @@ def print_reports(

del output_formats["sarif"]

if "cli" not in config.output and url:
print(f"More details: {url}")
if "cli" not in config.output:
if url:
print(f"More details: {url}")
elif bc_integration.s3_setup_failed:
print(S3_UPLOAD_DETAILS_MESSAGE)
if CONSOLE_OUTPUT in output_formats.values():
print(OUTPUT_DELIMITER)

Expand Down Expand Up @@ -617,6 +621,8 @@ def _print_to_console(self, output_formats: dict[str, str], output_format: str,
print(output)
if url:
print(f"More details: {url}")
elif bc_integration.s3_setup_failed:
print(S3_UPLOAD_DETAILS_MESSAGE)

if CONSOLE_OUTPUT in output_formats.values():
print(OUTPUT_DELIMITER)
Expand Down
Loading

0 comments on commit 361f251

Please sign in to comment.