Repository: pablosnt/rekono (language: python)

Issue:
Improve resources filter by likes in web UI
Use checkbox instead of select form to filter resources by user likes


Discussion / hints:

Fix patch:
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 6629a1fd7..1210e9ce5 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -9,7 +9,7 @@ repos:
             require_serial: true
             verbose: true
     - repo: https://github.com/Yelp/detect-secrets
-      rev: v1.3.0
+      rev: v1.4.0
       hooks:
           - id: detect-secrets
             args: ["--baseline", ".secrets.baseline"]
diff --git a/.secrets.baseline b/.secrets.baseline
index d557a43eb..08d264eb2 100644
--- a/.secrets.baseline
+++ b/.secrets.baseline
@@ -1,5 +1,5 @@
 {
-  "version": "1.3.0",
+  "version": "1.4.0",
   "plugins_used": [
     {
       "name": "ArtifactoryDetector"
@@ -192,7 +192,7 @@
         "filename": "rekono/findings/enums.py",
         "hashed_secret": "8be3c943b1609fffbfc51aad666d0a04adf83c9d",
         "is_verified": false,
-        "line_number": 24,
+        "line_number": 25,
         "is_secret": false
       }
     ],
@@ -784,7 +784,7 @@
         "filename": "rekono/testing/executions/test_base_tool.py",
         "hashed_secret": "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3",
         "is_verified": false,
-        "line_number": 311,
+        "line_number": 312,
         "is_secret": false
       }
     ],
@@ -849,5 +849,5 @@
       }
     ]
   },
-  "generated_at": "2022-12-31T14:34:49Z"
+  "generated_at": "2023-01-05T19:32:50Z"
 }
