Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions dojo/jira/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from rest_framework import serializers

from dojo.jira import services as jira_services
from dojo.jira.queries import get_authorized_jira_projects
from dojo.models import (
JIRA_Instance,
JIRA_Issue,
Expand All @@ -18,6 +19,20 @@ class Meta:
model = JIRA_Issue
fields = "__all__"

def get_fields(self):
fields = super().get_fields()
# The jira_project relation is writable, so restrict the choosable
# projects to the ones the caller is allowed to edit. Otherwise the
# field would resolve any project id from the full manager on write.
jira_project_field = fields.get("jira_project")
user = getattr(self.context.get("request"), "user", None)
# Only narrow for a real authenticated request. An anonymous/absent
# user (e.g. OpenAPI schema generation) keeps the default queryset and
# must not be handed to the authorization filter.
if jira_project_field is not None and user is not None and not user.is_anonymous:
jira_project_field.queryset = get_authorized_jira_projects("edit", user=user)
return fields

def get_url(self, obj) -> str:
return jira_services.get_issue_url(obj)

Expand Down
64 changes: 64 additions & 0 deletions unittests/test_rest_framework.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
App_Analysis,
BurpRawRequestResponse,
Development_Environment,
Dojo_User,
DojoMeta,
Endpoint,
Endpoint_Status,
Expand Down Expand Up @@ -2515,6 +2516,69 @@ def __init__(self, *args, **kwargs):
BaseClass.RESTEndpointTest.__init__(self, *args, **kwargs)


@versioned_fixtures
class JiraIssueProjectScopingTest(DojoAPITestCase):

"""
The jira project on a jira finding mapping is writable, so a caller may only
reference a project they are allowed to edit. A member of one product must
not be able to attach or repoint a mapping to another product's project.
"""

fixtures = ["dojo_testdata.json"]

def setUp(self):
super().setUp()
pt_a = self.create_product_type("scoping-pt-a")
pt_b = self.create_product_type("scoping-pt-b")
self.product_a = self.create_product("scoping-prod-a", prod_type=pt_a)
self.product_b = self.create_product("scoping-prod-b", prod_type=pt_b)

self.user = Dojo_User.objects.create(username="jira-scoping-user", is_staff=False, is_superuser=False)
self.product_a.authorized_users.add(self.user)

Test_Type.objects.get_or_create(name="scoping-tt")
engagement = self.create_engagement("scoping-eng", self.product_a)
test = self.create_test(engagement=engagement, scan_type="scoping-tt", title="scoping-test")
self.finding = Finding.objects.create(
title="scoping-finding", test=test, severity="Low",
numerical_severity="S3", reporter=self.user,
)

# jira_instance is nullable; the scoping check only needs the projects to exist.
self.project_a = JIRA_Project.objects.create(product=self.product_a, project_key="AAA")
self.project_b = JIRA_Project.objects.create(product=self.product_b, project_key="BBB")

token = Token.objects.create(user=self.user)
self.client = APIClient()
self.client.credentials(HTTP_AUTHORIZATION="Token " + token.key)
self.url = reverse("jira_issue-list")

def test_create_rejects_unauthorized_jira_project(self):
before = JIRA_Issue.objects.count()
response = self.client.post(self.url, {
"jira_project": self.project_b.id, "jira_id": "1", "jira_key": "BBB-1",
"finding": self.finding.id,
}, format="json")
self.assertEqual(status.HTTP_400_BAD_REQUEST, response.status_code, response.content[:500])
self.assertEqual(before, JIRA_Issue.objects.count())

def test_create_allows_authorized_jira_project(self):
response = self.client.post(self.url, {
"jira_project": self.project_a.id, "jira_id": "1", "jira_key": "AAA-1",
"finding": self.finding.id,
}, format="json")
self.assertEqual(status.HTTP_201_CREATED, response.status_code, response.content[:500])
self.assertEqual(self.project_a.id, JIRA_Issue.objects.get(id=response.data["id"]).jira_project_id)

def test_update_rejects_unauthorized_jira_project(self):
issue = JIRA_Issue.objects.create(jira_project=self.project_a, jira_id="1", jira_key="AAA-1", finding=self.finding)
response = self.client.patch(f"{self.url}{issue.id}/", {"jira_project": self.project_b.id}, format="json")
self.assertEqual(status.HTTP_400_BAD_REQUEST, response.status_code, response.content[:500])
issue.refresh_from_db()
self.assertEqual(self.project_a.id, issue.jira_project_id)


@versioned_fixtures
class JiraProjectTest(BaseClass.BaseClassTest):
fixtures = ["dojo_testdata.json"]
Expand Down
Loading