Skip to content

Commit a29e92c

Browse files
authored
Merge pull request DefectDojo#640 from devGregA/dev
Rules Rework
2 parents 72f55a0 + d8f2b78 commit a29e92c

File tree

13 files changed

+286
-66
lines changed

13 files changed

+286
-66
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ Proceeds are used for testing, infrastructure, etc.
109109

110110
[![Xing](https://raw.githubusercontent.com/DefectDojo/Documentation/master/doc/img/XING_logo.png)](https://corporate.xing.com/en/about-xing/security/)
111111

112-
Interested in becoming a sponsor and having your logo displayed? Please email [email protected]
112+
Interested in becoming a sponsor and having your logo displayed? Please review our [sponsorship information](SPONSORING.md) or email [email protected]
113113

114114
# License
115115

SPONSORING.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
On April 5th, 2018, OWASP clarified their sponsorship requirements to note that time, software, or any other quantifiable contribution can be counted towards the $1000 threshold outlined by the [OWASP Global Policy](https://www.owasp.org/index.php/Project_Sponsorship_Operational_Guidelines).
2+
3+
Below is our sponsorship guidelines to provide further clarification specific to our project for non-monetary contributions:
4+
5+
**Maintainers**
6+
7+
As a maintainer, you invest a significant number of hours reviewing code, contributing code, validating architecture, and helping the community. With the time you contribute, maintainers are entitled to name a sponsor, whether it be your employer or yourself, as long as you have written permission to have the entity’s logo displayed on the wiki and on our github repository. Should your relationship end with the entity you have designated (i.e. change of jobs) the entity will be grandfathered for calendar year at the time of your change.
8+
9+
**Contributors**
10+
11+
Contributors who earn 100 Sponsorship Points in a calendar year will qualify for sponsorship status. Sponsorship Points are available in the following forms.
12+
13+
For reporting a bug that is verified, the contributor will receive 10 point.
14+
15+
For merged pull requests, points will be awarded as follows:
16+
17+
1 - 10 lines = 1 Point
18+
19+
11 - 20 lines = 2 Points
20+
21+
21- 30 lines = 3 Points
22+
23+
Etc, etc
24+
25+
Issues will also be labeled with points. When a fix is merged, the contributor will receive the corresponding points, and the points associated with the line rewards above.

dojo/forms.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
Development_Environment, Dojo_User, Scan, Endpoint, Stub_Finding, Finding_Template, Report, FindingImage, \
1919
JIRA_Issue, JIRA_PKey, JIRA_Conf, UserContactInfo, Tool_Type, Tool_Configuration, Tool_Product_Settings, \
2020
Cred_User, Cred_Mapping, System_Settings, Notifications, Languages, Language_Type, App_Analysis, Objects, \
21-
Benchmark_Product, Benchmark_Requirement, Benchmark_Product_Summary, Rule
21+
Benchmark_Product, Benchmark_Requirement, Benchmark_Product_Summary, Rule, Child_Rule
2222

2323
RE_DATE = re.compile(r'(\d{4})-(\d\d?)-(\d\d?)$')
2424

@@ -1637,15 +1637,23 @@ class Meta:
16371637
exclude = ['key_product']
16381638

16391639

1640-
RuleFormSet = modelformset_factory(Rule, extra=2, max_num=10, exclude=[''], can_delete=True)
1640+
class ChildRuleForm(forms.ModelForm):
1641+
1642+
class Meta:
1643+
model = Child_Rule
1644+
exclude = ['key_product']
1645+
1646+
1647+
RuleFormSet = modelformset_factory(Child_Rule, extra=2, max_num=10, exclude=[''], can_delete=True)
16411648

16421649

16431650
class DeleteRuleForm(forms.ModelForm):
1651+
id = forms.IntegerField(required=True,
1652+
widget=forms.widgets.HiddenInput())
16441653

16451654
class Meta:
16461655
model = Rule
1647-
exclude = ['']
1648-
1656+
fields = ('id',)
16491657

16501658
class CredUserForm(forms.ModelForm):
16511659
# selenium_script = forms.FileField(widget=forms.widgets.FileInput(

dojo/models.py

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1820,17 +1820,17 @@ class Meta:
18201820
unique_together = [('product', 'benchmark_type')]
18211821

18221822

1823-
product_opts = [f.name for f in Product._meta.fields]
1824-
test_opts = [f.name for f in Test._meta.fields]
1825-
test_type_opts = [f.name for f in Test_Type._meta.fields]
1823+
# product_opts = [f.name for f in Product._meta.fields]
1824+
# test_opts = [f.name for f in Test._meta.fields]
1825+
# test_type_opts = [f.name for f in Test_Type._meta.fields]
18261826
finding_opts = [f.name for f in Finding._meta.fields]
1827-
endpoint_opts = [f.name for f in Endpoint._meta.fields]
1828-
engagement_opts = [f.name for f in Engagement._meta.fields]
1829-
product_type_opts = [f.name for f in Product_Type._meta.fields]
1830-
single_options = product_opts + test_opts + test_type_opts + finding_opts + \
1831-
endpoint_opts + engagement_opts + product_type_opts
1827+
# endpoint_opts = [f.name for f in Endpoint._meta.fields]
1828+
# engagement_opts = [f.name for f in Engagement._meta.fields]
1829+
# product_type_opts = [f.name for f in Product_Type._meta.fields]
1830+
# single_options = product_opts + test_opts + test_type_opts + finding_opts + \
1831+
# endpoint_opts + engagement_opts + product_type_opts
18321832
all_options = []
1833-
for x in single_options:
1833+
for x in finding_opts:
18341834
all_options.append((x, x))
18351835
operator_options = (('Matches', 'Matches'),
18361836
('Contains', 'Contains'))
@@ -1846,10 +1846,13 @@ class Rule(models.Model):
18461846
enabled = models.BooleanField(default=True)
18471847
text = models.TextField()
18481848
operator = models.CharField(max_length=30, choices=operator_options)
1849+
"""
18491850
model_object_options = (('Product', 'Product'),
18501851
('Engagement', 'Engagement'), ('Test', 'Test'),
18511852
('Finding', 'Finding'), ('Endpoint', 'Endpoint'),
18521853
('Product Type', 'Product_Type'), ('Test Type', 'Test_Type'))
1854+
"""
1855+
model_object_options = (('Finding', 'Finding'),)
18531856
model_object = models.CharField(max_length=30, choices=model_object_options)
18541857
match_field = models.CharField(max_length=200, choices=all_options)
18551858
match_text = models.TextField()
@@ -1858,10 +1861,28 @@ class Rule(models.Model):
18581861
# TODO: Add or ?
18591862
# and_rules = models.ManyToManyField('self')
18601863
applied_field = models.CharField(max_length=200, choices=(all_options))
1861-
child_rules = models.ManyToManyField('self', editable=False, null=True)
1864+
child_rules = models.ManyToManyField('self', editable=False)
18621865
parent_rule = models.ForeignKey('self', editable=False, null=True)
18631866

18641867

1868+
class Child_Rule(models.Model):
1869+
# add UI notification to let people know what rules were applied
1870+
operator = models.CharField(max_length=30, choices=operator_options)
1871+
"""
1872+
model_object_options = (('Product', 'Product'),
1873+
('Engagement', 'Engagement'), ('Test', 'Test'),
1874+
('Finding', 'Finding'), ('Endpoint', 'Endpoint'),
1875+
('Product Type', 'Product_Type'), ('Test Type', 'Test_Type'))
1876+
"""
1877+
model_object_options = (('Finding', 'Finding'),)
1878+
model_object = models.CharField(max_length=30, choices=model_object_options)
1879+
match_field = models.CharField(max_length=200, choices=all_options)
1880+
match_text = models.TextField()
1881+
# TODO: Add or ?
1882+
# and_rules = models.ManyToManyField('self')
1883+
parent_rule = models.ForeignKey(Rule, editable=False, null=True)
1884+
1885+
18651886
class FieldRule(models.Model):
18661887
field = models.CharField(max_length=200)
18671888
update_options = (('Append', 'Append'),

dojo/rules/urls.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,7 @@
66
url(r'^rule/add', views.new_rule, name='Add Rule'),
77
url(r'^rule/(?P<pid>\d+)/edit$', views.edit_rule,
88
name='Edit Rule'),
9+
url(r'^rule/(?P<pid>\d+)/add_child', views.add_child,
10+
name='Add Child'),
911
url(r'^rule/(?P<tid>\d+)/delete$', views.delete_rule,
1012
name='Delete Rule'), ]

dojo/rules/views.py

Lines changed: 64 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Standard library imports
22
import json
33
import logging
4+
import sys
45

56
# Third party imports
67
from django.contrib import messages
@@ -12,8 +13,8 @@
1213
# Local application/library imports
1314
from dojo.models import Rule,\
1415
System_Settings, Finding, Test, Test_Type, Engagement, \
15-
Product, Product_Type
16-
from dojo.forms import RuleFormSet, DeleteRuleForm
16+
Product, Product_Type, Child_Rule
17+
from dojo.forms import RuleFormSet, DeleteRuleForm, RuleForm
1718
from dojo.utils import add_breadcrumb
1819

1920
logger = logging.getLogger(__name__)
@@ -45,23 +46,50 @@ def rules(request):
4546
'rules': initial_queryset})
4647

4748

48-
@user_passes_test(lambda u: u.is_staff)
4949
def new_rule(request):
5050
if request.method == 'POST':
51-
form = RuleFormSet(request.POST)
52-
match_f = request.POST.get('match_field')
53-
apply_f = request.POST.get('applied_field')
51+
form = RuleForm(request.POST)
5452
if form.is_valid():
55-
form.save()
53+
rule = form.save()
5654
messages.add_message(request,
5755
messages.SUCCESS,
5856
'Rule created successfully.',
5957
extra_tags='alert-success')
58+
if "_Add Child" in request.POST:
59+
return HttpResponseRedirect(reverse('Add Child', args=(rule.id,)))
6060
return HttpResponseRedirect(reverse('rules'))
61-
form = RuleFormSet(queryset=Rule.objects.none())
61+
form = RuleForm()
62+
add_breadcrumb(title="New Dojo Rule", top_level=False, request=request)
63+
return render(request, 'dojo/new_rule2.html',
64+
{'form': form,
65+
'finding_fields': finding_fields,
66+
'test_fields': test_fields,
67+
'engagement_fields': engagement_fields,
68+
'product_fields': product_fields,
69+
'product_type_fields': product_type_fields,
70+
'field_dictionary': json.dumps(field_dictionary)})
71+
72+
73+
@user_passes_test(lambda u: u.is_staff)
74+
def add_child(request, pid):
75+
rule = get_object_or_404(Rule, pk=pid)
76+
if request.method == 'POST':
77+
forms = RuleFormSet(request.POST)
78+
for form in forms:
79+
if form.is_valid():
80+
cr = form.save(commit=False)
81+
cr.parent_rule = rule
82+
cr.save()
83+
messages.add_message(request,
84+
messages.SUCCESS,
85+
'Rule created successfully.',
86+
extra_tags='alert-success')
87+
return HttpResponseRedirect(reverse('rules'))
88+
form = RuleFormSet(queryset=Child_Rule.objects.filter(parent_rule=rule))
6289
add_breadcrumb(title="New Dojo Rule", top_level=False, request=request)
6390
return render(request, 'dojo/new_rule.html',
6491
{'form': form,
92+
'pid': pid,
6593
'finding_fields': finding_fields,
6694
'test_fields': test_fields,
6795
'engagement_fields': engagement_fields,
@@ -75,18 +103,20 @@ def edit_rule(request, pid):
75103
pt = get_object_or_404(Rule, pk=pid)
76104
children = Rule.objects.filter(parent_rule=pt)
77105
all_rules = children | Rule.objects.filter(pk=pid)
78-
form = RuleFormSet(queryset=all_rules)
106+
form = RuleForm(instance=pt)
79107
if request.method == 'POST':
80-
form = RuleFormSet(request.POST)
108+
form = RuleForm(request.POST, instance=pt)
81109
if form.is_valid():
82110
pt = form.save()
83111
messages.add_message(request,
84112
messages.SUCCESS,
85113
'Rule updated successfully.',
86114
extra_tags='alert-success')
115+
if "_Add Child" in request.POST:
116+
return HttpResponseRedirect(reverse('Add Child', args=(pt.id,)))
87117
return HttpResponseRedirect(reverse('rules'))
88118
add_breadcrumb(title="Edit Rule", top_level=False, request=request)
89-
return render(request, 'dojo/edit_rule2.html', {
119+
return render(request, 'dojo/edit_rule.html', {
90120
'name': 'Edit Rule',
91121
'metric': False,
92122
'user': request.user,
@@ -96,32 +126,39 @@ def edit_rule(request, pid):
96126

97127

98128
@user_passes_test(lambda u: u.is_staff)
99-
def delete_rule(request, pid):
100-
product = get_object_or_404(Rule, pk=pid)
101-
form = DeleteRuleForm(instance=product)
129+
def delete_rule(request, tid):
130+
rule = get_object_or_404(Rule, pk=tid)
131+
form = DeleteRuleForm(instance=rule)
102132

103133
from django.contrib.admin.utils import NestedObjects
104134
from django.db import DEFAULT_DB_ALIAS
105135

106136
collector = NestedObjects(using=DEFAULT_DB_ALIAS)
107-
collector.collect([product])
137+
collector.collect([rule])
108138
rels = collector.nested()
109139

110140
if request.method == 'POST':
111-
if 'id' in request.POST and str(product.id) == request.POST['id']:
112-
form = DeleteRuleForm(request.POST, instance=product)
113-
if form.is_valid():
114-
product.delete()
115-
messages.add_message(request,
116-
messages.SUCCESS,
117-
'Rule deleted.',
118-
extra_tags='alert-success')
119-
return HttpResponseRedirect(reverse('Rules'))
141+
print >> sys.stderr, 'id' in request.POST
142+
print >> sys.stderr, str(rule.id) == request.POST['id']
143+
print >> sys.stderr, str(rule.id) == request.POST['id']
144+
# if 'id' in request.POST and str(rule.id) == request.POST['id']:
145+
form = DeleteRuleForm(request.POST, instance=rule)
146+
print >> sys.stderr, form.is_valid()
147+
print >> sys.stderr, form.errors
148+
print >> sys.stderr, form.non_field_errors()
149+
print >> sys.stderr, 'id' in request.POST
150+
if form.is_valid():
151+
rule.delete()
152+
messages.add_message(request,
153+
messages.SUCCESS,
154+
'Rule deleted.',
155+
extra_tags='alert-success')
156+
return HttpResponseRedirect(reverse('rules'))
120157

121-
add_breadcrumb(parent=product, title="Delete", top_level=False, request=request)
158+
add_breadcrumb(parent=rule, title="Delete", top_level=False, request=request)
122159
system_settings = System_Settings.objects.get()
123-
return render(request, 'dojo/delete_product.html',
124-
{'product': product,
160+
return render(request, 'dojo/delete_rule.html',
161+
{'rule': rule,
125162
'form': form,
126163
'active_tab': 'findings',
127164
'system_settings': system_settings,

dojo/templates/dojo/delete_product.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
{% extends "base.html" %}
1+
delete_product.html{% extends "base.html" %}
22
{% block content %}
33
<h3> Delete Product {{ product }}</h3>
44
<p>
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
delete_product.html{% extends "base.html" %}
2+
{% block content %}
3+
<h3> Delete Rule {{ rule }}</h3>
4+
<p>
5+
Deleting this rule will remove any related objects associated
6+
with it. These relationships are listed below:
7+
</p>
8+
<div class="danger-zone panel panel-danger">
9+
<div class="panel-heading">
10+
<h3>Danger Zone</h3>
11+
</div>
12+
{% if rels|length > 1 %}
13+
<ul class="left">{{ rels|unordered_list }}</ul>
14+
{% else %}
15+
<p class="text-center">No relationships found.</p>
16+
{% endif %}
17+
<form class="form-horizontal" method="post">
18+
{% csrf_token %}
19+
{{ form }}
20+
21+
<div class="form-group">
22+
<button class="btn btn-danger" type="submit" name="delete_name" value="delete_test">Delete Rule</button>
23+
</div>
24+
</form>
25+
</div>
26+
<br/>
27+
<br/>
28+
{% endblock %}

0 commit comments

Comments
 (0)