Creating HTML reports

In this guide we're going to cover some of the possibilities that are available to you with our HTML report generators.

The possibilities here are endless and only limited by your HTML and CSS skills; we're just barely scratching the surface.

This guide contains

The result

Fist things first, what are we trying to achieve? A nice looking HTML report like this:

An ERB primer

The template engine we'll use for our HTML report is called ERB.

The rules are straightforward:

<% Ruby code -- inline without output %>
<%= Ruby expression -- evaluated and output added to page %>

Adding Issues and fields

First the list of issues, these are available in the issues array:

<% for issue in issues do %>
  <li><a href="#issue-a_<%= issue.id %>"><%= h issue.fields['Title'] %></a></li>
<% end %>
    

A simple for loop: we're outputting the Issue's Title and also preparing the href anchors that we'll use to link to the full note in the Detailed Findings.

Next we're going output the Title, CVSSv2, Description, Mitigation and References fields for each note:

<h1>Detailed findings</h1>
<% for issue in issues do %>
<div id="issue_<%= issue.id %>" class="note-content">
  <h2><%= h issue.fields['Title'] %></h2>
  <% ['CVSSv2', 'Description',
        'Mitigation', 'References' ].each do |field_name| %>
  <div class="field">
    <div class="field-name <%= field_name.downcase %>">
      <%= field_name %>
    </div>
    <div class="field-content <%= field_name.downcase %>">
      <%= markup(issue.fields[field_name]) %>
    </div>
  </div>
  <% end %>
</div>
<% end %>
    

First, we enclose each note's data in a <div> with note-content CSS class and an id of issue_<%= issue.id %>. This id is the one we used as href up in the list of issues.

We then output the Issue's title within <h2> tags and then we add each of the relevant fields.

Note how we add a CSS class corresponding to each field_name.downcase. This is so we can later refer to the fields to do some post-processing (e.g. CVSSv2 colors). To give you an idea of the markup generated by this code, it would look like this:

<div id="note_6903" class="note-content">
  <h2>Dangerous HTTP methods: TRACE</h2>
  
  <div class="field">
    <div class="field-name cvssv2">CVSSv2</div>
    <div class="field-content cvssv2">6.2</div>
  </div>

  <!-- ... -->
    

We need a bit of CSS to make this look nicer:

.field {
  margin: 10px 0;
}
.field .field-name {
  float: left;
  font-weight: bold;
  text-align: right;
  width: 12.5%;
}
.field .field-content {
  padding-left: 13.5%;
}
    

That's it for body of the report. However, we want to add some bells and whistles to make it look nicer.

Bells and whistles part 1: the chart

We're going to use the excellent Highcharts library, in particular the column chart (make sure you check their licensing page as it is only free for non-commercial use).

To keep things simple we start adding the reference to the library and a dummy chart inside our <head></head> block directly from their docs:

<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://code.highcharts.com/highcharts.js"></script>
<script type="text/javascript">
  $(function () {
      var chart;
      chart = new Highcharts.Chart({
        chart: {
            renderTo: 'bar-chart',
            type: 'column'
        },
        title: {
            text: 'Issue summary'
        },
        xAxis: {
            categories: [
                'Info',
                'Low',
                'Medium',
                'High'
            ]
        },
        yAxis: {
            min: 0,
            title: {
                text: 'Number of issues'
            }
        },
        legend: {
          enabled: false
        },
        tooltip: {
            formatter: function() {
                return ''+
                    this.x +': '+ this.y +' issues';
            }
        },
        plotOptions: {
            column: {
                pointPadding: 0.2,
                borderWidth: 0
            }
        },
        series: [{
          // dummy data
          data: [3, 5, 7, 3]
        }]
    });
  });
</script>
    

Then we need a holder for our chart in the <body></body>, we'll assign it a bar-chart id which matches the renderTo property above:

<div id="bar-chart" style="max-width: 600px; min-width: 400px; height: 400px; margin: 0 auto"></div>

And the final result:

Replacing real data in the chart, will require a bit of work.

We receive our issues in the notes array. Whilst all of them do have a CVSSv2 score, these are unsorted. Se need to go through the array and sort it based on the score.

We've defined the ranges as follows:

CVSSv2 score Category
0..0.9 Info
1.0..3.9 Low
4.0..6.9 Medium
> 7.0 High

We are going to create the sorted Ruby hash that we'll use to group the issues into these categories. We have to enclose this Ruby code within <% ... %> tags (no =) in the page:

<script type="text/javascript">
  // this hash will map issue id's to CSS classes associated with their
  // CVSS score
  var noteClassName = {};
  <% sorted = { :info => [], :low => [], :medium => [], :high => []} %>
  <% for issue in issues;
       cvss = issue.fields['CVSSv2'].to_f;
       case cvss
         when 0..0.9
           sorted[:info] << issue
         when 1.0..3.9
           sorted[:low] << issue
         when 4.0..6.9
           sorted[:medium] << issue
         else
           sored[:high] << issue
       end
     end %>

  // ...
</script>
    

So once the for loop has gone through all the issues in the array, we will have a sorted hash with all the issues grouped by category, for instance, something like:

