Skip to content

Commit bdcbafa

Browse files
author
Dan Hertz
committed
add findings result object
1 parent c288fbe commit bdcbafa

File tree

3 files changed

+100
-13
lines changed

3 files changed

+100
-13
lines changed

nightfall/api.py

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
from nightfall.detection_rules import DetectionRule
1616
from nightfall.exceptions import NightfallUserError, NightfallSystemError
17+
from nightfall.findings import Finding
1718

1819

1920
class Nightfall:
@@ -48,16 +49,13 @@ def __init__(self, key: str = None, signing_secret: str = None):
4849
self.logger = logging.getLogger(__name__)
4950

5051
def scan_text(self, texts: list[str], detection_rule_uuids: list[str] = None,
51-
detection_rules: list[DetectionRule] = None):
52+
detection_rules: list[DetectionRule] = None) -> [list[list[Finding]], list[str]]:
5253
"""Scan text with Nightfall.
5354
5455
This method takes the specified config and then makes
5556
one or more requests to the Nightfall API for scanning.
57+
At least one of detection_rule_uuids or detection_rules is required.
5658
57-
Either detection_rule_uuids or DetectionRule is required.
58-
::
59-
detection_rule_uuids: ["uuid",]
60-
detection_rules: [DetectionRule,]
6159
6260
:param texts: List of strings to scan.
6361
:type texts: list[str]
@@ -70,8 +68,7 @@ def scan_text(self, texts: list[str], detection_rule_uuids: list[str] = None,
7068
"""
7169

