Skip to content
Open
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
67 changes: 57 additions & 10 deletions checkov/secrets/git_history_store.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,17 @@ def _add_new_secret(self, secret_key: str, secret: PotentialSecret, commit: Comm
'removed_commit_hash': '',
'removed_date': ''})
return
code_line = search_for_code_line(commit.files[secret.filename], secret.secret_value, secret.is_added)
code_context = search_for_code_line(commit.files[secret.filename], secret.secret_value, secret.is_added)
enriched_potential_secret: EnrichedPotentialSecret = {
'added_commit_hash': commit.metadata.commit_hash,
'removed_commit_hash': '',
'potential_secret': secret,
'code_line': code_line,
'code_line': code_context['current_line'],
'line_before': code_context['previous_line'],
'line_after': code_context['next_line'],
'added_by': commit.metadata.committer,
'removed_date': '',
'added_date': commit.metadata.committed_datetime
'added_date': commit.metadata.committed_datetime,
}
self.secrets_by_file_value_type[secret_key].append(enriched_potential_secret)

Expand Down Expand Up @@ -101,19 +103,24 @@ def handle_renamed_file(self, rename_from: str, rename_to: str, commit: Commit)
new_secret = pickle_deepcopy(secret_data['potential_secret'])
new_secret.filename = rename_to
code = secret_data.get('code_line')
line_before = secret_data.get('line_before')
line_after = secret_data.get('line_after')
enriched_potential_secret: EnrichedPotentialSecret = {
'added_commit_hash': commit.metadata.commit_hash,
'removed_commit_hash': '',
'potential_secret': new_secret,
'code_line': code,
'line_before': line_before,
'line_after': line_after,
'added_by': secret_data.get('added_by'),
'removed_date': '',
'added_date': secret_data.get('added_date')
}
temp_secrets_by_file_value_type[new_secret_key].append(enriched_potential_secret)
self.secrets_by_file_value_type.update(temp_secrets_by_file_value_type)