diff --git a/README.md b/README.md
index afaca911a..4a809060b 100644
--- a/README.md
+++ b/README.md
@@ -59,6 +59,7 @@ Why not automate this process and focus on find vulnerabilities using your skill
 - [SSH Audit](https://github.com/jtesta/ssh-audit)
 - [SMBMap](https://github.com/ShawnDEvans/smbmap)
 - [Dirsearch](https://github.com/maurosoria/dirsearch)
+- [Gobuster](https://github.com/OJ/gobuster)
 - [GitLeaks](https://github.com/zricethezav/gitleaks) & [GitDumper](https://github.com/internetwache/GitTools/tree/master/Dumper)
 - [Log4j Scan](https://github.com/fullhunt/log4j-scan)
 - [Spring4Shell Scan](https://github.com/fullhunt/spring4shell-scan)
diff --git a/docker/kali/Dockerfile b/docker/kali/Dockerfile
index b8d2cc235..3d8ffcaec 100644
--- a/docker/kali/Dockerfile
+++ b/docker/kali/Dockerfile
@@ -16,7 +16,7 @@ RUN ln -s /usr/bin/python3 /usr/bin/python
 RUN pip install --upgrade pip
 RUN pip install -r /code/requirements.txt
 
-RUN apt install nmap dirsearch theharvester nikto sslscan sslyze cmseek zaproxy exploitdb metasploit-framework emailharvester joomscan gitleaks smbmap nuclei -y
+RUN apt install nmap dirsearch theharvester nikto sslscan sslyze cmseek zaproxy exploitdb metasploit-framework emailharvester joomscan gitleaks smbmap nuclei gobuster -y
 RUN setcap cap_net_raw,cap_net_admin,cap_net_bind_service+eip $(which nmap)
 RUN git clone https://github.com/fullhunt/log4j-scan /opt/log4j-scan
 RUN git clone https://github.com/fullhunt/spring4shell-scan.git /opt/spring4shell-scan
@@ -25,8 +25,7 @@ RUN pip install -r /opt/log4j-scan/requirements.txt
 RUN pip install -r /opt/spring4shell-scan/requirements.txt
 RUN pip install emailfinder ssh-audit
 
-RUN apt install wordlists seclists dirb -y
-RUN gzip -d /usr/share/wordlists/rockyou.txt.gz
+RUN apt install seclists dirb -y
 
 RUN adduser --disabled-password rekono
 RUN chown -R rekono:rekono /code
diff --git a/rekono/executions/filters.py b/rekono/executions/filters.py
index 0b090395a..93ac26a30 100644
--- a/rekono/executions/filters.py
+++ b/rekono/executions/filters.py
@@ -1,5 +1,6 @@
 from django_filters.rest_framework import FilterSet
 from django_filters.rest_framework.filters import OrderingFilter
+
 from executions.models import Execution
 
 
@@ -37,9 +38,9 @@ class Meta:
             'task__executor__username': ['exact', 'icontains'],
             'tool': ['exact'],
             'tool__name': ['exact', 'icontains'],
-            'tool__stage': ['exact'],
             'configuration': ['exact'],
             'configuration__name': ['exact', 'icontains'],
+            'configuration__stage': ['exact'],
             'status': ['exact'],
             'start': ['gte', 'lte', 'exact'],
             'end': ['gte', 'lte', 'exact']
diff --git a/rekono/findings/enums.py b/rekono/findings/enums.py
index 7aea5b892..15d392f37 100644
--- a/rekono/findings/enums.py
+++ b/rekono/findings/enums.py
@@ -16,6 +16,7 @@ class DataType(models.TextChoices):
 
     IP = 'IP'
     DOMAIN = 'Domain'
+    VHOST = 'VHOST'
     URL = 'Url'
     EMAIL = 'Email'
     LINK = 'Link'
diff --git a/rekono/findings/filters.py b/rekono/findings/filters.py
index ee2caf0a1..3e378db5c 100644
--- a/rekono/findings/filters.py
+++ b/rekono/findings/filters.py
@@ -4,6 +4,7 @@
 from django.db.models import QuerySet
 from django_filters.rest_framework import filters
 from django_filters.rest_framework.filters import OrderingFilter
+
 from findings.enums import OSType
 from findings.models import (OSINT, Credential, Exploit, Host, Path, Port,
                              Technology, Vulnerability)
@@ -33,7 +34,6 @@
     'executions__end': ['gte', 'lte', 'exact'],
     'detected_by': ['exact'],
     'detected_by__name': ['exact', 'icontains'],
-    'detected_by__stage': ['exact'],
     'first_seen': ['gte', 'lte', 'exact'],
     'last_seen': ['gte', 'lte', 'exact'],
     'is_active': ['exact'],
@@ -162,7 +162,9 @@ class PortFilter(FindingFilter):
     '''FilterSet to filter and sort Port entities.'''
 
     # Ordering fields including common ones
-    o = OrderingFilter(fields=FINDING_ORDERING + (('host__os_type', 'os_type'), 'host', 'port', 'protocol', 'service'))
+    o = OrderingFilter(
+        fields=FINDING_ORDERING + (('host__os_type', 'os_type'), 'host', 'port', 'protocol', 'service', 'status')
+    )
 
     class Meta:
         '''FilterSet metadata.'''
@@ -184,7 +186,7 @@ class PathFilter(FindingFilter):
     '''FilterSet to filter and sort Path entities.'''
 
     # Ordering fields including common ones
-    o = OrderingFilter(fields=FINDING_ORDERING + (('port__host', 'host'), 'port', 'path', 'status'))
+    o = OrderingFilter(fields=FINDING_ORDERING + (('port__host', 'host'), 'port', 'path', 'status', 'type'))
 
     class Meta:
         '''FilterSet metadata.'''
diff --git a/rekono/findings/migrations/0002_alter_osint_data_type.py b/rekono/findings/migrations/0002_alter_osint_data_type.py
new file mode 100644
index 000000000..8f17793ce
--- /dev/null
+++ b/rekono/findings/migrations/0002_alter_osint_data_type.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.2.16 on 2023-01-05 15:42
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('findings', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='osint',
+            name='data_type',
+            field=models.TextField(choices=[('IP', 'Ip'), ('Domain', 'Domain'), ('VHOST', 'Vhost'), ('Url', 'Url'), ('Email', 'Email'), ('Link', 'Link'), ('ASN', 'Asn'), ('Username', 'User'), ('Password', 'Password')], max_length=10),
+        ),
+    ]
diff --git a/rekono/findings/models.py b/rekono/findings/models.py
index 278401182..c79df9d2f 100644
--- a/rekono/findings/models.py
+++ b/rekono/findings/models.py
@@ -165,8 +165,12 @@ def filter(self, input: Input) -> bool:
         if not input.filter:
             return True
         try:
-            # Filter by address type
-            return cast(models.TextChoices, TargetType)[input.filter] == get_target_type(self.address)
+            distinct = input.filter[0] == '!'
+            filter_types = [
+                cast(models.TextChoices, TargetType)[f.upper()] for f in input.filter.replace('!', '').split(',s')
+            ]
+            host_type = get_target_type(self.address)
+            return host_type not in filter_types if distinct else host_type in filter_types
         except KeyError:
             return True
 
diff --git a/rekono/frontend/src/backend/RekonoApi.vue b/rekono/frontend/src/backend/RekonoApi.vue
index b477bfd47..22fffbae1 100644
--- a/rekono/frontend/src/backend/RekonoApi.vue
+++ b/rekono/frontend/src/backend/RekonoApi.vue
@@ -78,7 +78,7 @@ export default {
       cancellableStatuses: ['Requested', 'Running'],
       timeUnits: ['Weeks', 'Days', 'Hours', 'Minutes'],
       authenticationTypes: ['None', 'Basic', 'Bearer', 'Cookie', 'Digest', 'JWT', 'NTLM'],
-      wordlistTypes: ['Endpoint'],
+      wordlistTypes: ['Endpoint', 'Subdomain'],
       nameRegex: /^[\wÀ-ÿ\s.\-[\]()]{0,100}$/,
       textRegex: /^[\wÀ-ÿ\s.:,+\-'"?¿¡!#%$€[\]()]{0,300}$/,
       cveRegex: /^CVE-\d{4}-\d{1,7}$/,
diff --git a/rekono/frontend/src/common/TableHeader.vue b/rekono/frontend/src/common/TableHeader.vue
index f7cf8229e..7bd041e99 100644
--- a/rekono/frontend/src/common/TableHeader.vue
+++ b/rekono/frontend/src/common/TableHeader.vue
@@ -31,12 +31,13 @@
         <b-col v-for="ft in filters" :key="ft.name">
           <b-form-group :description="ft.name">
             <b-form-tags :ref="ft.name.toLowerCase().replace(' ', '_')" no-outer-focus v-if="!ft.values && ft.type === 'tags'" :value="$route.query[ft.name.toLowerCase().replace(' ', '_')] ? $route.query[ft.name.toLowerCase().replace(' ', '_')].split(',') : null" placeholder="" remove-on-delete size="md" tag-variant="dark" @input="addFilter(ft.name, ft.filterField, $event)"/>
-            <b-form-input :ref="ft.name.toLowerCase().replace(' ', '_')" v-if="!ft.values && ft.type !== 'tags'" :value="$route.query[ft.name.toLowerCase().replace(' ', '_')] ? $route.query[ft.name.toLowerCase().replace(' ', '_')] : null" :type="ft.type" @input="addFilter(ft.name, ft.filterField, $event)"/>
+            <b-form-input :ref="ft.name.toLowerCase().replace(' ', '_')" v-if="!ft.values && ft.type !== 'tags' && ft.type !== 'checkbox'" :value="$route.query[ft.name.toLowerCase().replace(' ', '_')] ? $route.query[ft.name.toLowerCase().replace(' ', '_')] : null" :type="ft.type" @input="addFilter(ft.name, ft.filterField, $event)"/>
             <b-form-select :ref="ft.name.toLowerCase().replace(' ', '_')" v-if="ft.values" :value="$route.query[ft.name.toLowerCase().replace(' ', '_')] ? $route.query[ft.name.toLowerCase().replace(' ', '_')] : ft.default" :options="ft.values" :value-field="ft.valueField" :text-field="ft.textField" @change="addFilter(ft.name, ft.filterField, $event)">
               <template #first>
                 <b-form-select-option :value="null">Select {{ ft.name.toLowerCase().replace(' ', '_') }}</b-form-select-option>
               </template>
             </b-form-select>
+            <b-form-checkbox :ref="ft.name.toLowerCase().replace(' ', '_')" v-if="ft.type === 'checkbox'" @change="addFilter(ft.name, ft.filterField, $event)"/>
           </b-form-group>
         </b-col>
       </b-row>
diff --git a/rekono/frontend/src/components/Processes.vue b/rekono/frontend/src/components/Processes.vue
index 0ec58cca3..7909f5fcc 100644
--- a/rekono/frontend/src/components/Processes.vue
+++ b/rekono/frontend/src/components/Processes.vue
@@ -107,7 +107,7 @@ export default {
         { key: 'icon', label: '', sortable: false },
         { key: 'tool.name', label: 'Tool', sortable: true },
         { key: 'configuration.name', label: 'Configuration', sortable: true },
-        { key: 'tool.stage_name', label: 'Stage', sortable: true },
+        { key: 'configuration.stage_name', label: 'Stage', sortable: true },
         { key: 'priority', sortable: true },
         { key: 'actions', sortable: false }
       ],
@@ -132,9 +132,9 @@ export default {
     data () {
       this.filters = [
         { name: 'Tags', filterField: 'tags__name__in', type: 'tags' },
-        { name: 'Stage', values: this.stages, valueField: 'id', textField: 'value', filterField: 'steps__tool__stage' },
+        { name: 'Stage', values: this.stages, valueField: 'id', textField: 'value', filterField: 'steps__configuration__stage' },
         { name: 'Creator', filterField: 'creator__username__icontains', type: 'text' },
-        { name: 'Favourities', values: [{ value: true, text: 'True' }, { value: false, text: 'False' }], valueField: 'value', textField: 'text', filterField: 'liked' }
+        { name: 'Favourities', type: 'checkbox', filterField: 'liked' }
       ]
     }
   },
diff --git a/rekono/frontend/src/components/Tools.vue b/rekono/frontend/src/components/Tools.vue
index 3c4c4e2f5..7c3b56c8e 100644
--- a/rekono/frontend/src/components/Tools.vue
+++ b/rekono/frontend/src/components/Tools.vue
@@ -8,6 +8,9 @@
           <b-img v-if="!row.item.icon" src="favicon.ico"/>
         </b-link>
       </template>
+      <template #cell(stages)="row">
+        <p>{{ Array.from(new Set(row.item.configurations.map(configuration => { return configuration.stage_name }))).join(', ') }}</p>
+      </template>
       <template #cell(intensities)="row">
         <div v-for="base in intensityByVariant" v-bind:key="base.intensity_rank" class="d-inline">
           <div v-for="i in row.item.intensities" v-bind:key="i.intensity_rank" class="d-inline">
@@ -16,7 +19,7 @@
         </div>
       </template>
       <template #cell(inputs)="row">
-        <p>{{ row.item.arguments.map(argument => { return argument.inputs.find(input => input.order === 1).type.name }).join(', ') }}</p>
+        <p>{{ Array.from(new Set(row.item.arguments.map(argument => { return argument.inputs.find(input => input.order === 1).type.name }))).join(', ') }}</p>
       </template>
       <template #cell(likes)="row">
         {{ row.item.likes }}
@@ -79,9 +82,9 @@ export default {
         { key: 'icon', label: '', sortable: false },
         { key: 'name', label: 'Tool', sortable: true },
         { key: 'command', sortable: true },
-        { key: 'stage_name', label: 'Stage', sortable: true },
+        { key: 'stages', label: 'Stages', sortable: false },
         { key: 'intensities', sortable: true },
-        { key: 'inputs', sortable: true },
+        { key: 'inputs', sortable: false },
         { key: 'likes', sortable: true },
         { key: 'actions', sortable: false }
       ],
@@ -107,16 +110,20 @@ export default {
   watch: {
     data () {
       this.filters = [
-        { name: 'Stage', values: this.stages, valueField: 'id', textField: 'value', filterField: 'stage' },
+        { name: 'Stage', values: this.stages, valueField: 'id', textField: 'value', filterField: 'configurations__stage' },
         { name: 'Input', values: this.inputTypes, valueField: 'value', textField: 'value', filterField: 'arguments__inputs__type__name' },
         { name: 'Output', values: this.inputTypes, valueField: 'value', textField: 'value', filterField: 'configurations__outputs__type__name' },
-        { name: 'Favourities', values: [{ value: true, text: 'True' }, { value: false, text: 'False' }], valueField: 'value', textField: 'text', filterField: 'liked' }
+        { name: 'Favourities', type: 'checkbox', filterField: 'liked' }
       ] 
     }
   },
   methods: {
     fetchData (params = null) {
-      return this.getOnePage('/api/tools/?o=stage,name', params)
+      if (!params) {
+        params = {}
+      }
+      params['o'] = 'configurations__stage,name'
+      return this.getOnePage('/api/tools/', params)
         .then(response => {
           this.data = response.data.results
           this.total = response.data.count
diff --git a/rekono/frontend/src/components/Wordlists.vue b/rekono/frontend/src/components/Wordlists.vue
index aa2984e4a..d50de9ea3 100644
--- a/rekono/frontend/src/components/Wordlists.vue
+++ b/rekono/frontend/src/components/Wordlists.vue
@@ -67,10 +67,10 @@ export default {
   watch: {
     data () {
       this.filters = [
-        { name: 'Type', values: ['Endpoint', 'Password'], valueField: 'value', textField: 'value', filterField: 'type' },
+        { name: 'Type', values: ['Endpoint', 'Subdomain'], valueField: 'value', textField: 'value', filterField: 'type' },
         { name: 'Max Size', filterField: 'size__lte', type: 'number' },
         { name: 'Creator', filterField: 'creator__username__icontains', type: 'text' },
-        { name: 'Favourities', values: [{ value: true, text: 'True' }, { value: false, text: 'False' }], valueField: 'value', textField: 'text', filterField: 'liked' }
+        { name: 'Favourities', type: 'checkbox', filterField: 'liked' }
       ] 
     }
   },
diff --git a/rekono/frontend/src/components/findings/Finding.vue b/rekono/frontend/src/components/findings/Finding.vue
index f2c711f43..3aab33c19 100644
--- a/rekono/frontend/src/components/findings/Finding.vue
+++ b/rekono/frontend/src/components/findings/Finding.vue
@@ -153,7 +153,7 @@ export default {
     getFilter () {
       let filter = {}
       if (this.order) {
-        filter.order = this.order
+        filter.o = this.order
       }
       if (this.execution) {
         filter.executions = this.execution
@@ -178,15 +178,17 @@ export default {
         filter = this.getFilter()
       }
       filter.severity = this.severities[index]
-      this.getAllPages('/api/vulnerabilities/', filter).then(results => {
-        this.findings.push(...results)
-        index += 1
-        if (index < this.severities.length) {
-          this.fetchVulnerabilities(filter, index)
-        } else {
-          this.$emit('end')
-        }
-      })
+      this.getAllPages('/api/vulnerabilities/', filter)
+        .then(results => {
+          this.findings.push(...results)
+          index += 1
+          if (index < this.severities.length) {
+            this.fetchVulnerabilities(filter, index)
+          } else {
+            this.$emit('end')
+          }
+        })
+        .catch(() => this.$emit('end'))
     },
     fetchData () {
       if (this.types && this.types.length > 0 && !this.types.includes(this.name.toLowerCase())) {
@@ -200,6 +202,7 @@ export default {
               this.findings = results
               this.$emit('end')
             })
+            .catch(() => this.$emit('end'))
         } else {
           this.fetchVulnerabilities()
         }
diff --git a/rekono/frontend/src/components/findings/Findings.vue b/rekono/frontend/src/components/findings/Findings.vue
index 1def574ff..3bb8d7d92 100644
--- a/rekono/frontend/src/components/findings/Findings.vue
+++ b/rekono/frontend/src/components/findings/Findings.vue
@@ -203,7 +203,7 @@ export default {
   methods: {
     fetchTargets () {
       if (!this.task) {
-        this.getAllPages('/api/targets/', { project: this.$route.params.id, order: 'target' })
+        this.getAllPages('/api/targets/', { project: this.$route.params.id, o: 'target' })
           .then(results => {
             this.targets = results
             if (results.length > 0) {
diff --git a/rekono/frontend/src/modals/ProjectMember.vue b/rekono/frontend/src/modals/ProjectMember.vue
index d31878895..58a936b67 100644
--- a/rekono/frontend/src/modals/ProjectMember.vue
+++ b/rekono/frontend/src/modals/ProjectMember.vue
@@ -38,7 +38,7 @@ export default {
   },
   methods: {
     fetchUsers () {
-      this.getAllPages('/api/users/', { project__ne: this.projectId, order: 'username' }).then(results => this.users = results)
+      this.getAllPages('/api/users/', { project__ne: this.projectId, o: 'username' }).then(results => this.users = results)
     },
     check () {
       this.memberState = (this.member !== null)
diff --git a/rekono/frontend/src/modals/Step.vue b/rekono/frontend/src/modals/Step.vue
index 51cfec296..ab8ae74b7 100644
--- a/rekono/frontend/src/modals/Step.vue
+++ b/rekono/frontend/src/modals/Step.vue
@@ -99,7 +99,7 @@ export default {
     initialized (initialized) {
       if (initialized) {
         if (!this.step && !this.tool) {
-          this.getAllPages('/api/tools/', { order: 'stage,name'}).then(results => this.tools = results)
+          this.getAllPages('/api/tools/', { o: 'configurations__stage,name'}).then(results => this.tools = results)
         } else if (this.step && !this.tool) {
           this.priority = this.step.priority
           this.step.tool.configurations = [this.step.configuration]
@@ -110,7 +110,7 @@ export default {
           this.selectTool(this.tool.id, this.tool)
         }
         if (!this.process) {
-          let filter = { order: 'name' }
+          let filter = { o: 'name' }
           if (this.$store.state.role !== 'Admin') {
             filter.creator = this.$store.state.user
           }
diff --git a/rekono/frontend/src/modals/Task.vue b/rekono/frontend/src/modals/Task.vue
index 74c04b1a2..1729ee5c5 100644
--- a/rekono/frontend/src/modals/Task.vue
+++ b/rekono/frontend/src/modals/Task.vue
@@ -49,7 +49,9 @@
             <b-icon icon="file-earmark-word-fill"/> Wordlists
           </template>
           <b-form-group description="Select wordlists to use">
-            <b-form-select v-model="wordlistsItems" :options="wordlists" multiple value-field="id" text-field="name" :select-size="10"/>
+            <b-form-select v-model="wordlistsItems" multiple :select-size="10">
+              <option v-for="wordlist in wordlists" :key="wordlist.id" :value="wordlist.id">{{ wordlist.name }} - {{ wordlist.type }}</option>
+            </b-form-select>
           </b-form-group>
         </b-tab>
         <b-tab title-link-class="text-secondary">
@@ -172,8 +174,8 @@ export default {
         } else if (this.process) {
           this.selectProcess(this.process.id, this.process)
         } else if (!this.tool && !this.process) {
-          this.getAllPages('/api/tools/', { order: 'stage,name'}).then(results => this.tools = results)
-          this.getAllPages('/api/processes/', { order: 'name' }).then(results => this.processes = results)
+          this.getAllPages('/api/tools/', { o: 'configurations__stage,name'}).then(results => this.tools = results)
+          this.getAllPages('/api/processes/', { o: 'name' }).then(results => this.processes = results)
         }
         if (this.target) {
           this.targetIds = [this.target.id]
@@ -181,7 +183,7 @@ export default {
         } else if (this.project) {
           this.get(`/api/projects/${this.project.id}/`).then(response => { this.selectProject(this.project.id, response.data) })
         } else {
-          this.getAllPages('/api/projects/', { order: 'name' }).then(results => this.projects = results)
+          this.getAllPages('/api/projects/', { o: 'name' }).then(results => this.projects = results)
         }
       }
     }
@@ -352,7 +354,7 @@ export default {
       }
     },
     updateWordlists () {
-      this.getAllPages('/api/resources/wordlists/', { order: 'type,name' }).then(results => this.wordlists = results)
+      this.getAllPages('/api/resources/wordlists/', { o: 'type,name' }).then(results => this.wordlists = results)
     },
     cleanScheduledIn () {
       this.scheduledIn = null
diff --git a/rekono/frontend/src/views/Task.vue b/rekono/frontend/src/views/Task.vue
index bbd2bf86b..e3425ea48 100644
--- a/rekono/frontend/src/views/Task.vue
+++ b/rekono/frontend/src/views/Task.vue
@@ -153,7 +153,7 @@ export default {
       }
     },
     fetchExecutions () {
-      this.getAllPages('/api/executions/', { task: this.$route.params.id, order: 'end,-status' })
+      this.getAllPages('/api/executions/', { task: this.$route.params.id, o: 'end,-status' })
         .then(results => {
           this.executions = results
           if (this.executions.length === 1) {
diff --git a/rekono/processes/executor/executor.py b/rekono/processes/executor/executor.py
index c0de8f2ae..e3ee42bee 100644
--- a/rekono/processes/executor/executor.py
+++ b/rekono/processes/executor/executor.py
@@ -58,7 +58,7 @@ def create_plan(task: Task) -> List[ExecutionJob]:
     ).filter(
         process=task.process
     ).order_by(
-        'tool__stage', '-priority', 'max_output', 'max_input'
+        'configuration__stage', '-priority', 'max_output', 'max_input'
     )
     for step in steps:                                                          # For each step
         # Get the greater intensity for this tool, limited to the task intensity
diff --git a/rekono/processes/filters.py b/rekono/processes/filters.py
index 3d1c7c8fc..55491dd64 100644
--- a/rekono/processes/filters.py
+++ b/rekono/processes/filters.py
@@ -1,5 +1,6 @@
 from django_filters.rest_framework import FilterSet, filters
 from likes.filters import LikeFilter
+
 from processes.models import Process, Step
 
 
@@ -19,9 +20,9 @@ class Meta:
             'creator__username': ['exact', 'icontains'],
             'steps__tool': ['exact'],
             'steps__tool__name': ['exact', 'icontains'],
-            'steps__tool__stage': ['exact'],
             'steps__configuration': ['exact'],
             'steps__configuration__name': ['exact', 'icontains'],
+            'steps__configuration__stage': ['exact'],
             'tags__name': ['in'],
         }
 
@@ -42,8 +43,8 @@ class Meta:
             'tool': ['exact'],
             'tool__name': ['exact', 'icontains'],
             'tool__command': ['exact', 'icontains'],
-            'tool__stage': ['exact'],
             'configuration': ['exact'],
             'configuration__name': ['exact', 'icontains'],
+            'configuration__stage': ['exact'],
             'priority': ['exact'],
         }
diff --git a/rekono/processes/fixtures/2_steps.json b/rekono/processes/fixtures/2_steps.json
index 697eabcdd..df455c11c 100644
--- a/rekono/processes/fixtures/2_steps.json
+++ b/rekono/processes/fixtures/2_steps.json
@@ -648,5 +648,75 @@
             "configuration": 39,
             "priority": 1
         }
+    },
+    {
+        "model": "processes.step",
+        "pk": 66,
+        "fields": {
+            "process": 1,
+            "tool": 20,
+            "configuration": 46,
+            "priority": 1
+        }
+    },
+    {
+        "model": "processes.step",
+        "pk": 67,
+        "fields": {
+            "process": 1,
+            "tool": 20,
+            "configuration": 47,
+            "priority": 1
+        }
+    },
+    {
+        "model": "processes.step",
+        "pk": 68,
+        "fields": {
+            "process": 1,
+            "tool": 20,
+            "configuration": 48,
+            "priority": 1
+        }
+    },
+    {
+        "model": "processes.step",
+        "pk": 69,
+        "fields": {
+            "process": 2,
+            "tool": 20,
+            "configuration": 48,
+            "priority": 1
+        }
+    },
+    {
+        "model": "processes.step",
+        "pk": 70,
+        "fields": {
+            "process": 6,
+            "tool": 20,
+            "configuration": 46,
+            "priority": 1
+        }
+    },
+    {
+        "model": "processes.step",
+        "pk": 71,
+        "fields": {
+            "process": 7,
+            "tool": 20,
+            "configuration": 47,
+            "priority": 1
+        }
+    },
+    {
+        "model": "processes.step",
+        "pk": 72,
+        "fields": {
+            "process": 7,
+            "tool": 20,
+            "configuration": 48,
+            "priority": 1
+        }
     }
 ]
\ No newline at end of file
diff --git a/rekono/processes/models.py b/rekono/processes/models.py
index 24a54188a..d7a9c1788 100644
--- a/rekono/processes/models.py
+++ b/rekono/processes/models.py
@@ -33,7 +33,7 @@ class Step(models.Model):
     process = models.ForeignKey(Process, related_name='steps', on_delete=models.CASCADE)    # Associated process
     tool = models.ForeignKey(Tool, on_delete=models.CASCADE)                    # Tool
     configuration = models.ForeignKey(Configuration, on_delete=models.CASCADE, blank=True, null=True)   # Configuration
-    # Priority value. Steps with greater priority will be executed before other of same process and with same tool stage
+    # Priority value. Steps with greater priority will be executed before other of same process and with same stage
     priority = models.IntegerField(default=1, validators=[validate_number])
 
     class Meta:
diff --git a/rekono/processes/serializers.py b/rekono/processes/serializers.py
index 547a57bca..6d293cfdc 100644
--- a/rekono/processes/serializers.py
+++ b/rekono/processes/serializers.py
@@ -101,15 +101,15 @@ class Meta:
 
     @extend_schema_field(StepSerializer(many=True, read_only=True))
     def get_steps(self, instance: Process) -> List[StepSerializer]:
-        '''Get process steps sorted by tool stage and priority (descendent).
+        '''Get process steps sorted by configuration stage and priority (descendent).
 
         Args:
             instance (Process): Process instance
 
         Returns:
-            List[StepSerializer]: Step list sorted by tool stage and priority (descendent)
+            List[StepSerializer]: Step list sorted by configuration stage and priority (descendent)
         '''
-        return StepSerializer(instance.steps.all().order_by('tool__stage', '-priority'), many=True).data
+        return StepSerializer(instance.steps.all().order_by('configuration__stage', '-priority'), many=True).data
 
 
 class SimplyProcessSerializer(serializers.ModelSerializer):
diff --git a/rekono/resources/apps.py b/rekono/resources/apps.py
index 10e1ed5b4..568c7181f 100644
--- a/rekono/resources/apps.py
+++ b/rekono/resources/apps.py
@@ -25,3 +25,13 @@ def load_resources_model(self, **kwargs: Any) -> None:
             loaddata.Command(),
             os.path.join(path, '1_wordlists.json')                              # Input type entities
         )
+        self.update_default_wordlists_size()
+
+    def update_default_wordlists_size(self) -> None:
+        '''Update default wordlists size.'''
+        from resources.models import Wordlist
+        for wordlist in Wordlist.objects.all().filter(size=None):               # For each default wordlist
+            if os.path.isfile(wordlist.path):                                   # If wordlist path exist
+                with open(wordlist.path, 'rb+') as wordlist_file:               # Open uploaded file
+                    wordlist.size = len(wordlist_file.readlines())              # Count entries from uploaded file
+                    wordlist.save(update_fields=['size'])
diff --git a/rekono/resources/enums.py b/rekono/resources/enums.py
index c8354fda2..7ff2a2810 100644
--- a/rekono/resources/enums.py
+++ b/rekono/resources/enums.py
@@ -5,3 +5,4 @@ class WordlistType(models.TextChoices):
     '''Wordlist type names.'''
 
     ENDPOINT = 'Endpoint'
+    SUBDOMAIN = 'Subdomain'
diff --git a/rekono/resources/filters.py b/rekono/resources/filters.py
index b974bac85..066345e17 100644
--- a/rekono/resources/filters.py
+++ b/rekono/resources/filters.py
@@ -1,5 +1,6 @@
 from django_filters.rest_framework import filters
 from likes.filters import LikeFilter
+
 from resources.models import Wordlist
 
 
diff --git a/rekono/resources/fixtures/1_wordlists.json b/rekono/resources/fixtures/1_wordlists.json
index 5ccfa8c76..fa2e0e144 100644
--- a/rekono/resources/fixtures/1_wordlists.json
+++ b/rekono/resources/fixtures/1_wordlists.json
@@ -3,10 +3,11 @@
         "model": "resources.wordlist",
         "pk": 1,
         "fields": {
-            "name": "Rockyou",
-            "type": "Password",
-            "path": "/usr/share/wordlists/rockyou.txt",
+            "name": "Common (dirb)",
+            "type": "Endpoint",
+            "path": "/usr/share/dirb/wordlists/common.txt",
             "checksum": null,
+            "size": null,
             "creator": null
         }
     },
@@ -14,10 +15,11 @@
         "model": "resources.wordlist",
         "pk": 2,
         "fields": {
-            "name": "Most used passwords 2020",
-            "type": "Password",
-            "path": "/usr/share/seclists/Passwords/2020-200_most_used_passwords.txt",
+            "name": "Big (dirb)",
+            "type": "Endpoint",
+            "path": "/usr/share/dirb/wordlists/big.txt",
             "checksum": null,
+            "size": null,
             "creator": null
         }
     },
@@ -25,10 +27,11 @@
         "model": "resources.wordlist",
         "pk": 3,
         "fields": {
-            "name": "Most 10k common passwords",
-            "type": "Password",
-            "path": "/usr/share/seclists/Passwords/Common-Credentials/10k-most-common.txt",
+            "name": "Small",
+            "type": "Endpoint",
+            "path": "/usr/share/dirb/wordlists/small.txt",
             "checksum": null,
+            "size": null,
             "creator": null
         }
     },
@@ -36,10 +39,11 @@
         "model": "resources.wordlist",
         "pk": 4,
         "fields": {
-            "name": "Top 500 worst passwords",
-            "type": "Password",
-            "path": "/usr/share/seclists/Passwords/Common-Credentials/500-worst-passwords.txt",
+            "name": "Spanish",
+            "type": "Endpoint",
+            "path": "/usr/share/dirb/wordlists/spanish.txt",
             "checksum": null,
+            "size": null,
             "creator": null
         }
     },
@@ -47,10 +51,11 @@
         "model": "resources.wordlist",
         "pk": 5,
         "fields": {
-            "name": "Top 10 Dark Web 2017",
-            "type": "Password",
-            "path": "/usr/share/seclists/Passwords/darkweb2017-top10.txt",
+            "name": "Backdoor Web Shells",
+            "type": "Endpoint",
+            "path": "/usr/share/seclists/Web-Shells/backdoor_list.txt",
             "checksum": null,
+            "size": null,
             "creator": null
         }
     },
@@ -58,10 +63,11 @@
         "model": "resources.wordlist",
         "pk": 6,
         "fields": {
-            "name": "Top 100 Dark Web 2017",
-            "type": "Password",
-            "path": "/usr/share/seclists/Passwords/darkweb2017-top100.txt",
+            "name": "Apache",
+            "type": "Endpoint",
+            "path": "/usr/share/seclists/Discovery/Web-Content/apache.txt",
             "checksum": null,
+            "size": null,
             "creator": null
         }
     },
@@ -69,10 +75,11 @@
         "model": "resources.wordlist",
         "pk": 7,
         "fields": {
-            "name": "Top 1000 Dark Web 2017",
-            "type": "Password",
-            "path": "/usr/share/seclists/Passwords/darkweb2017-top1000.txt",
+            "name": "Big (seclists)",
+            "type": "Endpoint",
+            "path": "/usr/share/seclists/Discovery/Web-Content/big.txt",
             "checksum": null,
+            "size": null,
             "creator": null
         }
     },
@@ -80,10 +87,11 @@
         "model": "resources.wordlist",
         "pk": 8,
         "fields": {
-            "name": "Top 10000 Dark Web 2017",
-            "type": "Password",
-            "path": "/usr/share/seclists/Passwords/darkweb2017-top10000.txt",
+            "name": "Common (seclists)",
+            "type": "Endpoint",
+            "path": "/usr/share/seclists/Discovery/Web-Content/common.txt",
             "checksum": null,
+            "size": null,
             "creator": null
         }
     },
@@ -91,10 +99,11 @@
         "model": "resources.wordlist",
         "pk": 9,
         "fields": {
-            "name": "Common endpoints",
+            "name": "Endpoints list for Dirsearch",
             "type": "Endpoint",
-            "path": "/usr/share/wordlists/dirb/common.txt",
+            "path": "/usr/share/seclists/Discovery/Web-Content/dirsearch.txt",
             "checksum": null,
+            "size": null,
             "creator": null
         }
     },
@@ -102,10 +111,11 @@
         "model": "resources.wordlist",
         "pk": 10,
         "fields": {
-            "name": "Big endpoints list",
+            "name": "Nginx",
             "type": "Endpoint",
-            "path": "/usr/share/wordlists/dirb/big.txt",
+            "path": "/usr/share/seclists/Discovery/Web-Content/nginx.txt",
             "checksum": null,
+            "size": null,
             "creator": null
         }
     },
@@ -113,10 +123,215 @@
         "model": "resources.wordlist",
         "pk": 11,
         "fields": {
-            "name": "Backdoor Web Shells",
+            "name": "Swagger",
             "type": "Endpoint",
-            "path": "/usr/share/seclists/Web-Shells/backdoor_list.txt",
+            "path": "/usr/share/seclists/Discovery/Web-Content/swagger.txt",
+            "checksum": null,
+            "size": null,
+            "creator": null
+        }
+    },
+    {
+        "model": "resources.wordlist",
+        "pk": 12,
+        "fields": {
+            "name": "WebLogic",
+            "type": "Endpoint",
+            "path": "/usr/share/seclists/Discovery/Web-Content/weblogic.txt",
+            "checksum": null,
+            "size": null,
+            "creator": null
+        }
+    },
+    {
+        "model": "resources.wordlist",
+        "pk": 13,
+        "fields": {
+            "name": "WebSphere",
+            "type": "Endpoint",
+            "path": "/usr/share/seclists/Discovery/Web-Content/websphere.txt",
+            "checksum": null,
+            "size": null,
+            "creator": null
+        }
+    },
+    {
+        "model": "resources.wordlist",
+        "pk": 14,
+        "fields": {
+            "name": "Drupal 7.20",
+            "type": "Endpoint",
+            "path": "/usr/share/seclists/Discovery/Web-Content/URLs/urls-Drupal-7.20.txt",
+            "checksum": null,
+            "size": null,
+            "creator": null
+        }
+    },
+    {
+        "model": "resources.wordlist",
+        "pk": 15,
+        "fields": {
+            "name": "Joomla 3.0.3",
+            "type": "Endpoint",
+            "path": "/usr/share/seclists/Discovery/Web-Content/URLs/urls-joomla-3.0.3.txt",
+            "checksum": null,
+            "size": null,
+            "creator": null
+        }
+    },
+    {
+        "model": "resources.wordlist",
+        "pk": 16,
+        "fields": {
+            "name": "SAP",
+            "type": "Endpoint",
+            "path": "/usr/share/seclists/Discovery/Web-Content/URLs/urls-SAP.txt",
+            "checksum": null,
+            "size": null,
+            "creator": null
+        }
+    },
+    {
+        "model": "resources.wordlist",
+        "pk": 17,
+        "fields": {
+            "name": "WordPress 3.3.1",
+            "type": "Endpoint",
+            "path": "/usr/share/seclists/Discovery/Web-Content/URLs/urls-wordpress-3.3.1.txt",
+            "checksum": null,
+            "size": null,
+            "creator": null
+        }
+    },
+    {
+        "model": "resources.wordlist",
+        "pk": 18,
+        "fields": {
+            "name": "Italian",
+            "type": "Subdomain",
+            "path": "/usr/share/seclists/Discovery/DNS/italian-subdomains.txt",
+            "checksum": null,
+            "size": null,
+            "creator": null
+        }
+    },
+    {
+        "model": "resources.wordlist",
+        "pk": 19,
+        "fields": {
+            "name": "Namelist",
+            "type": "Subdomain",
+            "path": "/usr/share/seclists/Discovery/DNS/namelist.txt",
+            "checksum": null,
+            "size": null,
+            "creator": null
+        }
+    },
+    {
+        "model": "resources.wordlist",
+        "pk": 20,
+        "fields": {
+            "name": "Shubs subdomains",
+            "type": "Subdomain",
+            "path": "/usr/share/seclists/Discovery/DNS/shubs-subdomains.txt",
+            "checksum": null,
+            "size": null,
+            "creator": null
+        }
+    },
+    {
+        "model": "resources.wordlist",
+        "pk": 21,
+        "fields": {
+            "name": "Shubs StackOverflow",
+            "type": "Subdomain",
+            "path": "/usr/share/seclists/Discovery/DNS/shubs-stackoverflow.txt",
+            "checksum": null,
+            "size": null,
+            "creator": null
+        }
+    },
+    {
+        "model": "resources.wordlist",
+        "pk": 22,
+        "fields": {
+            "name": "Top 1 Million: 5000",
+            "type": "Subdomain",
+            "path": "/usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt",
+            "checksum": null,
+            "size": null,
+            "creator": null
+        }
+    },
+    {
+        "model": "resources.wordlist",
+        "pk": 23,
+        "fields": {
+            "name": "Top 1 Million: 20000",
+            "type": "Subdomain",
+            "path": "/usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt",
+            "checksum": null,
+            "size": null,
+            "creator": null
+        }
+    },
+    {
+        "model": "resources.wordlist",
+        "pk": 24,
+        "fields": {
+            "name": "Top 1 Million: 110000",
+            "type": "Subdomain",
+            "path": "/usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt",
+            "checksum": null,
+            "size": null,
+            "creator": null
+        }
+    },
+    {
+        "model": "resources.wordlist",
+        "pk": 25,
+        "fields": {
+            "name": "Deep Magic: Top 500",
+            "type": "Subdomain",
+            "path": "/usr/share/seclists/Discovery/DNS/deepmagic.com-prefixes-top500.txt",
+            "checksum": null,
+            "size": null,
+            "creator": null
+        }
+    },
+    {
+        "model": "resources.wordlist",
+        "pk": 26,
+        "fields": {
+            "name": "Deep Magic: Top 50000",
+            "type": "Subdomain",
+            "path": "/usr/share/seclists/Discovery/DNS/deepmagic.com-prefixes-top50000.txt",
+            "checksum": null,
+            "size": null,
+            "creator": null
+        }
+    },
+    {
+        "model": "resources.wordlist",
+        "pk": 27,
+        "fields": {
+            "name": "Bitquark: Top 100000",
+            "type": "Subdomain",
+            "path": "/usr/share/seclists/Discovery/DNS/bitquark-subdomains-top100000.txt",
+            "checksum": null,
+            "size": null,
+            "creator": null
+        }
+    },
+    {
+        "model": "resources.wordlist",
+        "pk": 28,
+        "fields": {
+            "name": "Combined subdomains",
+            "type": "Subdomain",
+            "path": "/usr/share/seclists/Discovery/DNS/combined_subdomains.txt",
             "checksum": null,
+            "size": null,
             "creator": null
         }
     }
diff --git a/rekono/resources/migrations/0004_alter_wordlist_type.py b/rekono/resources/migrations/0004_alter_wordlist_type.py
new file mode 100644
index 000000000..2d2473e09
--- /dev/null
+++ b/rekono/resources/migrations/0004_alter_wordlist_type.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.2.16 on 2023-01-05 15:42
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('resources', '0003_alter_wordlist_type'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='wordlist',
+            name='type',
+            field=models.TextField(choices=[('Endpoint', 'Endpoint'), ('Subdomain', 'Subdomain')], max_length=10),
+        ),
+    ]
diff --git a/rekono/targets/models.py b/rekono/targets/models.py
index f983473aa..5544d1fc2 100644
--- a/rekono/targets/models.py
+++ b/rekono/targets/models.py
@@ -47,7 +47,11 @@ def filter(self, input: Input) -> bool:
         if not input.filter:
             return True
         try:
-            return cast(models.TextChoices, TargetType)[input.filter] == self.type
+            distinct = input.filter[0] == '!'
+            filter_types = [
+                cast(models.TextChoices, TargetType)[f.upper()] for f in input.filter.replace('!', '').split(',s')
+            ]
+            return self.type not in filter_types if distinct else self.type in filter_types
         except KeyError:
             return True
 
diff --git a/rekono/telegram_bot/conversations/ask.py b/rekono/telegram_bot/conversations/ask.py
index cd3cc4188..2842f46e8 100644
--- a/rekono/telegram_bot/conversations/ask.py
+++ b/rekono/telegram_bot/conversations/ask.py
@@ -1,6 +1,7 @@
 from typing import List
 
 from authentications.enums import AuthenticationType
+from input_types.enums import InputTypeNames
 from processes.models import Process
 from projects.models import Project
 from resources.models import Wordlist
@@ -216,7 +217,7 @@ def ask_for_tool(update: Update, context: CallbackContext, chat: TelegramChat) -
     tools = Tool.objects.order_by('name').all()                                 # Get all tools
     # Create keyboard buttons with the tools data
     keyboard = [InlineKeyboardButton(t.name, callback_data=t.id) for t in tools]
-    send_options(update, chat, ASK_FOR_TOOL, keyboard, 3)
+    send_options(update, chat, ASK_FOR_TOOL, keyboard, 2)
     return SELECT_TOOL                                                          # Go to selected tool management
 
 
@@ -254,20 +255,32 @@ def ask_for_wordlist(update: Update, context: CallbackContext, chat: TelegramCha
     '''
     wordlists = Wordlist.objects.all()                                          # Get all wordlists
     # Create keyboard buttons with the wordlists data
-    keyboard = [InlineKeyboardButton(w.name, callback_data=w.id) for w in wordlists]
+    keyboard = [InlineKeyboardButton(f'{w.name} - {w.type}', callback_data=w.id) for w in wordlists]
+    tools_with_required_wordlists = ['Gobuster']                                # Tools with required wordlists
     check_if_wordlist_is_required = None
-    if context.chat_data is not None and context.chat_data.get(TOOL):           # Filter inputs by tool
+    if (                                                                        # Filter inputs by tool
+        context.chat_data is not None and
+        context.chat_data.get(TOOL) and
+        context.chat_data.get(TOOL).name not in tools_with_required_wordlists
+    ):
+        print('TOOL')
         check_if_wordlist_is_required = {'argument__tool': context.chat_data[TOOL]}
-    elif context.chat_data is not None and context.chat_data.get(PROCESS):      # Filter inputs by process
+    elif (                                                                      # Filter inputs by process
+        context.chat_data is not None and
+        context.chat_data.get(PROCESS) and
+        not context.chat_data[PROCESS].steps.filter(tool__name__in=tools_with_required_wordlists).exists()
+    ):
+        print('PROCESS')
         check_if_wordlist_is_required = {'argument__tool__in': context.chat_data[PROCESS].steps.all().values('tool')}
+    print(check_if_wordlist_is_required)
     if check_if_wordlist_is_required:
         check_if_wordlist_is_required.update({                                  # Base arguments to check if required
             'argument__required': True,
-            'type__name': 'Wordlist'
+            'type__name': InputTypeNames.WORDLIST
         })
         if not Input.objects.filter(**check_if_wordlist_is_required).exists():  # Check if wordlist is required
-            keyboard.append(InlineKeyboardButton('Default wordlists', callback_data='Default wordlists'))
-    send_options(update, chat, ASK_FOR_WORDLIST, keyboard, 2)
+            keyboard.append(InlineKeyboardButton('Default tools wordlists', callback_data='Default tools wordlists'))
+    send_options(update, chat, ASK_FOR_WORDLIST, keyboard, 1)
     return SELECT_WORDLIST                                                      # Go to selected wordlist management
 
 
diff --git a/rekono/telegram_bot/conversations/selection.py b/rekono/telegram_bot/conversations/selection.py
index 11c1dd4ad..29073e401 100644
--- a/rekono/telegram_bot/conversations/selection.py
+++ b/rekono/telegram_bot/conversations/selection.py
@@ -226,7 +226,7 @@ def select_wordlist(update: Update, context: CallbackContext) -> int:
     if (
         chat and context.chat_data is not None and
         update.callback_query and update.callback_query.data and
-        update.callback_query.data != 'Default wordlists'
+        update.callback_query.data != 'Default tools wordlists'
     ):
         wordlist = Wordlist.objects.get(pk=int(update.callback_query.data))     # Get wordlist by Id
         context.chat_data[WORDLIST] = wordlist                                  # Save selected intensity
diff --git a/rekono/tools/filters.py b/rekono/tools/filters.py
index 4f81464e8..fd9a321b3 100644
--- a/rekono/tools/filters.py
+++ b/rekono/tools/filters.py
@@ -1,12 +1,13 @@
 from django_filters.rest_framework import FilterSet, filters
 from likes.filters import LikeFilter
+
 from tools.models import Configuration, Tool
 
 
 class ToolFilter(LikeFilter):
     '''FilterSet to filter and sort Tool entities.'''
 
-    o = filters.OrderingFilter(fields=('name', 'stage', 'likes_count'))         # Ordering fields
+    o = filters.OrderingFilter(fields=('name', 'configurations__stage', 'likes_count'))         # Ordering fields
 
     class Meta:
         '''FilterSet metadata.'''
@@ -17,16 +18,16 @@ class Meta:
             'command': ['exact', 'icontains'],
             'configurations': ['exact'],
             'configurations__name': ['exact', 'icontains'],
+            'configurations__stage': ['exact'],
             'arguments__inputs__type__name': ['exact'],
-            'configurations__outputs__type__name': ['exact'],
-            'stage': ['exact']
+            'configurations__outputs__type__name': ['exact']
         }
 
 
 class ConfigurationFilter(FilterSet):
     '''FilterSet to filter and sort Configuration entities.'''
 
-    o = filters.OrderingFilter(fields=('tool', 'name'))                         # Ordering fields
+    o = filters.OrderingFilter(fields=('tool', 'stage', 'name'))                # Ordering fields
 
     class Meta:
         '''FilterSet metadata.'''
@@ -36,8 +37,8 @@ class Meta:
             'tool': ['exact'],
             'tool__name': ['exact', 'icontains'],
             'tool__command': ['exact', 'icontains'],
-            'tool__stage': ['exact'],
             'name': ['exact', 'icontains'],
+            'stage': ['exact'],
             'default': ['exact'],
             'tool__arguments__inputs__type__name': ['exact'],
             'outputs__type__name': ['exact'],
diff --git a/rekono/tools/fixtures/1_tools.json b/rekono/tools/fixtures/1_tools.json
index 5014a4529..01bd242f5 100644
--- a/rekono/tools/fixtures/1_tools.json
+++ b/rekono/tools/fixtures/1_tools.json
@@ -7,7 +7,6 @@
             "command": "nmap",
             "output_format": "xml",
             "defectdojo_scan_type": "Nmap Scan",
-            "stage": 2,
             "reference": "https://nmap.org/",
             "icon": "https://www.kali.org/tools/nmap/images/nmap-logo.svg"
         }
@@ -20,7 +19,6 @@
             "command": "dirsearch",
             "output_format": "json",
             "defectdojo_scan_type": null,
-            "stage": 4,
             "reference": "https://github.com/maurosoria/dirsearch",
             "icon": "https://raw.githubusercontent.com/maurosoria/dirsearch/master/static/logo.png"
         }
@@ -33,7 +31,6 @@
             "command": "theHarvester",
             "output_format": "json",
             "defectdojo_scan_type": null,
-            "stage": 1,
             "reference": "https://github.com/laramies/theHarvester",
             "icon": "https://www.kali.org/tools/theharvester/images/theharvester-logo.svg"
         }
@@ -46,7 +43,6 @@
             "command": "nikto",
             "output_format": "xml",
             "defectdojo_scan_type": "Nikto Scan",
-            "stage": 4,
             "reference": "https://github.com/sullo/nikto",
             "icon": "https://www.kali.org/tools/nikto/images/nikto-logo.svg"
         }
@@ -59,7 +55,6 @@
             "command": "sslscan",
             "output_format": "xml",
             "defectdojo_scan_type": "Sslscan",
-            "stage": 4,
             "reference": "https://github.com/rbsec/sslscan",
             "icon": "https://www.kali.org/tools/sslscan/images/sslscan-logo.svg"
         }
@@ -72,7 +67,6 @@
             "command": "sslyze",
             "output_format": "json",
             "defectdojo_scan_type": "SSLyze 3 Scan (JSON)",
-            "stage": 4,
             "reference": "https://nabla-c0d3.github.io/sslyze/documentation/",
             "icon": "https://www.kali.org/tools/sslyze/images/sslyze-logo.svg"
         }
@@ -85,7 +79,6 @@
             "command": "cmseek",
             "output_format": "json",
             "defectdojo_scan_type": null,
-            "stage": 4,
             "reference": "https://github.com/Tuhinshubhra/CMSeeK/",
             "icon": "https://camo.githubusercontent.com/b1864e58e861aa4e938d17d4a50ae1a4bedec9cdb9e8b7ce7ac80a1b5cc711ed/68747470733a2f2f692e696d6775722e636f6d2f35565973316d322e706e67"
         }
@@ -98,7 +91,6 @@
             "command": "zaproxy",
             "output_format": "xml",
             "defectdojo_scan_type": "ZAP Scan",
-            "stage": 4,
             "reference": "https://www.zaproxy.org/",
             "icon": "https://www.kali.org/tools/zaproxy/images/zaproxy-logo.svg"
         }
@@ -111,7 +103,6 @@
             "command": "searchsploit",
             "output_format": "json",
             "defectdojo_scan_type": null,
-            "stage": 5,
             "reference": "https://www.exploit-db.com/searchsploit",
             "icon": "https://www.kali.org/tools/exploitdb/images/exploitdb-logo.svg"
         }
@@ -124,7 +115,6 @@
             "command": "msfconsole",
             "output_format": null,
             "defectdojo_scan_type": null,
-            "stage": 5,
             "reference": "https://www.metasploit.com/",
             "icon": "https://www.kali.org/tools/metasploit-framework/images/metasploit-framework-logo.svg"
         }
@@ -137,7 +127,6 @@
             "command": "python3",
             "output_format": null,
             "defectdojo_scan_type": null,
-            "stage": 4,
             "reference": "https://github.com/fullhunt/log4j-scan",
             "icon": "https://dkh9ehwkisc4.cloudfront.net/static/images/fullhunt.png"
         }
@@ -150,7 +139,6 @@
             "command": "emailfinder",
             "output_format": null,
             "defectdojo_scan_type": null,
-            "stage": 1,
             "reference": "https://github.com/Josue87/EmailFinder",
             "icon": null
         }
@@ -163,7 +151,6 @@
             "command": "emailharvester",
             "output_format": "txt",
             "defectdojo_scan_type": null,
-            "stage": 1,
             "reference": "https://github.com/maldevel/EmailHarvester",
             "icon": null
         }
@@ -176,7 +163,6 @@
             "command": "joomscan",
             "output_format": null,
             "defectdojo_scan_type": null,
-            "stage": 4,
             "reference": "https://github.com/OWASP/joomscan",
             "icon": "https://raw.githubusercontent.com/rezasp/Trash/master/joomscan.png"
         }
@@ -189,7 +175,6 @@
             "command": "gitleaks",
             "output_format": "json",
             "defectdojo_scan_type": "Gitleaks",
-            "stage": 4,
             "reference": "https://github.com/zricethezav/gitleaks",
             "icon": "https://raw.githubusercontent.com/zricethezav/gifs/master/gitleakslogo.png"
         }
@@ -202,7 +187,6 @@
             "command": "ssh-audit",
             "output_format": null,
             "defectdojo_scan_type": null,
-            "stage": 4,
             "reference": "https://github.com/jtesta/ssh-audit",
             "icon": null
         }
@@ -215,7 +199,6 @@
             "command": "smbmap",
             "output_format": null,
             "defectdojo_scan_type": null,
-            "stage": 4,
             "reference": "https://github.com/ShawnDEvans/smbmap",
             "icon": null
         }
@@ -228,7 +211,6 @@
             "command": "nuclei",
             "output_format": "json",
             "defectdojo_scan_type": "Nuclei",
-            "stage": 4,
             "reference": "https://nuclei.projectdiscovery.io",
             "icon": "https://raw.githubusercontent.com/projectdiscovery/nuclei/main/static/nuclei-logo.png"
         }
@@ -241,9 +223,20 @@
             "command": "python3",
             "output_format": null,
             "defectdojo_scan_type": null,
-            "stage": 4,
             "reference": "https://github.com/fullhunt/spring4shell-scan",
             "icon": "https://dkh9ehwkisc4.cloudfront.net/static/images/fullhunt.png"
         }
+    },
+    {
+        "model": "tools.tool",
+        "pk": 20,
+        "fields": {
+            "name": "Gobuster",
+            "command": "gobuster",
+            "output_format": "txt",
+            "defectdojo_scan_type": null,
+            "reference": "https://github.com/OJ/gobuster",
+            "icon": null
+        }
     }
 ]
\ No newline at end of file
diff --git a/rekono/tools/fixtures/2_intensities.json b/rekono/tools/fixtures/2_intensities.json
index cd41a9f38..c05a1ed41 100644
--- a/rekono/tools/fixtures/2_intensities.json
+++ b/rekono/tools/fixtures/2_intensities.json
@@ -268,5 +268,50 @@
             "argument": "",
             "value": 4
         }
+    },
+    {
+        "model": "tools.intensity",
+        "pk": 31,
+        "fields": {
+            "tool": 20,
+            "argument": "--threads 1",
+            "value": 1
+        }
+    },
+    {
+        "model": "tools.intensity",
+        "pk": 32,
+        "fields": {
+            "tool": 20,
+            "argument": "--threads 5",
+            "value": 2
+        }
+    },
+    {
+        "model": "tools.intensity",
+        "pk": 33,
+        "fields": {
+            "tool": 20,
+            "argument": "--threads 10",
+            "value": 3
+        }
+    },
+    {
+        "model": "tools.intensity",
+        "pk": 34,
+        "fields": {
+            "tool": 20,
+            "argument": "--threads 20",
+            "value": 4
+        }
+    },
+    {
+        "model": "tools.intensity",
+        "pk": 35,
+        "fields": {
+            "tool": 20,
+            "argument": "--threads 50",
+            "value": 5
+        }
     }
 ]
\ No newline at end of file
diff --git a/rekono/tools/fixtures/3_configurations.json b/rekono/tools/fixtures/3_configurations.json
index a4ef13c69..b02fac61a 100644
--- a/rekono/tools/fixtures/3_configurations.json
+++ b/rekono/tools/fixtures/3_configurations.json
@@ -6,6 +6,7 @@
             "tool": 1,
             "name": "TCP ports",
             "arguments": "--privileged {host} {intensity} {ports} -sS -oX {output}",
+            "stage": 2,
             "default": false
         }
     },
