Appearance
Plugin System
lib/plugin_loader.sh is the runtime extension mechanism for netvuln-tool. It loads community (core) and enterprise plugins with automatic discovery, dependency ordering, and non-fatal hook execution. It is Bash 3.2+ compatible (no associative arrays or namerefs): plugin state is kept as newline-delimited key-value strings and hook registrations as indexed arrays.
Design: Stub-First Safety
The loader follows a stub-first pattern. Before any plugin discovery runs, no-op stubs are defined for the enterprise entry points the pipeline calls unconditionally, so the pipeline can never crash on a missing plugin function:
bash
map_compliance() { return 0; } # report phase
build_topology() { return 0; } # report phase
generate_exec_summary() { return 0; } # config-gated
nv_submit_session() { return 1; } # portal upload (1 = not available)When a plugin loads successfully, its plugin_init overrides these stubs with real implementations. If the plugin is absent or its tier is not licensed, the harmless stub stands in.
Plugin Structure
Plugins live under scripts/plugins/ in two tiers:
scripts/plugins/
├── core/ # Community plugins (ship with the OSS repo)
│ ├── scan-diff/
│ │ └── plugin.sh
│ └── alert-notifier/
│ └── plugin.sh
└── enterprise/ # License-gated plugins (compliance, topology, portal, ...)
└── <name>/
└── plugin.shThe open-source repository ships two core plugins (scan-diff and alert-notifier); the enterprise/ directory is where license-gated plugins are installed. A third location can be added with the NV_PLUGIN_DIR environment variable.
Each plugin directory contains a plugin.sh manifest declaring metadata variables and a plugin_init function. plugin_init receives the plugin's own directory as $1:
bash
PLUGIN_NAME="scan-diff"
PLUGIN_VERSION="1.0.0"
PLUGIN_EDITION="community" # or "enterprise"
PLUGIN_DESCRIPTION="What this plugin does"
PLUGIN_DEPENDS="" # comma-separated dependency names
PLUGIN_HOOKS="pre-report" # hook points this plugin uses
plugin_init() {
local plugin_dir="$1"
# Source additional files, set up state, register hooks.
nv_register_hook "pre-report" "_nv_plugin_scan_diff"
return 0
}Hook Points
Plugins register functions to run at defined points via nv_register_hook. The pipeline fires them with nv_run_hooks:
| Hook Point | When It Fires |
|---|---|
post-discovery | After all discovery modules complete |
post-enumeration | After all enumeration modules complete |
post-assessment | After vulnerability assessment completes |
pre-report | Before report generation begins |
post-report | After report generation completes |
post-upload | After session upload to the portal |
post-pipeline | After the entire pipeline finishes |
Hook execution is non-fatal: a failing hook is logged and the pipeline continues. Multiple plugins can register for the same hook point, and they run in dependency-resolved order. Arguments passed to nv_run_hooks are forwarded to each registered function (for example, post-report receives the session JSON path, report file, and config path).
Dependency Ordering
Plugins declare dependencies with PLUGIN_DEPENDS (comma-separated names). The loader topologically sorts plugins so dependencies load before dependents. A missing dependency skips the dependent plugin with a warning, and circular dependencies are broken by visited-node detection.
API
bash
# Source the plugin loader (double-source guarded)
source lib/plugin_loader.sh
# Discover and load all plugins from core/, enterprise/, and NV_PLUGIN_DIR
nv_load_plugins
# Run every function registered at a hook point (args forwarded to each)
nv_run_hooks "post-report" "$session_json" "$report_file" "$config"
# Register a hook (typically inside plugin_init)
nv_register_hook "post-report" "my_function_name"The plugin registry records each plugin as loaded or skipped, queryable through the loader's metadata store.
Related
- Pipeline Phases: where each hook fires in the run.
- License Provisioning: how enterprise plugins are tier-gated.
