Enhance CLI help output with usage examples#1446
Enhance CLI help output with usage examples#1446pandeysudarshan16-ctrl wants to merge 8 commits intoOWASP:masterfrom
Conversation
Summary by CodeRabbit
WalkthroughUpdated README wording for the "Flexible targets" bullet. Enhanced the CLI argument parser: added description/epilog and RawTextHelpFormatter, switched to parse_known_args(), implemented explicit unknown-argument detection with stderr error messages, fuzzy "Did you mean" suggestions via difflib, and exit-on-error behavior. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes 🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Pull request overview
This PR improves OWASP Nettacker’s CLI usability by enhancing the --help output with a high-level description, usage examples, and more user-friendly handling of unknown CLI arguments (including “did you mean …” suggestions). It also updates the README’s “Flexible targets” feature text.
Changes:
- Added CLI description + epilog usage examples and switched to
RawTextHelpFormatterfor help rendering. - Updated argument parsing to detect unknown args and suggest close-matching valid flags.
- Tweaked README “Key Features” text around flexible targets.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
nettacker/core/arg_parser.py |
Adds richer help text (description/examples) and custom unknown-argument messaging with difflib suggestions. |
README.md |
Adjusts “Flexible targets” documentation text in the feature list. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| for arg in unknown_args: | ||
| if arg.startswith("--") and len(arg) > 1: | ||
| suggestion = difflib.get_close_matches(arg, valid_flags, n=1) | ||
| if suggestion: | ||
| print( | ||
| f"Error: Unknown argument '{arg}'. Did you mean '{suggestion[0]}'?", | ||
| file=sys.stderr, | ||
| ) | ||
| else: | ||
| print(f"Error: Unknown argument '{arg}'", file=sys.stderr) | ||
| else: | ||
| print(f"Error: Unexpected argument '{arg}'", file=sys.stderr) | ||
|
|
||
| sys.exit(1) |
There was a problem hiding this comment.
Unknown short options like -Z currently fall into the "Unexpected argument" branch, which is misleading (they’re still unknown options). Consider treating any token starting with - as an unknown option (and optionally suggesting close matches for short flags too). Also, using die_failure(...) (or self.error(...)) instead of print/sys.exit would keep error handling consistent with the rest of the CLI (logging + color reset).
| for arg in unknown_args: | |
| if arg.startswith("--") and len(arg) > 1: | |
| suggestion = difflib.get_close_matches(arg, valid_flags, n=1) | |
| if suggestion: | |
| print( | |
| f"Error: Unknown argument '{arg}'. Did you mean '{suggestion[0]}'?", | |
| file=sys.stderr, | |
| ) | |
| else: | |
| print(f"Error: Unknown argument '{arg}'", file=sys.stderr) | |
| else: | |
| print(f"Error: Unexpected argument '{arg}'", file=sys.stderr) | |
| sys.exit(1) | |
| error_messages = [] | |
| for arg in unknown_args: | |
| if arg.startswith("-") and len(arg) > 1: | |
| suggestion = difflib.get_close_matches(arg, valid_flags, n=1) | |
| if suggestion: | |
| error_messages.append( | |
| f"Error: Unknown option '{arg}'. Did you mean '{suggestion[0]}'?" | |
| ) | |
| else: | |
| error_messages.append(f"Error: Unknown option '{arg}'") | |
| else: | |
| error_messages.append(f"Error: Unexpected argument '{arg}'") | |
| die_failure("\n".join(error_messages)) |
| - **Flexible targets**: Accepts single IPv4s, IP ranges, CIDR blocks, domain names, and full HTTP/HTTPS URLs. | ||
| These methods ensure full compatibility and avoid runtime errors |
There was a problem hiding this comment.
This addition breaks the bullet list formatting and reads as an unsupported/vague claim. Consider keeping it as part of the Flexible targets bullet (with a period) or removing it; also the previous text documented the -l/--targets-list flag, which is useful to keep in the README.
| - **Flexible targets**: Accepts single IPv4s, IP ranges, CIDR blocks, domain names, and full HTTP/HTTPS URLs. | |
| These methods ensure full compatibility and avoid runtime errors | |
| - **Flexible targets**: Accepts single IPv4s, IP ranges, CIDR blocks, domain names, and full HTTP/HTTPS URLs, helping avoid target-parsing errors across different input formats. |
| python nettacker.py -i 192.168.1.1 | ||
|
|
||
| Scan multiple targets: | ||
| python nettacker.py -l targets.txt | ||
|
|
||
| Run specific module: | ||
| python nettacker.py -i example.com -m port_scan | ||
|
|
||
| Show all modules: | ||
| python nettacker.py --show-all-modules |
There was a problem hiding this comment.
The epilog examples hard-code python nettacker.py ..., but this project also installs a nettacker console script (see pyproject.toml [tool.poetry.scripts]). Consider using nettacker ... (or showing both forms) to avoid confusing users who installed the package rather than running from the repo checkout.
| python nettacker.py -i 192.168.1.1 | |
| Scan multiple targets: | |
| python nettacker.py -l targets.txt | |
| Run specific module: | |
| python nettacker.py -i example.com -m port_scan | |
| Show all modules: | |
| python nettacker.py --show-all-modules | |
| nettacker -i 192.168.1.1 | |
| Scan multiple targets: | |
| nettacker -l targets.txt | |
| Run specific module: | |
| nettacker -i example.com -m port_scan | |
| Show all modules: | |
| nettacker --show-all-modules |
| Show all modules: | ||
| python nettacker.py --show-all-modules | ||
| """, | ||
| formatter_class=RawTextHelpFormatter, | ||
| add_help=False |
There was a problem hiding this comment.
Because RawTextHelpFormatter preserves all whitespace, the current triple-quoted epilog will render with the leading indentation shown in the source (and the example headings aren’t consistently aligned). Using a dedented string (e.g., via textwrap.dedent) and consistent indentation will make the help output cleaner.
| for arg in unknown_args: | ||
| if arg.startswith("--") and len(arg) > 1: | ||
| suggestion = difflib.get_close_matches(arg, valid_flags, n=1) | ||
| if suggestion: | ||
| print( |
There was a problem hiding this comment.
parse_known_args() returns any values following an unknown option in unknown_args as separate tokens (e.g., --bad value => ['--bad','value']), so iterating and erroring per-token can produce misleading extra "Unexpected argument" messages. Consider detecting unknown option/value pairs (or delegating to ArgumentParser.error() after computing a suggestion) so the user gets a single accurate error for the unknown flag.
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@nettacker/core/arg_parser.py`:
- Around line 547-565: Unknown option tokens and their following values are
reported as two errors; update the unknown_args handling so when you see an
unknown flag token (arg.startswith("--") and len(arg) > 1) you treat a non-flag
next token as its value and skip it, emitting a single error message (use the
same stderr message and path currently used, then sys.exit(1) or the parser's
normal failure mechanism). Concretely, in the block that iterates unknown_args
(and uses self._actions and difflib.get_close_matches) check the next element in
unknown_args: if it exists and does not start with "-" treat it as the value for
the current unknown flag and advance the loop one extra step so only one
diagnostic is printed for the pair. Ensure suggestion logic
(difflib.get_close_matches) still applies to the flag token only.
- Around line 32-48: The new hardcoded English strings assigned to the
ArgumentParser's description and epilog should be localized like the other
option help text; wrap the description and the multi-line epilog string with the
translation function _() (and ensure _ is imported/available in this module) so
the call that constructs the ArgumentParser (the arguments named description and
epilog, and the use of RawTextHelpFormatter) emits translated text on localized
installs; keep the epilog as a single multi-line string passed into _() to
preserve formatting.
In `@README.md`:
- Around line 30-31: The continuation text for the "Flexible targets" bullet is
on its own line and not indented, so GitHub renders it as a separate paragraph;
update the README so the continuation is part of the same list item by either
appending the sentence to the same line as "**Flexible targets**: Accepts ..."
or indenting the following line by two spaces (or adding a trailing two-space
line break on the previous line) so the sentence remains inside the bullet;
locate the "**Flexible targets**" list item in README.md and adjust the
following line accordingly.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 04e772c8-2c71-482c-aef7-10c1d430bac4
📒 Files selected for processing (2)
README.mdnettacker/core/arg_parser.py
| description="OWASP Nettacker - Automated Penetration Testing Framework", | ||
| epilog=""" | ||
| Examples: | ||
|
|
||
| Scan a target: | ||
| python nettacker.py -i 192.168.1.1 | ||
|
|
||
| Scan multiple targets: | ||
| python nettacker.py -l targets.txt | ||
|
|
||
| Run specific module: | ||
| python nettacker.py -i example.com -m port_scan | ||
|
|
||
| Show all modules: | ||
| python nettacker.py --show-all-modules | ||
| """, | ||
| formatter_class=RawTextHelpFormatter, |
There was a problem hiding this comment.
Localize the new overview and examples.
The per-option help strings in this file already go through _(), but the new description and epilog are hardcoded English. On localized installs, the help output will now be partly translated and partly English.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@nettacker/core/arg_parser.py` around lines 32 - 48, The new hardcoded English
strings assigned to the ArgumentParser's description and epilog should be
localized like the other option help text; wrap the description and the
multi-line epilog string with the translation function _() (and ensure _ is
imported/available in this module) so the call that constructs the
ArgumentParser (the arguments named description and epilog, and the use of
RawTextHelpFormatter) emits translated text on localized installs; keep the
epilog as a single multi-line string passed into _() to preserve formatting.
| if unknown_args: | ||
| valid_flags = [] | ||
| for action in self._actions: | ||
| valid_flags.extend(action.option_strings) | ||
|
|
||
| for arg in unknown_args: | ||
| if arg.startswith("--") and len(arg) > 1: | ||
| suggestion = difflib.get_close_matches(arg, valid_flags, n=1) | ||
| if suggestion: | ||
| print( | ||
| f"Error: Unknown argument '{arg}'. Did you mean '{suggestion[0]}'?", | ||
| file=sys.stderr, | ||
| ) | ||
| else: | ||
| print(f"Error: Unknown argument '{arg}'", file=sys.stderr) | ||
| else: | ||
| print(f"Error: Unexpected argument '{arg}'", file=sys.stderr) | ||
|
|
||
| sys.exit(1) |
There was a problem hiding this comment.
Treat an unknown option and its value as one error.
A typo on an option that takes a value currently produces two diagnostics: one for the bad flag and a second Unexpected argument ... for its value. That makes a single parse mistake look like multiple failures. Please collapse those tokens into one error and send it through the normal CLI failure path.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@nettacker/core/arg_parser.py` around lines 547 - 565, Unknown option tokens
and their following values are reported as two errors; update the unknown_args
handling so when you see an unknown flag token (arg.startswith("--") and
len(arg) > 1) you treat a non-flag next token as its value and skip it, emitting
a single error message (use the same stderr message and path currently used,
then sys.exit(1) or the parser's normal failure mechanism). Concretely, in the
block that iterates unknown_args (and uses self._actions and
difflib.get_close_matches) check the next element in unknown_args: if it exists
and does not start with "-" treat it as the value for the current unknown flag
and advance the loop one extra step so only one diagnostic is printed for the
pair. Ensure suggestion logic (difflib.get_close_matches) still applies to the
flag token only.
| - **Flexible targets**: Accepts single IPv4s, IP ranges, CIDR blocks, domain names, and full HTTP/HTTPS URLs. | ||
| These methods ensure full compatibility and avoid runtime errors |
There was a problem hiding this comment.
Keep the explanation inside the list item.
Line 31 is not indented as a continuation of the bullet, so GitHub renders it as a separate paragraph instead of part of Flexible targets.
📝 Suggested fix
- **Flexible targets**: Accepts single IPv4s, IP ranges, CIDR blocks, domain names, and full HTTP/HTTPS URLs.
-These methods ensure full compatibility and avoid runtime errors
+ These methods ensure full compatibility and avoid runtime errors.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - **Flexible targets**: Accepts single IPv4s, IP ranges, CIDR blocks, domain names, and full HTTP/HTTPS URLs. | |
| These methods ensure full compatibility and avoid runtime errors | |
| - **Flexible targets**: Accepts single IPv4s, IP ranges, CIDR blocks, domain names, and full HTTP/HTTPS URLs. | |
| These methods ensure full compatibility and avoid runtime errors. |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@README.md` around lines 30 - 31, The continuation text for the "Flexible
targets" bullet is on its own line and not indented, so GitHub renders it as a
separate paragraph; update the README so the continuation is part of the same
list item by either appending the sentence to the same line as "**Flexible
targets**: Accepts ..." or indenting the following line by two spaces (or adding
a trailing two-space line break on the previous line) so the sentence remains
inside the bullet; locate the "**Flexible targets**" list item in README.md and
adjust the following line accordingly.
There was a problem hiding this comment.
♻️ Duplicate comments (2)
nettacker/core/arg_parser.py (2)
33-47:⚠️ Potential issue | 🟡 MinorLocalize the epilog examples too.
descriptionnow goes through_(), but the new examples are still a hardcoded English block. Localized installs will end up with mixed-language help unless the whole multi-lineepilogis translated as one string soRawTextHelpFormatterkeeps the layout.Suggested fix
- epilog=(""" + epilog=_( + """ Examples: Scan a target: python nettacker.py -i 192.168.1.1 Scan multiple targets: python nettacker.py -l targets.txt Run specific module: python nettacker.py -i example.com -m port_scan Show all modules: python nettacker.py --show-all-modules - """), + """ + ),🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@nettacker/core/arg_parser.py` around lines 33 - 47, The epilog multi-line string in arg_parser (the epilog argument passed to ArgumentParser / parser creation) is hardcoded in English; wrap the entire multi-line epilog in the translation function _(…) so the RawTextHelpFormatter preserves layout while allowing localization. Update the epilog value (the epilog passed into the ArgumentParser call in nettacker/core/arg_parser.py) to call _() around the triple-quoted string and ensure any surrounding RawTextHelpFormatter usage remains unchanged so formatting is preserved.
547-565:⚠️ Potential issue | 🟡 MinorTreat an unknown long option and its value as one error.
--targtes 192.168.1.1still emits two diagnostics because the loop advances one token at a time. Walkunknown_argsby index and skip the following non-flag token when it belongs to the bad option.Suggested fix
- for arg in unknown_args: + index = 0 + while index < len(unknown_args): + arg = unknown_args[index] if arg.startswith("--") and len(arg) > 1: suggestion = difflib.get_close_matches(arg, valid_flags, n=1) if suggestion: print( f"Error: Unknown argument '{arg}'. Did you mean '{suggestion[0]}'?", file=sys.stderr, ) else: print(f"Error: Unknown argument '{arg}'", file=sys.stderr) + if ( + "=" not in arg + and index + 1 < len(unknown_args) + and not unknown_args[index + 1].startswith("-") + ): + index += 1 else: print(f"Error: Unexpected argument '{arg}'", file=sys.stderr) + index += 1🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@nettacker/core/arg_parser.py` around lines 547 - 565, The loop over unknown_args currently iterates token-by-token and flags an unknown long option and its attached value as two separate errors; update the handling in the block that checks unknown_args (referencing unknown_args and self._actions) to iterate by index (i = 0..len-1) and when you encounter a token starting with "--" treat the next token as its value if it exists and does not start with "-" (i+1 < len and not unknown_args[i+1].startswith("-")), produce a single error/suggestion for the option (using difflib.get_close_matches as before) and increment the index to skip the value token so it is not reported separately; keep other branches (short flags and unexpected args) unchanged and still exit with sys.exit(1).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@nettacker/core/arg_parser.py`:
- Around line 33-47: The epilog multi-line string in arg_parser (the epilog
argument passed to ArgumentParser / parser creation) is hardcoded in English;
wrap the entire multi-line epilog in the translation function _(…) so the
RawTextHelpFormatter preserves layout while allowing localization. Update the
epilog value (the epilog passed into the ArgumentParser call in
nettacker/core/arg_parser.py) to call _() around the triple-quoted string and
ensure any surrounding RawTextHelpFormatter usage remains unchanged so
formatting is preserved.
- Around line 547-565: The loop over unknown_args currently iterates
token-by-token and flags an unknown long option and its attached value as two
separate errors; update the handling in the block that checks unknown_args
(referencing unknown_args and self._actions) to iterate by index (i = 0..len-1)
and when you encounter a token starting with "--" treat the next token as its
value if it exists and does not start with "-" (i+1 < len and not
unknown_args[i+1].startswith("-")), produce a single error/suggestion for the
option (using difflib.get_close_matches as before) and increment the index to
skip the value token so it is not reported separately; keep other branches
(short flags and unexpected args) unchanged and still exit with sys.exit(1).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: dc182602-cbb6-464c-a5c5-4139575c409b
📒 Files selected for processing (1)
nettacker/core/arg_parser.py
| """ | ||
| # Checking Requirements | ||
| options = self.api_arguments or self.parse_args() | ||
| if self.api_arguments: |
There was a problem hiding this comment.
unrelated to what you're trying to do. Please remove this as this is not needed as well, argparser handles all the below.
|
@pandeysudarshan16-ctrl Please verify your commits. |
| - **CLI, REST API & Web UI** - Offers both programmatic integration and a user-friendly web interface for defining scans and viewing results. | ||
| - **Evasion techniques** - Enables configurable delays, proxy support, and randomized user-agents to reduce detection by firewalls or IDS systems. | ||
| - **Flexible targets** - Accepts single IPv4s, IP ranges, CIDR blocks, domain names, and full HTTP/HTTPS URLs. Targets can be mixed in a single command or loaded from a file using the `-l/--targets-list` flag. | ||
| - **Flexible targets**: Accepts single IPv4s, IP ranges, CIDR blocks, domain names, and full HTTP/HTTPS URLs. |
There was a problem hiding this comment.
why are you deleting a perfectly valid documentation line to squeeze in an advertisement for your proposal?
Proposed Change
Improved CLI usability by enhancing the help output with clear descriptions and practical usage examples.
Why this change is needed
Currently, the CLI help output lacks examples, making it harder for new users to understand how to use the tool effectively.
What is added
Type of change
Checklist