Add RuboCop lint rules for module best practices - #21772
Conversation
…sage, prevent hardcoded payloads, and deprecate HttpFingerprint
There was a problem hiding this comment.
Pull request overview
Adds three custom RuboCop cops for Metasploit module best practices.
Changes:
- Detects missing AutoCheck, default payloads, and legacy HTTP fingerprints.
- Registers the cops for module paths.
- Adds focused RSpec coverage.
Impact Analysis:
- Blast radius: Module linting and CI for new and post-epoch modules; medium.
- Data and contract effects: No runtime schema or API changes.
- Rollback and test focus: Validate CI severity, AST scoping, and false-positive cases.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
.rubocop.yml |
Registers and configures the cops. |
lib/rubocop/cop/lint/module_missing_autocheck.rb |
Detects missing AutoCheck prepends. |
lib/rubocop/cop/lint/module_default_payload.rb |
Detects hardcoded default payloads. |
lib/rubocop/cop/lint/module_http_fingerprint.rb |
Detects legacy HTTP fingerprints. |
spec/rubocop/cop/lint/module_missing_autocheck_spec.rb |
Tests AutoCheck detection. |
spec/rubocop/cop/lint/module_default_payload_spec.rb |
Tests default-payload detection. |
spec/rubocop/cop/lint/module_http_fingerprint_spec.rb |
Tests fingerprint detection. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| Modules with a check method should prepend Msf::Exploit::Remote::AutoCheck | ||
| so users can verify vulnerability before exploitation. | ||
| Enabled: true | ||
| Severity: info |
There was a problem hiding this comment.
| class_node = node.each_ancestor(:class).first | ||
| return unless class_node | ||
|
|
||
| return if has_autocheck_prepend?(class_node) |
| # Parent must be a hash | ||
| parent_hash = node.parent | ||
| return false unless parent_hash&.hash_type? | ||
|
|
||
| # Grandparent must be a pair with key 'DefaultOptions' | ||
| grandparent_pair = parent_hash.parent | ||
| return false unless grandparent_pair&.pair_type? | ||
| return false unless grandparent_pair.key.str_type? && grandparent_pair.key.value == 'DefaultOptions' |
There was a problem hiding this comment.
Anchoring to update_info would introduce false negatives — 8 modules use super() directly with this pattern (e.g. vmware_vcenter_log4shell.rb#L56-L57). The structural match (L52-L65) — string 'PAYLOAD' inside string 'DefaultOptions' — is already unique to module metadata across 5000+ modules with zero non-metadata occurrences.
| Enabled: true | ||
| Severity: info | ||
| Include: | ||
| - 'modules/exploits/**/*' | ||
| - 'modules/auxiliary/**/*' |
There was a problem hiding this comment.
Incorrect — msftidy only lints files with git status A (Added): next unless summary[:status] == 'A'. Modified files (M) skip RuboCop entirely (L56-L60 — returns STATUS_SUCCESS immediately). Editing any existing module never invokes these cops.
…y primary module class is flagged
I had kiro look at our modules/PR comments etc and had it come up with some new linting rules to prevent us from needing to leave similar comments in the future this is what it came up with
Description
Adds three new custom RuboCop cops that enforce module best practices documented in CONTRIBUTING.md:
Lint/ModuleMissingAutocheck— flags exploit/auxiliary modules that define acheckmethod but don'tprepend Msf::Exploit::Remote::AutoCheck. Severity:info.Lint/ModuleDefaultPayload— flags modules that hardcode'PAYLOAD' => '...'insideDefaultOptions. Severity:warning. Contributors can suppress with an inline# rubocop:disable Lint/ModuleDefaultPayloadand a comment explaining why — making the exception searchable for future cleanup.Lint/ModuleHttpFingerprint— flags usage of the legacyHttpFingerprintconstant assignment. Severity:info.All three cops are registered in
.rubocop.ymland scoped tomodules/paths. They fire on newly-added modules via msftidy's existing CI pattern (which only lints files added after the RuboCop epoch commit3a046f01). New modules must pass these checks to merge — the severity levels affect display classification only, not whether CI fails.These cops codify conventions already documented in the "Modernizing Existing Modules" section of CONTRIBUTING.md, making them machine-enforceable for new module submissions rather than relying on reviewer memory.
Related Issue: None
Breaking Changes
None. Only newly-added module files are linted by msftidy in CI. Existing modules are unaffected unless a contributor explicitly runs
rubocopon them.Reviewer Notes
ModuleDefaultPayloadintentionally supports# rubocop:disablesuppression with a justification comment for the rare case where auto-selection genuinely picks an incompatible payload. This makes exceptions explicit and searchable rather than silently accepted.on_def,on_pair,on_casgn) with no external dependencies.Verification Steps
cd /path/to/metasploit-frameworkbundle exec rspec spec/rubocop/cop/lint/module_missing_autocheck_spec.rb spec/rubocop/cop/lint/module_default_payload_spec.rb spec/rubocop/cop/lint/module_http_fingerprint_spec.rb— expect 19 examples, 0 failuresbundle exec rubocop --only Lint/ModuleMissingAutocheck modules/exploits/linux/http/cacti_pollers_sqli_rce.rb(should report offense if module has check but no AutoCheck prepend)bundle exec rubocop --only Lint/ModuleDefaultPayload modules/exploits/linux/http/metabase_setup_token_rce.rb(should pass clean — no DefaultOptions PAYLOAD)bundle exec ruby tools/dev/msftidy.rb modules/exploits/linux/http/cacti_pollers_sqli_rce.rb— rubocop section fires and reports offenses (non-zero exit)AI Usage Disclosure
Kiro
Pre-Submission Checklist