dradis:professional

Support » Guides » Creating Word reports with XSL transformations

Creating Word reports with XSL transformations

This guide covers how you can use XSL transformations to generate advanced Word reports.

1 Why use XSL transformations?

Starting in Word 2003, Word documents are built using XML in what Microsoft calls the WordprocessingML.

Unfortunately, this is not such good news as one would think. The WordprocessingML language is quite complex and it takes some getting used to. You check out the Overview of WordprocessingML in MSDN.

On the bright side there are plenty of tools out there to work with XML documents. We are going to make use of one of such tools: XSL transformations (or XSLT). An XSLT document is itself an XML document that contains rules on how to transform an input XML into an output document (you can read more on the XSLT page of the Wikipedia).

In this case we are going to convert an input XML containing the information stored in your Dradis repository into an output document in the WordprocessingML format.

2 Introduction to WordprocessingML

Unfortunately WordprocessingML is a huge topic. The bare minimum WordprocessingML document is:

simple_word.xml

<?xml version="1.0"?>
<?mso-application progid="Word.Document"?>
<w:wordDocument 
  xmlns:w="http://schemas.microsoft.com/office/word/2003/wordml">

  <w:body>
    <w:p>
      <w:r>
        <w:t>Hello, World.</w:t>
      </w:r>
    </w:p>
  </w:body>
</w:wordDocument>

If you copy and paste the above text into a file and have Word installed, Windows will detect this XML (because of the mso-application declaration) and will launch Word if you double-click it.

In the example you can see some of the elements we will have to deal with when creating the template:

w:p
represents a paragraph
w:r
represents a text run, a contiguous set of WordprocessingML components with a consistent set of properties
w:t
represents a text

Other common items you will have to work with are tables, table rows and table cells (w:tbl, w:tr and w:tc respectively).

To get familiar with the other elements you will need to review the Overview of WordprocessingML and sharpen your Google skills.

Unfortunately Word is not going to keep your XML as clear and simple as our example. Try opening the file in Word, adding a second line to the document and saving it again. We have gone from 280 bytes to a 10KB of document. This is because Word starts including styling information, additional namespaces and a plethora of other bits and pieces. Thankfully we will be able to ignore most of it.

3 Introduction to XSLT

Say we have a very simple XML:

dradis_source.xml

<dradis>
  <notes>
    <note>
	  <fields>
	    <Title>Directory Listings</Title>
	    <!-- [...] -->
	  </fields>
	</note>
	<note>
	  <fields>
	    <Title>Out-of-date Apache</Title>
	    <!-- [...] -->
	  </fields>
	</note>
  </notes>
</dradis>

And a simple XSLT document:

simple.xslt

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet 
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="xml" indent="yes"/>
  <xsl:template match="/">

    <xsl:processing-instruction name="mso-application">
      <xsl:text>progid="Word.Document"</xsl:text>
    </xsl:processing-instruction>
    <w:wordDocument 
      xmlns:w="http://schemas.microsoft.com/office/word/2003/wordml">

      <w:body>
        <xsl:for-each select="dradis/notes/note/fields">
        <w:p>
          <w:r>
            <w:t><xsl:value-of select="Title"/></w:t>
          </w:r>
        </xsl:for-each>
      </w:body>
    </w:wordDocument>

  </xsl:template>
</xsl:stylesheet>

The above transformation will create a Word document with a new paragraph containing the Title field for every Note in the source XML:

output.xml

<?xml version="1.0"?>
<?mso-application progid="Word.Document"?>
<w:wordDocument 
  xmlns:w="http://schemas.microsoft.com/office/word/2003/wordml">

  <w:body>
    <w:p>
      <w:r>
        <w:t>Directory Listings</w:t>
      </w:r>
    </w:p>
    <w:p>
      <w:r>
        <w:t>Out-of-date Apache</w:t>
      </w:r>
    </w:p>
  </w:body>
</w:wordDocument>

If you want to verify this by yourself, create the dradis_source.xml and simple.xslt files in a temporary folder and fire the Ruby interpreter (install the Nokogiri gem first if it is not installed):


$ gem install nokogiri --no-rdoc --no-ri
$ irb -rrubygems
>> require 'nokogiri'
>> source = Nokogiri::XML( File.read('dradis_source.xml') )
>> xslt = Nokogiri::XSLT( File.read('simple.xslt') )
>> puts xslt.transform( source )