@@ -16,6 +17,7 @@
             "tool": 1,
             "name": "UDP ports",
             "arguments": "--privileged {host} {intensity} {ports} -sU -oX {output}",
+            "stage": 2,
             "default": false
         }
     },
@@ -26,6 +28,7 @@
             "tool": 1,
             "name": "TCP ports & service versions",
             "arguments": "--privileged {host} {intensity} {ports} -sS -sV -A -oX {output}",
+            "stage": 2,
             "default": true
         }
     },
@@ -36,6 +39,7 @@
             "tool": 1,
             "name": "UDP ports & service versions",
             "arguments": "--privileged {host} {intensity} {ports} -sU -sV -A -oX {output}",
+            "stage": 2,
             "default": false
         }
     },
@@ -46,6 +50,7 @@
             "tool": 1,
             "name": "TCP & UDP ports",
             "arguments": "--privileged {host} {intensity} {ports} -sS -sU -oX {output}",
+            "stage": 2,
             "default": false
         }
     },
@@ -56,6 +61,7 @@
             "tool": 1,
             "name": "TCP & UDP ports & service versions",
             "arguments": "--privileged {host} {intensity} {ports} -sS -sU -sV -A -oX {output}",
+            "stage": 2,
             "default": false
         }
     },
@@ -66,6 +72,7 @@
             "tool": 1,
             "name": "Fast TCP ports",
             "arguments": "--privileged {host} {intensity} {ports} -F -sS -oX {output}",
