dradis-calculator_mitre 4.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +2 -0
  3. data/LICENSE +339 -0
  4. data/README.md +45 -0
  5. data/Rakefile +1 -0
  6. data/app/assets/data/dradis/plugins/calculators/mitre/mitre_data.json +6066 -0
  7. data/app/assets/javascripts/dradis/plugins/calculators/mitre/base.js +5 -0
  8. data/app/assets/javascripts/dradis/plugins/calculators/mitre/manifests/hera.js +1 -0
  9. data/app/assets/javascripts/dradis/plugins/calculators/mitre/mitre_calculator.js.erb +283 -0
  10. data/app/assets/stylesheets/dradis/plugins/calculators/mitre/base.css.scss +10 -0
  11. data/app/controllers/dradis/plugins/calculators/mitre/base_controller.rb +9 -0
  12. data/app/controllers/dradis/plugins/calculators/mitre/issues_controller.rb +29 -0
  13. data/app/models/dradis/plugins/calculators/mitre/v1.rb +26 -0
  14. data/app/views/dradis/plugins/calculators/mitre/_ce_tools_menu.html.erb +3 -0
  15. data/app/views/dradis/plugins/calculators/mitre/_tools_menu.html.erb +5 -0
  16. data/app/views/dradis/plugins/calculators/mitre/base/_v1.html.erb +67 -0
  17. data/app/views/dradis/plugins/calculators/mitre/base/index.html.erb +5 -0
  18. data/app/views/dradis/plugins/calculators/mitre/issues/_show-content.html.erb +20 -0
  19. data/app/views/dradis/plugins/calculators/mitre/issues/_show-tabs.html.erb +3 -0
  20. data/app/views/dradis/plugins/calculators/mitre/issues/edit.html.erb +32 -0
  21. data/app/views/layouts/dradis/plugins/calculators/mitre/base.html.erb +28 -0
  22. data/config/routes.rb +12 -0
  23. data/lib/dradis/plugins/calculators/mitre/engine.rb +36 -0
  24. data/lib/dradis/plugins/calculators/mitre/gem_version.rb +21 -0
  25. data/lib/dradis/plugins/calculators/mitre/version.rb +15 -0
  26. data/lib/dradis-calculator_mitre.rb +13 -0
  27. metadata +109 -0
