Skip to content

requires_root was declared on four tools and enforced on none - #102

Merged
Nerds489 merged 1 commit into
mainfrom
fix/tool-root-enforcement
Sep 26, 2026
Merged

Nerds489 merged 1 commit into
mainfrom
fix/tool-root-enforcement

Conversation

@Nerds489

Copy link
Copy Markdown
Owner

What

requires_root=True was declared on four tool wrappers (reaver, airodump, masscan, aireplay) and enforced nowhere. A non-root run reached the process seam and failed there with the tool's own cryptic error, far from the cause and looking like a bug rather than a missing sudo. Same class #90 fixed for nmap, un-fixed for these four.

Fix

A single gate in BaseToolWrapper.execute, via a new needs_root() that defaults to METADATA.requires_root, so the declared flag is enforced for every tool and any future one:

  • Refused up front with a message that names the binary, before the spawn.
  • Dry run exempt (it spawns nothing).
  • nmap keeps its per-invocation override on top (it sets requires_root=False and decides from the scan type), so the two do not collide.

masscan is the plainest beneficiary, a direct CLI command (netreaper ... masscan -> MasscanTool().execute): it now gives a clean "requires root privileges; re-run with sudo" instead of masscan's "could not determine default interface".

Tests

  • test_tool_root_enforcement.py (new): the four tools report their root need through needs_root; an unprivileged run is refused before the seam and names the binary; dry-run and root runs are not gated; a tool without the flag is never gated.
  • The _Denying double in test_static_analysis_findings.py now carries METADATA, as a real wrapper does, since execute reads it.

Full suite 1042 passed. Bump 12.1.7 -> 12.1.8.

reaver, airodump, masscan and aireplay carried requires_root=True in their
METADATA and nothing ever read it: a non-root run reached the process seam and
failed there with masscan's "could not determine default interface" or
aircrack's terse "Operation not permitted", far from the cause and looking like
a bug rather than a missing sudo. masscan is the plainest case, a direct CLI
command (netreaper ... masscan -> MasscanTool().execute), exactly the shape #90
fixed for nmap.

The gate now lives once in BaseToolWrapper.execute via a new needs_root, which
defaults to METADATA.requires_root, so the declared flag is enforced for every
tool and any future one. A dry run is exempt (it spawns nothing), and nmap keeps
its per-invocation override on top (it sets requires_root=False and decides from
the scan type), so the two do not collide.

test_tool_root_enforcement.py pins the four tools' root need and the gate's
behaviour; the _Denying double in test_static_analysis_findings now carries
METADATA, as a real wrapper does, since execute reads it.

Bump 12.1.7 -> 12.1.8.
@Nerds489
Nerds489 merged commit 4be1964 into main Sep 26, 2026
2 checks passed
@Nerds489
Nerds489 deleted the fix/tool-root-enforcement branch September 26, 2026 02:40
Nerds489 added a commit that referenced this pull request Sep 26, 2026
An audit of the manifest cost-flags after #46 confirmed destructive and
requires_confirmation are enforced end to end: BaseToolWrapper.execute passes
destructive to the scope gate, and plan_exec authorises both where the manifest
and dispatch meet. needs_hardware is the one cost-flag still display-only, and
that is by design, not the #90/#102 defect repeated.

Unlike the other two it is a capability prerequisite, not an authorisation: the
leaf fails on its own when the adapter or interface is absent, no generic check
settles "hardware present" the way os.geteuid() settles root, and a
planning-only manifest already errors above. Folding it into the gate would
force an engagement onto every hardware chain for no safety gain.

Record the decision where the question arises: a note in plan_exec next to the
#46 enforcement, and test_needs_hardware_alone_does_not_gate alongside the tests
that pin the other two flags. The test fails if someone adds needs_hardware to
the gate condition, which is the reminder to read the note first.

Bump 12.1.8 -> 12.1.9.
Nerds489 added a commit that referenced this pull request Sep 26, 2026
aircrack-ng declared needs_capture_file=True and the preflight's ToolContext had
a matching capture_file slot, but PreflightRunner.prepare_tool never resolved it
(root/target/interface/wordlist/api_key/gpu all are) and nothing read the slot.
aircrack-ng is driven from the CLI and the chain with its .cap passed in, so it
never reaches the preflight: the flag connected to nothing, the dangling-
declaration shape of #90/#102.

Remove the dead flag, its aircrack-ng setter and the unused ToolContext slot, and
add test_requirement_flags_enforced: every needs_* field on ToolRequirement must
be read by the preflight or named carried-for-documentation (only needs_network).
It caught needs_capture_file.

Bump 12.1.9 -> 12.1.10.
Nerds489 added a commit that referenced this pull request Oct 1, 2026
An audit of the manifest cost-flags after #46 confirmed destructive and
requires_confirmation are enforced end to end: BaseToolWrapper.execute passes
destructive to the scope gate, and plan_exec authorises both where the manifest
and dispatch meet. needs_hardware is the one cost-flag still display-only, and
that is by design, not the #90/#102 defect repeated.

Unlike the other two it is a capability prerequisite, not an authorisation: the
leaf fails on its own when the adapter or interface is absent, no generic check
settles "hardware present" the way os.geteuid() settles root, and a
planning-only manifest already errors above. Folding it into the gate would
force an engagement onto every hardware chain for no safety gain.

Record the decision where the question arises: a note in plan_exec next to the
#46 enforcement, and test_needs_hardware_alone_does_not_gate alongside the tests
that pin the other two flags. The test fails if someone adds needs_hardware to
the gate condition, which is the reminder to read the note first.

Bump 12.1.8 -> 12.1.9.
Nerds489 added a commit that referenced this pull request Oct 1, 2026
aircrack-ng declared needs_capture_file=True and the preflight's ToolContext had
a matching capture_file slot, but PreflightRunner.prepare_tool never resolved it
(root/target/interface/wordlist/api_key/gpu all are) and nothing read the slot.
aircrack-ng is driven from the CLI and the chain with its .cap passed in, so it
never reaches the preflight: the flag connected to nothing, the dangling-
declaration shape of #90/#102.

Remove the dead flag, its aircrack-ng setter and the unused ToolContext slot, and
add test_requirement_flags_enforced: every needs_* field on ToolRequirement must
be read by the preflight or named carried-for-documentation (only needs_network).
It caught needs_capture_file.

Bump 12.1.9 -> 12.1.10.
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.

1 participant