+            "stage": 2,
             "default": false
         }
     },
@@ -76,6 +83,7 @@
             "tool": 1,
             "name": "Fast UDP ports",
             "arguments": "--privileged {host} {intensity} {ports} -F -sU -oX {output}",
+            "stage": 2,
             "default": false
         }
     },
@@ -86,6 +94,7 @@
             "tool": 1,
             "name": "Fast TCP ports & service versions",
             "arguments": "--privileged {host} {intensity} {ports} -F -sS -sV -A -oX {output}",
+            "stage": 2,
             "default": false
         }
     },
@@ -96,6 +105,7 @@
             "tool": 1,
             "name": "Fast UDP ports & service versions",
             "arguments": "--privileged {host} {intensity} {ports} -F -sU -sV -A -oX {output}",
+            "stage": 2,
             "default": false
         }
     },
@@ -106,6 +116,7 @@
             "tool": 1,
             "name": "Fast TCP & UDP ports",
             "arguments": "--privileged {host} {intensity} {ports} -F -sS -sU -oX {output}",
+            "stage": 2,
             "default": false
         }
     },
@@ -116,6 +127,7 @@
             "tool": 1,
             "name": "Fast TCP & UDP ports & service versions",
             "arguments": "--privileged {host} {intensity} {ports} -F -sS -sU -sV -A -oX {output}",