7270
if not detection_rule_uuids and not detection_rules:
73-
raise NightfallUserError("Need to supply detection rule ids list or detection rules dict with \
74-
key 'detection_rule_uuids' or 'detection_rules' respectively", 40001)
71+
raise NightfallUserError("at least one of detection_rule_uuids or detection_rules required", 40001)
7572

7673
config = {}
7774
if detection_rule_uuids:
@@ -88,7 +85,9 @@ def scan_text(self, texts: list[str], detection_rule_uuids: list[str] = None,
8885

8986
parsed_response = response.json()
9087

91-
return parsed_response["findings"], parsed_response["redactedPayload"]
88+
findings = [[Finding.from_dict(f) for f in item_findings] for item_findings in parsed_response["findings"]]
89+
return findings, parsed_response.get("redactedPayload")
90+
9291

9392
def _scan_text_v3(self, data):
9493
response = requests.post(
@@ -131,8 +130,8 @@ def scan_file(self, location: str, webhook_url: str, policy_uuid: str = None,
131130
"""
132131

133132
if not policy_uuid and not detection_rule_uuids and not detection_rules:
134-
raise NightfallUserError("Need to supply policy id or detection rule ids list or detection rules dict with \
135-
key 'policy_uuid', 'detection_rule_uuids', 'detection_rules' respectively", 40001)
133+
raise NightfallUserError("at least one of policy_uuid, detection_rule_uuids or detection_rules required",
134+
40001)
136135

137136
response = self._file_scan_initialize(location)
138137
_validate_response(response, 200)

nightfall/detection_rules.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,9 @@ class ExclusionRule:
6868
candidate finding from triggering a detector match.
6969
"""
7070
def __init__(self, match_type: str, regex: Regex = None, word_list: WordList = None):
71-
"""Instantiate an ExclusionRule object
71+
"""Instantiate an ExclusionRule object.
72+
One of regex or word_list is required.
73+
7274
:param match_type: the match type. One of "FULL" or "PARTIAL".
7375
:type match_type: str
7476
:param regex: The regular expression configuration to run on a candidate finding.
@@ -141,7 +143,9 @@ def __init__(
141143
infotype_substitution: bool = False,
142144
public_key: str = None
143145
):
144-
"""Instantiate a RedactionConfig object
146+
"""Instantiate a RedactionConfig object.
147+
One of mask_config, substitution_phrase, infotype_substitution or public_key is required.
148+
145149
:param remove_finding: Whether the original finding should be omitted in responses from the API.
146150
:type remove_finding: bool
147151
:param mask_config: Build a redaction config with masking.
@@ -194,7 +198,9 @@ def __init__(
194198
exclusion_rules: list[ExclusionRule] = None,
195199
redaction_config: RedactionConfig = None
196200
):
197-
"""Instantiate a Detector object
201+
"""Instantiate a Detector object.
202+
One of nightfall_detector, regex, word_list or uuid required.
203+
198204
:param min_confidence: The minimum confidence threshold for the detector trigger a finding.
199205
:type min_confidence: str
200206
:param min_num_findings: The minimum number of occurrences of the detector required to trigger a finding.

nightfall/findings.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
class Range:
2+
"""An object representing where a finding was discovered in content."""
3+
def __init__(self, start: int, end: int):
4+
"""Instantiate a Range object.
5+
:param start: The start of the range.
6+
:type start: int
7+
:param end: The end of the range.
8+
:type end: int
9+
"""
10+
self.start = start
11+
self.end = end
12+
13+
14+
class Finding:
15+
"""An object representing an occurrence of a configured detector (i.e. finding) in the provided data."""
16+
def __init__(self,
17+
finding: str,
18+
redacted_finding: str,
19+
before_context: str,
20+
after_context: str,
21+
detector_name: str,
22+
detector_uuid: str,
23+
confidence: str,
24+
byte_range: Range,
25+
codepoint_range: Range,
26+
matched_detection_rule_uuids: list[str],
27+
matched_detection_rules: list[str]):
28+
"""Instantiate a Finding object.
29+
:param finding: The data that triggered a detector match.
30+
31+
:type finding: str
32+
:param redacted_finding: The redacted finding if redaction was configured, None otherwise.
33+
:type redacted_finding: str
34+
:param before_context: The data that immediately preceded the finding if configured, None otherwise.
35+
:type before_context: str
36+
:param after_context: The data that immediately succeeded the finding if configured, None otherwise.
37+
:type after_context: str
38+
:param detector_name: The the name of the detector, if configured, None otherwise.
39+
:type detector_name: str
40+
:param detector_uuid: The ID that uniquely identifies this detector.
41+
:type detector_uuid: str
42+
:param confidence: The confidence that the data contained in Finding is an instance of the matched detector.
43+
:type confidence: str
44+
:param byte_range: The byte range in which a finding was detected within the item.
45+
:type byte_range: Range
46+
:param codepoint_range: The codepoint range in which a finding was detected within the item. This differs
47+
from byte range since a codepoint may contain multiple bytes.
48+
:type codepoint_range: Range
49+
:param matched_detection_rule_uuids: The list of detection rule UUIDs that contained a detector that triggered a
50+
match.
51+
:type matched_detection_rule_uuids: list[str]
52+
:param matched_detection_rules: The list of inline detection rules that contained a detector that triggered a
53+
match.
54+
:type matched_detection_rules: list[str]
55+
"""
56+
self.finding = finding
57+
self.redacted_finding = redacted_finding
58+
self.before_context = before_context
59+
self.after_context = after_context
60+
self.detector_name = detector_name
61+
self.detector_uuid = detector_uuid
62+
self.confidence = confidence
63+
self.byte_range = byte_range
64+
self.codepoint_range = codepoint_range
65+
self.matched_detection_rule_uuids = matched_detection_rule_uuids
66+
self.matched_detection_rules = matched_detection_rules
67+
68+
@classmethod
69+
def from_dict(cls, resp: dict) -> "Finding":
70+
return cls(
71+
resp["finding"],
72+
resp.get("redactedFinding"),
73+
resp.get("beforeContext"),
74+
resp.get("afterContext"),
75+
resp["detector"].get("name"),
76+
resp["detector"].get("uuid"),
77+
resp["confidence"],
78+
Range(resp["location"]["byteRange"]["start"], resp["location"]["byteRange"]["end"]),
79+
Range(resp["location"]["codepointRange"]["start"], resp["location"]["codepointRange"]["end"]),
80+
resp["matchedDetectionRuleUUIDs"],
81+
resp["matchedDetectionRules"]
82+
)

0 commit comments

Comments
 (0)