Skip to content

Commit a03cb02

Browse files
authored
Merge pull request #118 from GitGuardian/mmillet/-/handle_excluded_policy_breaks
handle excluded policy breaks
2 parents 3c20117 + f3cbef0 commit a03cb02

File tree

5 files changed

+134
-11
lines changed

5 files changed

+134
-11
lines changed
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<!--
2+
A new scriv changelog fragment.
3+
4+
Uncomment the section that is right (remove the HTML comment wrapper).
5+
-->
6+
7+
<!--
8+
### Removed
9+
10+
- A bullet item for the Removed category.
11+
12+
-->
13+
<!--
14+
### Added
15+
16+
- A bullet item for the Added category.
17+
18+
-->
19+
20+
### Changed
21+
22+
- `content_scan` and `multi_content_scan` now accept `all_secrets` parameter.
23+
- `PolicyBreak` now contains two new fields: `is_excluded` and `exclude_reason`.
24+
<!--
25+
26+
### Deprecated
27+
28+
- A bullet item for the Deprecated category.
29+
30+
-->
31+
32+
<!--
33+
### Fixed
34+
35+
- A bullet item for the Fixed category.
36+
37+
-->
38+
<!--
39+
### Security
40+
41+
- A bullet item for the Security category.
42+
43+
-->

pygitguardian/client.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,8 @@ def content_scan(
358358
document: str,
359359
filename: Optional[str] = None,
360360
extra_headers: Optional[Dict[str, str]] = None,
361+
*,
362+
all_secrets: Optional[bool] = None,
361363
) -> Union[Detail, ScanResult]:
362364
"""
363365
content_scan handles the /scan endpoint of the API.
@@ -368,6 +370,7 @@ def content_scan(
368370
:param filename: name of file, example: "intro.py"
369371
:param document: content of file
370372
:param extra_headers: additional headers to add to the request
373+
:param all_secrets: indicates whether all secrets should be returned
371374
:return: Detail or ScanResult response and status code
372375
"""
373376

