- name
- create-addon
- description
- Scaffold a new Dradis addon gem with the correct boilerplate structure. Ask questions to determine the addon type, name, description, and capabilities, then generate all required files in a new directory.
You are scaffolding a new Dradis addon. Follow these steps exactly.
## Step 1 — Gather information
Ask the user the following questions one group at a time. Keep the tone friendly and jargon-free. Wait for all answers in a group before moving to the next group.
**Group 1 — The basics:**
Ask these four questions together:
1. **What would you like to call your addon?** This can be a tool name, a product name, or just a short descriptive name. For example: "Custom Importer", "Custom Exporter", "ACME Risk Matrix". It will be used to name the addon directory and files.
2. **Describe what your addon does in one sentence.** For example: "Imports findings from our internal scanning tool into Dradis" or "Displays all project findings in a custom risk matrix table tailored to ACME's scoring methodology."
3. **Your name** — this will appear in the addon's metadata.
4. **Your email address** — also used in the addon's metadata.
---
**Group 2 — What should your addon do?**
Ask this as a simple choice. Frame it conversationally:
> Dradis addons can do one or more of the following things. Which fits what you're building?
>
> **Option A — Import files**
> Your addon will let users upload a file — any kind of file — and Dradis will read it and turn the contents into items in the project (like issues, notes, or evidence). Choose this if you have data in a file that you want to bring into Dradis automatically.
>
> **Option B — Export / generate reports**
> Your addon will take the data already in a Dradis project and produce an output file from it — for example, a custom HTML report, a CSV export, or any other format your team needs. Choose this if you want to generate something *from* Dradis.
>
> **Option C — Add new pages to Dradis**
> Your addon will add its own screens or interfaces inside the Dradis web app — for example, a custom risk matrix table, a team-specific findings view, or a bespoke workflow that only makes sense for your organisation. Choose this if you're building something that needs its own UI.
>
> **Option D — A combination**
> Mix and match any of the above. Just tell us which ones apply.
## Step 2 — Confirm before creating
Silently derive the following from the addon name:
- `<slug>` — lowercase, hyphenated (e.g. `custom-importer`, `acme-risk-matrix`)
- `<module_name>` — CamelCase (e.g. `CustomImporter`, `AcmeRiskMatrix`)
- `<plugin_name>` — underscored (e.g. `custom_importer`, `acme_risk_matrix`)
Then confirm with the user using only the details they gave you:
> Here's what I'll create:
>
> - **Name:** <addon name they provided>
> - **Description:** <description they provided>
> - **Type:** <what they chose — Import, Export, Custom pages, or a combination>
> - **Location:** `../plugins/dradis-<slug>/`
>
> Shall I go ahead?
## Step 3 — Generate files
Create all files inside `../plugins/dradis-<slug>/` (a sibling directory to `dradis-ce`, under a `plugins/` folder). Generate every file listed below with correct content based on the answers.
---
### Files to generate
#### `dradis-<slug>.gemspec`
```ruby
$:.push File.expand_path('../lib', __FILE__)
require 'dradis/plugins/<slug>/version'
version = Dradis::Plugins::<module_name>::VERSION::STRING
Gem::Specification.new do |spec|
spec.platform = Gem::Platform::RUBY
spec.name = 'dradis-<slug>'
spec.version = version
spec.summary = '<description>'
spec.description = '<description>'
spec.license = 'GPL-2'
spec.authors = ['<author_name>']
spec.email = ['<author_email>']
spec.homepage = 'https://dradis.com'
spec.files = `git ls-files`.split($\)
spec.executables = spec.files.grep(%r{^bin/}).map { |f| File.basename(f) }
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
spec.require_paths = ['lib']
spec.add_dependency 'dradis-plugins', '~> 4.0'
spec.add_development_dependency 'bundler', '~> 2.0'
spec.add_development_dependency 'combustion', '~> 1.3'
spec.add_development_dependency 'rake'
spec.add_development_dependency 'rspec-rails'
end
```
#### `Gemfile`
```ruby
source 'https://rubygems.org'
gemspec
```
#### `Rakefile`
```ruby
require 'bundler/gem_tasks'
require 'rspec/core/rake_task'
RSpec::Core::RakeTask.new(:spec)
task default: :spec
```
#### `lib/dradis-<slug>.rb`
```ruby
require 'dradis-plugins'
require 'dradis/plugins/<slug>'
```
#### `lib/dradis/plugins/<slug>.rb`
```ruby
module Dradis::Plugins::<module_name>
PLUGIN_NAME = :<plugin_name>
end
require 'dradis/plugins/<slug>/engine'
require 'dradis/plugins/<slug>/gem_version'
require 'dradis/plugins/<slug>/version'
```
If upload addon, also add:
```ruby
require 'dradis/plugins/<slug>/field_processor'
require 'dradis/plugins/<slug>/importer'
require 'dradis/plugins/<slug>/mapping'
```
#### `lib/dradis/plugins/<slug>/engine.rb`
The `provides` line reflects the user's chosen option(s):
- Import only: `provides :upload`
- Export only: `provides :export`
- Custom pages only: `provides :addon`
- Any combination: `provides :upload, :export` / `provides :upload, :addon` / etc.
```ruby
module Dradis::Plugins::<module_name>
class Engine < ::Rails::Engine
isolate_namespace Dradis::Plugins::<module_name>
include ::Dradis::Plugins::Base
description '<description>'
provides :<capability> # one or more of: :upload, :export, :addon
end
end
```
If `provides :addon` or `provides :export` is included, add an initializer block to mount the engine.
Use `routes.append` (not `prepend`) so the engine never overrides the main app's routes:
```ruby
initializer '<slug>.mount_engine' do
Rails.application.routes.append do
mount Dradis::Plugins::<module_name>::Engine => '/', as: :<plugin_name>
end
end
```
#### `lib/dradis/plugins/<slug>/version.rb`
```ruby
require_relative 'gem_version'
module Dradis
module Plugins
module <module_name>
module VERSION
MAJOR = 0
MINOR = 1
TINY = 0
PRE = nil
STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
end
end
end
end
```
#### `lib/dradis/plugins/<slug>/gem_version.rb`
```ruby
module Dradis
module Plugins
module <module_name>
def self.gem_version
Gem::Version.new VERSION::STRING
end
end
end
end
```
#### `lib/dradis/plugins/<slug>/importer.rb` (upload addons only)
```ruby
module Dradis::Plugins::<module_name>
class Importer < Dradis::Plugins::Upload::Importer
# Override the import method to process the uploaded file.
#
# The `params` hash includes:
# params[:file] - path to the uploaded file (a Tempfile or String path)
# params[:project] - the current Dradis project
#
# Use content_service to create nodes, issues, and evidence:
# content_service.create_node(label: 'hostname')
# content_service.create_note(node: node, text: '#[Title]#\nValue')
# content_service.create_issue(text: '#[Title]#\nValue')
# content_service.create_evidence(issue: issue, node: node, content: '#[Port]#\n443')
#
# Use mapping_service to apply field mappings from templates:
# mapping_service.apply_mapping(source: :<entity>, data: object)
def import(params = {})
file_content = File.read(params[:file])
# TODO: parse file_content and iterate over findings
logger.info { 'Done.' }
end
end
end
```
#### `lib/dradis/plugins/<slug>/field_processor.rb` (upload addons only)
```ruby
module Dradis::Plugins::<module_name>
class FieldProcessor < Dradis::Plugins::Upload::FieldProcessor
# Map a Liquid template field name to a value from the source data object.
#
# `params[:data]` is the object you passed to mapping_service.apply_mapping.
# `params[:field]` is the field name from the template (e.g. '<plugin_name>[entity.attribute]').
#
# Example:
# when '<plugin_name>[finding.title]' then object.title
def value(params = {})
_object = params[:data]
_field = params[:field]
case params[:field]
# TODO: add field mappings here
# when '<plugin_name>[finding.title]' then object.title
end
end
end
end
```
#### `lib/dradis/plugins/<slug>/mapping.rb` (upload addons only)
```ruby
module Dradis::Plugins::<module_name>
module Mapping
# DEFAULT_MAPPING defines the default Liquid templates used when importing.
# Keys are entity types (matching your importer), values are field-to-template maps.
# Template variables use the syntax: {{ <plugin_name>[entity.attribute] }}
DEFAULT_MAPPING = {
issue: {
'Title' => '{{ <plugin_name>[issue.title] }}',
'Description' => '{{ <plugin_name>[issue.description] }}'
},
evidence: {
'Output' => '{{ <plugin_name>[evidence.output] }}'
}
}.freeze
SOURCE_FIELDS = {
issue: %w[
issue.title
issue.description
],
evidence: %w[
evidence.output
]
}.freeze
end
end
```
Adjust entity names and fields based on the user's answers about their data model.
#### `templates/` (upload addons only)
Create both sample files. Use the Dradis `#[FieldName]#` syntax.
**`templates/issue.sample`:**
```
#[Title]#
{{ <plugin_name>[issue.title] }}
#[Description]#
{{ <plugin_name>[issue.description] }}
```
**`templates/evidence.sample`:**
```
#[Output]#
{{ <plugin_name>[evidence.output] }}
```
#### `spec/spec_helper.rb`
```ruby
ENV['RAILS_ENV'] ||= 'test'
require 'combustion'
Combustion.initialize! :all
require 'rspec/rails'
RSpec.configure do |config|
config.expect_with :rspec do |expectations|
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
end
config.mock_with :rspec do |mocks|
mocks.verify_partial_doubles = true
end
config.shared_context_metadata_behavior = :apply_to_host_groups
end
```
#### `spec/dradis/plugins/<slug>/importer_spec.rb` (upload addons only)
```ruby
require 'spec_helper'
describe Dradis::Plugins::<module_name>::Importer do
pending 'add importer specs here'
end
```
#### `spec/fixtures/files/.gitkeep`
Empty placeholder file so the fixtures directory is tracked.
#### `.gitignore`
```
*.gem
*.rbc
.bundle
.config
.yardoc
Gemfile.lock
InstalledFiles
_yardoc/
coverage/
doc/
lib/bundler/man/
pkg/
rdoc/
spec/reports/
test/tmp/
test/version_tmp/
tmp/
```
#### `LICENSE`
```
Copyright (c) <year> <author_name>
This software is open source and available under the GNU General Public License
version 2 (GPL-2). See https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
```
#### `CHANGELOG.md`
```markdown
# Changelog
## [Unreleased]
### Added
- Initial release
```
#### `README.md`
```markdown
# dradis-<slug>
<description>
## Installation
Add to your Dradis `Gemfile`:
```ruby
gem 'dradis-<slug>'
```
Then run:
```
bundle install
```
## Usage
<!-- TODO: describe how to use this addon -->
View on GitHub