+            "stage": 2,
             "default": false
         }
     },
@@ -126,6 +138,7 @@
             "tool": 1,
             "name": "Vulners",
             "arguments": "--privileged {host} {intensity} {ports} -sS -sV -A --script vulners -oX {output}",
+            "stage": 2,
             "default": false
         }
     },
@@ -136,6 +149,7 @@
             "tool": 1,
             "name": "FTP NSE scripts",
             "arguments": "--privileged {host} {intensity} {ports} -sS -sV -A --script ftp-anon,ftp-proftpd-backdoor,ftp-vsftpd-backdoor,ftp-libopie,ftp-vuln-cve2010-4221 -oX {output}",
+            "stage": 2,
             "default": false
         }
     },
@@ -146,6 +160,7 @@
             "tool": 2,
             "name": "Standard wordlist",
             "arguments": "{url} {intensity} -o {output} --format=json {wordlist} {authentication} {cookie}",
+            "stage": 4,
             "default": true
         }
     },
@@ -156,6 +171,7 @@
             "tool": 2,
             "name": "Wordlist with extensions",
             "arguments": "{url} {intensity} -o {output} --format=json --force-extensions {wordlist} {authentication} {cookie}",
+            "stage": 4,
             "default": false
         }
     },
@@ -166,6 +182,7 @@
             "tool": 2,
             "name": "Recursive",
             "arguments": "{url} {intensity} -o {output} --format=json --force-recursive --recursion-depth=10 {wordlist} {authentication} {cookie}",
+            "stage": 4,
             "default": false
         }
     },
@@ -176,6 +193,7 @@
             "tool": 2,
             "name": "Deep recursive",
             "arguments": "{url} {intensity} -o {output} --format=json --force-recursive --depth-recursive --recursion-depth=10 {wordlist} {authentication} {cookie}",
+            "stage": 4,
             "default": false
         }
     },
@@ -186,6 +204,7 @@
             "tool": 3,
             "name": "All available sources",
             "arguments": "{target} -b all -f {output}",
+            "stage": 1,
             "default": true
         }
     },
@@ -196,6 +215,7 @@
             "tool": 3,
             "name": "Google & Duckduckgo & Bing & Linkedin & Twitter",
             "arguments": "{target} -b google,duckduckgo,bing,linkedin,linkedin_links,twitter -f {output}",
+            "stage": 1,
             "default": false
         }
     },
@@ -206,6 +226,7 @@
             "tool": 4,
             "name": "Web scan",
             "arguments": "{url} -Format xml -output {output} {authentication} {cookie}",
+            "stage": 4,
             "default": true
         }
     },
@@ -216,6 +237,7 @@
             "tool": 5,
             "name": "SSL/TLS analysis",
             "arguments": "--xml={output} {intensity} {target}",
+            "stage": 4,
             "default": true
         }
     },
@@ -226,6 +248,7 @@
             "tool": 6,
             "name": "SSL/TLS analysis",
             "arguments": "--json_out={output} {target}",
+            "stage": 4,
             "default": true
         }
     },
@@ -236,6 +259,7 @@
             "tool": 7,
             "name": "CMS scan",
             "arguments": "{url} {intensity}",
+            "stage": 4,
             "default": true
         }
     },
@@ -246,6 +270,7 @@
             "tool": 8,
             "name": "Active scan",
             "arguments": "{authentication} {cookie} {command} -cmd {url} -quickprogress -quickout {output}",
+            "stage": 4,
             "default": true
         }
     },
@@ -256,6 +281,7 @@
             "tool": 9,
             "name": "Search by technology",
             "arguments": "{technology} --json > {output}",
+            "stage": 5,
             "default": true
         }
     },
@@ -266,6 +292,7 @@
             "tool": 10,
             "name": "Search by CVE",
             "arguments": "-q -x 'search {cve};quit'",
+            "stage": 5,
             "default": true
         }
     },
@@ -276,6 +303,7 @@
             "tool": 11,
             "name": "Log4Shell (CVE-2021-44228)",
             "arguments": "{script} {url} --dns-callback-provider dnslog.cn --run-all-tests",
+            "stage": 4,
             "default": true
         }
     },
@@ -286,6 +314,7 @@
             "tool": 11,
             "name": "Log4Shell (CVE-2021-44228) with WAF bypass",
             "arguments": "{script} {url} --dns-callback-provider dnslog.cn --run-all-tests --waf-bypass",
+            "stage": 4,
             "default": false
         }
     },
@@ -296,6 +325,7 @@
             "tool": 12,
             "name": "Search emails",
             "arguments": "{target}",
+            "stage": 1,
             "default": true
         }
     },
@@ -306,6 +336,7 @@
             "tool": 13,
             "name": "Search emails",
             "arguments": "{target} -s {output}",
+            "stage": 1,
             "default": true
         }
     },
@@ -316,6 +347,7 @@
             "tool": 14,
             "name": "Joomla scan",
             "arguments": "{url} {cookie}",
+            "stage": 4,
             "default": true
         }
     },
@@ -326,6 +358,7 @@
             "tool": 15,
             "name": "Dump .git and find secrets in all commits",
             "arguments": "detect -f json --report-path {output}",
+            "stage": 4,
             "default": true
         }
     },
@@ -336,6 +369,7 @@
             "tool": 16,
             "name": "SSH scan",
             "arguments": "{host} {port} --batch",
+            "stage": 4,
             "default": true
         }
     },
@@ -346,6 +380,7 @@
             "tool": 17,
             "name": "List shares",
             "arguments": "{host} {port} {authentication}",
+            "stage": 4,
             "default": true
         }
     },
@@ -356,6 +391,7 @@
             "tool": 17,
             "name": "List shares and directories recursively",
             "arguments": "{host} {port} -R {authentication}",
+            "stage": 4,
             "default": false
         }
     },
@@ -366,6 +402,7 @@
             "tool": 1,
             "name": "SMB NSE scripts",
             "arguments": "--privileged {host} {intensity} {ports} -sS -sV -A --script smb-enum-shares,smb-enum-users,smb-enum-groups,smb-enum-sessions,smb-protocols,smb-enum-domains,smb-enum-services,smb-mbenum,smb-ls,smb-security-mode,smb2-security-mode,smb-double-pulsar-backdoor,smb-vuln-webexec,smb2-vuln-uptime,smb-vuln-ms06-025,smb-vuln-ms07-029,smb-vuln-ms10-061,smb-vuln-ms17-010,smb-vuln-cve-2017-7494 -oX {output}",
+            "stage": 2,
             "default": false
         }
     },
@@ -376,6 +413,7 @@
             "tool": 1,
             "name": "FTP & SMB NSE scripts",
             "arguments": "--privileged {host} {intensity} {ports} -sS -sV -A --script ftp-anon,ftp-proftpd-backdoor,ftp-vsftpd-backdoor,ftp-libopie,ftp-vuln-cve2010-4221,smb-enum-shares,smb-enum-users,smb-enum-groups,smb-enum-sessions,smb-protocols,smb-enum-domains,smb-enum-services,smb-mbenum,smb-ls,smb-security-mode,smb2-security-mode,smb-double-pulsar-backdoor,smb-vuln-webexec,smb2-vuln-uptime,smb-vuln-ms06-025,smb-vuln-ms07-029,smb-vuln-ms10-061,smb-vuln-ms17-010,smb-vuln-cve-2017-7494 -oX {output}",
+            "stage": 2,
             "default": false
         }
     },
@@ -386,6 +424,7 @@
             "tool": 18,
             "name": "All templates",
             "arguments": "{url} {authentication} {cookie} -json -output {output}",
+            "stage": 4,
             "default": true
         }
     },
@@ -396,6 +435,7 @@
             "tool": 18,
             "name": "Automatic technology detection",
             "arguments": "{url} -automatic-scan {authentication} {cookie} -json -output {output}",
+            "stage": 4,
             "default": false
         }
     },
@@ -406,6 +446,7 @@
             "tool": 18,
             "name": "CVE templates",
             "arguments": "{url} -tags cve {authentication} {cookie} -json -output {output}",
+            "stage": 4,
             "default": false
         }
     },
@@ -416,6 +457,7 @@
             "tool": 19,
             "name": "SpringShell RCE (CVE-2022-22965)",
             "arguments": "{script} {url}",
+            "stage": 4,
             "default": true
         }
     },
@@ -426,6 +468,7 @@
             "tool": 19,
             "name": "Spring Cloud RCE (CVE-2022-22963)",
             "arguments": "{script} {url} --test-CVE-2022-22963",
+            "stage": 4,
             "default": false
         }
     },
@@ -436,6 +479,7 @@
             "tool": 19,
             "name": "SpringShell RCE (CVE-2022-22965) with WAF bypass",
             "arguments": "{script} {url} --waf-bypass",
+            "stage": 4,
             "default": false
         }
     },
@@ -446,6 +490,40 @@
             "tool": 19,
             "name": "Spring Cloud RCE (CVE-2022-22963) with WAF bypass",
             "arguments": "{script} {url} --waf-bypass --test-CVE-2022-22963",
+            "stage": 4,
+            "default": false
+        }
+    },
+    {
+        "model": "tools.configuration",
+        "pk": 46,
+        "fields": {
+            "tool": 20,
+            "name": "Subdomains enumeration",
+            "arguments": "dns {target} {subdomain_wordlist} {intensity} --wildcard --show-ips --no-color --no-progress --quiet --output {output}",
+            "stage": 1,
+            "default": true
+        }
+    },
+    {
+        "model": "tools.configuration",
+        "pk": 47,
+        "fields": {
+            "tool": 20,
+            "name": "VHOST enumeration",
+            "arguments": "vhost {target_url} {subdomain_wordlist} {basic_auth} {token_auth} {cookie} {intensity} --append-domain --no-tls-validation --no-color --no-progress --quiet --output {output}",
+            "stage": 2,
+            "default": false
+        }
+    },
+    {
+        "model": "tools.configuration",
+        "pk": 48,
+        "fields": {
+            "tool": 20,
+            "name": "Endpoints enumeration",
+            "arguments": "dir {url} {endpoint_wordlist} {basic_auth} {token_auth} {cookie} {intensity} --no-tls-validation --no-color --no-progress --quiet --output {output}",
+            "stage": 4,
             "default": false
         }
     }
diff --git a/rekono/tools/fixtures/4_arguments.json b/rekono/tools/fixtures/4_arguments.json
index bd500a8f6..279bb7e50 100644
--- a/rekono/tools/fixtures/4_arguments.json
+++ b/rekono/tools/fixtures/4_arguments.json
@@ -361,5 +361,93 @@
             "required": true,
             "multiple": false
         }
+    },
+    {
+        "model": "tools.argument",
+        "pk": 34,
+        "fields": {
+            "tool": 20,
+            "name": "target",
+            "argument": "--domain {target}",
+            "required": false,
+            "multiple": false
+        }
+    },
+    {
+        "model": "tools.argument",
+        "pk": 35,
+        "fields": {
+            "tool": 20,
+            "name": "target_url",
+            "argument": "--url {target}",
+            "required": false,
+            "multiple": false
+        }
+    },
+    {
+        "model": "tools.argument",
+        "pk": 36,
+        "fields": {
+            "tool": 20,
+            "name": "url",
+            "argument": "--url {url}",
+            "required": false,
+            "multiple": false
+        }
+    },
+    {
+        "model": "tools.argument",
+        "pk": 37,
+        "fields": {
+            "tool": 20,
+            "name": "subdomain_wordlist",
+            "argument": "--wordlist {wordlist}",
+            "required": false,
+            "multiple": false
+        }
+    },
+    {
+        "model": "tools.argument",
+        "pk": 38,
+        "fields": {
+            "tool": 20,
+            "name": "endpoint_wordlist",
+            "argument": "--wordlist {wordlist}",
+            "required": false,
+            "multiple": false
+        }
+    },
+    {
+        "model": "tools.argument",
+        "pk": 39,
+        "fields": {
+            "tool": 20,
+            "name": "basic_auth",
+            "argument": "--username {username} --password {secret}",
+            "required": false,
+            "multiple": false
+        }
+    },
+    {
+        "model": "tools.argument",
+        "pk": 40,
+        "fields": {
+            "tool": 20,
+            "name": "token_auth",
+            "argument": "--headers 'Authorization: {credential_type} {token}'",
+            "required": false,
+            "multiple": false
+        }
+    },
+    {
+        "model": "tools.argument",
+        "pk": 41,
+        "fields": {
+            "tool": 20,
+            "name": "cookie",
+            "argument": "--cookie '{cookie_name}={secret}'",
+            "required": false,
+            "multiple": false
+        }
     }
 ]
