Skip to content

Commit

Permalink
Make the number of request/response pairs returned by the API configu…
Browse files Browse the repository at this point in the history
…rable (#9967)

* limit req response via setting

* add unittest

* update running test documentation

* also must do here

* fix linting, add urls.py

* trailing space in a comment...

* switch back to finding - different test uses it

* change test data to not break

* reset data, use my own

* Update dojo/settings/settings.dist.py

Co-authored-by: Charles Neill <[email protected]>

---------

Co-authored-by: Charles Neill <[email protected]>
  • Loading branch information
hblankenship and cneill authored Apr 22, 2024
1 parent 3a25a44 commit 65bc290
Show file tree
Hide file tree
Showing 8 changed files with 1,177 additions and 6 deletions.
5 changes: 4 additions & 1 deletion dojo/api_v2/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -1806,8 +1806,11 @@ def build_relational_field(self, field_name, relation_info):

@extend_schema_field(BurpRawRequestResponseSerializer)
def get_request_response(self, obj):
# burp_req_resp = BurpRawRequestResponse.objects.filter(finding=obj)
# Not necessarily Burp scan specific - these are just any request/response pairs
burp_req_resp = obj.burprawrequestresponse_set.all()
var = settings.MAX_REQRESP_FROM_API
if var > -1:
burp_req_resp = burp_req_resp[:var]
burp_list = []
for burp in burp_req_resp:
request = burp.get_request()
Expand Down
6 changes: 5 additions & 1 deletion dojo/api_v2/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -1004,8 +1004,12 @@ def request_response(self, request, pk=None):
return Response(
burps.errors, status=status.HTTP_400_BAD_REQUEST
)

# Not necessarily Burp scan specific - these are just any request/response pairs
burp_req_resp = BurpRawRequestResponse.objects.filter(finding=finding)
var = settings.MAX_REQRESP_FROM_API
if var > -1:
burp_req_resp = burp_req_resp[:var]

burp_list = []
for burp in burp_req_resp:
request = burp.get_request()
Expand Down
376 changes: 376 additions & 0 deletions dojo/fixtures/unit_limit_reqresp.json

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion dojo/settings/settings.dist.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,8 @@
# maximum number of result in search as search can be an expensive operation
DD_SEARCH_MAX_RESULTS=(int, 100),
DD_SIMILAR_FINDINGS_MAX_RESULTS=(int, 25),
# The maximum number of request/response pairs to return from the API. Values <0 return all pairs.
DD_MAX_REQRESP_FROM_API=(int, -1),
DD_MAX_AUTOCOMPLETE_WORDS=(int, 20000),
DD_JIRA_SSL_VERIFY=(bool, True),
# You can set extra Jira issue types via a simple env var that supports a csv format, like "Work Item,Vulnerability"
Expand Down Expand Up @@ -233,7 +235,6 @@
DD_FEATURE_FINDING_GROUPS=(bool, True),
DD_JIRA_TEMPLATE_ROOT=(str, 'dojo/templates/issue-trackers'),
DD_TEMPLATE_DIR_PREFIX=(str, 'dojo/templates/'),

# Initial behaviour in Defect Dojo was to delete all duplicates when an original was deleted
# New behaviour is to leave the duplicates in place, but set the oldest of duplicates as new original
# Set to True to revert to the old behaviour where all duplicates are deleted
Expand Down Expand Up @@ -599,6 +600,7 @@ def generate_url(scheme, double_slashes, user, password, host, port, path, param

SEARCH_MAX_RESULTS = env('DD_SEARCH_MAX_RESULTS')
SIMILAR_FINDINGS_MAX_RESULTS = env('DD_SIMILAR_FINDINGS_MAX_RESULTS')
MAX_REQRESP_FROM_API = env('DD_MAX_REQRESP_FROM_API')
MAX_AUTOCOMPLETE_WORDS = env('DD_MAX_AUTOCOMPLETE_WORDS')

LOGIN_EXEMPT_URLS = (
Expand Down
2 changes: 1 addition & 1 deletion dojo/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@
v2_api.register(r'engagements', EngagementViewSet)
v2_api.register(r'development_environments', DevelopmentEnvironmentViewSet)
v2_api.register(r'finding_templates', FindingTemplatesViewSet)
v2_api.register(r'findings', FindingViewSet)
v2_api.register(r'findings', FindingViewSet, basename='finding')
v2_api.register(r'jira_configurations', JiraInstanceViewSet) # backwards compatibility
v2_api.register(r'jira_instances', JiraInstanceViewSet)
v2_api.register(r'jira_finding_mappings', JiraIssuesViewSet)
Expand Down
14 changes: 12 additions & 2 deletions readme-docs/DOCKER.md
Original file line number Diff line number Diff line change
Expand Up @@ -335,16 +335,26 @@ The integration-tests are under `tests`


## Running the unit tests

### All tests
This will run all unit-tests and leave the uwsgi container up:

```
docker/setEnv.sh unit_tests
./dc-up.sh
```
Enter the container to run more tests:

### Limited tests
If you want to enter the container to run more tests or a single test case, leave setEnv in normal or dev mode:
```
docker/setEnv.sh dev
./dc-up.sh
```
Then
```
docker-compose exec uwsgi bash
docker ps
#find the name of the uwsgi container from the above command
docker exec -ti [container-name] bash
```
Rerun all the tests:

Expand Down
734 changes: 734 additions & 0 deletions unittests/scans/burp_api/many_reqresp.json

Large diffs are not rendered by default.

42 changes: 42 additions & 0 deletions unittests/test_apiv2_limit_reqresp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from rest_framework.test import APITestCase, APIClient
from django.urls import reverse
from rest_framework.authtoken.models import Token
from django.conf import settings


class APILimitReqRespPairsTest(APITestCase):
"""
Test the MAX_REQRESP_FROM_API setting for /api/v2/findings/{id}/request_response/
"""

fixtures = ['unit_limit_reqresp.json']

def setUp(self: object):
token = Token.objects.get(user__username='admin')
self.client = APIClient()
self.client.credentials(HTTP_AUTHORIZATION='Token ' + token.key)

def assertReqrespValue(self: object, value: int, expect_notequal: bool = False) -> None:
settings.MAX_REQRESP_FROM_API = value
r = self.client.get(reverse('finding-list'), format='json')
results = r.json()['results']
# get finding with id 8
finding = self.getFinding(8, results)
if expect_notequal:
self.assertNotEqual(len(finding['request_response']['req_resp']), value)
else:
self.assertEqual(len(finding['request_response']['req_resp']), value)

def getFinding(self: object, idn: int, results: list) -> dict:
for result in results:
if result['id'] == idn:
return result
return None

def test_reqresp(self: object) -> None:
self.assertReqrespValue(5)
self.assertReqrespValue(10)
self.assertReqrespValue(18) # actual number of reqresp
self.assertReqrespValue(100, True) # more than the number in the request
self.assertReqrespValue(-1, True) # default value of MAX_REQRESP_FROM_API
self.assertReqrespValue(-100, True) # crazy negative value

0 comments on commit 65bc290

Please sign in to comment.