Skip to content

Enhance CLI error handling with argument suggestions#1445

Open
pandeysudarshan16-ctrl wants to merge 5 commits intoOWASP:masterfrom
pandeysudarshan16-ctrl:feature-cli-arg-suggestions-v2
Open

Enhance CLI error handling with argument suggestions#1445
pandeysudarshan16-ctrl wants to merge 5 commits intoOWASP:masterfrom
pandeysudarshan16-ctrl:feature-cli-arg-suggestions-v2

Conversation

@pandeysudarshan16-ctrl
Copy link
Copy Markdown

Proposed Change

This PR improves CLI usability by detecting unknown arguments and suggesting the closest valid flags.

Why this change is needed

Currently, when users pass an invalid CLI argument, the tool fails without clear guidance. This can confuse users and make debugging harder.

What is added

  • Detection of unknown CLI arguments using parse_known_args
  • Suggestion of closest valid flags using difflib
  • Clear and actionable error messages for invalid inputs

Example

Error: Unknown argument '--ip'. Did you mean '-i'?

Type of change

  • Other improvement (usability / UX)

Checklist

  • I have followed the contributing guidelines
  • I have tested the feature locally
  • No breaking changes introduced

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR aims to improve the Nettacker CLI UX by detecting unknown CLI arguments and suggesting the closest valid flags, instead of failing without guidance.

Changes:

  • Switches argument parsing to parse_known_args() to capture unknown args.
  • Uses difflib.get_close_matches() to suggest the nearest valid option flag for unknown flags.
  • Adds user-facing error output for invalid CLI inputs.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

"--set-hardware-usage",
action="store",
dest="set_hardware_usage",
deimport difflibst="set_hardware_usage",
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add_argument for --set-hardware-usage has a typo in the keyword argument name (deimport difflibst) which will raise a TypeError at runtime and prevents the option from being parsed. This should be dest="set_hardware_usage".

Suggested change
deimport difflibst="set_hardware_usage",
dest="set_hardware_usage",

Copilot uses AI. Check for mistakes.
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]}'?")
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indentation on the print(...) line is inconsistent with the surrounding block (it’s indented less than the if suggestion: body). This will raise an IndentationError and break CLI parsing.

Suggested change
print(f"Error: Unknown argument '{arg}'. Did you mean '{suggestion[0]}'?")
print(f"Error: Unknown argument '{arg}'. Did you mean '{suggestion[0]}'?")

Copilot uses AI. Check for mistakes.
Comment on lines +531 to +538
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]}'?")
else:
print(f"Error: Unknown argument '{arg}'")

Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If an unknown argument is positional (doesn’t start with -), the code still exits with status 1 but prints no error message. Ensure all entries in unknown_args are surfaced to the user (positional and flags), ideally in a single consolidated message.

Suggested change
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]}'?")
else:
print(f"Error: Unknown argument '{arg}'")
error_messages = []
for arg in unknown_args:
# Flag-like unknown argument (starts with "-" and not just "-")
if arg.startswith("-") and len(arg) > 1:
suggestion = difflib.get_close_matches(arg, valid_flags, n=1)
if suggestion:
error_messages.append(
f" {arg} (did you mean '{suggestion[0]}'?)"
)
else:
error_messages.append(f" {arg}")
# Positional unknown argument
else:
error_messages.append(f" {arg} (positional argument)")
if error_messages:
print(
"Error: Unknown arguments:\n" + "\n".join(error_messages)
)

Copilot uses AI. Check for mistakes.
Comment on lines +531 to +540
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]}'?")
else:
print(f"Error: Unknown argument '{arg}'")

sys.exit(1)

Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This path uses print(...) + sys.exit(1) for error handling, which bypasses the project’s existing die_failure() behavior (logging + color reset) and prints to stdout instead of stderr. Consider using die_failure(...) (or ArgumentParser.error(...) if you want argparse-style usage output) and include the suggestion text in that message.

