Improve UX of module explorer
This commit is contained in:
parent
292c160abf
commit
1b44973c80
|
@ -1,19 +1,60 @@
|
|||
// Handle opening/closing module overview list items
|
||||
jtd.onReady(function(ready) {
|
||||
var moduleStructures = document.querySelectorAll('.module-structure');
|
||||
for (var i = 0; i < moduleStructures.length; i++) {
|
||||
jtd.addEvent(moduleStructures[i], 'click', function (e) {
|
||||
var forEach = function (list, callback) {
|
||||
for (var i = 0; i < list.length; i++) {
|
||||
callback(list[i])
|
||||
}
|
||||
};
|
||||
|
||||
// Bind listeners for expand all / collapse all functionality
|
||||
var bindToggleAll = function (selector, options) {
|
||||
var isOpen = options.open;
|
||||
var expandAllButtons = document.querySelectorAll(selector);
|
||||
forEach(expandAllButtons, function (button) {
|
||||
jtd.addEvent(button, 'click', function (e) {
|
||||
var originalTarget = e.target || e.srcElement || e.originalTarget;
|
||||
if (originalTarget.tagName !== 'A') { return; }
|
||||
|
||||
var moduleList = originalTarget.closest('.module-list');
|
||||
forEach(moduleList.querySelectorAll('.folder > ul'), function (list) {
|
||||
if (isOpen) {
|
||||
list.classList.add('open');
|
||||
} else {
|
||||
list.classList.remove('open');
|
||||
}
|
||||
})
|
||||
|
||||
e.preventDefault();
|
||||
});
|
||||
});
|
||||
};
|
||||
bindToggleAll('.module-list [data-expand-all]', { open: true })
|
||||
bindToggleAll('.module-list [data-collapse-all]', { open: false })
|
||||
|
||||
// Bind listeners for collapsing module navigation items
|
||||
var moduleStructureElements = document.querySelectorAll('.module-structure');
|
||||
forEach(moduleStructureElements, function (moduleStructure) {
|
||||
jtd.addEvent(moduleStructure, 'click', function (e) {
|
||||
var originalTarget = e.target || e.srcElement || e.originalTarget;
|
||||
if (originalTarget.tagName !== 'A') { return; }
|
||||
|
||||
var parentListItem = originalTarget.closest('li');
|
||||
if (parentListItem.className.indexOf('folder') === -1) { return; }
|
||||
|
||||
var childList = parentListItem.querySelector('ul');
|
||||
if (childList) {
|
||||
childList.classList.toggle('open');
|
||||
}
|
||||
toggleChildModuleList(parentListItem)
|
||||
e.preventDefault();
|
||||
});
|
||||
})
|
||||
|
||||
var toggleChildModuleList = function (parent) {
|
||||
var list = parent.querySelector('ul');
|
||||
if (!list) {
|
||||
return;
|
||||
}
|
||||
list.classList.toggle('open');
|
||||
// Recursively automatically open any nested lists of size 1
|
||||
if (list.children.length === 1) {
|
||||
toggleChildModuleList(list.children[0])
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -6,6 +6,10 @@ require 'pathname'
|
|||
# Helper class for extracting information related to Metasploit framework's stats
|
||||
#
|
||||
class MetasploitStats
|
||||
def total_module_count
|
||||
modules.length
|
||||
end
|
||||
|
||||
# @return [Hash<String, Integer>] A map of module type to the amount of modules
|
||||
def module_counts
|
||||
module_counts_by_type = modules.group_by { |mod| mod['type'].to_s }.transform_values { |mods| mods.count }.sort_by(&:first).to_h
|
||||
|
@ -71,11 +75,27 @@ end
|
|||
module ModuleFilter
|
||||
# @param [Array<Hash>] modules The array of Metasploit cache information
|
||||
# @return [String] The module tree HTML representation of the given modules
|
||||
def module_tree(modules)
|
||||
def module_tree(modules, title = 'Modules', show_controls = false)
|
||||
rendered_children = render_modules(modules)
|
||||
controls = <<~EOF
|
||||
<div class="module-controls">
|
||||
<span><a href="#" data-expand-all>Expand All</a></span>
|
||||
<span><a href="#" data-collapse-all>Collapse All</a></span>
|
||||
</div>
|
||||
EOF
|
||||
|
||||
<<~EOF
|
||||
<ul class="module-structure">#{rendered_children}</ul>
|
||||
<div class="module-list">
|
||||
#{show_controls ? controls : ''}
|
||||
|
||||
<ul class="module-structure">
|
||||
<li class="folder"><a href=\"#\"><div class=\"target\">#{title}</div></a>
|
||||
<ul class="open">
|
||||
#{rendered_children}
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
EOF
|
||||
end
|
||||
|
||||
|
@ -85,7 +105,8 @@ module ModuleFilter
|
|||
# @return [String] The rendered tree HTML representation of the given modules
|
||||
def render_modules(modules)
|
||||
modules.map do |mod|
|
||||
result = "<li#{render_child_modules?(mod) ? ' class="folder"' : ''}>#{heading_for_mod(mod)}"
|
||||
classes = render_child_modules?(mod) ? ' class="folder"' : ''
|
||||
result = "<li#{classes}>#{heading_for_mod(mod)}"
|
||||
if render_child_modules?(mod)
|
||||
result += "\n<ul>#{render_modules(mod[:children].sort_by { |mod| "#{render_child_modules?(mod) ? 0 : 1}-#{mod[:name]}" })}</ul>\n"
|
||||
end
|
||||
|
@ -126,7 +147,7 @@ Jekyll::Hooks.register :site, :after_init do |site|
|
|||
|
||||
metasploit_stats = MetasploitStats.new
|
||||
|
||||
site.config['metasploit_total_module_count'] = metasploit_stats.module_counts.sum { |_type, count| count }
|
||||
site.config['metasploit_total_module_count'] = metasploit_stats.total_module_count
|
||||
site.config['metasploit_module_counts'] = metasploit_stats.module_counts
|
||||
site.config['metasploit_nested_module_counts'] = metasploit_stats.nested_module_counts
|
||||
|
||||
|
|
|
@ -45,14 +45,32 @@
|
|||
width: 90%;
|
||||
}
|
||||
|
||||
.module-controls {
|
||||
line-height: 0;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.module-controls a {
|
||||
line-height: 1;
|
||||
padding: 0.5rem;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.module-controls span {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.module-structure a, .module-structure a:hover {
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
.module-structure a:hover .target {
|
||||
.module-structure a .target {
|
||||
pointer-events: none;
|
||||
display: inline-block;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.module-structure a:hover .target {
|
||||
background-image: linear-gradient(rgba(114, 83, 237, 0.45) 0%, rgba(114, 83, 237, 0.45) 100%);
|
||||
background-repeat: repeat-x;
|
||||
background-position: 0 100%;
|
||||
|
@ -70,6 +88,11 @@
|
|||
border-left: 1px dashed #d1d7de;
|
||||
}
|
||||
|
||||
/* Never allow the top-most files/folders to be collapsed */
|
||||
.module-structure > li.folder > ul {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.module-structure li p {
|
||||
margin: 0;
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
There are currently {{ site.metasploit_total_module_count }} Metasploit modules:
|
||||
|
||||
{{ site.metasploit_nested_module_counts | module_tree }}
|
||||
{{ site.metasploit_nested_module_counts | module_tree: "All Modules", true }}
|
||||
|
||||
## Module types
|
||||
|
||||
|
|
Loading…
Reference in New Issue