\ No newline at end of file
diff --git a/rekono/tools/fixtures/5_inputs.json b/rekono/tools/fixtures/5_inputs.json
index a731b4234..ade902a85 100644
--- a/rekono/tools/fixtures/5_inputs.json
+++ b/rekono/tools/fixtures/5_inputs.json
@@ -45,7 +45,7 @@
         "fields": {
             "argument": 3,
             "type": 2,
-            "filter": null,
+            "filter": "!ip_range,network",
             "order": 2
         }
     },
@@ -65,7 +65,7 @@
         "fields": {
             "argument": 5,
             "type": 2,
-            "filter": "DOMAIN",
+            "filter": "domain",
             "order": 1
         }
     },
@@ -85,7 +85,7 @@
         "fields": {
             "argument": 6,
             "type": 2,
-            "filter": null,
+            "filter": "!ip_range,network",
             "order": 2
         }
     },
@@ -105,7 +105,7 @@
         "fields": {
             "argument": 7,
             "type": 2,
-            "filter": null,
+            "filter": "!ip_range,network",
             "order": 2
         }
     },
@@ -125,7 +125,7 @@
         "fields": {
             "argument": 8,
             "type": 2,
-            "filter": null,
+            "filter": "!ip_range,network",
             "order": 2
         }
     },
@@ -145,7 +145,7 @@
         "fields": {
             "argument": 9,
             "type": 2,
-            "filter": null,
+            "filter": "!ip_range,network",
             "order": 2
         }
     },
@@ -165,7 +165,7 @@
         "fields": {
             "argument": 10,
             "type": 2,
-            "filter": null,
+            "filter": "!ip_range,network",
             "order": 2
         }
     },
@@ -205,7 +205,7 @@
         "fields": {
             "argument": 13,
             "type": 2,
-            "filter": null,
+            "filter": "!ip_range,network",
             "order": 2
         }
     },
@@ -215,7 +215,7 @@
         "fields": {
             "argument": 14,
             "type": 2,
-            "filter": "DOMAIN",
+            "filter": "domain",
             "order": 1
         }
     },
@@ -225,7 +225,7 @@
         "fields": {
             "argument": 15,
             "type": 2,
-            "filter": "DOMAIN",
+            "filter": "domain",
             "order": 1
         }
     },
@@ -245,7 +245,7 @@
         "fields": {
             "argument": 16,
             "type": 2,
-            "filter": null,
+            "filter": "!ip_range,network",
             "order": 2
         }
     },
@@ -265,7 +265,7 @@
         "fields": {
             "argument": 17,
             "type": 2,
-            "filter": null,
+            "filter": "!ip_range,network",
             "order": 2
         }
     },
@@ -285,7 +285,7 @@
         "fields": {
             "argument": 18,
             "type": 2,
-            "filter": null,
+            "filter": "!ip_range,network",
             "order": 2
         }
     },
@@ -315,7 +315,7 @@
         "fields": {
             "argument": 20,
             "type": 2,
-            "filter": null,
+            "filter": "!ip_range,network",
             "order": 2
         }
     },
@@ -425,7 +425,7 @@
         "fields": {
             "argument": 30,
             "type": 2,
-            "filter": null,
+            "filter": "!ip_range,network",
             "order": 2
         }
     },
@@ -465,8 +465,98 @@
         "fields": {
             "argument": 33,
             "type": 2,
-            "filter": null,
+            "filter": "!ip_range,network",
+            "order": 2
+        }
+    },
+    {
+        "model": "tools.input",
+        "pk": 48,
+        "fields": {
+            "argument": 34,
+            "type": 2,
+            "filter": "domain",
+            "order": 1
+        }
+    },
+    {
+        "model": "tools.input",
+        "pk": 49,
+        "fields": {
+            "argument": 35,
+            "type": 2,
+            "filter": "domain",
+            "order": 1
+        }
+    },
+    {
+        "model": "tools.input",
+        "pk": 50,
+        "fields": {
+            "argument": 36,
+            "type": 3,
+            "filter": "http",
+            "order": 1
+        }
+    },
+    {
+        "model": "tools.input",
+        "pk": 51,
+        "fields": {
+            "argument": 36,
+            "type": 2,
+            "filter": "!ip_range,network",
             "order": 2
         }
+    },
+    {
+        "model": "tools.input",
+        "pk": 52,
+        "fields": {
+            "argument": 37,
+            "type": 9,
+            "filter": "subdomain",
+            "order": 1
+        }
+    },
+    {
+        "model": "tools.input",
+        "pk": 53,
+        "fields": {
+            "argument": 38,
+            "type": 9,
+            "filter": "endpoint",
+            "order": 1
+        }
+    },
+    {
+        "model": "tools.input",
+        "pk": 54,
+        "fields": {
+            "argument": 39,
+            "type": 10,
+            "filter": "basic",
+            "order": 1
+        }
+    },
+    {
+        "model": "tools.input",
+        "pk": 55,
+        "fields": {
+            "argument": 40,
+            "type": 10,
+            "filter": "!cookie,basic",
+            "order": 1
+        }
+    },
+    {
+        "model": "tools.input",
+        "pk": 56,
+        "fields": {
+            "argument": 41,
+            "type": 10,
+            "filter": "cookie",
+            "order": 1
+        }
     }
 ]
\ No newline at end of file
diff --git a/rekono/tools/fixtures/6_outputs.json b/rekono/tools/fixtures/6_outputs.json
index dfae41f6d..3008002af 100644
--- a/rekono/tools/fixtures/6_outputs.json
+++ b/rekono/tools/fixtures/6_outputs.json
@@ -742,5 +742,29 @@
             "configuration": 45,
             "type": 6
         }
+    },
+    {
+        "model": "tools.output",
+        "pk": 94,
+        "fields": {
+            "configuration": 46,
+            "type": 1
+        }
+    },
+    {
+        "model": "tools.output",
+        "pk": 95,
+        "fields": {
+            "configuration": 47,
+            "type": 1
+        }
+    },
+    {
+        "model": "tools.output",
+        "pk": 96,
+        "fields": {
+            "configuration": 48,
+            "type": 4
+        }
     }
 ]
\ No newline at end of file
diff --git a/rekono/tools/migrations/0003_auto_20230105_1642.py b/rekono/tools/migrations/0003_auto_20230105_1642.py
new file mode 100644
index 000000000..885640225
--- /dev/null
+++ b/rekono/tools/migrations/0003_auto_20230105_1642.py
@@ -0,0 +1,23 @@
+# Generated by Django 3.2.16 on 2023-01-05 15:42
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('tools', '0002_initial'),
+    ]
+
+    operations = [
+        migrations.RemoveField(
+            model_name='tool',
+            name='stage',
+        ),
+        migrations.AddField(
+            model_name='configuration',
+            name='stage',
+            field=models.IntegerField(choices=[(1, 'Osint'), (2, 'Enumeration'), (3, 'Vulnerabilities'), (4, 'Services'), (5, 'Exploitation')], default=1),
+            preserve_default=False,
+        ),
+    ]
diff --git a/rekono/tools/models.py b/rekono/tools/models.py
index 676b0d48f..bd612e043 100644
--- a/rekono/tools/models.py
+++ b/rekono/tools/models.py
@@ -1,6 +1,7 @@
 from django.db import models
 from input_types.models import InputType
 from likes.models import LikeBase
+
 from tools.enums import IntensityRank, Stage
 
 # Create your models here.
@@ -13,7 +14,6 @@ class Tool(LikeBase):
     command = models.TextField(max_length=30, blank=True, null=True)            # Tool command
     output_format = models.TextField(max_length=5, blank=True, null=True)       # Tool output file format
     defectdojo_scan_type = models.TextField(max_length=50, blank=True, null=True)   # Related Defect-Dojo scan type
-    stage = models.IntegerField(choices=Stage.choices)                          # Related pentesting stage
     reference = models.TextField(max_length=250, blank=True, null=True)         # Tool reference link
     icon = models.TextField(max_length=250, blank=True, null=True)              # Tool icon link
 
@@ -48,6 +48,7 @@ class Configuration(models.Model):
     name = models.TextField(max_length=30)                                      # Configuration name
     tool = models.ForeignKey(Tool, related_name='configurations', on_delete=models.CASCADE)         # Related tool
     arguments = models.TextField(max_length=250, default='', blank=True)
+    stage = models.IntegerField(choices=Stage.choices)                          # Related pentesting stage
     default = models.BooleanField(default=False)                                # Indicate if it's default configuration
 
     class Meta:
diff --git a/rekono/tools/serializers.py b/rekono/tools/serializers.py
index 0ebaaf4af..dbd872bae 100644
--- a/rekono/tools/serializers.py
+++ b/rekono/tools/serializers.py
@@ -6,6 +6,7 @@
 from likes.serializers import LikeBaseSerializer
 from rest_framework import serializers
 from rest_framework.fields import SerializerMethodField
+
 from tools.enums import IntensityRank, Stage
 from tools.models import (Argument, Configuration, Input, Intensity, Output,
                           Tool)
@@ -63,13 +64,15 @@ class Meta:
 class ConfigurationSerializer(serializers.ModelSerializer):
     '''Serializer to get Configuration data via API.'''
 
+    stage_name = StageField(source='stage')                                     # Stage name for read operations
     outputs = SerializerMethodField(method_name='get_outputs', read_only=True)  # Outputs details for read operations
 
     class Meta:
         '''Serializer metadata.'''
 
         model = Configuration