def get_added_and_removed_commit_hash(self, key: str, secret: PotentialSecret, root_folder: Optional[str]) -> EnrichedPotentialSecretMetadata:
def get_added_and_removed_commit_hash(self, key: str, secret: PotentialSecret,
root_folder: Optional[str]) -> EnrichedPotentialSecretMetadata:
"""
now we have only the current commit_hash - in the added_commit_hash or in the removed_commit_hash.
in the next step we will add the connection and the missing data
Expand Down Expand Up @@ -149,6 +156,8 @@ def get_added_and_removed_commit_hash(self, key: str, secret: PotentialSecret, r
'added_commit_hash': chosen_secret.get('added_commit_hash', ''),
'removed_commit_hash': chosen_secret.get('removed_commit_hash', ''),
'code_line': chosen_secret.get('code_line'),
'line_before': chosen_secret.get('line_before'),
'line_after': chosen_secret.get('line_after'),
'added_by': chosen_secret.get('added_by'),
'removed_date': chosen_secret.get('removed_date'),
'added_date': chosen_secret.get('added_date')
Expand All @@ -158,18 +167,56 @@ def get_added_and_removed_commit_hash(self, key: str, secret: PotentialSecret, r
return {}


def search_for_code_line(commit_diff: CommitDiff, secret_value: Optional[str], is_added: Optional[bool]) -> str:
class CodeLineContext(TypedDict):
previous_line: Optional[str]
current_line: str
next_line: Optional[str]


def search_for_code_line(commit_diff: CommitDiff, secret_value: Optional[str],
is_added: Optional[bool]) -> CodeLineContext:
default_result: CodeLineContext = {
'previous_line': None,
'current_line': '',
'next_line': None
}

if not commit_diff:
logging.warning(f'missing file name for {commit_diff}, hence no available code line')
return default_result

if secret_value is None:
return ''
return default_result

split = commit_diff.split('\n')
start_char = '+' if is_added else '-'
for line in split:

for index, line in enumerate(split):
if line.startswith(start_char) and secret_value in line:
# remove +/- in the beginning & spaces and omit
return omit_secret_value_from_line(secret_value, line[1:].strip()) or ''
return '' # not found
# Found the line with the secret
current_line = omit_secret_value_from_line(secret_value, line[1:].strip()) or ''

# Get previous line (if exists)
previous_line = None
if index > 0:
prev = split[index - 1]
if prev.startswith(('+', '-', ' ')): # Valid diff line
previous_line = prev[1:].strip()

# Get next line (if exists)
next_line = None
if index + 1 < len(split):
next = split[index + 1]
if next.startswith(('+', '-', ' ')): # Valid diff line
next_line = next[1:].strip()

return {
'previous_line': previous_line,
'current_line': current_line,
'next_line': next_line
}

return default_result # not found


def get_secret_key(file_name: str, secret_hash: str, secret_type: str) -> str:
Expand Down
2 changes: 2 additions & 0 deletions checkov/secrets/git_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ class EnrichedPotentialSecretMetadata(TypedDict, total=False):
added_by: Optional[str]
removed_date: Optional[str]
added_date: Optional[str]
line_before: Optional[str]
line_after: Optional[str]


class EnrichedPotentialSecret(EnrichedPotentialSecretMetadata):
Expand Down
31 changes: 21 additions & 10 deletions checkov/secrets/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -323,15 +323,16 @@ def get_report(self, secrets: SecretsCollection, runner_filter: RunnerFilter,
continue
secret_key = f'{key}_{secret.line_number}_{secret.secret_hash}'
# secret history
secret_history_details = None
added_commit_hash, removed_commit_hash, code_line, added_by, removed_date, added_date = '', '', '', '', '', ''
if runner_filter.enable_git_history_secret_scan and history_store is not None:
enriched_potential_secret = history_store.get_added_and_removed_commit_hash(key, secret, root_folder)
added_commit_hash = enriched_potential_secret.get('added_commit_hash') or ''
removed_commit_hash = enriched_potential_secret.get('removed_commit_hash') or ''
code_line = enriched_potential_secret.get('code_line') or ''
added_by = enriched_potential_secret.get('added_by') or ''
removed_date = enriched_potential_secret.get('removed_date') or ''
added_date = enriched_potential_secret.get('added_date') or ''
secret_history_details = history_store.get_added_and_removed_commit_hash(key, secret, root_folder)
added_commit_hash = secret_history_details.get('added_commit_hash') or ''
removed_commit_hash = secret_history_details.get('removed_commit_hash') or ''
code_line = secret_history_details.get('code_line') or ''
added_by = secret_history_details.get('added_by') or ''
removed_date = secret_history_details.get('removed_date') or ''
added_date = secret_history_details.get('added_date') or ''
# run over secret key
if isinstance(secret.secret_value, str) and secret.secret_value:
stripped = secret.secret_value.strip(',";\'')
Expand Down Expand Up @@ -375,7 +376,8 @@ def get_report(self, secrets: SecretsCollection, runner_filter: RunnerFilter,
severity=severity,
secret=secret,
runner_filter=runner_filter,
root_folder=root_folder
root_folder=root_folder,
secret_history_details=secret_history_details
) or result

relative_file_path = f'/{os.path.relpath(secret.filename, root_folder)}'
Expand Down Expand Up @@ -492,7 +494,8 @@ def search_for_suppression(
severity: Severity | None,
secret: PotentialSecret,
runner_filter: RunnerFilter,
root_folder: str | None
root_folder: str | None,
secret_history_details: dict | None,
) -> _CheckResult | None:
if not runner_filter.should_run_check(
check_id=check_id,
Expand All @@ -509,7 +512,15 @@ def search_for_suppression(

# Check for suppression comment in the line before, the line of, and the line after the secret
for line_number in [secret.line_number, secret.line_number - 1, secret.line_number + 1]:
lt = linecache.getline(secret.filename, line_number)
if secret_history_details:
if line_number == secret.line_number:
lt = secret_history_details['code_line'] or ''
elif line_number == secret.line_number - 1:
lt = secret_history_details['line_before'] or ''
elif line_number == secret.line_number + 1:
lt = secret_history_details['line_after'] or ''
else:
lt = linecache.getline(secret.filename, line_number)
skip_search = re.search(COMMENT_REGEX, lt)
if skip_search and (skip_search.group(2) == check_id or skip_search.group(2) == bc_check_id):
comment: str = skip_search.group(3)[1:] if skip_search.group(3) else "No comment provided"
Expand Down
Loading