Skip to content

Commit 462ade6

Browse files
authored
Merge pull request #124 from killuazhu/contribute-respect-option
Respect options from baseline
2 parents a3d30c8 + 024ffc4 commit 462ade6

File tree

10 files changed

+451
-22
lines changed

10 files changed

+451
-22
lines changed

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@
55
/.coverage
66
/.pytest_cache
77
/.tox
8-
/venv**
8+
/venv*
99
/tmp
1010

1111
.*ignore
1212
!.gitignore
13+
.python-version
14+
.vscode

detect_secrets/core/secrets_collection.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,13 @@ def load_baseline_from_string(cls, string):
4343
:raises: IOError
4444
"""
4545
try:
46-
return cls._load_baseline_from_dict(json.loads(string))
46+
return cls.load_baseline_from_dict(json.loads(string))
4747
except (IOError, ValueError):
4848
log.error('Incorrectly formatted baseline!')
4949
raise
5050

5151
@classmethod
52-
def _load_baseline_from_dict(cls, data):
52+
def load_baseline_from_dict(cls, data):
5353
"""Initializes a SecretsCollection object from dictionary.
5454
5555
:type data: dict

detect_secrets/core/usage.py

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@
66
from detect_secrets import VERSION
77

88

9+
def add_use_all_plugins_argument(parser):
10+
parser.add_argument(
11+
'--use-all-plugins',
12+
action='store_true',
13+
help='Use all available plugins to scan files.',
14+
)
15+
16+
917
class ParserBuilder(object):
1018

1119
def __init__(self):
@@ -21,7 +29,8 @@ def add_default_arguments(self):
2129

2230
def add_pre_commit_arguments(self):
2331
self._add_filenames_argument()\
24-
._add_set_baseline_argument()
32+
._add_set_baseline_argument()\
33+
._add_use_all_plugins_argument()
2534

2635
PluginOptions(self.parser).add_arguments()
2736

@@ -78,6 +87,9 @@ def _add_set_baseline_argument(self):
7887
)
7988
return self
8089

90+
def _add_use_all_plugins_argument(self):
91+
add_use_all_plugins_argument(self.parser)
92+
8193

8294
class ScanOptions(object):
8395

@@ -122,6 +134,9 @@ def _add_initialize_baseline_argument(self):
122134
dest='import_filename',
123135
)
124136

