csp_report 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +5 -0
  3. data/README.md +64 -24
  4. data/app/assets/javascripts/csp_report.js +2 -0
  5. data/app/assets/javascripts/csp_report/application.js +1 -0
  6. data/app/assets/javascripts/csp_report/report.js.coffee +4 -0
  7. data/app/assets/stylesheets/csp_report/bootstrap_and_overrides.css +7 -0
  8. data/app/assets/stylesheets/csp_report/csp_report.css.sass +19 -15
  9. data/app/controllers/csp_report/csp_reports_controller.rb +135 -0
  10. data/app/helpers/csp_report/headers_helpers.rb +32 -0
  11. data/app/views/csp_report/csp_reports/index.html.haml +60 -35
  12. data/app/views/csp_report/csp_reports/report_by_ip.html.haml +45 -0
  13. data/app/views/csp_report/csp_reports/report_by_rule.html.haml +46 -0
  14. data/app/views/csp_report/csp_reports/report_by_source.html.haml +46 -0
  15. data/config/routes.rb +4 -1
  16. data/lib/csp_report.rb +2 -0
  17. data/lib/csp_report/version.rb +1 -1
  18. data/lib/generators/csp_report/highcharts_include_generator.rb +14 -0
  19. data/lib/generators/csp_report/install_generator.rb +2 -0
  20. data/spec/controllers/csp_report/csp_reports_controller_spec.rb +132 -47
  21. data/spec/dummy/log/test.log +32398 -0
  22. data/spec/dummy/tmp/cache/assets/test/sass/745019acb880ec9412f97713489f02ba42209a06/csp_report.css.sassc +0 -0
  23. data/spec/dummy/tmp/cache/assets/test/sprockets/04b3e69eb694573268093bc96a12cb36 +0 -0
  24. data/spec/dummy/tmp/cache/assets/test/sprockets/05c90a274261cfca1eb2c000659570f5 +0 -0
  25. data/spec/dummy/tmp/cache/assets/test/sprockets/22f7ca496c173fb6290675535b2ea867 +0 -0
  26. data/spec/dummy/tmp/cache/assets/test/sprockets/27b25bddc744ae04b7e3f44fc2ff7b0a +0 -0
  27. data/spec/dummy/tmp/cache/assets/test/sprockets/2a6dae572905cab4d1414332fe21dd75 +0 -0
  28. data/spec/dummy/tmp/cache/assets/test/sprockets/325cfe516884cee28f48dc297246b1fc +0 -0
  29. data/spec/dummy/tmp/cache/assets/test/sprockets/391546e93f4c1edddf588160a1cedc5a +0 -0
  30. data/spec/dummy/tmp/cache/assets/test/sprockets/4949b199f7a3f61704ee406dfc99e38c +0 -0
  31. data/spec/dummy/tmp/cache/assets/test/sprockets/4f12538a5db5fd7fe75ebdcb6dc9b251 +0 -0
  32. data/spec/dummy/tmp/cache/assets/test/sprockets/50d961649ccb35a76acd4f63f6250361 +0 -0
  33. data/spec/dummy/tmp/cache/assets/test/sprockets/51522c13c1c4f9cf7dcfad7b204cf749 +0 -0
  34. data/spec/dummy/tmp/cache/assets/test/sprockets/56f7ae58d3064c45987cc434559f4dcb +0 -0
  35. data/spec/dummy/tmp/cache/assets/test/sprockets/5d4b85a2814f8d22d23ad047618fe032 +0 -0
  36. data/spec/dummy/tmp/cache/assets/test/sprockets/5d91dbec07ef096002d3f884edec8b55 +0 -0
  37. data/spec/dummy/tmp/cache/assets/test/sprockets/641ce249906eb1e5e40fe7309193e5a6 +0 -0
  38. data/spec/dummy/tmp/cache/assets/test/sprockets/71f1e2bbe4e3226070159a95e1690af9 +0 -0
  39. data/spec/dummy/tmp/cache/assets/test/sprockets/7f9fe739367238fbe3fe92a0362d8f33 +0 -0
  40. data/spec/dummy/tmp/cache/assets/test/sprockets/9b94cd42c6d3c0778772d609a4d7006d +0 -0
  41. data/spec/dummy/tmp/cache/assets/test/sprockets/b2fb1d4fcfbdd0431ec50d36c324e769 +0 -0
  42. data/spec/dummy/tmp/cache/assets/test/sprockets/bf4db1839fb7adc3305b797e33f5902c +0 -0
  43. data/spec/dummy/tmp/cache/assets/test/sprockets/d1841c51cd5922191135ddd5c6ed2b70 +0 -0
  44. data/spec/dummy/tmp/cache/assets/test/sprockets/dc4c1ce2dc434402713320ef23981262 +0 -0
  45. data/spec/dummy/tmp/cache/assets/test/sprockets/dd40235a4b424b2f80c80fe5cb993736 +0 -0
  46. data/spec/dummy/tmp/cache/assets/test/sprockets/de041484110f42c73b00c21167cfc7c2 +0 -0
  47. data/spec/dummy/tmp/cache/assets/test/sprockets/faef24ee109a081ff0b9346c27872274 +0 -0
  48. data/spec/factories/csp_report_csp_reports.rb +14 -6
  49. data/spec/features/csp_report/csp_reports_index_spec.rb +17 -0
  50. data/spec/features/csp_report/csp_reports_report_by_ip_spec.rb +12 -2
  51. data/spec/features/csp_report/csp_reports_report_by_rule_spec.rb +21 -0
  52. data/spec/features/csp_report/csp_reports_report_by_source_spec.rb +24 -0
  53. data/spec/routing/csp_reports_routes_spec.rb +9 -0
  54. data/spec/spec_helper.rb +1 -3
  55. metadata +114 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ed867030fb43d8172decce4b81cebc84457f71ac