{ :info=>[],
  :low=>[#<Issue id: 6905 [...]>],
  :medium=>[#<Issue id: 6903 [...]>, #<Issue id: 6904 [...]>],
  :high=>[]
}
    

Which is exactly what we need for the data series in our chart. We can now replace the dummy values with the real data:

//...
series: [{
  data: [
    <%= sorted[:info].count %>,
    <%= sorted[:low].count %>,
    <%= sorted[:medium].count %>,
    <%= sorted[:high].count %>
  ]
}]
//...
    

Where we are just counting the number of issues in each category.

Bells and whistles part 2: CVSSv2 colors

First we're going to add color to the CVSSv2 scores:

  1. First we locate each CVSSv2 field. We'll use .cvssv2 class name assigned to the field.
  2. For each CVSSv2 field, we locate the corresponding issue container (with note-content class) and extract the issue id.
  3. Finally, we add a new CSS class given by the mapping in the noteClassName hash (see below).
<script type="text/javascript">
$(function(){
  // color-code CVSSv2 scores
  var note_id;
  var $cvssv2_content;
  $('.field-content.cvssv2').each(function(){
    // each note-content <div> has an id of note_<issue.id>
    note_id = $(this).parents('.note-content').attr('id').split('_')[1];

    // we add a CSS class to the CVSSv2 content depending on the
    // issue's score
    $(this).addClass( noteClassName[ note_id ] );
  });
})
</script>
    

We are going to define a bunch of CSS classes mapping to the Info, Low, Medium and High ranges we used in our chart:

.cvss-info { color: green; }
.cvss-low { color: blue; }
.cvss-medium { color: orange; }
.cvss-high { color: red; }      
    

We're going to re-use the case statement we used for the chart for saving the mapping between each issue's CVSSv2 score and the corresponding CSS class.

  1. We define the global noteClassName hash that will contain note ids as keys and the CSS class as value.
  2. Inside the case/when statement:
    1. We close the ERB evaluation (with %>)
    2. Assign the noteClassName value
    3. Re-open the ERB evaluation (with <%)
<script type="text/javascript">
  // this hash will map note id's to CSS classes associated with their
  // CVSS score
  var noteClassName = {};
  <%# Create a Ruby hash mapping each note to a risk level %>
  <% sorted = { :info => [], :low => [], :medium => [], :high => []} %>
  <% for issue in issues;
       cvss = issue.fields['CVSSv2'].to_f;
       case cvss
         when 0..0.9
           sorted[:info] << issue
           %>noteClassName[<%= issue.id %>] = 'cvss-info'; <%
         when 1.0..3.9
           sorted[:low] << issue
           %>noteClassName[<%= issue.id %>] = 'cvss-low'; <%
         when 4.0..6.9
           sorted[:medium] << issue
           %>noteClassName[<%= issue.id %>] = 'cvss-medium'; <%
         else
           sored[:high] << issue
           %>noteClassName[<%= issue.id %>] = 'cvss-high'; <%
       end
     end %>

  //...
</script>
    

Remember that anything between <% ... %> tags does not generate any output whilst anything enclosed in <%= ... %> does. So, the snippet above results in the following HTML:

<script type="text/javascript">
  // this hash will map issue id's to CSS classes associated with their
  // CVSS score
  var noteClassName = {};
  
  
  noteClassName[6903] = 'cvss-medium'; noteClassName[6904] = 'cvss-medium'; noteClassName[6905] = 'cvss-low'; 
</script>
    

The white lines correspond to the ERB tags that evaluated Ruby code but did not generate any output.

This is a bit tricky, we have used a single @case@ statement to:

  • Generate the sorted hash in Ruby.
  • Generate the noteClassName hash in JavaScript.

Now if you go back to our color-coding JavaScript snippet, you can see that we use the @noteClassName@ to figure out the CSS class we need to use in each case:

// we add a CSS class to the CVSSv2 content depending on the
// issue's score
$(this).addClass( noteClassName[ issue_id ] );
    

This is the easiest one to add, just a bit of text post-processing:

  1. Locate the References content.
  2. Match against a regular expression for URLs.
  3. Replace plaint-text URL with an HTML link.
<script type="text/javascript">
$(function(){
  // auto-link URLs
  var link_regexp = /(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig;

  $('.field-content.references').each(function(){
    $(this).html( $(this).html().replace(link_regexp,"<a href='$1'>$1</a>") );
  });
})
</script>
    

That's it for now. We just need to copy the template across and use it. Follow the instructions on the Report Templates page of the Administration guide.

Generating the report

Once the template is uploaded into Dradis, you can generate a report through the Export menu:

Export your report in the webapp

  1. Sign into Dradis as an Administrator
  2. Open the project you want to export
  3. Navigate to Export results in the header, then open the Generate advanced HTML reports tab
  4. Select the correct template from the list that appears, then click Export

Export your report through the command line

If you run into any problems exporting your project through the Export Manager or your export takes too long, try exporting through the command line instead.

Before you begin, locate the ID of the project you want to export and the location of your HTML report template.

You can get your project ID from the browser location bar once you open the project in the main Dradis interface. For example, if the browser shows https://192.168.56.102/pro/projects/50, your project ID is 50.

The location of the template can be anywhere in the appliance. However, if you used the web interface to upload your template there is a good chance it would have ended under this location:

/opt/dradispro/dradispro/current/templates/reports/html_export/

However, to make the examples more readable we'll use /tmp/test.html.erb as the sample location of our template in the following steps.

To export to HTML from the command line:

  1. SSH into the box as dradispro and navigate to the current folder:

    $ cd /opt/dradispro/dradispro/current
  2. Run the dradis:pro:plugins:html:export command.

    For our example, with a project ID of 50, the command we need to run is this:

    $ PROJECT_ID=50 RAILS_ENV=production bundle exec thor \
            dradis:pro:plugins:html:export \
            --output=/tmp/ \
            --template=/tmp/test.html.erb
  3. Download your report from the output location (/tmp/) displayed after the export finishes.

Next help article: Export your project to CSV →