137+
# Pairing `--update` with `--use-all-plugins` to overwrite plugins list from baseline
138+
add_use_all_plugins_argument(self.parser)
139+
125140
self.parser.add_argument(
126141
'--all-files',
127142
action='store_true',
@@ -275,6 +290,14 @@ def add_arguments(self):
275290

276291
return self
277292

293+
@staticmethod
294+
def get_disabled_plugins(args):
295+
return [
296+
plugin.classname
297+
for plugin in PluginOptions.all_plugins
298+
if plugin.classname not in args.plugins
299+
]
300+
278301
@staticmethod
279302
def consolidate_args(args):
280303
"""There are many argument fields related to configuring plugins.
@@ -293,14 +316,15 @@ def consolidate_args(args):
293316
return
294317

295318
active_plugins = {}
319+
is_using_default_value = {}
296320

297321
for plugin in PluginOptions.all_plugins:
298322
arg_name = PluginOptions._convert_flag_text_to_argument_name(
299323
plugin.disable_flag_text,
300324
)
301325

302326
# Remove disabled plugins
303-
is_disabled = getattr(args, arg_name)
327+
is_disabled = getattr(args, arg_name, False)
304328
delattr(args, arg_name)
305329
if is_disabled:
306330
continue
@@ -323,12 +347,14 @@ def consolidate_args(args):
323347

324348
if default_value and related_args[arg_name] is None:
325349
related_args[arg_name] = default_value
350+
is_using_default_value[arg_name] = True
326351

327352
active_plugins.update({
328353
plugin.classname: related_args,
329354
})
330355

331356
args.plugins = active_plugins
357+
args.is_using_default_value = is_using_default_value
332358

333359
def _add_custom_limits(self):
334360
high_entropy_help_text = (

detect_secrets/main.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from detect_secrets.core import baseline
1010
from detect_secrets.core.common import write_baseline_to_file
1111
from detect_secrets.core.log import log
12+
from detect_secrets.core.secrets_collection import SecretsCollection
1213
from detect_secrets.core.usage import ParserBuilder
1314
from detect_secrets.plugins.common import initialize
1415

@@ -39,10 +40,7 @@ def main(argv=None):
3940
_scan_string(line, plugins)
4041

4142
else:
42-
baseline_dict = _perform_scan(
43-
args,
44-
plugins,
45-
)
43+
baseline_dict = _perform_scan(args, plugins)
4644

4745
if args.import_filename:
4846
write_baseline_to_file(
@@ -79,6 +77,14 @@ def main(argv=None):
7977
return 0
8078

8179

80+
def _get_plugin_from_baseline(old_baseline):
81+
plugins = []
82+
if old_baseline and "plugins_used" in old_baseline:
83+
secrets_collection = SecretsCollection.load_baseline_from_dict(old_baseline)
84+
plugins = secrets_collection.plugins
85+
return plugins
86+
87+
8288
def _scan_string(line, plugins):
8389
longest_plugin_name_length = max(
8490
map(
@@ -106,6 +112,10 @@ def _perform_scan(args, plugins):
106112
:rtype: dict
107113
"""
108114
old_baseline = _get_existing_baseline(args.import_filename)
115+
if old_baseline:
116+
plugins = initialize.merge_plugin_from_baseline(
117+
_get_plugin_from_baseline(old_baseline), args,
118+
)
109119

110120
# Favors --exclude argument over existing baseline's regex (if exists)
111121
if args.exclude:

detect_secrets/plugins/common/initialize.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from ..private_key import PrivateKeyDetector # noqa: F401
1414
from ..slack import SlackDetector # noqa: F401
1515
from detect_secrets.core.log import log
16+
from detect_secrets.core.usage import PluginOptions
1617

1718

1819
def from_parser_builder(plugins_dict):
@@ -31,6 +32,93 @@ def from_parser_builder(plugins_dict):
3132
return tuple(output)
3233

3334

35+
def _get_prioritized_parameters(plugins_dict, is_using_default_value_map, prefer_default=True):
36+
"""
37+
:type plugins_dict: dict(plugin_name => plugin_params)
38+
:param plugin_dict: mapping of plugin name to all plugin params
39+
40+
:type is_using_default_value_map: dict(str => bool)
41+
:param is_using_default_value_map: mapping of parameter name to whether its value is derived
42+
from a default value.
43+
44+
:param prefer_default: if True, will yield if plugin parameters are from default values.
45+
Otherwise, will yield if plugin parameters are *not* from default values.
46+
"""
47+
for plugin_name, plugin_params in plugins_dict.items():
48+
for param_name, param_value in plugin_params.items():
49+
is_using_default = is_using_default_value_map.get(param_name, False)
50+
if is_using_default == prefer_default:
51+
yield plugin_name, param_name, param_value
52+
53+
54+
def merge_plugin_from_baseline(baseline_plugins, args):
55+
"""
56+
:type baseline_plugins: tuple of BasePlugin
57+
:param baseline_plugins: BasePlugin instances from baseline file
58+
59+
:type args: dict
60+
:param args: diction of arguments parsed from usage
61+
62+
param priority: input param > baseline param > default
63+
64+
:Returns tuple of initialized plugins
65+
"""
66+
def _remove_key(d, key):
67+
r = dict(d)
68+
r.pop(key)
69+
return r
70+
baseline_plugins_dict = {
71+
vars(plugin)["name"]: _remove_key(vars(plugin), "name")
72+
for plugin in baseline_plugins
73+
}
74+
75+
# Use input plugin as starting point
76+
if args.use_all_plugins:
77+
# input param and default param are used
78+
plugins_dict = dict(args.plugins)
79+
80+
# baseline param priority > default
81+
for plugin_name, param_name, param_value in _get_prioritized_parameters(
82+
baseline_plugins_dict,
83+
args.is_using_default_value,
84+
prefer_default=True,
85+
):
86+
try:
87+
plugins_dict[plugin_name][param_name] = param_value
88+
except KeyError:
89+
log.warning(
90+
'Baseline contain plugin %s which is not in all plugins! Ignoring...'
91+
% (plugin_name),
92+
)
93+
94+
return from_parser_builder(plugins_dict)
95+
96+
# Use baseline plugin as starting point
97+
disabled_plugins = PluginOptions.get_disabled_plugins(args)
98+
plugins_dict = {
99+
plugin_name: plugin_params
100+
for plugin_name, plugin_params in baseline_plugins_dict.items()
101+
if plugin_name not in disabled_plugins
102+
}
103+
104+
# input param priority > baseline
105+
input_plugins_dict = dict(args.plugins)
106+
for plugin_name, param_name, param_value in _get_prioritized_parameters(
107+
input_plugins_dict,
108+
args.is_using_default_value,
109+
prefer_default=False,
110+
):
111+
try:
112+
plugins_dict[plugin_name][param_name] = param_value
113+
except KeyError:
114+
log.warning(
115+
'%s specified, but %s not configured! Ignoring...'
116+
% ("".join(["--", param_name.replace("_", "-")]), plugin_name),
117+
)
118+
119+
return from_parser_builder(plugins_dict)
120+
121+
34122
def from_plugin_classname(plugin_classname, **kwargs):
35123
"""Initializes a plugin class, given a classname and kwargs.
36124

detect_secrets/pre_commit_hook.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ def main(argv=None):
3636
return 1
3737

3838
plugins = initialize.from_parser_builder(args.plugins)
39+
40+
# Merge plugin from baseline
41+
if baseline_collection:
42+
plugins = initialize.merge_plugin_from_baseline(baseline_collection.plugins, args)
43+
baseline_collection.plugins = plugins
44+
3945
results = find_secrets_in_files(args, plugins)
4046
if baseline_collection:
4147
original_results = results
@@ -59,7 +65,6 @@ def main(argv=None):
5965
)
6066

6167
if VERSION != baseline_collection.version:
62-
baseline_collection.plugins = plugins
6368
baseline_collection.version = VERSION
6469
baseline_modified = True
6570

tests/core/baseline_test.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,30 @@ def test_new_results_has_nothing(self):
424424

425425
assert merge_results(old_result, {}) == {}
426426

427+
def test_old_results_have_diff_type_will_carry_over(self):
428+
secretA = self.get_secret()
429+
secretA["type"] = "different type"
430+
secretB = self.get_secret()
431+
432+
assert merge_results(
433+
{
434+
'filenameA': [
435+
secretA,
436+
],
437+
},
438+
{
439+
'filenameA': [
440+
secretA,
441+
secretB,
442+
],
443+
},
444+
) == {
445+
'filenameA': [
446+
secretA,
447+
secretB,
448+
],
449+
}
450+
427451
def test_old_results_have_subset_of_new_results(self):
428452
secretA = self.get_secret()
429453
secretB = self.get_secret()

tests/core/secrets_collection_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ def test_output(self, mock_gmtime):
283283

284284
def test_load_baseline_from_string(self, mock_gmtime):
285285
"""
286-
We use load_baseline_from_string as a proxy to testing _load_baseline_from_dict,
286+
We use load_baseline_from_string as a proxy to testing load_baseline_from_dict,
287287
because it's the most entry into the private function.
288288
"""
289289
original = self.get_baseline_dict(mock_gmtime)

0 commit comments

Comments
 (0)