Skip to content

Commit

Permalink
Merge pull request #158 from grendel513/master
Browse files Browse the repository at this point in the history
Takes care of #64:
  • Loading branch information
Greg Anderson committed Nov 9, 2016
2 parents 8127fdc + 67202ed commit cd18071
Show file tree
Hide file tree
Showing 16 changed files with 338 additions and 21 deletions.
55 changes: 52 additions & 3 deletions dojo/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
from tastypie.validation import CleanedDataFormValidation

from dojo.models import Product, Engagement, Test, Finding, \
User, ScanSettings, IPScan, Scan, Stub_Finding, Risk_Acceptance
User, ScanSettings, IPScan, Scan, Stub_Finding, Risk_Acceptance, Finding_Template
from dojo.forms import ProductForm, EngForm2, TestForm, \
ScanSettingsForm, FindingForm, StubFindingForm
ScanSettingsForm, FindingForm, StubFindingForm, FindingTemplateForm

"""
Setup logging for the api
Expand Down Expand Up @@ -420,7 +420,7 @@ class FindingResource(BaseModelResource):
class Meta:
resource_name = 'findings'
queryset = Finding.objects.select_related("test")
# deleting of findings is not allowed via UI or API.
# deleting of findings is not allowed via API.
# Admin interface can be used for this.
list_allowed_methods = ['get', 'post']
detail_allowed_methods = ['get', 'post', 'put']
Expand Down Expand Up @@ -458,6 +458,55 @@ def dehydrate(self, bundle):
"/api/v1/products/%s/" % engagement[0].product.id
return bundle

"""
/api/v1/finding_templates/
GET [/id/], DELETE [/id/]
Expects: no params or test_id
Returns test: ALL or by test_id
Relevant apply filter ?active=True, ?id=?, ?severity=?
POST, PUT [/id/]
Expects *title, *severity, *description, *mitigation, *impact,
*endpoint, *test, cwe, active, false_p, verified,
mitigated, *reporter
"""


class FindingTemplateResource(BaseModelResource):

class Meta:
resource_name = 'finding_templates'
queryset = Finding_Template.objects.all()
excludes= ['numerical_severity']
# deleting of Finding_Template is not allowed via API.
# Admin interface can be used for this.
list_allowed_methods = ['get', 'post']
detail_allowed_methods = ['get', 'post', 'put']
include_resource_uri = True
"""
title = models.TextField(max_length=1000)
cwe = models.IntegerField(default=None, null=True, blank=True)
severity = models.CharField(max_length=200, null=True, blank=True)
description = models.TextField(null=True, blank=True)
mitigation = models.TextField(null=True, blank=True)
impact = models.TextField(null=True, blank=True)
references = models.TextField(null=True, blank=True, db_column="refs")
numerical_severity
"""
filtering = {
'id': ALL,
'title': ALL,
'cwe': ALL,
'severity': ALL,
'description': ALL,
'mitigated': ALL,
}
authentication = DojoApiKeyAuthentication()
authorization = DjangoAuthorization()
serializer = Serializer(formats=['json'])
validation = CleanedDataFormValidation(form_class=FindingTemplateForm)


class StubFindingResource(BaseModelResource):
reporter = fields.ForeignKey(UserResource, 'reporter', null=False)
Expand Down
4 changes: 4 additions & 0 deletions dojo/finding/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
views.edit_finding, name='edit_finding'),
url(r'^finding/(?P<fid>\d+)/touch',
views.touch_finding, name='touch_finding'),
url(r'^finding/(?P<fid>\d+)/request_review',
views.request_finding_review, name='request_finding_review'),
url(r'^finding/(?P<fid>\d+)/review',
views.clear_finding_review, name='clear_finding_review'),
url(r'^finding/(?P<fid>\d+)/delete$',
views.delete_finding, name='delete_finding'),
url(r'^finding/(?P<fid>\d+)/mktemplate$', views.mktemplate,
Expand Down
105 changes: 101 additions & 4 deletions dojo/finding/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@
OpenFingingSuperFilter, AcceptedFingingSuperFilter, \
ClosedFingingSuperFilter, TemplateFindingFilter
from dojo.forms import NoteForm, CloseFindingForm, FindingForm, PromoteFindingForm, FindingTemplateForm, \
DeleteFindingTemplateForm, FindingImageFormSet
DeleteFindingTemplateForm, FindingImageFormSet, ReviewFindingForm, ClearFindingReviewForm
from dojo.models import Product_Type, Finding, Notes, \
Risk_Acceptance, BurpRawRequestResponse, Stub_Finding, Endpoint, Finding_Template, FindingImage, \
FindingImageAccessToken
from dojo.utils import get_page_items, add_breadcrumb, FileIterWrapper
FindingImageAccessToken, Dojo_User
from dojo.utils import get_page_items, add_breadcrumb, FileIterWrapper, send_review_email

localtz = timezone(settings.TIME_ZONE)

Expand Down Expand Up @@ -137,7 +137,9 @@ def closed_findings(request):

def view_finding(request, fid):
finding = get_object_or_404(Finding, id=fid)

user = request.user
dojo_user = get_object_or_404(Dojo_User, id=user.id)
if user.is_staff or user in finding.test.engagement.product.authorized_users.all():
pass # user is authorized for this product
else:
Expand Down Expand Up @@ -177,7 +179,7 @@ def view_finding(request, fid):
return render(request, 'dojo/view_finding.html',
{'finding': finding,
'burp_request': burp_request,
'burp_response': burp_response,
'burp_response': burp_response, 'dojo_user': dojo_user,
'user': user, 'notes': notes, 'form': form})


Expand Down Expand Up @@ -340,6 +342,101 @@ def touch_finding(request, fid):
return HttpResponseRedirect(reverse('view_finding', args=(finding.id,)))


@user_passes_test(lambda u: u.is_staff)
def request_finding_review(request, fid):
finding = get_object_or_404(Finding, id=fid)
user = get_object_or_404(Dojo_User, id=request.user.id)
# in order to review a finding, we need to capture why a review is needed
# we can do this with a Note
if request.method == 'POST':
form = ReviewFindingForm(request.POST)

if form.is_valid():
now = datetime.now(tz=localtz)
new_note = Notes()

new_note.entry = "Review Request: " + form.cleaned_data['entry']
new_note.author = request.user
new_note.date = now
new_note.save()
finding.notes.add(new_note)
finding.active = False
finding.verified = False
finding.under_review = True
finding.review_requested_by = user
finding.last_reviewed = now
finding.last_reviewed_by = request.user

users = form.cleaned_data['reviewers']
finding.reviewers = users
finding.save()

send_review_email(request, user, finding, users, new_note)

messages.add_message(request,
messages.SUCCESS,
'Finding marked for review and reviewers notified.',
extra_tags='alert-success')
return HttpResponseRedirect(reverse('view_finding', args=(finding.id,)))

else:
form = ReviewFindingForm()

add_breadcrumb(parent=finding, title="Review Finding", top_level=False, request=request)
return render(request, 'dojo/review_finding.html',
{'finding': finding,
'user': user, 'form': form})


@user_passes_test(lambda u: u.is_staff)
def clear_finding_review(request, fid):
finding = get_object_or_404(Finding, id=fid)
user = get_object_or_404(Dojo_User, id=request.user.id)
# in order to clear a review for a finding, we need to capture why and how it was reviewed
# we can do this with a Note

if user == finding.review_requested_by or user in finding.reviewers.all():
pass
else:
return HttpResponseForbidden()

if request.method == 'POST':
form = ClearFindingReviewForm(request.POST, instance=finding)

if form.is_valid():
now = datetime.now(tz=localtz)
new_note = Notes()
new_note.entry = "Review Cleared: " + form.cleaned_data['entry']
new_note.author = request.user
new_note.date = now
new_note.save()

finding = form.save(commit=False)

finding.under_review = False
finding.last_reviewed = now
finding.last_reviewed_by = request.user

finding.reviewers = []
finding.save()

finding.notes.add(new_note)

messages.add_message(request,
messages.SUCCESS,
'Finding review has been updated successfully.',
extra_tags='alert-success')
return HttpResponseRedirect(reverse('view_finding', args=(finding.id,)))

else:
form = ClearFindingReviewForm(instance=finding)

add_breadcrumb(parent=finding, title="Clear Finding Review", top_level=False, request=request)
return render(request, 'dojo/clear_finding_review.html',
{'finding': finding,
'user': user, 'form': form})


@user_passes_test(lambda u: u.is_staff)
def mktemplate(request, fid):
finding = get_object_or_404(Finding, id=fid)
Expand Down
43 changes: 37 additions & 6 deletions dojo/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,6 @@ def __init__(self, *args, **kwargs):
self.fields['authorized_users'].queryset = non_staff
self.fields['tags'].widget.choices = t


class Meta:
model = Product
fields = ['name', 'description', 'tags', 'product_manager', 'technical_contact', 'team_manager', 'prod_type',
Expand Down Expand Up @@ -220,7 +219,8 @@ def __init__(self, *args, **kwargs):

class Meta:
model = Product
fields = ['name', 'description', 'product_manager', 'technical_contact', 'team_manager', 'prod_type', 'authorized_users']
fields = ['name', 'description', 'product_manager', 'technical_contact', 'team_manager', 'prod_type',
'authorized_users']


class ImportScanForm(forms.Form):
Expand Down Expand Up @@ -582,7 +582,8 @@ def clean(self):
class Meta:
model = Finding
order = ('title', 'severity', 'endpoints', 'description', 'impact')
exclude = ('reporter', 'url', 'numerical_severity', 'endpoint', 'images')
exclude = ('reporter', 'url', 'numerical_severity', 'endpoint', 'images', 'under_review', 'reviewers',
'review_requested_by')


class PromoteFindingForm(forms.ModelForm):
Expand All @@ -609,7 +610,7 @@ class Meta:
model = Finding
order = ('title', 'severity', 'endpoints', 'description', 'impact')
exclude = ('reporter', 'url', 'numerical_severity', 'endpoint', 'active', 'false_p', 'verified', 'is_template',
'duplicate', 'out_of_scope', 'images')
'duplicate', 'out_of_scope', 'images', 'under_review', 'reviewers', 'review_requested_by')


class FindingForm(forms.ModelForm):
Expand Down Expand Up @@ -657,7 +658,8 @@ def clean(self):
class Meta:
model = Finding
order = ('title', 'severity', 'endpoints', 'description', 'impact')
exclude = ('reporter', 'url', 'numerical_severity', 'endpoint', 'images')
exclude = ('reporter', 'url', 'numerical_severity', 'endpoint', 'images', 'under_review', 'reviewers',
'review_requested_by')


class StubFindingForm(forms.ModelForm):
Expand Down Expand Up @@ -730,7 +732,7 @@ def clean(self):

class Meta:
model = Finding
fields = ('severity', 'active', 'verified', 'false_p','duplicate','out_of_scope')
fields = ('severity', 'active', 'verified', 'false_p', 'duplicate', 'out_of_scope')


class EditEndpointForm(forms.ModelForm):
Expand Down Expand Up @@ -956,6 +958,35 @@ class Meta:
fields = ['entry']


class ClearFindingReviewForm(forms.ModelForm):
entry = forms.CharField(
required=True, max_length=2400,
help_text='Please provide a message.',
widget=forms.Textarea, label='Notes:',
error_messages={'required': ('The reason for clearing a review is '
'required, please use the text area '
'below to provide documentation.')})

class Meta:
model = Finding
fields = ['active', 'verified', 'false_p', 'out_of_scope', 'duplicate']


class ReviewFindingForm(forms.Form):
reviewers = forms.ModelMultipleChoiceField(queryset=Dojo_User.objects.filter(is_staff=True, is_active=True),
help_text="Select all users who can review Finding.")
entry = forms.CharField(
required=True, max_length=2400,
help_text='Please provide a message for reviewers.',
widget=forms.Textarea, label='Notes:',
error_messages={'required': ('The reason for requesting a review is '
'required, please use the text area '
'below to provide documentation.')})

class Meta:
fields = ['reviewers', 'entry']


class WeeklyMetricsForm(forms.Form):
dates = forms.ChoiceField()

Expand Down
6 changes: 5 additions & 1 deletion dojo/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,9 @@ class Finding(models.Model):
false_p = models.BooleanField(default=False, verbose_name="False Positive")
duplicate = models.BooleanField(default=False)
out_of_scope = models.BooleanField(default=False)
under_review = models.BooleanField(default=False)
review_requested_by = models.ForeignKey(Dojo_User, null=True, blank=True, related_name='review_requested_by')
reviewers = models.ManyToManyField(Dojo_User, blank=True)
thread_id = models.IntegerField(default=0, editable=False)
mitigated = models.DateTimeField(editable=False, null=True, blank=True)
mitigated_by = models.ForeignKey(User, null=True, editable=False, related_name="mitigated_by")
Expand Down Expand Up @@ -684,7 +687,7 @@ class Finding_Template(models.Model):
mitigation = models.TextField(null=True, blank=True)
impact = models.TextField(null=True, blank=True)
references = models.TextField(null=True, blank=True, db_column="refs")
numerical_severity = models.CharField(max_length=4, null=True, blank=True)
numerical_severity = models.CharField(max_length=4, null=True, blank=True, editable=False)

SEVERITIES = {'Info': 4, 'Low': 3, 'Medium': 2,
'High': 1, 'Critical': 0}
Expand Down Expand Up @@ -867,6 +870,7 @@ def save(self, *args, **kwargs):
auditlog.register(Product)
auditlog.register(Test)
auditlog.register(Risk_Acceptance)
auditlog.register(Finding_Template)

# Register tagging for models
tag_register(Product)
Expand Down
3 changes: 2 additions & 1 deletion dojo/reports/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,10 @@ def report_url_resolver(request):
url_resolver = request.META['HTTP_X_FORWARDED_PROTO'] + "://" + request.META['HTTP_X_FORWARDED_FOR']
except:
url_resolver = request.scheme + "://" + request.META['HTTP_HOST']
pass

return url_resolver


def report_builder(request):
add_breadcrumb(title="Report Builder", top_level=True, request=request)
findings = Finding.objects.all()
Expand Down
4 changes: 4 additions & 0 deletions dojo/static/dojo/css/dojo.css
Original file line number Diff line number Diff line change
Expand Up @@ -754,6 +754,10 @@ th a {
content: "\e901";
}

.icon-user-check:before {
content: "\e975";
}

#side-menu ul a {
/*border-top: 1px solid #ddd;*/
border-radius: 4px;
Expand Down
Binary file modified dojo/static/dojo/fonts/icomoon.eot
Binary file not shown.
1 change: 1 addition & 0 deletions dojo/static/dojo/fonts/icomoon.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified dojo/static/dojo/fonts/icomoon.ttf
Binary file not shown.
Binary file modified dojo/static/dojo/fonts/icomoon.woff
Binary file not shown.

0 comments on commit cd18071

Please sign in to comment.