Suggested change
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]}'?")
else:
print(f"Error: Unknown argument '{arg}'")
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 argument '{arg}'. Did you mean '{suggestion[0]}'?"
)
else:
error_messages.append(f"Error: Unknown argument '{arg}'")
if error_messages:
die_failure("\n".join(error_messages))

Copilot uses AI. Check for mistakes.
Comment on lines +524 to +541
known_args, unknown_args = self.parse_known_args()

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]}'?")
else:
print(f"Error: Unknown argument '{arg}'")

sys.exit(1)

options = known_args
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New behavior around unknown-argument detection/suggestion is introduced here, but there are no existing tests covering ArgParser behavior. Please add pytest coverage for cases like an unknown flag with a close match (suggestion emitted) and an unknown positional argument (error message emitted).

Copilot uses AI. Check for mistakes.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 23, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 790bda5c-d05d-4254-a3d3-e656f580a928

📥 Commits

Reviewing files that changed from the base of the PR and between eea1fbe and 634964e.

📒 Files selected for processing (1)
  • nettacker/core/arg_parser.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • nettacker/core/arg_parser.py

Summary by CodeRabbit

  • New Features

    • CLI now suggests the closest matching flag when an unrecognized option is provided.
  • Bug Fixes

    • Unrecognized or malformed arguments produce clearer "did you mean" suggestions or explicit errors and now cause immediate termination to avoid ambiguous behavior.
    • Argument parsing more strictly separates recognized inputs from extras to prevent unintended processing.
  • Documentation

    • Updated "Flexible targets" README entry for clearer description and compatibility guidance.

Walkthrough

Reworks CLI parsing to prefer provided api_arguments or use parse_known_args() to separate known/unknown flags, adds difflib-based closest-flag suggestions for unknown -- options and exits on errors. README "Flexible targets" bullet text/formatting was adjusted.

Changes

Cohort / File(s) Summary
Argument parser
nettacker/core/arg_parser.py
Switched parsing to use self.api_arguments when present or parse_known_args() otherwise; gathers unrecognized tokens, suggests a single close -- flag via difflib.get_close_matches(..., n=1), prints errors for unexpected args and calls sys.exit(1) when unknown args exist; sets options = known_args on success.
Documentation
README.md
Reworded "Flexible targets" bullet: removed the -l/--targets-list flag mention, adjusted line breaks/formatting, and replaced the trailing sentence with "These methods ensure full compatibility and avoid runtime errors".

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Enhance CLI error handling with argument suggestions' directly and clearly summarizes the main change: improving error handling by adding argument suggestions.
Description check ✅ Passed The description is directly related to the changeset, explaining the motivation, implementation details, and providing a concrete example of the new feature.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 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 384-389: Replace the malformed argparse keyword on the
"--set-hardware-usage" argument: change the invalid token `deimport
difflibst="set_hardware_usage"` to the correct argparse parameter
`dest="set_hardware_usage"` so the parser option uses the intended destination
name (refer to the argument declaration for "--set-hardware-usage" and the
variable Config.settings.set_hardware_usage).
- Around line 526-539: The unknown_args handling in the arg parser currently
only reports unknown tokens that start with '-' and silently drops bare
positional leftovers; update the block that iterates unknown_args (in the method
using parse_known_args where unknown_args and self._actions are used) to emit an
error for every leftover token (both flag-like and bare positional tokens) and
write errors to stderr (use print(..., file=sys.stderr) or sys.stderr.write).
Keep the existing suggestion logic that uses difflib.get_close_matches against
valid_flags (from self._actions) for flag-like tokens, but for bare tokens print
a clear stderr error such as "Error: Unexpected positional argument '...'"
before calling sys.exit(1).
🪄 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: 2df58cdb-16e5-4cba-aa57-7ced954fb767

📥 Commits

Reviewing files that changed from the base of the PR and between 74d89e2 and 2a25a98.

📒 Files selected for processing (1)
  • nettacker/core/arg_parser.py

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
nettacker/core/arg_parser.py (1)