4 Creating the template

Now that we have a basic understanding of the inner workings of the WordprocessingML and XSLT technologies we can start working on our Dradis Pro template.

Open Word and create a new document. You can also start from your existing reporting template but we are going to keep things simple in this guide. A very basic report skeleton could look like this:

We could save this document as XML and start working on it, but we are going to make our life easier by adding some placeholders:

Now we are ready to convert this into an XSL transformation. Save the document (File > Save As) as Word 2003 XML Document. Mine is about 25 KB.

5 Converting the template into an XSL transformation

The first thing we need to do with our document to convert it into an XSLT is to replace the Word document preamble with an XSLT preamble. Lets create a copy of the document and rename it to template.xslt.

Open it in a text-editor and replace the Word preamble:

template.xslt

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<?mso-application progid="Word.Document"?>

With the XSLT preamble:

template.xslt

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet 
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="xml" indent="yes"/>
  <xsl:template match="/">

    <xsl:processing-instruction name="mso-application">
      <xsl:text>progid="Word.Document"</xsl:text>
    </xsl:processing-instruction>
    
  	<w:wordDocument ...

And then go all the way down to the bottom and close the XSLT tags:

template.xslt

  </xsl:template>
</xsl:stylesheet>

So now officially your template.xslt is a well-formed XSLT document. It does not do anything interesting but it is a start.

6 Adding content to the template

Now comes the tricky part. We need to navigate through the source of the XSLT document to locate the placeholders we created for our fields.

For instance, the first one is going to be the phTitle placeholder in the first cell of the table. Using your editor’s search function look for phTitle. Mine is in line 12, column 21689 but ymmv. Because we know this is a table cell, we know that our placeholder is going to be locate within a set of w:tc tags. After finding them and indenting the code around them we have:

template.xslt

<!-- Title -->
<w:tc>
  <w:tcPr><!-- [...] --></w:tcPr>
  <w:p wsp:rsidR="00DC17EC" wsp:rsidRPr="002B061B" wsp:rsidRDefault="002B061B" wsp:rsidP="002B061B">
	<w:pPr><!-- [...] --></w:pPr>
	<w:proofErr w:type="spellStart"/>
	  <w:r wsp:rsidRPr="002B061B">
	    <w:rPr><w:b/><w:b-cs/></w:rPr>
	    <w:t>phTitle</w:t>
	  </w:r>
	<w:proofErr w:type="spellEnd"/>
  </w:p>
</w:tc>

<!-- Impact -->
<w:tc><!-- [...] -->

There is a lot in the markup for that table cell. Thankfully you can ignore most of it. For instance, w:tcPr contains cell properties, all the wsp attributes can be ignored or deleted, w:pPr are paragraph properties, etc.

One interesting thing to note is the w:proofErr block. This has been added by word because our placeholder (phTitle) is not a valid English word. For our purposes we can ignore it but lets just say that if your placeholder had been Title you would not see this w:proofErr element.

The next thing we need to do is to replace our placeholder text with an XSLT xsl:value-of tag that extracts the field from the source (note that I have also removed the w:proofErr element):

template.xslt

<!-- Title -->
<w:tc>
  <w:tcPr><!-- [...] --></w:tcPr>
  <w:p wsp:rsidR="00DC17EC" wsp:rsidRPr="002B061B" wsp:rsidRDefault="002B061B" wsp:rsidP="002B061B">
	<w:pPr><!-- [...] --></w:pPr>
  <w:r wsp:rsidRPr="002B061B">
    <w:rPr><w:b/><w:b-cs/></w:rPr>
    <w:t><xsl:value-of select="Title"/></w:t>
  </w:r>
  </w:p>
</w:tc>

<!-- Impact -->
<w:tc><!-- [...] -->

We need to repeat this (tedious) process for the other cells in the row.

Once that is done, we need to ensure that a new row is created for each note in our Dradis Pro source XML. We do this by enclosing the w:tr element within an XSLT xsl:for-each tag (I have removed the wsp attributes in the w:tr tag):

template.xslt

<xsl:for-each select="dradis/notes/note/fields">
  <w:tr>
    <!-- Title -->
    <w:tc><!-- [...] -->
    <!-- Impact -->
    <w:tc><!-- [...] -->
    
    <!-- [...] -->
  </w:tr>
