dradis:professional

Support » Guides » Creating HTML reports

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 quite endless and only limited by your HTML and CSS skills, we’re just barely scratching the surface.

1 The result

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

2 An ERB premier

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 %>

3 Adding issue list and details

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


<p>Issues identified in this report:</p>
<ul>
  <% for note in notes do %>
  <li><a href="#note_<%= note.id %>"><%= h note.fields['Title'] %></a></li>
  <% end %>
</ul>

A simple for loop: we’re outputting the note’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 note in notes do %>
<div id="note_<%= note.id %>" class="note-content">
  <h2><%= h note.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(note.fields[field_name]) %>
    </div>
  </div>
  <% end %>
</div>
<% end %>

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

We then output the note’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’)”). 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.

4 Bells & whistles part 1: the chart

We’re going to use the excellent Highchats 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 notes into these categories. We have to enclose this Ruby code within <% ... %> tags (no =) in the page:


<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 note in notes;
       cvss = note.fields['CVSSv2'].to_f;
       case cvss
         when 0..0.9
           sorted[:info] << note
         when 1.0..3.9
           sorted[:low] << note
         when 4.0..6.9
           sorted[:medium] << note
         else
           sored[:high] << note
       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=>[#<Note id: 6905 [...]>],
  :medium=>[#<Note id: 6903 [...]>, #<Note 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 notes in each category.

5 Bells & 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 note container (with note-content class) and extract the note 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_<note.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 note’s CVSSv2 score and the corresponding CSS class.

  1. We define the global noteClassName hash that will contain note @id@s 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 note in notes;
       cvss = note.fields['CVSSv2'].to_f;
       case cvss
         when 0..0.9
           sorted[:info] << note
           %>noteClassName[<%= note.id %>] = 'cvss-info'; <%
         when 1.0..3.9
           sorted[:low] << note
           %>noteClassName[<%= note.id %>] = 'cvss-low'; <%
         when 4.0..6.9
           sorted[:medium] << note
           %>noteClassName[<%= note.id %>] = 'cvss-medium'; <%
         else
           sored[:high] << note
           %>noteClassName[<%= note.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 note 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[ note_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.

7 Configure the template

Go to the Configuration Manager (configuration link in the upper-right corner of the project view) to locate the htmlexport:template property and set it to:


/usr/local/rails/dradispro/shared/templates/reports/advanced_html.html.erb

This is where Dradis will look for the report template.

If the ./shared/templates/reports/ folder doesn’t exist, create it. It is important that you use the ./shared/templates/ folder because this will ensure that the template will be available even after you upgrade to a new version of the app.

Finally upload the template to this location.

8 Generating the report

Once the template is uploaded into the appliance and the htmlexport:template setting is pointing to it, you can generate a report through the Export menu:

Guide contents

  1. The result

  2. An ERB premier

  3. Adding issue list and details

  4. Bells & whistles part 1: the chart

  5. Bells & whistles part 2: CVSSv2 colors

  6. Bells & whistles part 3: external links

  7. Configure the template

  8. Generating the report

Download resources

Our users can download the resources used in this guide here.