Skip to content

Harden InputHandler Against Event Flooding and Oversized Payload Attacks (Add Rate Limiting & Input Safeguards)#87

Merged
imxade merged 9 commits intoAOSSIE-Org:mainfrom
aniket866:fixing-input-validation
Feb 18, 2026
Merged

Harden InputHandler Against Event Flooding and Oversized Payload Attacks (Add Rate Limiting & Input Safeguards)#87
imxade merged 9 commits intoAOSSIE-Org:mainfrom
aniket866:fixing-input-validation

Conversation

@aniket866
Copy link
Contributor

@aniket866 aniket866 commented Feb 14, 2026

Addressed Issues:

Closes # 28

Why these changes are important

If a client keeps sending mouse move or scroll events very fast in a loop, the server handles all of them without limits. This can overload the system and make the Mac freeze or become very slow. Also, if someone sends a very large text message, it can use too much memory and crash the server.

fixed InputHandler.ts with changes to implement input validation (text length, coordinate bounds) and rate limiting (throttling) for high-frequency events.

Changes Applied:

Rate Limiting: Added a lastEventTime timestamp and a check at the beginning of handleMessage to throttle move and scroll events to approximately 60 FPS (~16ms).

Input Sanitation: Added a check to truncate msg.text to 500 characters.

Coordinate Validation: Added clamping for msg.dx and msg.dy to ensure they stay within a sane range (+/- 2000 pixels) before processing.

Checklist

  • My PR addresses a single issue, fixes a single bug or makes a single improvement.
  • My code follows the project's code style and conventions
  • If applicable, I have made corresponding changes or additions to the documentation
  • If applicable, I have made corresponding changes or additions to tests
  • My changes generate no new warnings or errors
  • I have joined the Discord server and I will share a link to this PR with the project maintainers there
  • I have read the Contribution Guidelines
  • Once I submit my PR, CodeRabbit AI will automatically review it and I will address CodeRabbit's comments.

⚠️ AI Notice - Important!

We encourage contributors to use AI tools responsibly when creating Pull Requests. While AI can be a valuable aid, it is essential to ensure that your contributions meet the task requirements, build successfully, include relevant tests, and pass all linters. Submissions that do not meet these standards may be closed without warning to maintain the quality and integrity of the project. Please take the time to understand the changes you are proposing and their impact.

@imxade Please review this , can be merged ,let me know if any changes required

Thankyou

Summary by CodeRabbit

  • Bug Fixes

    • Sanitized incoming input to cap text length and clamp out-of-range coordinates; added guards for invalid key/combo entries and improved error logging.
  • Performance

    • Throttled high-frequency move and scroll events with batching to reduce redundant processing and improve responsiveness.
  • Behavior

    • Made event processing more consistent and predictable for rapid or queued move, scroll, key, combo, and text interactions.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 14, 2026

Warning

Rate limit exceeded

@aniket866 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 4 minutes and 55 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📝 Walkthrough

Walkthrough

Added input sanitization and per-event throttling to InputHandler: text capped at 500 chars, dx/dy clamped to [-2000,2000], move/scroll events debounced/batched with ~8ms timers and pending-event state, plus normalized combo/key handling and extra logging.

Changes

Cohort / File(s) Summary
InputHandler core changes
src/server/InputHandler.ts
Added private timers/fields (lastMoveTime, lastScrollTime, pendingMove, pendingScroll, moveTimer, scrollTimer); centralized input validation (text length cap 500, clamp dx/dy to [-2000,2000]) before switch; introduced throttling/queuing for high-frequency move and scroll messages (~8ms); adjusted combo/key normalization and string-key checks; expanded error/logging paths and minor formatting tweaks.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I trim the text and bind the jump,
I cap the hops and quiet the thump,
Eight ms pauses, patience in store,
Batches of moves that nudge the floor,
A tidy stream — now run, explore!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically summarizes the main change: adding rate limiting and input safeguards to InputHandler to protect against event flooding and oversized payloads.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

@aniket866 aniket866 changed the title fixing-input-validation Harden InputHandler Against Event Flooding and Oversized Payload Attacks (Add Rate Limiting & Input Safeguards) Feb 14, 2026
Copy link
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

🤖 Fix all issues with AI agents
In `@src/server/InputHandler.ts`:
- Around line 30-32: The current clamping using MAX_COORD on msg.dx/msg.dy
allows NaN to pass through because Math.max/Math.min return NaN for non-numeric
inputs; update the InputHandler logic that mutates msg.dx and msg.dy to first
validate values with Number.isFinite or typeof === 'number' before clamping
(MAX_COORD), and if invalid either discard/normalize to 0 or skip assigning so
downstream calls like mouse.setPosition don't receive NaN; ensure you update the
branches that handle msg.dx and msg.dy accordingly.
- Around line 34-41: The current leading-edge throttle in InputHandler (using
lastEventTime and checking msg.type === 'move' || msg.type === 'scroll') drops
the trailing event and shares one timestamp across move and scroll; change it to
a per-event-type trailing-edge throttle: replace the single lastEventTime with
separate state (e.g., lastMoveEventTime, lastScrollEventTime and
pendingMoveEvent, pendingScrollEvent) and on a rapid burst, let the first event
go through immediately but if subsequent events are skipped schedule the last
skipped event via setTimeout (~16ms) to fire after the window expires; ensure
the dispatch logic references msg.type to select the correct timestamp/pending
slot so move and scroll throttling are independent.

aniket866 and others added 4 commits February 15, 2026 01:45
Copy link
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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/server/InputHandler.ts (1)

143-144: ⚠️ Potential issue | 🟠 Major

msg.delta is missing the same Number.isFinite guard applied to dx/dyNaN slips through.

NaN !== undefined and NaN !== 0 are both true, so a NaN delta enters the zoom block. Math.sign(NaN)NaN, making scaledDelta = NaN and amount = NaN. Since NaN !== 0 is true, keyboard.pressKey(Key.LeftControl) fires followed by mouse.scrollUp(NaN), leaving the system in an inconsistent state.

🐛 Proposed fix
-            if (msg.delta !== undefined && msg.delta !== 0) {
+            if (typeof msg.delta === 'number' && Number.isFinite(msg.delta) && msg.delta !== 0) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/server/InputHandler.ts` around lines 143 - 144, The zoom branch allows
NaN because it only checks msg.delta !== undefined and !== 0; update the
condition to mirror the dx/dy guards (use Number.isFinite) — i.e. change the
case 'zoom' guard to ensure Number.isFinite(msg.delta) && msg.delta !== 0 before
computing scaledDelta and amount (these variables inside the zoom handling use
Math.sign and will become NaN otherwise), and ensure you skip calling
keyboard.pressKey(Key.LeftControl) and mouse.scrollUp/scrollDown when the delta
is not a finite non-zero number. This keeps the existing variable names
(msg.delta, scaledDelta, amount) and the side-effect calls (keyboard.pressKey,
mouse.scrollUp) intact but prevents NaN from propagating.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/server/InputHandler.ts`:
- Line 174: The console.log that prints raw keystrokes (console.log(`Processing
key: ${msg.key}`)) in InputHandler should be removed or gated to avoid leaking
sensitive input; update the InputHandler message-processing code to either (a)
use a proper logger with debug-level checks (e.g., logger.debug(...) or if
(DEBUG) {...}) before emitting any key-related logs, or (b) redact the sensitive
field (log only masked info such as length or "<redacted>") instead of msg.key;
apply the same change to the similar logs in the combo handling branches
referencing msg.key/combo so production logs never contain plaintext keystrokes.
- Around line 17-22: Add a destroy() method to the InputHandler class that
cancels any pending throttle timers and clears pending messages and state: clear
and nullify moveTimer and scrollTimer (clearTimeout), set pendingMove and
pendingScroll to null, and reset any per-connection references/flags used by
handleMessage; then call inputHandler.destroy() from the websocket client close
handler (the ws.on('close') block) so pending timers won't fire after a client
disconnects. Ensure the destroy() method references the same fields shown
(moveTimer, scrollTimer, pendingMove, pendingScroll, lastMoveTime/lastScrollTime
if needed) and that no lingering closures can invoke handleMessage for a closed
connection.

---

Outside diff comments:
In `@src/server/InputHandler.ts`:
- Around line 143-144: The zoom branch allows NaN because it only checks
msg.delta !== undefined and !== 0; update the condition to mirror the dx/dy
guards (use Number.isFinite) — i.e. change the case 'zoom' guard to ensure
Number.isFinite(msg.delta) && msg.delta !== 0 before computing scaledDelta and
amount (these variables inside the zoom handling use Math.sign and will become
NaN otherwise), and ensure you skip calling keyboard.pressKey(Key.LeftControl)
and mouse.scrollUp/scrollDown when the delta is not a finite non-zero number.
This keeps the existing variable names (msg.delta, scaledDelta, amount) and the
side-effect calls (keyboard.pressKey, mouse.scrollUp) intact but prevents NaN
from propagating.

@aniket866
Copy link
Contributor Author

hi @imxade
can you assign someone to review this, I have resolved all the merge conflicts of this PR

@Rozerxshashank
Copy link
Contributor

Looks good to me!

Copy link
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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/server/InputHandler.ts (1)

143-157: ⚠️ Potential issue | 🟡 Minor

msg.delta is not guarded against NaN / non-numeric values, unlike dx/dy.

If msg.delta is NaN or a non-numeric type, it passes the !== undefined && !== 0 checks (since NaN !== 0 is true), and NaN propagates through Math.sign/Math.abs/Math.round into mouse.scrollUp(NaN) while LeftControl is held. Apply the same typeof + Number.isFinite guard used for dx/dy.

🐛 Proposed fix
-            case 'zoom':
-                if (msg.delta !== undefined && msg.delta !== 0) {
+            case 'zoom':
+                if (typeof msg.delta === 'number' && Number.isFinite(msg.delta) && msg.delta !== 0) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/server/InputHandler.ts` around lines 143 - 157, The zoom branch currently
checks only msg.delta !== undefined && msg.delta !== 0 which allows NaN or
non-numeric values through; change the guard to the same pattern used for dx/dy
(verify typeof msg.delta === 'number' && Number.isFinite(msg.delta') in the case
'zoom' block before computing scaledDelta/amount) so
Math.sign/Math.abs/Math.round never receive NaN; update the conditional around
the scaledDelta/amount computation (the code surrounding msg.delta,
sensitivityFactor, MAX_ZOOM_STEP, scaledDelta, amount) to bail out if the
numeric/finite check fails.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/server/InputHandler.ts`:
- Around line 43-78: The timer callbacks for moveTimer and scrollTimer call the
async method handleMessage(pending) without handling its returned Promise,
causing unhandled rejection risk; update both setTimeout callbacks (the ones
that reference this.moveTimer/this.scrollTimer and
this.pendingMove/this.pendingScroll) to call handleMessage(pending).catch(err =>
/* handle/log error */) (or await inside an async IIFE) so any rejection is
caught and logged/handled instead of crashing the process.

---

Outside diff comments:
In `@src/server/InputHandler.ts`:
- Around line 143-157: The zoom branch currently checks only msg.delta !==
undefined && msg.delta !== 0 which allows NaN or non-numeric values through;
change the guard to the same pattern used for dx/dy (verify typeof msg.delta ===
'number' && Number.isFinite(msg.delta') in the case 'zoom' block before
computing scaledDelta/amount) so Math.sign/Math.abs/Math.round never receive
NaN; update the conditional around the scaledDelta/amount computation (the code
surrounding msg.delta, sensitivityFactor, MAX_ZOOM_STEP, scaledDelta, amount) to
bail out if the numeric/finite check fails.

---

Duplicate comments:
In `@src/server/InputHandler.ts`:
- Around line 172-185: The console.log calls in the InputHandler handling
(specifically the 'key' case logging msg.key and similar logs in the 'combo'
handling) expose raw keystrokes; remove these plaintext logs or gate them behind
a controlled debug flag/log level. Update the code in the 'key' case (and
mirrored lines in the combo handling) to either delete the console.log calls or
replace them with a logger.debug(...) that only runs when a DEBUG/LOG_LEVEL flag
is enabled (e.g., check process.env.DEBUG or a configured
logger.isDebugEnabled()) and ensure no raw msg.key is emitted in production.
Ensure the change consistently touches the console.log usages where msg.key or
combo keystrokes are logged (e.g., the Processing key / Unmapped key messages
and the combo-case logs).
- Around line 17-22: Add a new destroy() method on InputHandler that clears
moveTimer and scrollTimer via clearTimeout if present, sets moveTimer and
scrollTimer to null, and also nulls pendingMove and pendingScroll to avoid
callbacks after disconnect; then ensure websocket.ts calls
inputHandler.destroy() from the ws.on('close') handler so no timers or pending
messages remain after client disconnect. Reference the existing fields
moveTimer, scrollTimer, pendingMove, pendingScroll and the new destroy() method
when implementing and invoking it.

Copy link
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 `@src/server/InputHandler.ts`:
- Around line 71-73: The error log in the scroll-timer catch currently prints
"Error processing pending move event" which is a copy-paste mistake; update the
console.error inside the this.handleMessage(pending).catch(...) block in
InputHandler.ts to reference "scroll event" (or include the actual event type
from pending, e.g., pending.type) so the message correctly identifies a
scroll-related failure when calling handleMessage with pending.
- Around line 49-57: The moveTimer callback in InputHandler currently calls
this.handleMessage(pending) without handling promise rejections, causing
unhandled rejections; update the callback so the call mirrors the scrollTimer
pattern by chaining .catch(...) on this.handleMessage(pending) (and keep
this.moveTimer/nulling logic intact), i.e., locate the moveTimer closure and
replace the direct this.handleMessage(pending) call with a promise chain that
catches errors and logs or handles them the same way scrollTimer does.

---

Duplicate comments:
In `@src/server/InputHandler.ts`:
- Around line 17-22: Add a destroy() method to InputHandler that clears and
nulls moveTimer and scrollTimer and cancels any pendingMove/pendingScroll; call
this.destroy() from the ws.on('close') handler so timers cannot fire after
disconnect. Specifically, implement InputHandler.destroy() to call
clearTimeout(this.moveTimer) and clearTimeout(this.scrollTimer) if not null, set
moveTimer/scrollTimer/pendingMove/pendingScroll to null, and ensure any other
cleanup needed (e.g., remove event listeners) is performed; update the
ws.on('close') callback to invoke inputHandler.destroy().
- Line 176: Remove the plaintext keystroke logging or guard it with a runtime
debug flag: in InputHandler (the method that logs `console.log(\`Processing key:
${msg.key}\`)` and the similar logs at the other spots), either delete those
console.log calls entirely or wrap them in a check like `if
(DEBUG_INPUT_LOGGING) { ... }` so they are only emitted when an explicit,
non-default debug flag is enabled; ensure the debug flag is configurable off by
default and never logs raw msg.key values in production.

@aniket866
Copy link
Contributor Author

hi @imxade I have fixed the frame size from 16ms to 8ms
can be merged now

@imxade imxade merged commit 2436391 into AOSSIE-Org:main Feb 18, 2026
2 checks passed
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