Skip to content

Commit bd8951b

Browse files
author
Dan Hertz
authored
Merge pull request #26 from nightfallai/dan/plat-1364-python-sdk-missing-requestmetadata-param
add request metadata
2 parents 97abe6d + f3b75ec commit bd8951b

File tree

2 files changed

+54
-16
lines changed

2 files changed

+54
-16
lines changed

nightfall/api.py

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,11 @@
44
~~~~~~~~~~~~~
55
This module provides a class which abstracts the Nightfall REST API.
66
"""
7-
import time
87
from datetime import datetime, timedelta
98
import hmac
109
import hashlib
11-
import json
1210
import logging
1311
import os
14-
from functools import wraps
1512
from typing import List, Tuple, Optional
1613

1714
import requests
@@ -104,7 +101,7 @@ def scan_text(self, texts: List[str], detection_rules: Optional[List[DetectionRu
104101
return findings, parsed_response.get("redactedPayload")
105102

106103
def _scan_text_v3(self, data: dict):
107-
response = self.session.post(url=self.TEXT_SCAN_ENDPOINT_V3, data=json.dumps(data))
104+
response = self.session.post(url=self.TEXT_SCAN_ENDPOINT_V3, json=data)
108105

109106
self.logger.debug(f"HTTP Request URL: {response.request.url}")
110107
self.logger.debug(f"HTTP Request Body: {response.request.body}")
@@ -119,7 +116,8 @@ def _scan_text_v3(self, data: dict):
119116

120117
def scan_file(self, location: str, webhook_url: Optional[str] = None, policy_uuid: Optional[str] = None,
121118
detection_rules: Optional[List[DetectionRule]] = None,
122-
detection_rule_uuids: Optional[List[str]] = None) -> Tuple[str, str]:
119+
detection_rule_uuids: Optional[List[str]] = None,
120+
request_metadata: Optional[str] = None) -> Tuple[str, str]:
123121
"""Scan file with Nightfall.
124122
At least one of policy_uuid, detection_rule_uuids or detection_rules is required.
125123
@@ -131,6 +129,8 @@ def scan_file(self, location: str, webhook_url: Optional[str] = None, policy_uui
131129
:type detection_rules: List[DetectionRule] or None
132130
:param detection_rule_uuids: list of detection rule UUIDs.
133131
:type detection_rule_uuids: List[str] or None
132+
:param request_metadata: additional metadata that will be returned with the webhook response
133+
:type request_metadata: str or None
134134
:returns: (scan_id, message)
135135
"""
136136

@@ -153,7 +153,8 @@ def scan_file(self, location: str, webhook_url: Optional[str] = None, policy_uui
153153
response = self._file_scan_scan(session_id,
154154
detection_rules=detection_rules,
155155
detection_rule_uuids=detection_rule_uuids,
156-
webhook_url=webhook_url, policy_uuid=policy_uuid)
156+
webhook_url=webhook_url, policy_uuid=policy_uuid,
157+
request_metadata=request_metadata)
157158
_validate_response(response, 200)
158159
parsed_response = response.json()
159160

@@ -163,7 +164,7 @@ def _file_scan_initialize(self, location: str):
163164
data = {
164165
"fileSizeBytes": os.path.getsize(location)
165166
}
166-
response = self.session.post(url=self.FILE_SCAN_INITIALIZE_ENDPOINT, data=json.dumps(data))
167+
response = self.session.post(url=self.FILE_SCAN_INITIALIZE_ENDPOINT, json=data)
167168

168169
return response
169170

@@ -200,7 +201,7 @@ def _file_scan_finalize(self, session_id: str):
200201

201202
def _file_scan_scan(self, session_id: str, detection_rules: Optional[List[DetectionRule]] = None,
202203
detection_rule_uuids: Optional[List[str]] = None, webhook_url: Optional[str] = None,
203-
policy_uuid: Optional[str] = None) -> requests.Response:
204+
policy_uuid: Optional[str] = None, request_metadata: Optional[str] = None) -> requests.Response:
204205
if policy_uuid:
205206
data = {"policyUUID": policy_uuid}
206207
else:
@@ -210,7 +211,10 @@ def _file_scan_scan(self, session_id: str, detection_rules: Optional[List[Detect
210211
if detection_rules:
211212
data["policy"]["detectionRules"] = [d.as_dict() for d in detection_rules]
212213

213-
response = self.session.post(url=self.FILE_SCAN_SCAN_ENDPOINT.format(session_id), data=json.dumps(data))
214+
if request_metadata:
215+
data["requestMetadata"] = request_metadata
216+
217+
response = self.session.post(url=self.FILE_SCAN_SCAN_ENDPOINT.format(session_id), json=data)
214218
return response
215219

216220
def validate_webhook(self, request_signature: str, request_timestamp: str, request_data: str) -> bool:

tests/test_api.py

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -263,24 +263,58 @@ def test_scan_file(tmpdir):
263263
responses.add(responses.POST, 'https://api.nightfall.ai/v3/upload/1/scan', status=200,
264264
json={"id": 1, "message": "scan_started"})
265265

266-
id, message = nightfall.scan_file(file, "https://my-website.example/callback", detection_rule_uuids=["a_uuid"])
266+
id, message = nightfall.scan_file(file, "https://my-website.example/callback", detection_rule_uuids=["a_uuid"], request_metadata="some test data")
267267

268268
assert len(responses.calls) == 5
269269
for call in responses.calls:
270270
assert call.request.headers.get("Authorization") == "Bearer NF-NOT_REAL"
271271

272-
assert responses.calls[0].request.body == '{"fileSizeBytes": 44}'
273-
assert responses.calls[1].request.body == "4916-6734-7572-5015 is"
272+
assert responses.calls[0].request.body == b'{"fileSizeBytes": 44}'
273+
assert responses.calls[1].request.body == b"4916-6734-7572-5015 is"
274274
assert responses.calls[1].request.headers.get("X-UPLOAD-OFFSET") == '0'
275-
assert responses.calls[2].request.body == " my credit card number"
275+
assert responses.calls[2].request.body == b" my credit card number"
276276
assert responses.calls[2].request.headers.get("X-UPLOAD-OFFSET") == '22'
277-
assert responses.calls[4].request.body == '{"policy": {"webhookURL": "https://my-website.example/callback", ' \
278-
'"detectionRuleUUIDs": ["a_uuid"]}}'
279-
277+
assert responses.calls[4].request.body == b'{"policy": {"webhookURL": "https://my-website.example/callback", ' \
278+
b'"detectionRuleUUIDs": ["a_uuid"]}, "requestMetadata": "some test data"}'
280279
assert id == 1
281280
assert message == "scan_started"
282281

283282

283+
@responses.activate
284+
def test_file_scan_upload_short(tmpdir):
285+
file = tmpdir.mkdir("test_data").join("file.txt")
286+
287+
file.write("4916-6734-7572-5015 is my credit card number")
288+
289+
nightfall = Nightfall("NF-NOT_REAL")
290+
291+
responses.add(responses.PATCH, 'https://api.nightfall.ai/v3/upload/1', status=204)
292+
293+
assert nightfall._file_scan_upload(1, file, 200)
294+
assert len(responses.calls) == 1
295+
assert responses.calls[0].request.headers.get("Authorization") == "Bearer NF-NOT_REAL"
296+
assert responses.calls[0].request.body == b"4916-6734-7572-5015 is my credit card number"
297+
assert responses.calls[0].request.headers.get("X-UPLOAD-OFFSET") == "0"
298+
299+
300+
@responses.activate
301+
def test_file_scan_upload_long(tmpdir):
302+
file = tmpdir.mkdir("test_data").join("file.txt")
303+
test_str = b"4916-6734-7572-5015 is my credit card number"
304+
file.write_binary(test_str)
305+
306+
responses.add(responses.PATCH, 'https://api.nightfall.ai/v3/upload/1', status=204)
307+
308+
nightfall = Nightfall("NF-NOT_REAL")
309+
310+
assert nightfall._file_scan_upload(1, file, 1)
311+
assert len(responses.calls) == 44
312+
for i, call in enumerate(responses.calls):
313+
assert call.request.headers.get("Authorization") == "Bearer NF-NOT_REAL"
314+
assert call.request.body.decode('utf-8') == test_str.decode('utf-8')[i]
315+
assert call.request.headers.get("X-UPLOAD-OFFSET") == str(i)
316+
317+
284318
@freeze_time("2021-10-04T17:30:50Z")
285319
def test_validate_webhook(nightfall):
286320
nightfall.signing_secret = "super-secret-shhhh"

0 commit comments

Comments
 (0)