Skip to content

Add tree-wide argv-validation hook / middleware #2327

@coilysiren

Description

@coilysiren

Summary

Add a middleware-style argv-validation hook on *cli.Command:

// OnArgValidate, if non-nil, is called once per argv before Action runs.
// Returning a non-nil error short-circuits Action and propagates as the
// command's exit error. Inherited by subcommands unless they set their
// own. Useful for security-boundary CLIs that reject shell metacharacters
// or other patterns before the user-supplied bytes reach an exec call.
OnArgValidate func(argv []string) error

(or equivalently, a cli.Middleware interface that allows wrapping any Action.)

Motivation

cli-guard is a security-boundary framework for urfave/cli that wraps every command's Action with a fixed pipeline: argv validation → action → audit log. The argv-validation step rejects shell metacharacters before they can reach execve in a passthrough wrapper.

Today this is achieved by wrapping each Action with a verb.Wrap() helper:

&cli.Command{
    Name:   "aws",
    Action: verb.Wrap(verb.Spec{
        Name:   "aws",
        Action: realAction,
    }, auditWriter),
}

Working but every command has to opt in by hand. A tree-wide middleware chain (or an OnArgValidate hook that subcommands inherit) would let consumers register the validation once at the root and have it apply automatically to every action in the tree.

This is the same shape as the "Before" hook, except:

  • Before is per-Command (no inheritance).
  • Before runs before Action but does not naturally short-circuit the action.
  • A Middleware interface is also more natural for the "wrap and observe" pattern (audit logging, tracing, metrics) than for the "validate and possibly reject" pattern.

Alternatives considered

  • Make Before inheritable. Cleaner but changes semantics; existing code that sets Before per-Command might be surprised.
  • Document the manual-wrap pattern (verb.Wrap-equivalent) as the supported approach. Works but is hand-pluming for every command.

Use cases beyond cli-guard

  • Tracing (OpenTelemetry span per command, propagated via context).
  • Metrics (Prometheus counter per command execution).
  • Authz (per-command role gating beyond what Hidden provides).

Happy to discuss API shape before sending a PR.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions