Skip to content

Commit

Permalink
fix(secret_ignore): Add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
salome-voltz committed Sep 13, 2024
1 parent 7498c93 commit ef6bb9d
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 18 deletions.
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
### Added

- Default behavior for `ggshield secret ignore` command to allow ignoring secrets using their hash with optional `--name` argument.
- It is now possible to ignore a secret manually using `ggshield secret ignore SECRET_SHA --name NAME`.
23 changes: 14 additions & 9 deletions ggshield/cmd/secret/ignore.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@

@click.command()
@click.argument(
"hash",
"secret_sha",
nargs=1,
type=str,
required=False,
metavar="HASH",
metavar="SECRET_SHA",
)
@click.option(
"--last-found",
Expand All @@ -27,12 +27,13 @@
"--name",
type=str,
help="Name of the secret to ignore.",
metavar="NAME",
)
@add_common_options()
@click.pass_context
def ignore_cmd(
ctx: click.Context,
hash: str,
secret_sha: str,
name: str,
last_found: bool,
**kwargs: Any,
Expand All @@ -46,7 +47,7 @@ def ignore_cmd(
Option `--last-found` ignores all secrets found during the last scan.
The command adds the ignored secrets to the `secrets.ignored-matches` section of your local
The command adds the ignored secrets to the `secrets.ignored_matches` section of your local
configuration file. If no local configuration file is found, a `.gitguardian.yaml` file is created.
"""

Expand All @@ -55,20 +56,24 @@ def ignore_cmd(
path = config.config_path

if last_found:
if hash or name:
if secret_sha or name:
raise click.UsageError(
"Option `--last-found` cannot be used with `HASH` or `--name`."
"Option `--last-found` cannot be used with `SECRET_SHA` or `--name`."
)
nb = ignore_last_found(config, ctx_obj.cache)
else:
match = IgnoredMatch(name=name if name else "", match=hash)
config.add_ignored_match(match)
if not name:
raise click.UsageError(
"Option `--name` is required when ignoring a secret."
)
ignored_match = IgnoredMatch(name=name, match=secret_sha)
config.add_ignored_match(ignored_match)
nb = 1

config.save()
secrets_word = pluralize("secret", nb)
click.echo(
f"Added {nb} {secrets_word} to the secret.ignored-matches section of {path}."
f"Added {nb} {secrets_word} to the secret.ignored_matches section of {path}."
)


Expand Down
70 changes: 65 additions & 5 deletions tests/unit/cmd/test_ignore.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
import os
import tempfile
from unittest.mock import Mock
from unittest.mock import Mock, patch

from pygitguardian.models import Match, PolicyBreak

from ggshield.__main__ import cli
from ggshield.cmd.secret.ignore import ignore_last_found
from ggshield.core.cache import Cache
from ggshield.core.config import Config
from ggshield.core.errors import ExitCode
from ggshield.core.scan import Commit, ScanContext, ScanMode
from ggshield.core.types import IgnoredMatch
from ggshield.verticals.secret import SecretScanner
from tests.unit.conftest import _MULTIPLE_SECRETS_PATCH, my_vcr
from tests.unit.conftest import (
_MULTIPLE_SECRETS_PATCH,
assert_invoke_exited_with,
assert_invoke_ok,
my_vcr,
)


DOT_GITGUARDIAN_YAML = os.path.join(tempfile.gettempdir(), ".gitguardian.yml")
Expand All @@ -27,6 +34,59 @@ def compare_matches_ignore(match):
return (match.name, match.match) if isinstance(match, IgnoredMatch) else (match,)


def test_ignore_sha(cli_fs_runner):
"""
GIVEN an empty cache and an empty config ignored_matches section
WHEN I ignore a secret sha
THEN the ignore secret is added to the config and saved
"""
ignored_match = IgnoredMatch(
name="test_name",
match="41b8889e5e794b21cb1349d8eef1815960bf5257330fd40243a4895f26c2b5c8",
)
config = Config()

with patch("ggshield.cmd.utils.context_obj.ContextObj.get") as mock_get_ctx:
mock_get_ctx.return_value.config = config

cmd = ["secret", "ignore", ignored_match.match, "--name", ignored_match.name]
result = cli_fs_runner.invoke(cli, cmd, color=False, catch_exceptions=False)

assert_invoke_ok(result)
assert config.user_config.secret.ignored_matches == [ignored_match]


def test_error_sha_last_found(cli_fs_runner):
"""
GIVEN any config and cache
WHEN I run the command with invalid arguments
THEN an error should be raised
"""

cmd = ["secret", "ignore", "some_secret_sha", "--last-found"]
result = cli_fs_runner.invoke(cli, cmd, color=False, catch_exceptions=False)

assert_invoke_exited_with(result, ExitCode.USAGE_ERROR)
assert (
"Option `--last-found` cannot be used with `SECRET_SHA` or `--name`."
in result.output
)


def test_error_ignore_sha_no_name(cli_fs_runner):
"""
GIVEN any config and cache
WHEN I run the command with a secret sha but no name
THEN an error should be raised
"""

cmd = ["secret", "ignore", "some_secret_sha"]
result = cli_fs_runner.invoke(cli, cmd, color=False, catch_exceptions=False)

assert_invoke_exited_with(result, ExitCode.USAGE_ERROR)
assert "Option `--name` is required when ignoring a secret." in result.output


def test_cache_catches_last_found_secrets(client, isolated_fs):
"""
GIVEN an empty cache and an empty config ignored_matches section
Expand Down Expand Up @@ -66,7 +126,7 @@ def test_cache_catches_last_found_secrets(client, isolated_fs):

def test_cache_catches_nothing(client, isolated_fs):
"""
GIVEN a cache of last found secrets same as config ignored-matches
GIVEN a cache of last found secrets same as config ignored_matches
WHEN I run a scan (therefore finding no secret)
THEN config matches is unchanged and cache is empty
"""
Expand Down Expand Up @@ -97,7 +157,7 @@ def test_ignore_last_found(client, isolated_fs):
"""
GIVEN a cache of last found secrets not empty
WHEN I run a ignore last found command
THEN config ignored-matches is updated accordingly
THEN config ignored_matches is updated accordingly
"""
config = Config()

Expand All @@ -117,7 +177,7 @@ def test_ignore_last_found(client, isolated_fs):

def test_ignore_last_found_with_manually_added_secrets(client, isolated_fs):
"""
GIVEN a cache containing part of config ignored-matches secrets
GIVEN a cache containing part of config ignored_matches secrets
WHEN I run ignore command
THEN only new discovered secrets are added to the config
"""
Expand Down
6 changes: 3 additions & 3 deletions tests/unit/core/config/test_user_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,13 +159,13 @@ def test_load_mixed_versions(self, local_config_path, global_config_path):
def test_load_ignored_matches_with_empty_names(self):
config_path = "config.yaml"
# Use write_text() here because write_yaml() cannot generate a key with a really
# empty value, like we need for secret.ignored-matches[0].name
# empty value, like we need for secret.ignored_matches[0].name
write_text(
config_path,
"""
version: 2
secret:
ignored-matches:
ignored_matches:
- name:
match: abcd
- name: ""
Expand Down Expand Up @@ -535,7 +535,7 @@ def test_user_config_unknown_keys(self, local_config_path, capsys):
"iac": {"ignored_paths": ["myglobalpath"], "iac_unknown": [""]},
"secret": {
"secret_invalid_key": "invalid key",
"ignored-matches": [
"ignored_matches": [
{
"name": "",
"match": "one",
Expand Down

0 comments on commit ef6bb9d

Please sign in to comment.