</xsl:for-each>

The code above will ensure that a new table row (i.e. a new w:tr element) will be created for each Dradis note.

Now we need to repeat the same process for the Technical Details section of our report. It is going to be a bit trickier this time because the fields are not enclosed by table cells or table rows but in essence this is what we have to do:

  1. Locate the placeholder
  2. Replace the placeholder with a xsl:value-of XSLT element
  3. Repeat for every field
  4. Enclose the block in a xsl:for-each XSLT element

Using the editor’s search function, search again for phTitle. Once you find it, walk backwards to the previous paragraph (an empty one) and the one before (containing the Detailed Findings header). See the snippet below (superfluous wsp attributes have been removed) where I have already replaced the phTitle with an xsl:value-of:

template.xslt

<w:p>
  <w:pPr><w:pStyle w:val="Heading2"/></w:pPr>
<w:r><w:t>Detailed Findings</w:t></w:r>
</w:p>

<w:p/>

<w:p>
  <w:r><w:t>Title: </w:t></w:r>
  <w:r><w:t><xsl:value-of select="Title"/></w:t></w:r>
</w:p>
<!-- [...] -->

The process of going through the rest of the fields is straightforward. And once we have identified all the paragraphs that compose the block we want to repeat for each issue, we just need to enclose it in a xsl:for-each element:

template.xslt

<w:p/>
<xsl:for-each select="dradis/notes/note/fields">
  <w:p>
    <w:r><w:t>Title: </w:t></w:r>
    <w:r><w:t><xsl:value-of select="Title"/></w:t></w:r>
  </w:p>
  
  <w:p>
    <w:r><w:t>Impact: </w:t></w:r>
    <w:r><w:t><xsl:value-of select="Impact"/></w:t></w:r>
  </w:p>
  
  <!-- [... 
  CVSSv2, Description, Recommendation
  ...] -->
  <w:p/>
</xsl:for-each>

We are all done here. Lets wrap this up by uploading the XSLT file to the Dradis Pro appliance and making it the default template.

7 Uploading the template

We are going to copy the template into a shared templates directory. If your appliance does not have one, you will need to create it:


$ ssh dradispro@dradispro.host
$ mkdir -p /usr/local/rails/dradispro/shared/templates/plugins/advanced_word_export/
$ exit
$ scp template.xslt dradispro@dradispro.host:/usr/local/rails/dradispro/shared/templates/plugins/advanced_word_export/

Once the file is uploaded you need to configure the Advanced Word export plugin to use the new template. Go to the configuration manager:

https://dradispro.host/pro/configurations

And adjust the advanced_word_export:template to point to the file you just uploaded.


/usr/local/rails/dradispro/shared/templates/plugins/advanced_word_export/template.xslt

8 Providing the content and obtaining final result

Now for the easy part. We just need to create our notes in Dradis Pro and assign them to the Advanced WordExport ready category.

And generate the report. If you are running Dradis Pro v1.2 or earlier you can do this through Export > Advanced word export. If you are running v1.3 or later you can do it through Export > Advanced word export > Via xslt:

And the results:

(continues)

9 Wrapping up

As you can see, XSL transformations are a powerful and versatile technology. They are not for the faint of heart though.

This guide only scratches the surface but there is much more that can be done with XSLT (think images, charts, the xsl:choose element, etc.). There are plenty of resources out there to help you with WordprocessingML and XSLT. It will take some time but it will be worth it.

However, if you currently do not have the time to fully investigate this on your own, at Security Roots we have several years of experience on WordprocessingML and XSLT and we can help you create a custom template. Visit our professional services section to find out more and do not hesitate to contact us if you would like to enquire about our consultancy services.

Remember that DradisReports is an alternative way to generate customized Word reports using Dradis Pro and there is a guide for that too!

Creating Word reports with DradisReports

You can only generate Word reports using XSL transformations with Dradis Professional Edition. If you are ready to give it a try get it now and start working on your report template.

Guide contents

  1. Why use XSL transformations?

  2. Introduction to WordprocessingML

  3. Introduction to XSLT

  4. Creating the template

  5. Converting the template into an XSL transformation

  6. Adding content to the template

  7. Uploading the template

  8. Providing the content and obtaining final result

  9. Wrapping up

Download resources

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