-        fields = ('id', 'name', 'tool', 'arguments', 'default', 'outputs')      # Configuration fields exposed via API
+        # Configuration fields exposed via API
+        fields = ('id', 'name', 'tool', 'arguments', 'stage_name', 'default', 'outputs')
 
     def get_outputs(self, instance: Configuration) -> OutputSerializer:
         '''Get configuration outputs sorted by Id.
@@ -121,7 +124,6 @@ def get_inputs(self, instance: Argument) -> InputSerializer:
 class ToolSerializer(serializers.ModelSerializer, LikeBaseSerializer):
     '''Serializer to get Tool data via API.'''
 
-    stage_name = StageField(source='stage')                                     # Stage name for read operations
     # Intensitys details for read operations
     intensities = SerializerMethodField(method_name='get_intensities', read_only=True)
     # Configurations details for read operations
@@ -133,7 +135,7 @@ class Meta:
 
         model = Tool
         fields = (                                                              # Tool fields exposed via API
-            'id', 'name', 'command', 'stage_name', 'reference', 'icon',
+            'id', 'name', 'command', 'reference', 'icon',
             'liked', 'likes', 'intensities', 'configurations', 'arguments'
         )
 
@@ -165,7 +167,6 @@ def get_configurations(self, instance: Tool) -> List[ConfigurationSerializer]:
 class SimplyToolSerializer(serializers.ModelSerializer):
     '''Simply serializer to include tool main data in other serializers.'''
 
-    stage_name = StageField(source='stage')                                     # Stage name
     arguments = ArgumentSerializer(many=True, read_only=True)                   # Arguments details for read operations
 
     class Meta:
@@ -173,4 +174,4 @@ class Meta:
 
         model = Tool
         # Tool fields exposed via API
-        fields = ('id', 'name', 'command', 'stage_name', 'reference', 'icon', 'arguments')
+        fields = ('id', 'name', 'command', 'reference', 'icon', 'arguments')
diff --git a/rekono/tools/tools/base_tool.py b/rekono/tools/tools/base_tool.py
index bd5c16303..b5168f288 100644
--- a/rekono/tools/tools/base_tool.py
+++ b/rekono/tools/tools/base_tool.py
@@ -390,11 +390,16 @@ def on_start(self) -> None:
         self.execution.start = timezone.now()                                   # Set execution start date
         self.execution.save(update_fields=['start'])
 
-    def on_skipped(self) -> None:
-        '''Perform changes in Execution entity when tool execution is skipped.'''
+    def on_skipped(self, message: str = None) -> None:
+        '''Perform changes in Execution entity when tool execution is skipped.
+
+        Args:
+            message (str, optional): Descriptive message about the execution skipping
+        '''
         self.execution.status = Status.SKIPPED                                  # Set execution status to Skipped
+        self.execution.output_error = message
         self.execution.end = timezone.now()                                     # Set execution end date
-        self.execution.save(update_fields=['status', 'end'])
+        self.execution.save(update_fields=['status', 'end', 'output_error'])
 
     def on_running(self) -> None:
         '''Perform changes in Execution entity when command execution starts.'''
@@ -440,9 +445,9 @@ def run(self, targets: List[BaseInput], previous_findings: List[Finding]) -> Non
         self.on_start()                                                         # Start execution
         try:
             self.check_installation()                                           # Check tool installation
-        except ToolExecutionException:                                          # Tool installation not found
+        except ToolExecutionException as ex:                                    # Tool installation not found
             logger.error(f'[Tool] Tool {self.tool.name} is not installed in the system. This execution will be skipped')
-            self.on_skipped()                                                   # Skip execution
+            self.on_skipped(str(ex))                                            # Skip execution
             return
         try:
             # Get arguments to include in command
@@ -450,7 +455,7 @@ def run(self, targets: List[BaseInput], previous_findings: List[Finding]) -> Non
         except ToolExecutionException as ex:
             logger.error(f'[Tool] {str(ex)}')
             # Targets and findings aren't enough to build the command
-            self.on_skipped()                                                   # Skip execution
+            self.on_skipped(str(ex))                                            # Skip execution
             return
         self.prepare_environment()                                              # Prepare environment
         self.on_running()                                                       # Run execution
diff --git a/rekono/tools/tools/cmseek.py b/rekono/tools/tools/cmseek.py
index f5e6c068b..44c1f9bde 100644
--- a/rekono/tools/tools/cmseek.py
+++ b/rekono/tools/tools/cmseek.py
@@ -11,7 +11,7 @@
 from rekono.settings import TOOLS
 
 
-class CmseekTool(BaseTool):
+class Cmseek(BaseTool):
     '''CMSeeK tool class.'''
 
     # CMSeeK directory where output files can be stored
diff --git a/rekono/tools/tools/dirsearch.py b/rekono/tools/tools/dirsearch.py
index dbb56756c..ff7f5c36d 100644
--- a/rekono/tools/tools/dirsearch.py
+++ b/rekono/tools/tools/dirsearch.py
@@ -5,7 +5,7 @@
 from tools.tools.base_tool import BaseTool
 
 
-class DirsearchTool(BaseTool):
+class Dirsearch(BaseTool):
     '''Dirsearch tool class.'''
 
     # Exit code ignored because Dirsearch report will include findings until error occurs
diff --git a/rekono/tools/tools/emailfinder.py b/rekono/tools/tools/emailfinder.py
index 4b92b0d91..dffd893f7 100644
--- a/rekono/tools/tools/emailfinder.py
+++ b/rekono/tools/tools/emailfinder.py
@@ -5,7 +5,7 @@
 from tools.tools.base_tool import BaseTool
 
 
-class EmailfinderTool(BaseTool):
+class Emailfinder(BaseTool):
     '''EmailFinder tool class.'''
 
     def parse_plain_output(self, output: str) -> None:
diff --git a/rekono/tools/tools/emailharvester.py b/rekono/tools/tools/emailharvester.py
index e00bd1d88..17c2c12b2 100644
--- a/rekono/tools/tools/emailharvester.py
+++ b/rekono/tools/tools/emailharvester.py
@@ -3,7 +3,7 @@
 from tools.tools.base_tool import BaseTool
 
 
-class EmailharvesterTool(BaseTool):
+class Emailharvester(BaseTool):
     '''EmailHarvester tool class.'''
 
     def parse_output_file(self) -> None:
diff --git a/rekono/tools/tools/gitleaks.py b/rekono/tools/tools/gitleaks.py
index 031e03b33..652d5564f 100644
--- a/rekono/tools/tools/gitleaks.py
+++ b/rekono/tools/tools/gitleaks.py
@@ -13,7 +13,7 @@
 from tools.tools.base_tool import BaseTool
 
 
-class GitleaksTool(BaseTool):
+class Gitleaks(BaseTool):
     '''GitLeaks tool class.'''
 
     # Exit code ignored because GitLeaks fails when find secrets
diff --git a/rekono/tools/tools/gobuster.py b/rekono/tools/tools/gobuster.py
new file mode 100644
index 000000000..d7b6e9961
--- /dev/null
+++ b/rekono/tools/tools/gobuster.py
@@ -0,0 +1,63 @@
+from typing import List
+
+from findings.enums import DataType, PathType
+from findings.models import OSINT, Finding, Path
+from input_types.models import BaseInput
+
+from tools.exceptions import ToolExecutionException
+from tools.tools.base_tool import BaseTool
+
+
+class Gobuster(BaseTool):
+    '''Gobuster tool class.'''
+
+    def get_arguments(self, targets: List[BaseInput], previous_findings: List[Finding]) -> List[str]:
+        '''Get tool arguments for the tool command.
+
+        Args:
+            targets (List[BaseInput]): List of targets and resources that can be included in the tool arguments
+            previous_findings (List[Finding]): List of previous findings that can be included in the tool arguments
+
+        Raises:
+            ToolExecutionException: Raised if targets and previous findings aren't enough to build the arguments
+
+        Returns:
+            List[str]: List of tool arguments to use in the tool execution
+        '''
+        arguments = super().get_arguments(targets, previous_findings)           # Get arguments
+        if '--url' not in arguments and '--domain' not in arguments:            # URL or domain is required
+            raise ToolExecutionException('Tool configuration requires url or domain argument')
+        if '--wordlist' not in arguments:                                       # Wordlist is required
+            raise ToolExecutionException('Tool configuration requires wordlist argument')
+        return arguments
+
+    def parse_output_file(self) -> None:
+        '''Parse tool output file to create finding entities.'''
+        with open(self.path_output, 'r', encoding='utf-8') as output_file:
+            data = output_file.readlines()                                      # Read output file
+        for line in data:                                                       # Iterate over lines
+            if ' (Status: ' in line and ') [Size: ' in line:                    # Endpoint format
+                aux = line.split(' (Status: ')
+                self.create_finding(
+                    Path,
+                    path=aux[0].strip(),
+                    status=int(aux[1].split(')')[0].strip()),
+                    type=PathType.ENDPOINT
+                )
+            elif ' Status: ' in line and ' [Size: ' in line:                    # VHOST format
+                vhost, status = line.replace('Found: ', '').split(' Status: ')
+                if status.split(' [')[0].strip().startswith('2'):               # Create only VHOST with status 2XX
+                    if '://' in vhost:
+                        vhost = vhost.split('://')[1]                           # Remove schema from VHOST URL
+                    self.create_finding(
+                        OSINT,
+                        data=vhost.strip(),
+                        data_type=DataType.VHOST,
+                        source='Enumeration'
+                    )
+            elif ' [' in line and ']' in line:                                  # Subdomain format
+                subdomain, addresses = line.replace('Found: ', '').split(' [')  # Get subdomains and IP addresses
+                ips = addresses.replace(']', '').split(',')
+                self.create_finding(OSINT, data=subdomain.strip(), data_type=DataType.DOMAIN, source='DNS')
+                for ip in ips:
+                    self.create_finding(OSINT, data=ip.strip(), data_type=DataType.IP, source='DNS')
diff --git a/rekono/tools/tools/joomscan.py b/rekono/tools/tools/joomscan.py
index 5410ecd24..b0fbfb9df 100644
--- a/rekono/tools/tools/joomscan.py
+++ b/rekono/tools/tools/joomscan.py
@@ -3,7 +3,7 @@
 from tools.tools.base_tool import BaseTool
 
 
-class JoomscanTool(BaseTool):
+class Joomscan(BaseTool):
     '''JoomScan tool class.'''
 
     def parse_plain_output(self, output: str) -> None:
diff --git a/rekono/tools/tools/log4j_scan.py b/rekono/tools/tools/log4j_scan.py
index ed1bd9349..80b82baca 100644
--- a/rekono/tools/tools/log4j_scan.py
+++ b/rekono/tools/tools/log4j_scan.py
@@ -6,7 +6,7 @@
 from tools.tools.base_tool import BaseTool
 
 
-class Log4jscanTool(BaseTool):
+class Log4jscan(BaseTool):
     '''Log4j Scan tool class.'''
 
     run_directory = TOOLS['log4j-scan']['directory']
diff --git a/rekono/tools/tools/metasploit.py b/rekono/tools/tools/metasploit.py
index 56efe7d09..e35457d77 100644
--- a/rekono/tools/tools/metasploit.py
+++ b/rekono/tools/tools/metasploit.py
@@ -3,7 +3,7 @@
 from tools.tools.base_tool import BaseTool
 
 
-class MetasploitTool(BaseTool):
+class Metasploit(BaseTool):
     '''Metasploit tool class.'''
 
     def parse_plain_output(self, output: str) -> None:
diff --git a/rekono/tools/tools/nikto.py b/rekono/tools/tools/nikto.py
index aed1c423e..2b15cc04a 100644
--- a/rekono/tools/tools/nikto.py
+++ b/rekono/tools/tools/nikto.py
@@ -5,7 +5,7 @@
 from tools.tools.base_tool import BaseTool
 
 
-class NiktoTool(BaseTool):
+class Nikto(BaseTool):
     '''Nikto tool class.'''
 
     # Exit code ignored because Nikto report will include findings until error occurs
diff --git a/rekono/tools/tools/nmap.py b/rekono/tools/tools/nmap.py
index 9350835ea..d17948515 100644
--- a/rekono/tools/tools/nmap.py
+++ b/rekono/tools/tools/nmap.py
@@ -10,7 +10,7 @@
 from tools.tools.base_tool import BaseTool
 
 
-class NmapTool(BaseTool):
+class Nmap(BaseTool):
     '''Nmap tool class.'''
 
     def get_smb_technology(self, technologies: Dict[str, Technology]) -> Union[Technology, None]:
diff --git a/rekono/tools/tools/nuclei.py b/rekono/tools/tools/nuclei.py
index bf94ed489..9dcdba709 100644
--- a/rekono/tools/tools/nuclei.py
+++ b/rekono/tools/tools/nuclei.py
@@ -7,7 +7,7 @@
 from tools.tools.base_tool import BaseTool
 
 
-class NucleiTool(BaseTool):
+class Nuclei(BaseTool):
     '''Nuclei tool class.'''
 
     def parse_output_file(self) -> None:
diff --git a/rekono/tools/tools/searchsploit.py b/rekono/tools/tools/searchsploit.py
index b647eadec..f16f234ab 100644
--- a/rekono/tools/tools/searchsploit.py
+++ b/rekono/tools/tools/searchsploit.py
@@ -4,7 +4,7 @@
 from tools.tools.base_tool import BaseTool
 
 
-class SearchsploitTool(BaseTool):
+class Searchsploit(BaseTool):
     '''SearchSploit tool class.'''
 
     def parse_output_file(self) -> None:
diff --git a/rekono/tools/tools/smbmap.py b/rekono/tools/tools/smbmap.py
index 74d96d8a7..9826e1723 100644
--- a/rekono/tools/tools/smbmap.py
+++ b/rekono/tools/tools/smbmap.py
@@ -3,7 +3,7 @@
 from tools.tools.base_tool import BaseTool
 
 
-class SmbmapTool(BaseTool):
+class Smbmap(BaseTool):
     '''Smbmap tool class.'''
 
     def parse_plain_output(self, output: str) -> None:
diff --git a/rekono/tools/tools/spring4shell_scan.py b/rekono/tools/tools/spring4shell_scan.py
index fd5561999..718aac035 100644
--- a/rekono/tools/tools/spring4shell_scan.py
+++ b/rekono/tools/tools/spring4shell_scan.py
@@ -6,7 +6,7 @@
 from tools.tools.base_tool import BaseTool
 
 
-class Spring4shellscanTool(BaseTool):
+class Spring4shellscan(BaseTool):
     '''Spring4Shell Scan tool class.'''
 
     # Indicate the script path to execute
diff --git a/rekono/tools/tools/ssh_audit.py b/rekono/tools/tools/ssh_audit.py
index d008996f3..c234706e2 100644
--- a/rekono/tools/tools/ssh_audit.py
+++ b/rekono/tools/tools/ssh_audit.py
@@ -5,7 +5,7 @@
 from tools.tools.base_tool import BaseTool
 
 
-class SshauditTool(BaseTool):
+class Sshaudit(BaseTool):
     '''SSH Audit tool class.'''
 
     # Exit code ignored because SSH Audit fails when find vulnerabilities
diff --git a/rekono/tools/tools/sslscan.py b/rekono/tools/tools/sslscan.py
index debcc9875..a0586124c 100644
--- a/rekono/tools/tools/sslscan.py
+++ b/rekono/tools/tools/sslscan.py
@@ -7,7 +7,7 @@
 from tools.tools.base_tool import BaseTool
 
 
-class SslscanTool(BaseTool):
+class Sslscan(BaseTool):
     '''Sslscan tool class.'''
 
     def get_technology(self, technologies: List[Technology], sslversion: str) -> Union[Technology, None]:
diff --git a/rekono/tools/tools/sslyze.py b/rekono/tools/tools/sslyze.py
index 410b4ca42..f506cf8d9 100644
--- a/rekono/tools/tools/sslyze.py
+++ b/rekono/tools/tools/sslyze.py
@@ -7,7 +7,7 @@
 from tools.tools.base_tool import BaseTool
 
 
-class SslyzeTool(BaseTool):
+class Sslyze(BaseTool):
     '''SSLyze tool class.'''
 
     # Exit code ignored because SSLyze can "fail" when find vulnerabilities
diff --git a/rekono/tools/tools/theharvester.py b/rekono/tools/tools/theharvester.py
index bd3e66e5f..4462128ea 100644
--- a/rekono/tools/tools/theharvester.py
+++ b/rekono/tools/tools/theharvester.py
@@ -2,17 +2,18 @@
 
 from findings.enums import DataType
 from findings.models import OSINT
+
 from tools.tools.base_tool import BaseTool
 
 
-class TheharvesterTool(BaseTool):
+class Theharvester(BaseTool):
     '''theHarvester tool class.'''
 
     # Mapping between theHarvester types and OSINT data types
     data_types = [
         ('ips', DataType.IP),
         ('hosts', DataType.DOMAIN),
-        ('vhosts', DataType.DOMAIN),
+        ('vhosts', DataType.VHOST),
         ('urls', DataType.URL),
         ('trello_urls', DataType.URL),
         ('interesting_urls', DataType.URL),
diff --git a/rekono/tools/tools/zap.py b/rekono/tools/tools/zap.py
index 3fa98379d..75bf347e1 100644
--- a/rekono/tools/tools/zap.py
+++ b/rekono/tools/tools/zap.py
@@ -6,7 +6,7 @@
 from tools.tools.base_tool import BaseTool
 
 
-class ZapTool(BaseTool):
+class Zap(BaseTool):
     '''OWASP ZAP tool class.'''
 
     # Mapping between OWASP ZAP severity values and Rekono severity values
diff --git a/rekono/tools/utils.py b/rekono/tools/utils.py
index 9fb6b3d7f..0b4cb69ce 100644
--- a/rekono/tools/utils.py
+++ b/rekono/tools/utils.py
@@ -14,7 +14,7 @@ def get_tool_class_by_name(name: str) -> Any:
     try:
         tools_module = importlib.import_module(f'tools.tools.{name.lower().replace(" ", "_")}')   # Import tool module
         # Get tool class
-        tool_class = getattr(tools_module, f'{name[0].upper()}{name[1:].lower().replace(" ", "")}Tool')
+        tool_class = getattr(tools_module, name[0].upper() + name[1:].lower().replace(' ', ''))
     except (AttributeError, ModuleNotFoundError):                               # Error during import
         tools_module = importlib.import_module('tools.tools.base_tool')         # Get base tool module
         tool_class = getattr(tools_module, 'BaseTool')                          # Get base tool class


Test patch:
diff --git a/rekono/testing/api/test_processes.py b/rekono/testing/api/test_processes.py
index 5abae9559..e384c03b9 100644
--- a/rekono/testing/api/test_processes.py
+++ b/rekono/testing/api/test_processes.py
@@ -54,7 +54,7 @@ def test_unauthorized_delete(self) -> None:
 
     def test_like_dislike(self) -> None:
         '''Test like and dislike features for processes.'''
-        count = self.api_test(self.client.get, f'{self.endpoint}?o=-name')['count']     # Get total count of processes
+        count = self.api_test(self.client.get, f'{self.endpoint}?order=-name')['count']  # Get total count of processes
         # Like testing process
         self.api_test(self.client.post, f'{self.endpoint}{self.process.id}/like/', 201)
         self.api_test(self.client.get, f'{self.endpoint}{self.process.id}/', expected={'liked': True, 'likes': 1})
diff --git a/rekono/testing/api/test_resources.py b/rekono/testing/api/test_resources.py
index 398d4a1b5..74b2aea46 100644
--- a/rekono/testing/api/test_resources.py
+++ b/rekono/testing/api/test_resources.py
@@ -54,8 +54,8 @@ def create_wordlist(self, name: str, path: str, type: str, status_code: int = 20
     def test_create(self) -> None:
         '''Test wordlist creation feature.'''
         # Create new wordlist
-        new_wordlist = self.create_wordlist(self.name + self.name, self.endpoints, 'Endpoint')
-        content = self.api_test(self.client.get, f'{self.endpoint}?o=-name')    # Get all wordlists
+        new_wordlist = self.create_wordlist(self.name + self.name, self.endpoints, WordlistType.ENDPOINT)
+        content = self.api_test(self.client.get, f'{self.endpoint}?order=-name')    # Get all wordlists
         # Check that the first one is the new wordlist
         self.check_fields(['name', 'type', 'size', 'size', 'creator'], content['results'][0], new_wordlist)
         db_wordlist = Wordlist.objects.get(pk=new_wordlist['id'])
@@ -73,7 +73,7 @@ def test_invalid_create(self) -> None:
 
     def test_update(self) -> None:
         '''Test wordlist update feature.'''
-        data = {'name': self.name, 'type': 'Endpoint'}
+        data = {'name': self.name, 'type': WordlistType.ENDPOINT}
         # Update wordlist
         self.api_test(self.client.put, f'{self.endpoint}{self.wordlist.id}/', 200, data=data, expected=data)
 
@@ -88,10 +88,10 @@ def test_invalid_update(self) -> None:
 
     def test_delete(self) -> None:
         '''Test wordlist deletion feature.'''
-        before = self.api_test(self.client.get, f'{self.endpoint}?o=-name')     # Get wordlists
+        before = self.api_test(self.client.get, f'{self.endpoint}?order=-name')     # Get wordlists
         # Delete testing wordlist
         self.api_test(self.client.delete, f'{self.endpoint}{self.wordlist.id}/', 204)
-        self.api_test(self.client.get, f'{self.endpoint}?o=-name', expected={'count': before['count'] - 1})
+        self.api_test(self.client.get, f'{self.endpoint}?order=-name', expected={'count': before['count'] - 1})
 
     def test_unauthorized_delete(self) -> None:
         '''Test wordlist deletion feature without Admin or process creator.'''
@@ -102,7 +102,7 @@ def test_unauthorized_delete(self) -> None:
 
     def test_like_dislike(self) -> None:
         '''Test like and dislike features for wordlists.'''
-        count = self.api_test(self.client.get, f'{self.endpoint}?o=-name')['count']     # Get total count of wordlists
+        count = self.api_test(self.client.get, f'{self.endpoint}?order=-name')['count']  # Get total count of wordlists
         # Like testing wordlist
         self.api_test(self.client.post, f'{self.endpoint}{self.wordlist.id}/like/', 201)
         self.api_test(self.client.get, f'{self.endpoint}{self.wordlist.id}/', expected={'liked': True, 'likes': 1})
diff --git a/rekono/testing/api/test_tools.py b/rekono/testing/api/test_tools.py
index ce07f4ecb..6ab959979 100644
--- a/rekono/testing/api/test_tools.py
+++ b/rekono/testing/api/test_tools.py
@@ -31,13 +31,15 @@ def test_get_tools_and_configurations(self) -> None:
         '''Test read tool and configuration data.'''
         for stage_value, stage_name in self.stages:
             # Get tools by stage
-            tools = self.api_test(self.client.get, f'{self.tools}?stage={stage_value}&limit=100')
+            tools = self.api_test(self.client.get, f'{self.tools}?configurations__stage={stage_value}&limit=100')
             for tool in tools['results']:
-                self.assertEqual(stage_name, tool.get('stage_name'))
                 # Get configurations by tool
-                configs = self.api_test(self.client.get, f'{self.configurations}?tool={tool.get("id")}&limit=100')
+                configs = self.api_test(
+                    self.client.get, f'{self.configurations}?tool={tool.get("id")}&stage={stage_value}&limit=100'
+                )
                 for config in configs['results']:
                     self.assertEqual(tool.get('id'), config.get('tool'))
+                    self.assertEqual(stage_name, config.get('stage_name'))
 
     def test_like_dislike(self) -> None:
         '''Test like and dislike features for tools.'''
diff --git a/rekono/testing/api/test_users.py b/rekono/testing/api/test_users.py
index b829f60bb..6e7a22e23 100644
--- a/rekono/testing/api/test_users.py
+++ b/rekono/testing/api/test_users.py
@@ -107,21 +107,21 @@ def test_filter_by_project(self) -> None:
         '''Test filter feature by project.'''
         # Get users that belong to created project
         content = self.api_test(
-            self.client.get, f'{self.endpoint}?project={self.project.id}&o=-username', expected={'count': 1}
+            self.client.get, f'{self.endpoint}?project={self.project.id}&order=-username', expected={'count': 1}
         )
         self.check_fields(['id', 'username', 'email'], content['results'][0], self.admin)
         # Get users that don't belong to created project
         content = self.api_test(
-            self.client.get, f'{self.endpoint}?project__ne={self.project.id}&o=-username', expected={'count': 1}
+            self.client.get, f'{self.endpoint}?project__ne={self.project.id}&order=-username', expected={'count': 1}
         )
         self.check_fields(['id', 'username', 'email'], content['results'][0], self.other)
 
     def test_filter_by_project_not_found(self) -> None:
         '''Test filter feature by not found project.'''
         # Get users that belong to unexisting project: No users
-        self.api_test(self.client.get, f'{self.endpoint}?project=-1&o=-username', expected={'count': 0})
+        self.api_test(self.client.get, f'{self.endpoint}?project=-1&order=-username', expected={'count': 0})
         # Get users that don't belong to unexisting project: All users
-        self.api_test(self.client.get, f'{self.endpoint}?project__ne=-1&o=-username', expected={'count': 2})
+        self.api_test(self.client.get, f'{self.endpoint}?project__ne=-1&order=-username', expected={'count': 2})
 
     def test_change_role(self) -> None:
         '''Test change role feature.'''
diff --git a/rekono/testing/data/reports/gobuster/dir.txt b/rekono/testing/data/reports/gobuster/dir.txt
new file mode 100644
index 000000000..e781e17f7
--- /dev/null
+++ b/rekono/testing/data/reports/gobuster/dir.txt
@@ -0,0 +1,13 @@
+/.gitignore           (Status: 200) [Size: 57]
+/.hta                 (Status: 403) [Size: 290]
+/.htaccess            (Status: 403) [Size: 295]
+/.htpasswd            (Status: 403) [Size: 295]
+/config               (Status: 301) [Size: 314] [--> http://example.com/config/]
+/docs                 (Status: 301) [Size: 312] [--> http://example.com/docs/]
+/external             (Status: 301) [Size: 316] [--> http://example.com/external/]
+/favicon.ico          (Status: 200) [Size: 1406]
+/index.php            (Status: 302) [Size: 0] [--> login.php]
+/php.ini              (Status: 200) [Size: 148]
+/phpinfo.php          (Status: 302) [Size: 0] [--> login.php]
+/robots.txt           (Status: 200) [Size: 26]
+/server-status        (Status: 403) [Size: 299]
diff --git a/rekono/testing/data/reports/gobuster/dns.txt b/rekono/testing/data/reports/gobuster/dns.txt
new file mode 100644
index 000000000..358d354c0
--- /dev/null
+++ b/rekono/testing/data/reports/gobuster/dns.txt
@@ -0,0 +1,2 @@
+Found: chat.example.com [10.10.10.10,10.10.10.11]
+Found: echo.example.com [10.10.10.10,10.10.10.11]
diff --git a/rekono/testing/data/reports/gobuster/vhost.txt b/rekono/testing/data/reports/gobuster/vhost.txt
new file mode 100644
index 000000000..d63c2dac6
--- /dev/null
+++ b/rekono/testing/data/reports/gobuster/vhost.txt
@@ -0,0 +1,23 @@
+Found: dns:monportail.example.com Status: 400 [Size: 427]
+Found: http://mobility.example.com Status: 400 [Size: 427]
+Found: http://enquetes.example.com Status: 200 [Size: 427]
+Found: http://partner.example.com Status: 400 [Size: 427]
+Found: https:.example.com Status: 400 [Size: 427]
+Found: https://archives.example.com Status: 400 [Size: 427]
+Found: https://assurance.example.com Status: 400 [Size: 427]
+Found: https://collaboratif.example.com Status: 400 [Size: 427]
+Found: https://conseil.example.com Status: 400 [Size: 427]
+Found: https://ee.example.com Status: 400 [Size: 427]
+Found: https://escale.example.com Status: 400 [Size: 427]
+Found: https://idees.example.com Status: 400 [Size: 427]
+Found: https://lvelizy.example.com Status: 400 [Size: 427]
+Found: https://igc.example.com Status: 400 [Size: 427]
+Found: https://mobility.example.com Status: 400 [Size: 427]
+Found: https://nomade.example.com Status: 400 [Size: 427]
+Found: https://partner.example.com Status: 400 [Size: 427]
+Found: https://pam.example.com Status: 400 [Size: 427]
+Found: https://protocoltraining.example.com Status: 400 [Size: 427]
+Found: https://scm.example.com Status: 400 [Size: 427]
+Found: https://sft.example.com Status: 400 [Size: 427]
+Found: https://webpam.example.com Status: 400 [Size: 427]
+Found: https://www.example.com Status: 400 [Size: 427]
diff --git a/rekono/testing/executions/test_base_tool.py b/rekono/testing/executions/test_base_tool.py
index 272be5677..f7a65d44e 100644
--- a/rekono/testing/executions/test_base_tool.py
+++ b/rekono/testing/executions/test_base_tool.py
@@ -51,7 +51,8 @@ def setUp(self) -> None:
             arguments=(
                 '{intensity} {test_osint} {test_only_host} {test_host} {test_port} {test_path} {test_technology} '
                 '{test_credential} {test_vulnerability} {test_exploit} {test_wordlist} {test_authentication}'
-            )
+            ),
+            stage=Stage.ENUMERATION
         )
         self.authentication_argument = Argument.objects.create(
             tool=self.nmap,
@@ -546,7 +547,7 @@ def test_check_arguments(self) -> None:
     def test_tool_execution(self) -> None:
         '''Test tool_execution feature using ls command.'''
         # Testing tool with ls command
-        tool = Tool.objects.create(name='Test', command='ls', stage=Stage.ENUMERATION)
+        tool = Tool.objects.create(name='Test', command='ls')
         self.new_execution.tool = tool
         self.new_execution.save(update_fields=['tool'])
         self.tool_class = get_tool_class_by_name(tool.name)                     # Related tool class
diff --git a/rekono/testing/executions/test_executions_from_findings.py b/rekono/testing/executions/test_executions_from_findings.py
index 3577055bb..9857ef5e2 100644
--- a/rekono/testing/executions/test_executions_from_findings.py
+++ b/rekono/testing/executions/test_executions_from_findings.py
@@ -27,8 +27,13 @@ def setUp(self) -> None:
         '''Create initial data before run tests.'''
         super().setUp()
         # Tool for testing
-        self.tool = Tool.objects.create(name='Test', command='ls', stage=Stage.ENUMERATION)
-        configuration = Configuration.objects.create(name='Test', tool=self.tool, arguments='-la')
+        self.tool = Tool.objects.create(name='Test', command='ls')
+        configuration = Configuration.objects.create(
+            name='Test',
+            tool=self.tool,
+            arguments='-la',
+            stage=Stage.ENUMERATION
+        )
         # Host argument
         test_host = Argument.objects.create(tool=self.tool, name='test_host', required=True)
         Input.objects.create(argument=test_host, type=InputType.objects.get(name='Host'))
diff --git a/rekono/testing/tools/test_gobuster.py b/rekono/testing/tools/test_gobuster.py
new file mode 100644
index 000000000..3bf4a80ec
--- /dev/null
+++ b/rekono/testing/tools/test_gobuster.py
@@ -0,0 +1,62 @@
+from findings.enums import DataType, PathType
+from findings.models import OSINT, Path
+from testing.tools.base import ToolParserTest
+
+
+class GobusterParserTest(ToolParserTest):
+    '''Test cases for Gobuster parser.'''
+
+    tool_name = 'Gobuster'
+
+    def test_dir(self) -> None:
+        '''Test to parse dir report with endpoints.'''
+        expected = [
+            {'model': Path, 'path': '/.gitignore', 'status': 200, 'type': PathType.ENDPOINT},
+            {'model': Path, 'path': '/.hta', 'status': 403, 'type': PathType.ENDPOINT},
+            {'model': Path, 'path': '/.htaccess', 'status': 403, 'type': PathType.ENDPOINT},
+            {'model': Path, 'path': '/.htpasswd', 'status': 403, 'type': PathType.ENDPOINT},
+            {'model': Path, 'path': '/config', 'status': 301, 'type': PathType.ENDPOINT},
+            {'model': Path, 'path': '/docs', 'status': 301, 'type': PathType.ENDPOINT},
+            {'model': Path, 'path': '/external', 'status': 301, 'type': PathType.ENDPOINT},
+            {'model': Path, 'path': '/favicon.ico', 'status': 200, 'type': PathType.ENDPOINT},
+            {'model': Path, 'path': '/index.php', 'status': 302, 'type': PathType.ENDPOINT},
+            {'model': Path, 'path': '/php.ini', 'status': 200, 'type': PathType.ENDPOINT},
+            {'model': Path, 'path': '/phpinfo.php', 'status': 302, 'type': PathType.ENDPOINT},
+            {'model': Path, 'path': '/robots.txt', 'status': 200, 'type': PathType.ENDPOINT},
+            {'model': Path, 'path': '/server-status', 'status': 403, 'type': PathType.ENDPOINT},
+        ]
+        super().check_tool_file_parser('dir.txt', expected)
+
+    def test_dns(self) -> None:
+        '''Test to parse dns report with domains and IPs.'''
+        expected = [
+            {
+                'model': OSINT,
+                'data': 'chat.example.com',
+                'data_type': DataType.DOMAIN,
+                'source': 'DNS'
+            },
+            {'model': OSINT, 'data': '10.10.10.10', 'data_type': DataType.IP, 'source': 'DNS'},
+            {'model': OSINT, 'data': '10.10.10.11', 'data_type': DataType.IP, 'source': 'DNS'},
+            {
+                'model': OSINT,
+                'data': 'echo.example.com',
+                'data_type': DataType.DOMAIN,
+                'source': 'DNS'
+            },
+            {'model': OSINT, 'data': '10.10.10.10', 'data_type': DataType.IP, 'source': 'DNS'},
+            {'model': OSINT, 'data': '10.10.10.11', 'data_type': DataType.IP, 'source': 'DNS'},
+        ]
+        super().check_tool_file_parser('dns.txt', expected)
+
+    def test_vhost(self) -> None:
+        '''Test to parse vhost report with VHOSTs.'''
+        expected = [
+            {
+                'model': OSINT,
+                'data': 'enquetes.example.com',
+                'data_type': DataType.VHOST,
+                'source': 'Enumeration'
+            }
+        ]
+        super().check_tool_file_parser('vhost.txt', expected)