4
- data.tar.gz: 984d3c43474832016053f1eb3897ce2b8aff975a
3
+ metadata.gz: 4c3df7be47bd57116be91c9f07e8abdd00e1d34a
4
+ data.tar.gz: 66ff93264d5f432ecd4b878a2fbd9ce0a29024bf
5
5
  SHA512:
6
- metadata.gz: b7420708abd3a6e5766508f96c00cc5b8263396fd3ddfaa7336b529413e755589a11200c07dd8f8d690769a7bf0a9601dc40bea6d66a38928092a6aa8ef3161e
7
- data.tar.gz: 27a94ad89c75a763a98af01e8fc4b3872594270713bb85cc0018a5dd3fe748035b6bcad35fd4e5d09d58475585925ceb73280c410012099345f2b4bd778a39e2
6
+ metadata.gz: 0ed72f415782aa6d14d49b9d3a64a416ae1eea4e4d9f15dd3dcafb0aca4a47d035ed1787e922a253e7e28c0e82acad7be78b4d9dca664ec2ca519f2bc699d361
7
+ data.tar.gz: 9805a4912c9111cebbc1a91e530ef4bd68b2edc05d08fb090eb1d651a5a8e6474f4ff297fd776a5fc75f753c7f1bd7e53f165a6cb02311598d0d864cd53a6228
data/CHANGELOG CHANGED
@@ -1,3 +1,8 @@
1
+ * From 0.4.0
2
+ - Follow the upgrade instruction to add the JS hook
3
+ - Adds charting capabilities for data visualization
4
+ - Adds styling from bootstrap and a common navigation pattern
5
+
1
6
  * From 0.3.0
2
7
  - Developer additions (test coverage, Guard+Spork, ...)
3
8
  - Mount point for the engine is now configurable as a parameter of the
data/README.md CHANGED
@@ -18,11 +18,17 @@ page. However, elements have a class so you can add some CSS style before I
18
18
 
19
19
  I promise something cleaner when I'll get to v1.
20
20
 