@@ -379,11 +382,15 @@ def content_scan(
379382
DocumentSchema.validate_size(
380383
request_obj, self.secret_scan_preferences.maximum_document_size
381384
)
385+
params = {}
386+
if all_secrets is not None:
387+
params["all_secrets"] = all_secrets
382388

383389
resp = self.post(
384390
endpoint="scan",
385391
data=request_obj,
386392
extra_headers=extra_headers,
393+
params=params,
387394
)
388395

389396
obj: Union[Detail, ScanResult]
@@ -401,6 +408,8 @@ def multi_content_scan(
401408
documents: List[Dict[str, str]],
402409
extra_headers: Optional[Dict[str, str]] = None,
403410
ignore_known_secrets: Optional[bool] = None,
411+
*,
412+
all_secrets: Optional[bool] = None,
404413
) -> Union[Detail, MultiScanResult]:
405414
"""
406415
multi_content_scan handles the /multiscan endpoint of the API.
@@ -413,6 +422,7 @@ def multi_content_scan(
413422
example: [{"document":"example content","filename":"intro.py"}]
414423
:param extra_headers: additional headers to add to the request
415424
:param ignore_known_secrets: indicates whether known secrets should be ignored
425+
:param all_secrets: indicates whether all secrets should be returned
416426
:return: Detail or ScanResult response and status code
417427
"""
418428
max_documents = self.secret_scan_preferences.maximum_documents_per_scan
@@ -433,11 +443,12 @@ def multi_content_scan(
433443
document, self.secret_scan_preferences.maximum_document_size
434444
)
435445

436-
params = (
437-
{"ignore_known_secrets": ignore_known_secrets}
438-
if ignore_known_secrets
439-
else {}
440-
)
446+
params = {}
447+
if ignore_known_secrets is not None:
448+
params["ignore_known_secrets"] = ignore_known_secrets
449+
if all_secrets is not None:
450+
params["all_secrets"] = all_secrets
451+
441452
resp = self.post(
442453
endpoint="multiscan",
443454
data=request_obj,

pygitguardian/models.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,8 +260,10 @@ class PolicyBreakSchema(BaseSchema):
260260
policy = fields.String(required=True)
261261
validity = fields.String(required=False, load_default=None, dump_default=None)
262262
known_secret = fields.Boolean(required=False, load_default=False, dump_default=None)
263-
incident_url = fields.String(required=False, load_default=False, dump_default=None)
263+
incident_url = fields.String(required=False, load_default=None, dump_default=None)
264264
matches = fields.List(fields.Nested(MatchSchema), required=True)
265+
is_excluded = fields.Boolean(required=False, load_default=False, dump_default=False)
266+
exclude_reason = fields.String(required=False, load_default=None, dump_default=None)
265267

266268
@post_load
267269
def make_policy_break(self, data: Dict[str, Any], **kwargs: Any) -> "PolicyBreak":
@@ -286,6 +288,8 @@ def __init__(
286288
matches: List[Match],
287289
known_secret: bool = False,
288290
incident_url: Optional[str] = None,
291+
is_excluded: bool = False,
292+
exclude_reason: Optional[str] = None,
289293
**kwargs: Any,
290294
) -> None:
291295
super().__init__()
@@ -295,6 +299,8 @@ def __init__(
295299
self.known_secret = known_secret
296300
self.incident_url = incident_url
297301
self.matches = matches
302+
self.is_excluded = is_excluded
303+
self.exclude_reason = exclude_reason
298304

299305
@property
300306
def is_secret(self) -> bool:

tests/test_client.py

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -575,19 +575,53 @@ def test_extra_headers(
575575

576576

577577
@responses.activate
578-
def test_multiscan_parameters(
579-
client: GGClient,
580-
):
578+
@pytest.mark.parametrize("all_secrets", (None, True, False))
579+
def test_scan_parameters(client: GGClient, all_secrets):
580+
"""
581+
GIVEN a ggclient
582+
WHEN calling content_scan with parameters
583+
THEN the parameters are passed in the request
584+
"""
585+
586+
to_match = {}
587+
if all_secrets is not None:
588+
to_match["all_secrets"] = all_secrets
589+
590+
mock_response = responses.post(
591+
url=client._url_from_endpoint("scan", "v1"),
592+
status=200,
593+
match=[matchers.query_param_matcher(to_match)],
594+
)
595+
596+
client.content_scan(
597+
DOCUMENT,
598+
FILENAME,
599+
all_secrets=all_secrets,
600+
)
601+
602+
assert mock_response.call_count == 1
603+
604+
605+
@responses.activate
606+
@pytest.mark.parametrize("ignore_known_secrets", (None, True, False))
607+
@pytest.mark.parametrize("all_secrets", (None, True, False))
608+
def test_multiscan_parameters(client: GGClient, ignore_known_secrets, all_secrets):
581609
"""
582610
GIVEN a ggclient
583611
WHEN calling multi_content_scan with parameters
584612
THEN the parameters are passed in the request
585613
"""
586614

615+
to_match = {}
616+
if ignore_known_secrets is not None:
617+
to_match["ignore_known_secrets"] = ignore_known_secrets
618+
if all_secrets is not None:
619+
to_match["all_secrets"] = all_secrets
620+
587621
mock_response = responses.post(
588622
url=client._url_from_endpoint("multiscan", "v1"),
589623
status=200,
590-
match=[matchers.query_param_matcher({"ignore_known_secrets": True})],
624+
match=[matchers.query_param_matcher(to_match)],
591625
json=[
592626
{
593627
"policy_break_count": 1,
@@ -610,7 +644,8 @@ def test_multiscan_parameters(
610644

611645
client.multi_content_scan(
612646
[{"filename": FILENAME, "document": DOCUMENT}],
613-
ignore_known_secrets=True,
647+
ignore_known_secrets=ignore_known_secrets,
648+
all_secrets=all_secrets,
614649
)
615650

616651
assert mock_response.call_count == 1

tests/test_models.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,34 @@ def test_document_handle_surrogates(self):
111111
"matches": [{"match": "hello", "type": "hello"}],
112112
},
113113
),
114+
(
115+
PolicyBreakSchema,
116+
PolicyBreak,
117+
{
118+
"type": "hello",
119+
"policy": "hello",
120+
"validity": "hey",
121+
"known_secret": True,
122+
"incident_url": "https://api.gitguardian.com/workspace/2/incidents/3",
123+
"matches": [{"match": "hello", "type": "hello"}],
124+
"is_excluded": True,
125+
"exclude_reason": "bad secret",
126+
},
127+
),
128+
(
129+
PolicyBreakSchema,
130+
PolicyBreak,
131+
{
132+
"type": "hello",
133+
"policy": "hello",
134+
"validity": "hey",
135+
"known_secret": True,
136+
"incident_url": "https://api.gitguardian.com/workspace/2/incidents/3",
137+
"matches": [{"match": "hello", "type": "hello"}],
138+
"is_excluded": False,
139+
"exclude_reason": None,
140+
},
141+
),
114142
(
115143
QuotaSchema,
116144
Quota,

0 commit comments

Comments
 (0)