Add command-signatures list CLI with graceful empty-input handling - #394
Add command-signatures list CLI with graceful empty-input handling#394warp-factories[bot] wants to merge 3 commits into
Conversation
Adds a command-signatures binary with a list subcommand that summarizes Fig-compatible command signatures, either the embedded repository signatures or an external --file document. Implements the approved spec for issue #377 (specs/GH377 on PR #379): - --file accepts one Command object or an array of them; empty bytes, whitespace-only input, [], and {} are all successful empty results. - External input is bounded before full JSON materialization: 10 MiB max size, JSON nesting depth 64, and 10000 top-level commands, using a bounded reader, a string/escape-aware depth preflight, and a streaming Serde sequence visitor for the array case. - Deterministic sorted text table and --json output. - Listing never executes generators or other shell commands. - CLI misuse (via clap) exits 2; input errors exit 1 with the exact diagnostics from the spec; success exits 0. Adds unit tests for the listing module and CLI integration tests covering the empty-input grammar, resource limits, and exit codes.
|
This PR was generated with Warp. Comment |
…errors Two important findings from code review on PR #394: 1. Output injection (security): only descriptions were normalized before being written to the TSV text output; an external command name containing a tab or newline could forge extra columns or rows. Names now go through the same control-character normalization as descriptions. Added a CLI regression test with control characters in both fields, asserting the output has exactly one header line, one data line, and two tab separators. 2. Error-variant spoofing: TooManyCommands was previously detected by checking whether the formatted serde_json error text contained a marker string, which let attacker-controlled field values (e.g. a schema-invalid 'priority' string containing that marker) forge a TooManyCommands diagnostic instead of the correct parse error. Replaced the marker string with a Cell<bool> side channel set only by the bounded sequence visitor's own overflow check, so the error variant is chosen by trusted Rust state instead of untrusted formatted text. Added a regression test using a schema-invalid document that embeds the old marker text, asserting it still yields a parse error.
…/U+2029 Follow-up to the previous review fix: normalize_text_field only replaced tab, CR, and LF, leaving ESC, vertical tab, form feed, other C0/C1 controls, and the Unicode line/paragraph separators (U+2028/U+2029) able to affect terminal or text-renderer layout when sourced from untrusted --file content. ESC in particular could emit terminal escape sequences. Now replaces every char::is_control() character plus U+2028 and U+2029 with a space. Extended the CLI regression test to cover ESC, vertical tab, form feed, and both Unicode separators in both the name and the description, built via serde_json::json! for correct JSON escaping, and added an explicit assertion that no ESC byte reaches stdout.
There was a problem hiding this comment.
Overview
Adds a command-signatures list CLI and a public listing API over the embedded specs, with a bounded --file override and explicitly specified non-panicking behavior for degenerate input — the defect reported in #377. The factory's review is accepted after three passes.
Concerns
.gitignoreandCargo.lock: this change stops ignoringCargo.lockand commits its ~1,265 lines, which is outside the approved spec's footprint and departs from prior practice in this repo even though a binary already existed. Committing a lockfile is defensible for a shipped executable, but it is a repository-policy call rather than an implementation defect, so it is left for a maintainer to keep or split out.- CI note:
test,lint,format,Analyze (actions),Analyze (python)and the CLA check pass atbbd95f0;Analyze (rust)was still queued when this review was written and should be confirmed green before merge.
Verdict: accepted
Three issues were found and fixed during review, all in the untrusted-input path that this issue exists to harden:
- Output injection. Command names from
--filereached the TSV output unnormalized, so a name containing a tab or newline could forge columns or rows. Names now pass through the same normalizer as descriptions. - Error-variant spoofing. The parser selected its error variant by string-matching formatted error text, so a schema-invalid document embedding the marker string could masquerade as the
TooManyCommandslimit error. Replaced with a typed overflow signal set only by trusted code. - Under-broad normalization. The normalizer stripped only LF, CR and tab, leaving ESC, VT, FF and U+2028/U+2029 able to affect terminal rendering. It now replaces all
char::is_control()characters plus the two Unicode separators.
Verified by source review at bbd95f0: the bounded 10 MiB reader, the escape-aware depth preflight and its 64/65 boundary, the streaming visitor's 10,000/10,001 boundary, that listing never executes a generator, and that the existing AGENTS.md invariants (generator name resolution, spec deserialization, no unquoted newlines) remain intact.
Checks: build pass, tests pass, CI green except one queued job, visual proof n/a
Found: 0 critical, 0 important, 0 suggestions, 0 nits
This review is advisory. Merging is a maintainer's decision.
Responding as Warp for OSS: View run
|
Thanks for the review @captainsafia. Taking this out of draft now that it's approved — all checks are green and the branch is mergeable and clean, so the draft state was the only thing left blocking a merge. Merging remains entirely your call; the factory won't merge or close it. Responding as Warp for OSS: View run |



Summary
Implements the approved spec for #377 (spec PR #379): a
command-signatures listCLI that summarizes Fig-compatible command signatures without panicking on degenerate input.command-signaturesbinary (required-features = ["embed-signatures"]) with alist [--file <PATH>] [--json]subcommand, built onclap.warp_command_signatures::listing(list_signatures,SignatureSource,SignatureSummary,ListSignaturesError) reused by the CLI and covered by its own unit tests.--fileaccepts one Fig-compatibleCommandobject or an array of them. Empty bytes, whitespace-only input,[], and the exact empty object{}are all successful, empty results (exit 0, "No signatures found." /[]), never an error or panic.--jsonarray.clap).Cargo.lockis now tracked, per the repository's own.gitignoreguidance for crates that build an executable.Does not modify anything under
command-signatures/json/,completion-metadata/, or the PowerShell generator.Validation
Run from a Rust 1.92 toolchain (matching
rust-toolchain.toml):cargo build --workspace --all-targets --all-features— succeeds.cargo test --workspace --all-features— 198 + 15 tests pass (listing unit tests, CLI integration tests incommand-signatures/tests/cli_list.rs, and the full existing suite, including theAGENTS.mdinvariants: all referenced generators/aliases resolve, all embedded specs deserialize, and generator commands have no unsafe unquoted newlines).cargo fmt -p warp-command-signatures -p warp-completion-metadata --check— clean.cargo clippy -p warp-command-signatures -p warp-completion-metadata --all-targets --all-features -- -D warnings— clean.panicked at/index out of boundstext on stderr for: empty file, whitespace-only file,[],{},{"name":[]}, malformed JSON, non-empty object missingname, a nonexistent path, an oversized (10 MiB + 1) file, depth-65 JSON, an array with 10,001 commands, valid single-object/array inputs, the default embedded source, an unknown subcommand, and a missing--filevalue.npm run format:check) is unaffected — no files undercommand-signatures/json/were changed.Open concerns
None. The spec's open questions (source enum ownership, test dependency choice) were both resolved in favor of the spec's own recommendations (
PathBuf-owningSignatureSource::File,tempfilefor fixtures).Related: #377 (not closing automatically — a maintainer should confirm the fix is complete before closing).