@@ -0,0 +1,5 @@
1
+ //= require jquery3
2
+ //= require popper
3
+ //= require bootstrap
4
+
5
+ //= require dradis/plugins/calculators/mitre/mitre_calculator
@@ -0,0 +1 @@
1
+ //= require dradis/plugins/calculators/mitre/mitre_calculator
@@ -0,0 +1,283 @@
1
+ document.addEventListener('turbo:load', () => {
2
+ if (!document.querySelector('[data-behavior~=mitre-calc]')) return;
3
+
4
+ class MitreCalculator {
5
+ constructor() {
6
+ this.matrices = ['enterprise', 'mobile', 'ics'];
7
+ this.mitreData = {};
8
+ this.result = document.querySelector('[data-behavior="mitre-result"]');
9
+ this.selects = {};
10
+ this.init();
11
+ }
12
+
13
+ async init() {
14
+ try {
15
+ await this.loadMitreData();
16
+ this.initializeSelectors();
17
+ this.setupEventListeners();
18
+ this.preSelectFromResult();
19
+ } catch (error) {
20
+ console.error('Failed to initialize MITRE Calculator:', error);
21
+ }
22
+ }
23
+
24
+ async loadMitreData() {
25
+ const response = await fetch("<%= asset_path('dradis/plugins/calculators/mitre/mitre_data.json') %>");
26
+ if (!response.ok)
27
+ throw new Error(`HTTP error! status: ${response.status}`);
28
+ this.mitreData = await response.json();
29
+ }
30
+
31
+ initializeSelectors() {
32
+ this.matrices.forEach((matrix) => {
33
+ const tacticSelect = document.querySelector(
34
+ `select[data-type="${matrix}-tactic"]`
35
+ );
36
+ const techniqueSelect = document.querySelector(
37
+ `select[data-type="${matrix}-technique"]`
38
+ );
39
+ const subtechniqueSelect = document.querySelector(
40
+ `select[data-type="${matrix}-subtechnique"]`
41
+ );
42
+
43
+ this.selects[matrix] = {
44
+ tactic: tacticSelect,
45
+ technique: techniqueSelect,
46
+ subtechnique: subtechniqueSelect,
47
+ };
48
+
49
+ this.setupMatrix(matrix);
50
+ });
51
+ }
52
+
53
+ setupMatrix(matrix) {
54
+ const { tactic, technique, subtechnique } = this.selects[matrix];
55
+
56
+ this.setPrompt(tactic, 'Select a tactic');
57
+ this.setPrompt(technique, 'Select a technique');
58
+ this.setPrompt(subtechnique, 'Select a sub-technique');
59
+ technique.disabled = true;
60
+ subtechnique.disabled = true;
61
+
62
+ this.mitreData[matrix].tactics.forEach((tacticData) => {
63
+ const option = document.createElement('option');
64
+ option.value = tacticData.id;
65
+ option.textContent = tacticData.name;
66
+ tactic.appendChild(option);
67
+ });
68
+ }
69
+
70
+ setupEventListeners() {
71
+ this.matrices.forEach((matrix) => {
72
+ const { tactic, technique, subtechnique } = this.selects[matrix];
73
+
74
+ tactic.addEventListener('change', () => {
75
+ this.handleTacticChange(matrix);
76
+ });
77
+
78
+ technique.addEventListener('change', () => {
79
+ this.handleTechniqueChange(matrix);
80
+ });
81
+
82
+ subtechnique.addEventListener('change', () => {
83
+ this.handleSubtechniqueChange(matrix);
84
+ });
85
+ });
86
+ }
87
+
88
+ handleTacticChange(matrix) {
89
+ const { tactic, technique, subtechnique } = this.selects[matrix];
90
+ const selectedTactic = this.mitreData[matrix].tactics.find(
91
+ (t) => t.id === tactic.value
92
+ );
93
+
94
+ this.setPrompt(technique, 'Select a technique');
95
+ this.setPrompt(subtechnique, 'Select a sub-technique');
96
+ technique.disabled = true;
97
+ subtechnique.disabled = true;
98
+
99
+ if (selectedTactic) {
100
+ this.populateTechniques(selectedTactic, technique);
101
+ technique.disabled = false;
102
+ }
103
+
104
+ this.updateTacticResults(matrix, selectedTactic);
105
+ }
106
+
107
+ handleTechniqueChange(matrix) {
108
+ const { tactic, technique, subtechnique } = this.selects[matrix];
109
+ const selectedTactic = this.mitreData[matrix].tactics.find(
110
+ (t) => t.id === tactic.value
111
+ );
112
+ const selectedTechnique = selectedTactic.techniques.find(
113
+ (tech) => tech.id === technique.value
114
+ );
115
+
116
+ this.setPrompt(subtechnique, 'Select a sub-technique');
117
+ subtechnique.disabled = true;
118
+
119
+ if (selectedTechnique.subtechniques.length > 0) {
120
+ this.populateSubtechniques(selectedTechnique, subtechnique);
121
+ subtechnique.disabled = false;
122
+ }
123
+
124
+ this.updateTechniqueResults(matrix, selectedTechnique);
125
+ }
126
+
127
+ handleSubtechniqueChange(matrix) {
128
+ const { tactic, technique, subtechnique } = this.selects[matrix];
129
+ const selectedTactic = this.mitreData[matrix].tactics.find(
130
+ (t) => t.id === tactic.value
131
+ );
132
+ const selectedTechnique = selectedTactic.techniques.find(
133
+ (tech) => tech.id === technique.value
134
+ );
135
+ const selectedSubtechnique = selectedTechnique.subtechniques.find(
136
+ (s) => s.id === subtechnique.value
137
+ );
138
+
139
+ this.updateSubtechniqueResults(matrix, selectedSubtechnique);
140
+ }
141
+
142
+ preSelectFromResult() {
143
+ this.matrices.forEach((matrix) => {
144
+ this.preSelectMatrix(matrix);
145
+ });
146
+ }
147
+
148
+ preSelectMatrix(matrix) {
149
+ const base = `MITRE.${this.titleCase(matrix)}`;
150
+ const tacticId = this.getResultValue(`${base}.Tactic.ID`);
151
+
152
+ if (!tacticId || tacticId === 'N/A') return;
153
+
154
+ const { tactic, technique, subtechnique } = this.selects[matrix];
155
+
156
+ if (this.selectOption(tactic, tacticId)) {
157
+ const selectedTactic = this.mitreData[matrix].tactics.find(
158
+ (t) => t.id === tacticId
159
+ );
160
+ this.populateTechniques(selectedTactic, technique);
161
+ technique.disabled = false;
162
+
163
+ const techniqueId = this.getResultValue(`${base}.Technique.ID`);
164
+ if (
165
+ techniqueId &&
166
+ techniqueId !== 'N/A' &&
167
+ this.selectOption(technique, techniqueId)
168
+ ) {
169
+ const selectedTechnique = selectedTactic.techniques.find(
170
+ (t) => t.id === techniqueId
171
+ );
172
+
173
+ if (selectedTechnique.subtechniques.length > 0) {
174
+ this.populateSubtechniques(selectedTechnique, subtechnique);
175
+ subtechnique.disabled = false;
176
+
177
+ const subtechniqueId = this.getResultValue(
178
+ `${base}.Sub-technique.ID`
179
+ );
180
+ if (subtechniqueId && subtechniqueId !== 'N/A') {
181
+ this.selectOption(subtechnique, subtechniqueId);
182
+ }
183
+ }
184
+ }
185
+ }
186
+ }
187
+
188
+ getResultValue(label) {
189
+ // Captures the line with the label and returns its value
190
+ const regex = new RegExp(
191
+ `\\#\\[${this.escapeRegex(label)}\\]\\#\\n(.*?)(?=\\n|$)`,
192
+ 'i'
193
+ );
194
+ const match = this.result.value.match(regex);
195
+ return match ? match[1].trim() : null;
196
+ }
197
+
198
+ selectOption(select, value) {
199
+ const option = select.querySelector(`option[value="${value}"]`);
200
+ if (option) {
201
+ select.value = value;
202
+ return true;
203
+ }
204
+ return false;
205
+ }
206
+
207
+ escapeRegex(string) {
208
+ // Escapes special characters in a regex string since . and [] have special meanings
209
+ return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
210
+ }
211
+
212
+ setPrompt(select, prompt) {
213
+ select.innerHTML = `<option value="" disabled selected>${prompt}</option>`;
214
+ }
215
+
216
+ populateTechniques(tactic, select) {
217
+ tactic.techniques.forEach((tech) => {
218
+ const option = document.createElement('option');
219
+ option.value = tech.id;
220
+ option.textContent = tech.name;
221
+ select.appendChild(option);
222
+ });
223
+ }
224
+
225
+ populateSubtechniques(technique, select) {
226
+ technique.subtechniques.forEach((sub) => {
227
+ const option = document.createElement('option');
228
+ option.value = sub.id;
229
+ option.textContent = sub.name;
230
+ select.appendChild(option);
231
+ });
232
+ }
233
+
234
+ updateResult(label, value) {
235
+ // Captures the line with the label and replaces its value
236
+ const regex = new RegExp(`(\\#\\[${label}\\]\\#\\n)(.*?)(\\n|$)`, 'gi');
237
+ this.result.value = this.result.value.replace(regex, `$1${value}$3`);
238
+ }
239
+
240
+ updateTacticResults(matrix, tactic) {
241
+ const base = `MITRE.${this.titleCase(matrix)}`;
242
+ this.updateResult(`${base}.Tactic`, tactic.name);
243
+ this.updateResult(`${base}.Tactic.ID`, tactic.id);
244
+ this.resetTechniqueAndSubtechniqueResults(matrix);
245
+ }
246
+
247
+ updateTechniqueResults(matrix, technique) {
248
+ const base = `MITRE.${this.titleCase(matrix)}`;
249
+ this.updateResult(`${base}.Technique`, technique.name);
250
+ this.updateResult(`${base}.Technique.ID`, technique.id);
251
+ this.resetSubtechniqueResults(matrix);
252
+ }
253
+
254
+ updateSubtechniqueResults(matrix, subtechnique) {
255
+ const base = `MITRE.${this.titleCase(matrix)}`;
256
+ this.updateResult(`${base}.Sub-technique`, subtechnique.name);
257
+ this.updateResult(`${base}.Sub-technique.ID`, subtechnique.id);
258
+ }
259
+
260
+ resetTechniqueAndSubtechniqueResults(matrix) {
261
+ this.resetTechniqueResults(matrix);
262
+ this.resetSubtechniqueResults(matrix);
263
+ }
264
+
265
+ resetTechniqueResults(matrix) {
266
+ const base = `MITRE.${this.titleCase(matrix)}`;
267
+ this.updateResult(`${base}.Technique`, 'N/A');
268
+ this.updateResult(`${base}.Technique.ID`, 'N/A');
269
+ }
270
+
271
+ resetSubtechniqueResults(matrix) {
272
+ const base = `MITRE.${this.titleCase(matrix)}`;
273
+ this.updateResult(`${base}.Sub-technique`, 'N/A');
274
+ this.updateResult(`${base}.Sub-technique.ID`, 'N/A');
275
+ }
276
+
277
+ titleCase(str) {
278
+ return str.charAt(0).toUpperCase() + str.slice(1);
279
+ }
280
+ }
281
+
282
+ new MitreCalculator();
283
+ });
@@ -0,0 +1,10 @@
1
+ @import '_bootstrap';
2
+
3
+ @import 'hera/variables';
4
+ @import 'hera/base';
5
+
6
+ #mitre_fields {
7
+ font-size: 0.9rem;
8
+ height: 81em;
9
+ margin-bottom: 1rem;
10
+ }
@@ -0,0 +1,9 @@
1
+ module Dradis::Plugins::Calculators::MITRE
2
+ class BaseController < ActionController::Base
3
+ def index
4
+ @issue_fields = Dradis::Plugins::Calculators::MITRE::V1::FIELDS.map do |field|
5
+ "#[#{field}]#\nN/A"
6
+ end.join("\n\n")
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,29 @@
1
+ module Dradis::Plugins::Calculators::MITRE
2
+ class IssuesController < ::IssuesController
3
+
4
+ before_action only: :edit
5
+
6
+ def edit
7
+ @issue_fields = Dradis::Plugins::Calculators::MITRE::V1::FIELDS.map do |field|
8
+ value = @issue.fields[field]
9
+ value = 'N/A' if value.blank?
10
+ "#[#{field}]#\n#{value}"
11
+ end.join("\n\n")
12
+ end
13
+
14
+ def update
15
+ raw = params[:mitre_fields].to_s
16
+ mitre_fields = Hash[*raw.scan(FieldParser::FIELDS_REGEX).flatten.map(&:strip)]
17
+
18
+ mitre_fields.each do |name, value|
19
+ @issue.set_field(name, value)
20
+ end
21
+
22
+ if @issue.save
23
+ redirect_to main_app.project_issue_path(current_project, @issue), notice: 'MITRE fields updated.'
24
+ else
25
+ render :edit
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,26 @@
1
+ module Dradis::Plugins::Calculators::MITRE
2
+ class V1
3
+ FIELD_NAMES = %i[
4
+ Enterprise.Tactic
5
+ Enterprise.Tactic.ID
6
+ Enterprise.Technique
7
+ Enterprise.Technique.ID
8
+ Enterprise.Sub-technique
9
+ Enterprise.Sub-technique.ID
10
+ Mobile.Tactic
11
+ Mobile.Tactic.ID
12
+ Mobile.Technique
13
+ Mobile.Technique.ID
14
+ Mobile.Sub-technique
15
+ Mobile.Sub-technique.ID
16
+ ICS.Tactic
17
+ ICS.Tactic.ID
18
+ ICS.Technique
19
+ ICS.Technique.ID
20
+ ICS.Sub-technique
21
+ ICS.Sub-technique.ID
22
+ ].freeze
23
+
24
+ FIELDS = FIELD_NAMES.map { |name| "MITRE.#{name}".freeze }.freeze
25
+ end
26
+ end
@@ -0,0 +1,3 @@
1
+ <li>
2
+ <%= link_to 'Risk Calculators - MITRE ATT&CK', mitre_calculator.calculators_mitre_path, class: 'dropdown-item', data: { turbolinks: false } %>
3
+ </li>
@@ -0,0 +1,5 @@
1
+ <% if defined?(Dradis::Pro) && !current_user.role?(:contributor) %>
2
+ <li>
3
+ <%= link_to 'Risk Calculators - MITRE ATT&CK', mitre_calculator.calculators_mitre_path, class: 'dropdown-item', data: { turbolinks: false } %>
4
+ </li>
5
+ <% end %>
@@ -0,0 +1,67 @@
1
+ <div data-behavior="mitre-calc">
2
+ <ul class="nav nav-pills w-100">
3
+ <li class="nav-item">
4
+ <a href="#enterprise" class="nav-link active" data-bs-toggle="tab"><strong>Enterprise</strong></a>
5
+ </li>
6
+ <li class="nav-item">
7
+ <a href="#mobile" class="nav-link" data-bs-toggle="tab"><strong>Mobile</strong></a>
8
+ </li>
9
+ <li class="nav-item">
10
+ <a href="#ics" class="nav-link" data-bs-toggle="tab"><strong>ICS</strong></a>
11
+ </li>
12
+ </ul>
13
+
14
+ <div class="row mt-3">
15
+ <div class="col-md-6">
16
+ <div class="tab-content">
17
+ <div id="enterprise" class="tab-pane active show">
18
+ <div class="mb-4">
19
+ <label class="form-label" for="enterprise-tactic-select">Enterprise Tactic</label>
20
+ <select id="enterprise-tactic-select" class="form-select" data-type="enterprise-tactic" data-combobox-config="no-combobox"></select>
21
+ </div>
22
+ <div class="mb-4">
23
+ <label class="form-label" for="enterprise-technique-select">Enterprise Technique</label>
24
+ <select id="enterprise-technique-select" class="form-select" data-type="enterprise-technique" data-combobox-config="no-combobox"></select>
25
+ </div>
26
+ <div class="mb-1">
27
+ <label class="form-label" for="enterprise-subtechnique-select">Enterprise Sub-Technique</label>
28
+ <select id="enterprise-subtechnique-select" class="form-select" data-type="enterprise-subtechnique" data-combobox-config="no-combobox"></select>
29
+ </div>
30
+ </div>
31
+ <div id="mobile" class="tab-pane">
32
+ <div class="mb-4">
33
+ <label class="form-label" for="mobile-tactic-select">Mobile Tactic</label>
34
+ <select id="mobile-tactic-select" class="form-select" data-type="mobile-tactic" data-combobox-config="no-combobox"></select>
35
+ </div>
36
+ <div class="mb-4">
37
+ <label class="form-label" for="mobile-technique-select">Mobile Technique</label>
38
+ <select id="mobile-technique-select" class="form-select" data-type="mobile-technique" data-combobox-config="no-combobox"></select>
39
+ </div>
40
+ <div class="mb-1">
41
+ <label class="form-label" for="mobile-subtechnique-select">Mobile Sub-Technique</label>
42
+ <select id="mobile-subtechnique-select" class="form-select" data-type="mobile-subtechnique" data-combobox-config="no-combobox"></select>
43
+ </div>
44
+ </div>
45
+ <div id="ics" class="tab-pane">
46
+ <div class="mb-4">
47
+ <label class="form-label" for="ics-tactic-select">ICS Tactic</label>
48
+ <select id="ics-tactic-select" class="form-select" data-type="ics-tactic" data-combobox-config="no-combobox"></select>
49
+ </div>
50
+ <div class="mb-4">
51
+ <label class="form-label" for="ics-technique-select">ICS Technique</label>
52
+ <select id="ics-technique-select" class="form-select" data-type="ics-technique" data-combobox-config="no-combobox"></select>
53
+ </div>
54
+ <div class="mb-1">
55
+ <label class="form-label" for="ics-subtechnique-select">ICS Sub-Technique</label>
56
+ <select id="ics-subtechnique-select" class="form-select" data-type="ics-subtechnique" data-combobox-config="no-combobox"></select>
57
+ </div>
58
+ </div>
59
+ </div>
60
+ </div>
61
+
62
+ <div class="col-md-6 d-flex flex-column">
63
+ <label for="mitre_fields" class="form-label">Result</label>
64
+ <%= text_area_tag :mitre_fields, @issue_fields, class: 'form-control flex-grow-1', data: { behavior: 'mitre-result' } %>
65
+ </div>
66
+ </div>
67
+ </div>
@@ -0,0 +1,5 @@
1
+ <%= content_tag :div, class: 'page-header' do %>
2
+ <h1>MITRE ATT&CK Calculator</h1>
3
+ <% end %>
4
+
5
+ <%= render 'dradis/plugins/calculators/mitre/base/v1' %>
@@ -0,0 +1,20 @@
1
+ <div class="tab-pane" id="mitre-tab">
2
+ <div class="inner">
3
+ <h4 class="header-underline">MITRE ATT&CK -
4
+ <span class="actions">
5
+ <%= link_to mitre_calculator.mitre_project_issue_path(current_project, @issue) do %>
6
+ <i class="fa-solid fa-pencil"></i> Edit
7
+ <% end %>
8
+ </h4>
9
+
10
+ <div class="mb-4 content-textile">
11
+ <%=
12
+ markup(
13
+ @issue.fields
14
+ .select { |k,v| Dradis::Plugins::Calculators::MITRE::V1::FIELDS.include?(k) }
15
+ .map { |k,v| "#[#{k}]#\n#{v}" }.join("\n\n")
16
+ )
17
+ %>
18
+ </div>
19
+ </div>
20
+ </div>
@@ -0,0 +1,3 @@
1
+ <li class="nav-item">
2
+ <a href="#mitre-tab" data-bs-toggle="tab" class="nav-link"><i class="fa-solid fa-calculator"></i> MITRE</a>
3
+ </li>
@@ -0,0 +1,32 @@
1
+ <% content_for :title, 'Edit MITRE' %>
2
+
3
+ <% content_for :sidebar do %>
4
+ <%= render 'issues/sidebar' %>
5
+ <% end %>
6
+
7
+ <ol class="breadcrumb">
8
+ <li class="breadcrumb-item">
9
+ <%= link_to current_project.name, main_app.project_path(current_project) %>
10
+ </li>
11
+ <li class="breadcrumb-item">
12
+ <%= link_to 'All issues', main_app.project_issues_path(current_project) %>
13
+ </li>
14
+ <li class="breadcrumb-item">
15
+ <%= link_to @issue.title? ? @issue.title : "Issue ##{@issue.id}", main_app.project_issue_path(current_project, @issue) %>
16
+ </li>
17
+ <li class="breadcrumb-item active">
18
+ MITRE ATT&CK
19
+ </li>
20
+ </ol>
21
+
22
+ <div class="content-container">
23
+ <h4 class="header-underline mb-2">Edit MITRE ATT&CK</h4>
24
+
25
+ <%= simple_form_for [:mitre, current_project, @issue], method: :patch do |f| %>
26
+ <%= render 'dradis/plugins/calculators/mitre/base/v1' %>
27
+ <div class="form-actions mt-2">
28
+ <%= f.button :submit, nil, class: 'btn btn-primary' %> or
29
+ <%= link_to 'Cancel', main_app.project_issue_path(current_project, @issue), class: 'cancel-link' %>
30
+ </div>
31
+ <% end %>
32
+ </div>
@@ -0,0 +1,28 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>MITRE ATT&CK Calculator | Dradis Framework</title>
5
+ <%= stylesheet_link_tag 'dradis/plugins/calculators/mitre/base', media: 'all', 'data-turbo-track': 'reload' %>
6
+ <%= javascript_include_tag 'dradis/plugins/calculators/mitre/base', 'data-turbo-track': 'reload' %>
7
+ <%= csrf_meta_tags %>
8
+ <%= javascript_importmap_tags %>
9
+
10
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
11
+ </head>
12
+ <body class="authenticated">
13
+ <div class="container">
14
+ <nav class="navbar">
15
+ <a href="javascript:void(0)" class="navbar-brand">MITRE ATT&CK Calculator</a>
16
+ <ul class="navbar-nav pull-right">
17
+ <li class="nav-item">
18
+ <%= link_to main_app.root_path, class: 'nav-link', data: { turbo: false } do %>
19
+ Back to the app &rarr;
20
+ <% end %>
21
+ </li>
22
+ </ul>
23
+ </nav>
24
+
25
+ <%= yield %>
26
+ </div>
27
+ </body>
28
+ </html>
data/config/routes.rb ADDED
@@ -0,0 +1,12 @@
1
+ Dradis::Plugins::Calculators::MITRE::Engine.routes.draw do
2
+ get '/calculators/mitre' => 'base#index'
3
+
4
+ resources :projects, only: [] do
5
+ resources :issues, only: [] do
6
+ member do
7
+ get 'mitre' => 'issues#edit'
8
+ patch 'mitre' => 'issues#update'
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,36 @@
1
+ module Dradis::Plugins::Calculators::MITRE
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace Dradis::Plugins::Calculators::MITRE
4
+
5
+ include Dradis::Plugins::Base
6
+ provides :addon
7
+ description 'Risk Calculators: MITRE'
8
+
9
+ initializer 'calculator_mitre.asset_precompile_paths' do |app|
10
+ app.config.assets.precompile += [
11
+ 'dradis/plugins/calculators/mitre/base.css',
12
+ 'dradis/plugins/calculators/mitre/base.js',
13
+ 'dradis/plugins/calculators/mitre/manifests/hera.css',
14
+ 'dradis/plugins/calculators/mitre/manifests/hera.js',
15
+ 'dradis/plugins/calculators/mitre/mitre_data.json'
16
+ ]
17
+ end
18
+
19
+ initializer "calculator_mitre.inflections" do |app|
20
+ ActiveSupport::Inflector.inflections do |inflect|
21
+ inflect.acronym('MITRE')
22
+ end
23
+ end
24
+
25
+ initializer 'calculator_mitre.mount_engine' do
26
+ Rails.application.routes.append do
27
+ # Enabling/disabling integrations calls Rails.application.reload_routes! we need the enable
28
+ # check inside the block to ensure the routes can be re-enabled without a server restart
29
+ if Engine.enabled?
30
+ mount Engine => '/', as: :mitre_calculator
31
+ end
32
+ end
33
+ # end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,21 @@
1
+ module Dradis
2
+ module Plugins
3
+ module Calculators
4
+ module MITRE
5
+ # Returns the version of the currently loaded Nessus as a <tt>Gem::Version</tt>
6
+ def self.gem_version
7
+ Gem::Version.new VERSION::STRING
8
+ end
9
+
10
+ module VERSION
11
+ MAJOR = 4
12
+ MINOR = 16
13
+ TINY = 0
14
+ PRE = nil
15
+
16
+ STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,15 @@
1
+ require_relative 'gem_version'
2
+
3
+ module Dradis
4
+ module Plugins
5
+ module Calculators
6
+ module MITRE
7
+ # Returns the version of the currently loaded Nessus as a
8
+ # <tt>Gem::Version</tt>.
9
+ def self.version
10
+ gem_version
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,13 @@
1
+ require 'dradis-plugins'
2
+
3
+ module Dradis
4
+ module Plugins
5
+ module Calculators
6
+ module MITRE
7
+ end
8
+ end
9
+ end
10
+ end
11
+
12
+ require 'dradis/plugins/calculators/mitre/engine'
13
+ require 'dradis/plugins/calculators/mitre/version'