One of the latest additions in Dradis Pro release 2.6.0 was the attachments API. Until now that was only available using the web interface:
As documented here that new API endpoint allows to manipulate node attachments via REST requests. Here there are a couple of examples, using curl.
Read attachments associated to a specific node:
curl \ -H 'Authorization: Token token="iOEFCQDR-miTHNTjiBxObjWC"' \ -H 'Dradis-Project-Id: 8' \ http://dradis.ip/pro/api/nodes/18/attachments
The response to this request is a JSON list of attachments in that node:
[ { "filename": "burp.xml", "link": "/nodes/18/attachments/burp.xml" }, { "filename": "screenshot.png", "link": "/nodes/18/attachments/screenshot.png" } ]
This is a request to attach some other files to that node:
curl \ -H 'Authorization: Token token="iOEFCQDR-miTHNTjiBxObjWC"' \ -H 'Dradis-Project-Id: 8' \ -X POST \ -F 'files[]=@/your/local/path/image1.png' -F 'files[]=@/your/local/path/image2.png' \ http://dradis.ip/pro/api/nodes/18/attachments
The response to this request is a JSON list containing the new attachments info:
[ { "filename": "image1.png", "link": "/nodes/18/attachments/image1.png" }, { "filename": "image2.png", "link": "/nodes/18/attachments/image2.png" } ]
In addition in this post we would like to extend that documentation providing examples on how to do that using a programming language. Since Dradis is implemented in ruby, here is how we could do that in ruby.
Using ruby there are many libraries that allow us to perform http requests, from the basic
already included ‘net/http‘ to more high level options like ‘rest_client‘, ‘faraday‘, etc…
We will show basic examples using these three mentioned options.
For each option we provide two examples:
- a request to get all attachments in a node
- a requests to upload a couple of files to a node (in the attachments endpoint many files can be uploaded with a single request).
If you intend to use the examples below, remember that you should use your virtual appliance IP instead of ‘dradis.ip‘. Also change the token, project id and node id in the examples to your own values.
Attachments API using ‘rest-client’ ruby gem:
First of all we will need to install the ‘rest-client’ ruby gem. It can be installed with:
gem install rest-client
Read attachments associated to a specific node:
require 'rest_client' RestClient.get( 'http://dradis.ip/pro/api/nodes/18/attachments', { 'Authorization' => 'Token token="iOEFCQDR-miTHNTjiBxObjWC"', 'Dradis-Project-Id' => '8' } )
Attach some other files to that node:
require 'rest_client' RestClient.post( 'http://dradis.ip/pro/api/nodes/18/attachments', { 'files' => [ File.new("/your/local/path/image1.png", 'rb'), File.new("/your/local/path/image2.png", 'rb') ] }, { 'Authorization' => 'Token token="iOEFCQDR-miTHNTjiBxObjWC"', 'Dradis-Project-Id' => '8' } )
Attachments API using ‘faraday’ ruby gem:
To install faraday:
gem install faraday
In this case we are trying to reuse the same connection, probably useful when building a script that sends many requests to the same endpoint.
require 'faraday' # Establish connection conn = Faraday.new( url: 'http://dradis.ip/pro/api/nodes/18/attachments', headers: { 'Authorization' => 'Token token="iOEFCQDR-miTHNTjiBxObjWC"', 'Dradis-Project-Id' => '8' } ) do |faraday| faraday.request :multipart faraday.adapter :net_http end # Read attachments associated to a specific node: get = conn.get puts get.body # Attach some other files to that node post = conn.post( nil, { 'files' => [ Faraday::UploadIO.new("/your/local/path/image1.png", 'image/png'), Faraday::UploadIO.new("/your/local/path/image2.png", 'image/png') ] } ) puts post.body
Attachments API using ruby ‘net/http’:
‘net/http’ is part of the ruby standard library, so if you already have ruby nothing else should be installed to run this script. As a counterpart this option works at a lower level than the previous ones, therefore the code looks a bit more complex.
require 'net/http' uri = URI('http://dradis.ip/pro/api/nodes/18/attachments') Net::HTTP.start(uri.host, uri.port) do |http| # Read attachments associated to a specific node: get_request = Net::HTTP::Get.new uri get_request['Authorization'] = 'Token token="iOEFCQDR-miTHNTjiBxObjWC"' get_request['Dradis-Project-Id'] = '8' get_response = http.request(get_request) puts get_response.body # Attach some other files to that node: BOUNDARY = "AaB03x" file1 = '/your/local/path/image1.png' file2 = '/your/local/path/image2.png' post_body = [] post_body << "--#{BOUNDARY}\r\n" post_body << "Content-Disposition: form-data; name=\"files[]\"; filename=\"#{File.basename(file1)}\"\r\n" post_body << "Content-Type: image/png\r\n" post_body << "\r\n" post_body << File.read(file1) post_body << "\r\n--#{BOUNDARY}\r\n" post_body << "Content-Disposition: form-data; name=\"files[]\"; filename=\"#{File.basename(file2)}\"\r\n" post_body << "Content-Type: image/png\r\n" post_body << "\r\n" post_body << File.read(file2) post_body << "\r\n--#{BOUNDARY}--\r\n" post_request = Net::HTTP::Post.new uri post_request['Authorization'] = 'Token token="iOEFCQDR-miTHNTjiBxObjWC"' post_request['Dradis-Project-Id'] = '8' post_request.body = post_body.join post_request["Content-Type"] = "multipart/form-data, boundary=#{BOUNDARY}" post_response = http.request(post_request) puts post_response.body end
Final thoughts
In conclusion, sending requests to the API should be easy enough from any programming language. In the ruby case, using a specialized gem seems like the best choice.