532-533: ⚠️ Potential issue | 🟡 Minor

Include single-dash flags in the suggestion path.

Line 532 only treats --... tokens as flag-like, so typos like -I fall through as generic unexpected arguments and never get a closest-flag hint.

💡 Proposed fix
-                    if arg.startswith("--") and len(arg) > 1:
+                    if arg.startswith("-") and len(arg) > 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 532 - 533, The current check only
treats double-dash flags (arg.startswith("--")) when computing suggestions with
difflib.get_close_matches, so single-dash flags like "-I" never get a
closest-flag hint; update the condition that precedes suggestion creation (the
branch using arg, valid_flags, and difflib.get_close_matches) to include
single-dash tokens as well (e.g., treat any arg that starts with "-" and has
length > 1) so that single-dash typos also trigger suggestion lookup.
🤖 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 535-542: The three hardcoded English messages printed for
unknown/unexpected args should be routed through the i18n catalog and the normal
CLI failure path: replace the direct print(...) calls that reference arg and
suggestion (the branches using suggestion[0], the else for "Unknown argument"
and the "Unexpected argument" branch) so the message strings are wrapped with
_(...) and passed to the existing failure handler used elsewhere (instead of raw
print), e.g. build the localized message with _("Error: Unknown argument
'{arg}'. Did you mean '{suggestion}'?").format(...) or _("Error: Unexpected
argument '{arg}'").format(...) and forward that to the same failure function or
parser.error call used by the parser.

---

Duplicate comments:
In `@nettacker/core/arg_parser.py`:
- Around line 532-533: The current check only treats double-dash flags
(arg.startswith("--")) when computing suggestions with
difflib.get_close_matches, so single-dash flags like "-I" never get a
closest-flag hint; update the condition that precedes suggestion creation (the
branch using arg, valid_flags, and difflib.get_close_matches) to include
single-dash tokens as well (e.g., treat any arg that starts with "-" and has
length > 1) so that single-dash typos also trigger suggestion lookup.
🪄 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: c77b078a-2435-46f4-a0d6-e38e87bffc6d

📥 Commits

Reviewing files that changed from the base of the PR and between ad5461b and 46280fa.

📒 Files selected for processing (1)
  • nettacker/core/arg_parser.py

Comment on lines +535 to +542
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)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Localize the new CLI error messages.

These strings are hardcoded in English, so unknown-argument errors bypass the existing message catalog even though the CLI supports -L/--language. Please route them through _() / the normal 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 535 - 542, The three hardcoded
English messages printed for unknown/unexpected args should be routed through
the i18n catalog and the normal CLI failure path: replace the direct print(...)
calls that reference arg and suggestion (the branches using suggestion[0], the
else for "Unknown argument" and the "Unexpected argument" branch) so the message
strings are wrapped with _(...) and passed to the existing failure handler used
elsewhere (instead of raw print), e.g. build the localized message with
_("Error: Unknown argument '{arg}'. Did you mean '{suggestion}'?").format(...)
or _("Error: Unexpected argument '{arg}'").format(...) and forward that to the
same failure function or parser.error call used by the parser.

@pandeysudarshan16-ctrl pandeysudarshan16-ctrl force-pushed the feature-cli-arg-suggestions-v2 branch from d4594f6 to 249b8fc Compare March 23, 2026 21:46
@pandeysudarshan16-ctrl
Copy link
Copy Markdown
Author

Hi maintainers,
I've addressed all review comments and ensured commits are signed.
Kindly review when possible. Thank you!

- **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.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's true but not for this PR.


if unknown_args:
valid_flags = []
for action in self._actions:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

self._actions is not defined anywhere. This code will break.

@pandeysudarshan16-ctrl
Copy link
Copy Markdown
Author

Hi, I’ve submitted my GSoC proposal focused on CLI usability and error handling improvements. I would really appreciate any feedback from the maintainers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants