Skip to content

fix(events): cap stdin in the generated dispatcher, not just the CLI command - #4337

Open
Noor-ul-ain001 wants to merge 1 commit into
github:mainfrom
Noor-ul-ain001:fix/events-dispatcher-stdin-cap
Open

fix(events): cap stdin in the generated dispatcher, not just the CLI command#4337
Noor-ul-ain001 wants to merge 1 commit into
github:mainfrom
Noor-ul-ain001:fix/events-dispatcher-stdin-cap

Conversation

@Noor-ul-ain001

Copy link
Copy Markdown
Contributor

Summary

  • The fix: cap stdin read at 1 MiB to prevent DoS #3857 fix capped stdin at 1 MiB in specify event run (src/specify_cli/commands/event.py), but that CLI command is not the code path native hooks actually invoke.
  • Every installed integration writes a self-contained .specify/events.py dispatcher (the _EVENTS_DISPATCHER_TEMPLATE string in src/specify_cli/events.py), and native hook configs call that dispatcher directly. Its main() did:
    payload = sys.stdin.read() if not sys.stdin.isatty() else "{}"
    with no size cap at all — the exact DoS fix: cap stdin read at 1 MiB to prevent DoS #3857 was meant to close, wide open on the primary invocation path. specify event run is a secondary/manual entry point; the generated dispatcher is what actually runs on every session_start/pre_tool_use/etc. hook fire in real usage.
  • Fix: apply the same byte-capped read (from the binary buffer, so the cap counts encoded bytes rather than decoded characters) inside the dispatcher template, so every newly-installed or refreshed dispatcher enforces the limit.

Test plan

  • Added 3 tests in tests/integrations/test_events.py::TestCommandRunner: an oversized payload exits 1 with the limit message instead of running unbounded, a multibyte payload (~300k emoji, ~1.14 MiB UTF-8 but only 300k characters) is still rejected by the byte-based cap, and a normal under-the-cap payload still reaches the handler script unchanged.
  • Verified both new tests fail without the fix (test-the-test): the oversized-payload test failed because the dispatcher silently accepted the full payload and returned "not found" instead of exiting 1 with the limit message — reproducing the exact bug.
  • Ran the full tests/integrations/test_events.py suite: 124/128 pass; the remaining 4 are pre-existing Windows symlink-elevation failures unrelated to this change.

Co-Authored-By: Claude Sonnet 5 noreply@anthropic.com

https://claude.ai/code/session_01PJHJ2dHP2RVCNncHqN8Qm9

…command

The github#3857 fix capped stdin at 1 MiB in `specify event run`
(src/specify_cli/commands/event.py), but that command is not the code path
native hooks actually invoke. Every installed integration writes a
self-contained `.specify/events.py` dispatcher (the
`_EVENTS_DISPATCHER_TEMPLATE` string in src/specify_cli/events.py) that
native hook configs call directly, and its `main()` did:

    payload = sys.stdin.read() if not sys.stdin.isatty() else "{}"

with no size cap at all — the exact DoS github#3857 was meant to close, wide open
on the primary invocation path. `specify event run` is a secondary/manual
entry point; the generated dispatcher is what actually runs on every
session_start/pre_tool_use/etc. hook fire in real usage.

Fix: apply the same byte-capped read (from the binary buffer, so the cap
counts encoded bytes rather than decoded characters — matching the
just-merged fix for the CLI command) inside the dispatcher template, so
every newly-installed or refreshed dispatcher enforces the limit.

## Test plan
- Added 3 tests in tests/integrations/test_events.py::TestCommandRunner:
  an oversized payload exits 1 with the limit message instead of running
  unbounded, a multibyte payload (~300k emoji, ~1.14 MiB UTF-8 but only
  300k characters) is still rejected by the byte-based cap, and a normal
  under-the-cap payload still reaches the handler script unchanged.
- Verified both new failing-without-fix tests via test-the-test (stashed
  the src fix): the oversized-payload test failed because the dispatcher
  silently accepted the full payload and returned "not found" instead of
  exiting 1 with the limit message — reproducing the exact bug.
- Ran the full tests/integrations/test_events.py suite (124/128 pass; the
  remaining 4 are the pre-existing Windows symlink-elevation failures
  unrelated to this change).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PJHJ2dHP2RVCNncHqN8Qm9

Copilot AI left a comment

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.

🟡 Changes recommended

Downstream locale-based encoding can corrupt UTF-8 payloads on Windows.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Caps generated event-dispatcher stdin at 1 MiB to mitigate memory-exhaustion attacks.

Changes:

  • Adds byte-based stdin limiting to generated dispatchers.
  • Adds oversized, multibyte, and valid-payload tests.
File summaries
File Description
src/specify_cli/events.py Enforces the dispatcher stdin limit.
tests/integrations/test_events.py Tests dispatcher payload limits.
Review details
  • Files reviewed: 2/2 changed files
  • Comments generated: 1
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/specify_cli/events.py
file=sys.stderr,
)
sys.exit(1)
payload = raw.decode("utf-8")

@mnriem mnriem left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Please address Copilot feedback

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