21
+ [Installation](#install) | [Upgrade](#upgrade-notes) |
22
+ [Configuration](#trying-it-out) | [Description](#what-is-csp)
23
+
21
24
  **Careful**: If migrating from 0.1.x, please follow
22
25
  [these instructions](#upgrade-from-01x)
23
26
 
24
27
  **Careful**: If migrating from 0.2.x or below, you can follow
25
- [these instructions](#upgrade-from-02x). This is not mandatory.
28
+ [these instructions](#upgrade-from-02x-or-below). This is not mandatory.
29
+
30
+ **Careful**: If migrating from 0.3.x or below, you can follow
31
+ [these instructions](#upgrade-from-03x-or-below). This is mandatory.
26
32
 
27
33
  What is CSP
28
34
  ===========
@@ -48,8 +54,29 @@ Features
48
54
 
49
55
  * Provides a *csp_report* resource that stores the reported violations.
50
56
  * Displays the violation for analysis
57
+ * Keeps up-to-date with the CSP W3C RFC
51
58
  * Future: provide visualization aids on the report data
52
59
 
60
+ Why using this gem
61
+ ==================
62
+
63
+ CSP is yet another layer of protection, basically relying on the browser to do
64
+ some level of control. This is a way to prevent some man in the middle attack
65
+ where someone intercepts the server response and try to change it. While not
66
+ foolproof, it's a good additional security layer.
67
+
68
+ This gem comes in handy for 2 reasons:
69
+ * First, when activating CSP directives on your existing site, it is likely
70
+ that you'll have a hard
71
+ time figuring out all the sources you are using. By recording all the breaches,
72
+ this gem allows you to setup a policy, run a crawler for example, and then
73
+ look at what is reported as breaches. It will help you getting rid of your
74
+ inline js and so on.
75
+ * Second, in normal production mode, it'll help you monitor the situation and
76
+ see if your server has been victim of some injection (if some input is not
77
+ sanitize properly) or if your users are being attacked in some way (in which
78
+ case you might gather stats and maybe warn them in one way or another).
79
+
53
80
  Install
54
81
  =======
55
82
 
@@ -117,6 +144,26 @@ in one of your HTML rendered file and launch it in a browser. If the setup is
117
144
  correct and you browser supports CSP, the script will not play (no pop-up) and
118
145
  you'll have one more record in the /csp/csp_reports list.
119
146
 
147
+ Tuning the engine
148
+ =================
149
+
150
+ #### Overriding the engine's CSS
151
+
152
+ The engine comes packages with some CSS so that the page do not look ugly.
153
+ Since it is meant to be available for site admins or developers, the look&feel
154
+ is a secondary concern. Still you might want to customize it for consistency
155
+ with your site.
156
+ This is easy to do. Indeed, all the classes used are namespaced with *csp-report*.
157
+ To customize the CSS, just create the following file:
158
+ *app/assets/stylesheets/csp_report/csp_report.css*
159
+
160
+ Careful though, this is going to remove all the styles definition, so you'll
161
+ have to redefine every single one of them.
162
+
163
+ #### Changing the CSP rule per controller/action
164
+
165
+ TODO - gbataille - Fill in this section
166
+
120
167
  Utilities
121
168
  =========
122
169
 
@@ -127,26 +174,6 @@ typically used in the response header construction.
127
174
  I could not get it to work as I wanted, in a view you can use *csp_report.routes.url_helpers*
128
175
  and it will give you access to all the engine URL helpers.
129
176
 
130
- Why using this gem
131
- ==================
132
-
133
- CSP is yet another layer of protection, basically relying on the browser to do
134
- some level of control. This is a way to prevent some man in the middle attack
135
- where someone intercepts the server response and try to change it. While not
136
- foolproof, it's a good additional security layer.
137
-
138
- This gem comes in handy for 2 reasons:
139
- * First, when activating CSP directives on your existing site, it is likely
140
- that you'll have a hard
141
- time figuring out all the sources you are using. By recording all the breaches,
142
- this gem allows you to setup a policy, run a crawler for example, and then
143
- look at what is reported as breaches. It will help you getting rid of your
144
- inline js and so on.
145
- * Second, in normal production mode, it'll help you monitor the situation and
146
- see if your server has been victim of some injection (if some input is not
147
- sanitize properly) or if your users are being attacked in some way (in which
148
- case you might gather stats and maybe warn them in one way or another).
149
-
150
177
  To come
151
178
  =======
152
179
 
@@ -154,8 +181,11 @@ To come
154
181
  * Support of CSP 1.1 draft spec
155
182
  * Eased data mining
156
183
 
184
+ Upgrade notes
185
+ =============
186
+
157
187
  Upgrade from 0.1.x
158
- ==================
188
+ ------------------
159
189
 
160
190
  CAREFUL, 0.2.0 comes with DB changes. I won't do that in a minor after we are at
161
191
  v1, but for the moment, I thought it would not trouble too many people.
@@ -167,8 +197,8 @@ rake db:migrate
167
197
  ```
168
198
  before continuing
169
199
 
170
- Upgrade from 0.2.x
171
- ==================
200
+ Upgrade from 0.2.x or below
201
+ ---------------------------
172
202
 
173
203
  Version 0.3.0 comes with a configurable mount point and a couple of helpers that
174
204
  are accessible through the generators.
@@ -185,6 +215,16 @@ rails generate csp_report:mount [NAMESPACE]
185
215
  rails generate csp_report:initializer_install
186
216
  ```
187
217
 
218
+ Upgrade from 0.3.x or below
219
+ ---------------------------
220
+
221
+ Version 0.4.0 and above introduce a new javascript integration point to add the
222
+ highchart library used to produce some visualization of the reporting data.
223
+ The install generator would provide the additional hook. Rather than re-running
224
+ the entire install (not tested), you can either
225
+ * run the `csp_report:highcharts_include` generator
226
+ * or add the `//= require csp_report` in your application.js file.
227
+
188
228
  License
189
229
  =======
190
230
 
@@ -0,0 +1,2 @@
1
+ //= require highcharts/highcharts
2
+ //= require highcharts/highcharts-more
@@ -10,4 +10,5 @@
10
10
  // Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
11
11
  // about supported directives.
12
12
  //
13
+ //= require twitter/bootstrap
13
14
  //= require_tree .
@@ -0,0 +1,4 @@
1
+ $(document).ready(() ->
2
+ data = $.parseJSON( $("#chart")[0].attributes["data-chart"].value )
3
+ window.chart = new Highcharts.Chart(data)
4
+ )
@@ -0,0 +1,7 @@
1
+ /*
2
+ =require twitter-bootstrap-static/bootstrap
3
+
4
+ Use Font Awesome icons (default)
5
+ To use Glyphicons sprites instead of Font Awesome, replace with "require twitter-bootstrap-static/sprites"
6
+ =require twitter-bootstrap-static/fontawesome
7
+ */
@@ -1,15 +1,19 @@
1
- .csp-report.report-table
2
- border-style: solid
3
- border-color: #0000AA
4
- border-width: 5px
5
- border-collapse: collapse
6
-
7
- .csp-report.report-cell
8
- padding: 5px
9
- border-style: solid
10
- border-width: 2px
11
-
12
- .csp-report.report-header
13
- padding: 5px
14
- border-style: solid
15
- border-width: 3px
1
+ //= require csp_report/bootstrap_and_overrides
2
+
3
+ .csp-report.row
4
+ // You can override the bootstrap scoffolding here
5
+
6
+ .csp-report.offset3
7
+ // You can override the bootstrap scoffolding here
8
+
9
+ .csp-report.span6
10
+ // You can override the bootstrap scoffolding here
11
+
12
+ // .csp-report.report-table
13
+ //
14
+ // .csp-report.report-cell
15
+ //
16
+ // .csp-report.report-header
17
+ //
18
+ .csp-report.padding-navbar
19
+ padding-top: 45px
@@ -31,4 +31,139 @@ class CspReport::CspReportsController < ApplicationController
31
31
  CspReport::CspReport.delete_all
32
32
  redirect_to csp_reports_path
33
33
  end
34
+
35
+ def report_by_ip
36
+ @report_by_ip = CspReport::CspReport.select(
37
+ "incoming_ip, count(*) as count").group("incoming_ip")
38
+
39
+ data = []
40
+ for report in @report_by_ip
41
+ data.push [report.incoming_ip, report.count]
42
+ end
43
+
44
+ @chart = {
45
+ chart: {
46
+ :defaultSeriesType=>"pie" ,
47
+ :margin=> [50, 200, 60, 170]
48
+ },
49
+ series: [{
50
+ :type=> 'pie',
51
+ :name=> 'Violations by client IP',
52
+ :data=> data
53
+ }],
54
+ title: {text: "By IP"},
55
+ legend: {
56
+ :layout=> 'vertical',
57
+ :style=> {
58
+ :left=> 'auto',
59
+ :bottom=> 'auto',
60
+ :right=> '50px',
61
+ :top=> '100px'
62
+ }
63
+ },
64
+ plotOptions: {
65
+ :pie=>{
66
+ :allowPointSelect=>true,
67
+ :cursor=>"pointer" ,
68
+ :dataLabels=>{
69
+ :enabled=>true,
70
+ :color=>"black",
71
+ :style=>{
72
+ :font=>"13px Trebuchet MS, Verdana, sans-serif"
73
+ }
74
+ }
75
+ }
76
+ }
77
+ }
78
+ end
79
+
80
+ def report_by_rule
81
+ @report_by_rule = CspReport::CspReport.select(
82
+ "violated_directive, count(*) as count").group("violated_directive")
83
+
84
+ data = []
85
+ for report in @report_by_rule
86
+ data.push [report.violated_directive, report.count]
87
+ end
88
+
89
+ @chart = {
90
+ chart: {
91
+ :defaultSeriesType=>"pie" ,
92
+ :margin=> [50, 200, 60, 170]
93
+ },
94
+ series: [{
95
+ :type=> 'pie',
96
+ :name=> 'Violations by violated policy',
97
+ :data=> data
98
+ }],
99
+ title: {text: "By rule"},
100
+ legend: {
101
+ :layout=> 'vertical',
102
+ :style=> {
103
+ :left=> 'auto',
104
+ :bottom=> 'auto',
105
+ :right=> '50px',
106
+ :top=> '100px'
107
+ }
108
+ },
109
+ plotOptions: {
110
+ :pie=>{
111
+ :allowPointSelect=>true,
112
+ :cursor=>"pointer" ,
113
+ :dataLabels=>{
114
+ :enabled=>true,
115
+ :color=>"black",
116
+ :style=>{
117
+ :font=>"13px Trebuchet MS, Verdana, sans-serif"
118
+ }
119
+ }
120
+ }
121
+ }
122
+ }
123
+ end
124
+
125
+ def report_by_source
126
+ @report_by_source = CspReport::CspReport.select(
127
+ "document_uri, count(*) as count").group("document_uri")
128
+
129
+ data = []
130
+ for report in @report_by_source
131
+ data.push [report.document_uri, report.count]
132
+ end
133
+
134
+ @chart = {
135
+ chart: {
136
+ :defaultSeriesType=>"pie" ,
137
+ :margin=> [50, 200, 60, 170]
138
+ },
139
+ series: [{
140
+ :type=> 'pie',
141
+ :name=> 'Violations by source document URI',
142
+ :data=> data
143
+ }],
144
+ title: {text: "By source"},
145
+ legend: {
146
+ :layout=> 'vertical',
147
+ :style=> {
148
+ :left=> 'auto',
149
+ :bottom=> 'auto',
150
+ :right=> '50px',
151
+ :top=> '100px'
152
+ }
153
+ },
154
+ plotOptions: {
155
+ :pie=>{
156
+ :allowPointSelect=>true,
157
+ :cursor=>"pointer" ,
158
+ :dataLabels=>{
159
+ :enabled=>true,
160
+ :color=>"black",
161
+ :style=>{
162
+ :font=>"13px Trebuchet MS, Verdana, sans-serif"
163
+ }
164
+ }
165
+ }
166
+ }
167
+ }
168
+ end
34
169
  end
@@ -0,0 +1,32 @@
1
+ module CspReport
2
+ module HeadersHelper
3
+ def add_navigation_header
4
+ <<-CONTENT
5
+ <div class='csp-report row'>
6
+ <div class='csp-report navbar navbar-fixed-top'>
7
+ <div class='csp-report navbar-inner'>
8
+ <div class='csp-report container'>
9
+ <a class='brand' href="#">CSP Reports</a>
10
+ <ul class='nav'>
11
+ <li class='active'>
12
+ <a href=#{csp_reports_path}>Violations</a>
13
+ </li>
14
+ <li class='divider-vertical'/>
15
+ <li>
16
+ <a href=#{csp_reports_report_by_ip_path}>By IP</a>
17
+ </li>
18
+ <li>
19
+ <a href=#{csp_reports_report_by_rule_path}>By Violated Directive</a>
20
+ </li>
21
+ <li>
22
+ <a href=#{csp_reports_report_by_source_path}>By Source Document URI</a>
23
+ </li>
24
+ </ul>
25
+ </div>
26
+ </div>
27
+ </div>
28
+ </div>
29
+ CONTENT
30
+ end
31
+ end
32
+ end
@@ -1,38 +1,63 @@
1
1
  =stylesheet_link_tag "csp_report/csp_report.css"
2
2
 
3
- %table.csp-report.report-table
4
- %tr.csp-report.report-row
5
- %th.csp-report.report-header
6
- ID
7
- %th.csp-report.report-header
8
- Document URI
9
- %th.csp-report.report-header
10
- Referrer
11
- %th.csp-report.report-header
12
- Server Policy
13
- %th.csp-report.report-header
14
- Violated Directive
15
- %th.csp-report.report-header
16
- Blocked URI
17
- %th.csp-report.report-header
18
- Incoming IP
19
- %th.csp-report.report-header
20
- Reported At
21
- %th.csp-report.report-header
22
- Actions
23
- - @reports.each do |report|
24
- %tr.csp-report.report-row
25
- %td.csp-report.report-cell=report.id
26
- %td.csp-report.report-cell=report.document_uri
27
- %td.csp-report.report-cell=report.referrer
28
- %td.csp-report.report-cell=report.original_policy
29
- %td.csp-report.report-cell=report.violated_directive
30
- %td.csp-report.report-cell=report.blocked_uri
31
- %td.csp-report.report-cell=report.incoming_ip
32
- %td.csp-report.report-cell=report.created_at
33
- %td.csp-report.report-cell
34
- =link_to('Delete violation', csp_report_path(report.id), method: 'delete')
3
+ -# TODO: gbataille - Factorize this in a layout
4
+ .csp-report.row
5
+ .csp-report.navbar.navbar-fixed-top
6
+ .csp-report.navbar-inner
7
+ .csp-report.container
8
+ %a.brand{href: "#"}
9
+ CSP Reports
10
+ %ul.nav
11
+ %li.active
12
+ =link_to "Violations", csp_reports_path
13
+ %li.divider-vertical
14
+ %li
15
+ =link_to "By IP", csp_reports_report_by_ip_path
16
+ %li
17
+ =link_to "By Violated Directive", csp_reports_report_by_rule_path
18
+ %li
19
+ =link_to "By Source Document URI", csp_reports_report_by_source_path
35
20
 
36
- %p
37
- =link_to "Delete All", csp_reports_destroy_all_path,
38
- data: {confirm: "Are you sure you want to delete all the violation reports?"}
21
+ .csp-report.row.padding-navbar
22
+ .csp-report.offset2.span8
23
+ %table.csp-report.report-table.table.table-striped.table-condensed
24
+ %thead
25
+ %tr.csp-report.report-row
26
+ %th.csp-report.report-header
27
+ ID
28
+ %th.csp-report.report-header
29
+ Document URI
30
+ %th.csp-report.report-header
31
+ Referrer
32
+ %th.csp-report.report-header
33
+ Server Policy
34
+ %th.csp-report.report-header
35
+ Violated Directive
36
+ %th.csp-report.report-header
37
+ Blocked URI
38
+ %th.csp-report.report-header
39
+ Incoming IP
40
+ %th.csp-report.report-header
41
+ Reported At
42
+ %th.csp-report.report-header
43
+ Actions
44
+ %tbody
45
+ - @reports.each do |report|
46
+ %tr.csp-report.report-row
47
+ %td.csp-report.report-cell=report.id
48
+ %td.csp-report.report-cell=report.document_uri
49
+ %td.csp-report.report-cell=report.referrer
50
+ %td.csp-report.report-cell=report.original_policy
51
+ %td.csp-report.report-cell=report.violated_directive
52
+ %td.csp-report.report-cell=report.blocked_uri
53
+ %td.csp-report.report-cell=report.incoming_ip
54
+ %td.csp-report.report-cell=report.created_at
55
+ %td.csp-report.report-cell
56
+ =link_to('Delete violation', csp_report_path(report.id), method: 'delete')
57
+
58
+ .csp-report.row
59
+ .csp-report.offset2.span8
60
+
61
+ %p.csp-report.btn.btn-danger
62
+ =link_to "Delete All", csp_reports_destroy_all_path,
63
+ data: {confirm: "Are you sure you want to delete all the violation reports?"}