Feb 15 2009
dradis reporting: quick & neat word export
With over 800 downloads in the first two weeks of dradis v2.0, there is lots of interest on what is going to be next. We are working on solutions for importing and exporting data to and from the repository, but for those of you that can’t wait, we have put together a plug-in for the server that exports your notes to a Word file.
It is not the final solution, and it is not integrated with the web interface, but hopefully it will give you an idea of how easy is to get your own exporting modules for dradis.
The Template
So the first step was to use Word to generate a template we could use for our export plug-in. I just created a simple empty document that looked like this:-

Save your template as an XML document (you can get mine here: template.xml). The template contains these sections:-
- Issue title
- Date of discovery
- Issue description
- Mitigation recommendations
- Additional information
What our plug-in is going to do is to walk through the notes in the repository and repeat this template for each one. In order for the code to find the different sections, you have to edit the XML file an add some id attributes to the tags associated with the sections. For instance:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | <!-- Note Section --> <wx:sub-section> <w:p wsp:rsidR="005F557A" wsp:rsidRDefault="00C27C1C" wsp:rsidP="00C27C1C"> <w:pPr> <w:pStyle w:val="Heading1"/> </w:pPr> <w:r><w:t id="vulntitle">Directory Listings</w:t></w:r> </w:p> <w:p wsp:rsidR="00C27C1C" wsp:rsidRPr="00C27C1C" wsp:rsidRDefault="00C27C1C" wsp:rsidP="00C27C1C"> <w:pPr> <w:jc w:val="right"/> <w:rPr> <w:rFonts w:ascii="Arial" w:h-ansi="Arial" w:cs="Arial"/> <wx:font wx:val="Arial"/> <w:sz w:val="16"/> <w:sz-cs w:val="16"/> </w:rPr> </w:pPr> <w:r wsp:rsidRPr="00C27C1C"> <w:rPr><w:rFonts w:ascii="Arial" w:h-ansi="Arial" w:cs="Arial"/><wx:font wx:val="Arial"/><w:sz w:val="16"/><w:sz-cs w:val="16"/></w:rPr> <w:t id="vulncreated">2009-02-10 00:07 GMT</w:t> </w:r> </w:p> <wx:sub-section id="vulndesc"> <w:p wsp:rsidR="00C27C1C" wsp:rsidRDefault="00C27C1C" wsp:rsidP="00C27C1C"> <w:pPr><w:pStyle w:val="Heading2"/></w:pPr> <w:r><w:t>Description</w:t></w:r> </w:p> </wx:sub-section> <wx:sub-section id="vulnrec"> <w:p wsp:rsidR="00C27C1C" wsp:rsidRDefault="00C27C1C" wsp:rsidP="00C27C1C"> <w:pPr><w:pStyle w:val="Heading2"/></w:pPr> <w:r><w:t>Recommendation</w:t></w:r> </w:p> </wx:sub-section> <wx:sub-section id="vulnextra"> <w:p wsp:rsidR="00C27C1C" wsp:rsidRDefault="00C27C1C" wsp:rsidP="00C27C1C"> <w:pPr><w:pStyle w:val="Heading2"/></w:pPr> <w:r><w:t>Additional Information</w:t></w:r> </w:p> </wx:sub-section> </wx:sub-section> <!-- /Note Section --> |
- Issue title:
<w:t id="vulntitle">(line 7) - Date of discovery:
<w:t id="vulncreated">(line 21) - Issue description:
<wx:sub-section id="vulndesc">(line 25) - Mitigation recommendations:
<wx:sub-section id="vulnrec">(line 32) - Additional information:
<wx:sub-section id="vulnextra">(line 39)
For the issue title and the date of discovery we will be modifying the text of the XML node. For the description, recommendation and additional information we will be attaching child nodes to the one tagged.
The Code-fu
We are already thinking on ways of mapping your template sections with you repository fields, but for the time being most of the information is extracted from the the Note text. So the structure of our notes would be:-
Title: Whatever is the title of the issue Descripton: Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Recommendation: Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Additional stuff: - this and that - blah blah
The code will take the Note text, then apply a regular expression to find the different sections and extract the fields used in the template:-
1 2 3 4 5 6 | Note.find(:all).each do |n| #... more stuff here puts "processing Note #{n.id}" fields = n.text.split(/.+?:\n.*?/).collect do |field| field.strip end #... more stuff here end |
The code will be smart enough to detect if the Additional stuff section is present or not, this is done by simply counting the number of extracted fields.
The XML Documents
For To process XML we will be using REXML:
1 2 3 4 5 6 7 8 9 10 | begin doc = REXML::Document.new(File.new('./vendor/plugins/word_export/template.xml','r')) rescue REXML::ParseException => e # re-raise exception raise Exception.new(e) end vulns = [] body = REXML::XPath.first(doc, '//w:body') vuln_template = REXML::Document.new( doc.root.clone.to_s ) vuln_template.root.add body.children[3] |
First we need to load the template, then in lines 8-10 we locate the section of the code we will be repeating for each Note. and store a copy of the full section in vuln_template.
Using REXML there are two ways of copying an XML node, one is with element.clone() (that will create a copy of the node, but not of its children) and the other is creating a new document: Document.new( element.to_s ) (that will create a swallow copy). Ideally I would have created a new Document with the subsection that contains the template for the Note, however, Word defines a number of XML namespaces at the begining of the document, and REXML complains if we try to create a document without the right namespaces (this would be a simple: Document.new(body.children[3].to_s)).
So instead we need to create a new document that contains the Word namespaces, and then include the Note template we want to use. We do this by cloning the root node of the original document and then attaching the desired structure of the Note.
Also note in line 7 we are initialising an array of vulnerabilities. We will be looping through our notes and filling in the template with the information contained in the Note text and storing the vulnerability in this array. Afterwards, we will attach all the vulnerabilities to the w:body of our document.
Filling in the template
So for each Note we need to create a new XML section that will be stored in the vulns array. This is done by duplicating the vuln_template and filling it with content from the Note.
v = REXML::Document.new(vuln_template.to_s)
The rest of the code is standard XML processing. For the Title and Date fields, just assign the text of the node:
1 2 3 4 | #title title = REXML::XPath.first(v, "//w:t[@id='vulntitle']") title.delete_attribute('id') title.text = fields[1] |
For the Description, Recommendation and Additional information sections, we create a new XML paragraph for each paragraph in the original text:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | description = REXML::XPath.first(v, "//wx:sub-section[@id='vulndesc']") description.delete_attribute('id') fields[2].split("\n\n").each do |paragraph| par = REXML::Element.new('w:t') par.text = paragraph r = REXML::Element.new('w:r') r.elements << par chunk = REXML::Element.new('w:p') chunk.attributes['wsp:rsidR']='00C27C1C' chunk.attributes['wsp:rsidRDefault']='00C27C1C' chunk.attributes['wsp:rsidP']='00C27C1C' chunk.elements << r description.elements << chunk end |
Attaching the Vulnerabilities to the Document
Finally we need to attach all the vulnerabilities to the body of the main document. This is done with the following code:-
vulns.each do |v| body.insert_before('//w:sectPr', v.root.children[0]) end
Note that we are not adding the root element, but its first child. This is because the root element is w:wordDocument tag that contains the namespace declarations as already discussed.
And the last thing that is left is to write the document in our output report.xml file:
doc.write(File.new('report.xml','w'), -1, true)
Get the Plug-in
The plugin is already in the repository (in server/trunk/vendor/plugins/), but if you have dradis already in your box you can get it by going to the server folder and executing:
ruby script\plugin install http://dradis.svn.sourceforge.net/svnroot/dradis/server/trunk/vendor/plugins/word_export/
And to run it:
rake export:wordJust remember that you need your notes to formatted as the plugin expects. You can download a sample development.sqlite3.
Popularity: 13% [?]
