Skip to content

fix(viz): self-DOS guards on sensor URL overrides - #87

Merged
WomB0ComB0 merged 1 commit into
mainfrom
fix/sensor-url-bounds
Apr 29, 2026
Merged

WomB0ComB0 merged 1 commit into
mainfrom
fix/sensor-url-bounds

Conversation

@WomB0ComB0

Copy link
Copy Markdown
Member

Summary

Three security follow-up tweaks from the post-merge review of #85 / #86. URL overrides for world bounds and LiDAR scan params previously accepted any positive finite value; a typo like ?worldGrid=8192 (~2 TB voxel buffer) or ?lidarRange=1e30 (DDA walks every voxel + float-precision erosion) would lock up the GPU.

Self-DOS only — no server amplification, no cross-origin attack surface — but a sane upper bound prevents "my browser tab died" footguns when an operator typos a test URL.

Caps

Param Range Reason
worldGrid ≤ 1024 1024³ × 4 B = 4 GB voxel buffer ceiling
voxelScale ≤ 256 1024 × 256 = 256 km cube ceiling
lidarElev / lidarAzim ≤ 4096 LiDAR manager capacity from registry.ts
lidarRange 0.1–10 000 m Visualization terrain is only 4 km
lidarInterval 0.001–60 s 1 minute is the practical floor for "useful sensor"

Log hygiene

Truncate raw log value to 64 chars (+ … ellipsis) in all URL warn paths, so a poisoned URL with megabytes of garbage can't bloat the console or log aggregator. Shared _truncRaw helper in both files.

The lenient _readPositiveFinite helper in effects.ts is removed — the two call sites (lidarRange, lidarInterval) now use _readNumberInRange with explicit bounds, matching the pattern already used for lidarFov.

Bundle

Main 757.7 → 757.9 KB (+0.2 KB; 92.5 % of cap).

Test plan

  • npm run build passes
  • tsc --noEmit clean
  • npm test 17/17 green
  • Browser: ?worldGrid=99999 → warn-and-fall-back
  • Browser: ?lidarRange=1e30 → warn-and-fall-back
  • Browser: ?worldGrid= + 100 KB of garbage → log shows truncated …

🤖 Generated with Claude Code

Address the security review on PRs #85 / #86. URL overrides for
world bounds and LiDAR scan params previously accepted any positive
finite value; a typo like `?worldGrid=8192` (~2 TB voxel buffer) or
`?lidarRange=1e30` (DDA walks every voxel + float precision erosion)
would lock up the GPU. Self-DOS only — no server amplification, no
cross-origin attack surface — but a sane upper bound prevents
"my browser tab died" footguns.

Caps:
  worldGrid       ≤ 1024     (1024³ × 4 B = 4 GB voxel buffer ceiling)
  voxelScale      ≤ 256      (1024 × 256 = 256 km cube ceiling)
  lidarElev/azim  ≤ 4096     (the LiDAR manager capacity)
  lidarRange      0.1–10000  (m; visualization terrain is only 4 km)
  lidarInterval   0.001–60   (s)

Also: truncate `raw` log value to 64 chars (+ `…` ellipsis) so a
poisoned URL with megabytes of garbage can't bloat the console or
log aggregator. Helpers in both files share the same `_truncRaw`
implementation.

The lenient `_readPositiveFinite` helper in effects.ts is removed —
the two call sites (lidarRange, lidarInterval) now use
`_readNumberInRange` with explicit bounds, matching the pattern
already used for `lidarFov`.

Bundle: main 757.7 → 757.9 KB (+0.2 KB).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Apr 29, 2026

Copy link
Copy Markdown
Contributor

Warning

Rate limit exceeded

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

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ 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.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: fe593a24-5f6b-4205-b73a-8c515a883cd0

📥 Commits

Reviewing files that changed from the base of the PR and between 3890d8e and 971461e.

📒 Files selected for processing (2)
  • src/ResQ.Viz.Web/client/effects.ts
  • src/ResQ.Viz.Web/client/webgpu/sensors.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/sensor-url-bounds

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
Review rate limit: 0/1 reviews remaining, refill in 32 minutes and 56 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist gemini-code-assist Bot 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.

Code Review

This pull request introduces sanity upper bounds for LiDAR and world grid parameters passed via URL overrides to prevent potential Denial-of-Service (DoS) scenarios and GPU lockups. It also adds a _truncRaw utility to truncate long URL parameter values in warning logs, protecting against log bloat. I have no feedback to provide as there were no review comments to evaluate.

@WomB0ComB0
WomB0ComB0 merged commit f39c839 into main Apr 29, 2026
37 checks passed
@WomB0ComB0
WomB0ComB0 deleted the fix/sensor-url-bounds branch April 29, 2026 07:29
WomB0ComB0 added a commit that referenced this pull request Apr 29, 2026
* docs: refresh README for the WebGPU sensor stack + version bumps

The README was written before the .NET 10 / Vite 8 / TS 6 upgrade and
predates the entire WebGPU sensor arc (PRs #66–#87). Bring it back
into sync with reality.

- Tech Stack: .NET 9 → 10, Three.js r175 → 0.184, TS 5 + Vite 6 →
  TS 6 + Vite 8; new rows for the WebGPU sensor primitive and Vitest
  frontend tests.
- Features: add the brick-map raymarcher (mesh-link LoS + per-drone
  LiDAR off one kernel) and the SignalR lazy chunk.
- Project Layout: include `webgpu/` (device, sensors, registry,
  world, brickmap, los, lidar, rays, shaders/) and `__tests__/`,
  plus the new `sensorStatsOverlay.ts`.
- Keyboard shortcuts: add `i` for the sensor stats overlay.
- License footer: 2024 ResQ Technologies Ltd. → 2026 ResQ Systems,
  Inc. — matches the SPDX headers across the codebase.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs: address review feedback on PR #88

- "voxelizes once" was misleading — the brick-map rebuilds when the
  terrain preset switches or a heightmap override is installed (the
  `onTerrainChange` path landed in #74). Reword to "voxelizes at
  boot (and rebuilds on terrain edits)" so the docs match the code.
- Drop "(~17 cases, ~380 ms)" from the Vitest row in Tech Stack —
  machine-specific runtime that drifts with each new test, not a
  capability claim.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
WomB0ComB0 added a commit that referenced this pull request Apr 29, 2026
* docs: refresh README for the WebGPU sensor stack + version bumps

The README was written before the .NET 10 / Vite 8 / TS 6 upgrade and
predates the entire WebGPU sensor arc (PRs #66–#87). Bring it back
into sync with reality.

- Tech Stack: .NET 9 → 10, Three.js r175 → 0.184, TS 5 + Vite 6 →
  TS 6 + Vite 8; new rows for the WebGPU sensor primitive and Vitest
  frontend tests.
- Features: add the brick-map raymarcher (mesh-link LoS + per-drone
  LiDAR off one kernel) and the SignalR lazy chunk.
- Project Layout: include `webgpu/` (device, sensors, registry,
  world, brickmap, los, lidar, rays, shaders/) and `__tests__/`,
  plus the new `sensorStatsOverlay.ts`.
- Keyboard shortcuts: add `i` for the sensor stats overlay.
- License footer: 2024 ResQ Technologies Ltd. → 2026 ResQ Systems,
  Inc. — matches the SPDX headers across the codebase.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs: address review feedback on PR #88

- "voxelizes once" was misleading — the brick-map rebuilds when the
  terrain preset switches or a heightmap override is installed (the
  `onTerrainChange` path landed in #74). Reword to "voxelizes at
  boot (and rebuilds on terrain edits)" so the docs match the code.
- Drop "(~17 cases, ~380 ms)" from the Vitest row in Tech Stack —
  machine-specific runtime that drifts with each new test, not a
  capability claim.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs: document missing keyboard shortcuts in README

The marketing intro mentioned `5`, `K`, and `Ctrl+Shift+R` but those
never appeared in the Keyboard Shortcuts table. The `multi-agency-sar`
scenario was also missing from the REST scenarios list, and the
camera presets (`Shift+1..5`) plus the drone-strip cycling (`[`/`]`)
were undocumented.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs: correct wwwroot description (not committed)

The Project Layout entry claimed wwwroot/ is "committed for zero-install
deploys", but `git ls-files src/ResQ.Viz.Web/wwwroot/` returns 0 files
and `.gitignore` excludes index.html, assets/, and .vite/. The
zero-install path is actually the CI artifact `viz-wwwroot-{sha}`
produced by ci.yml's client/build job, not anything in the repo.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs: split long WebGPU sensor primitive sentence

Addresses gemini-code-assist review feedback on PR #89 — the
"voxelizes ... and serves ..." compound was a single 72-word sentence.
Splitting at the rebuild-trigger clause keeps the trigger conditions
visible while letting the LoS/LiDAR purpose start a fresh sentence.

Backticks around `peakSlotDepth`, `raysOutsideWorld`, and `i` were
already present in the source; only the bot's suggestion block had
stripped them.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
WomB0ComB0 added a commit that referenced this pull request Sep 10, 2026
* docs: correct the SDK pin, and guard what the compiler cannot

CLAUDE.md said the submodule is "pinned to a release tag". It is not. It is
pinned to a3f8b89 on release/0.6.x, three commits past v0.6.0, and those three
commits (#85-#87) are what added drone attitude, the explicit yaw command, and
landing recovery. No tag contains them. The line invited exactly the change that
would drop them.

Two findings while checking it, one of which corrects an assumption I had been
repeating.

FIRST, THE SDK's main HAS MOVED PAST US. ResQ.Simulation.Engine, ResQ.Mavlink,
ResQ.Mavlink.Dialect and ResQ.Mavlink.Mesh do not exist on main - MAVLink is gone
from it entirely, and main carries a different set (Core, Clients, Protocols,
Blockchain, Storage, Simulation). All four are ProjectReferences in
ResQ.Viz.Web.csproj. So viz is not on a stale pin; it is on a branch main has
structurally abandoned, 43 commits back on a divergent line. Reconciling that is
an architecture decision for a human, and this commit does not attempt it - it
writes it down where the next person will look.

SECOND, THE SILENT-REVERT RISK IS NOT REAL, and I had been asserting it was.
Both ways of moving the pin fail loudly:

  v0.6.0  -> compile error; viz calls Hover(yaw) and GoTo(..., yaw:), which that
             tag has no overloads for
  main    -> the four project references do not resolve at all

Measured, not reasoned: checking out v0.6.0 in the submodule produced five
CS1501/CS1739 errors in viz's own production code before any test ran.

What neither catches is a behaviour that changed without changing a signature.
Landing recovery is a re-arm inside ApplyCommand, and attitude integration is the
roll/pitch term inside IntegrateAttitude; reverting either compiles cleanly.
Verified by doing it: the re-arm revert and the heading-only attitude revert each
build green and each fail exactly one of the new tests.

So SdkFlightContractTests covers the part that is genuinely silent. Four tests:
explicit yaw steers heading, forward flight produces real pitch, a landed drone
re-arms and climbs on a non-Land command, and Land still latches so the re-arm
did not make HasLanded meaningless. They duplicate tests the SDK already has, on
purpose - this is viz asserting the submodule it is pinned to still behaves the
way its client and physics assume.

1376 tests green. dotnet format clean. The submodule pin is unchanged and its
working tree was restored after every mutation.

* test: make the re-arm test cover what its name claims, and drop a stale claim

Two review findings, both right.

FIRST, the test file's own doc still said moving the pin to v0.6.0 "still builds,
still passes every other test, and silently reverts takeoff and rotation". That
was the assumption this PR exists to correct, and CLAUDE.md now says the opposite
two files away: v0.6.0 is a compile error because viz calls Hover(yaw) and
GoTo(..., yaw:). The stale wording survived where the argument for the tests
lives, which is the worst place for it. Rewritten to say what was measured, and
to say plainly what the tests actually guard - behaviour that changed without
changing a signature, which no compiler can see.

SECOND, the re-arm test was named "on any non-Land command" and exercised GoTo
alone. The re-arm is keyed on command.Type, so a regression that cleared
HasLanded for GoTo and not for Hover or RTL would have passed. Now a Theory over
GoTo, Hover and RTL.

Verified rather than assumed: rewriting the guard to
`command.Type == FlightCommandType.GoToWaypoint` fails the Hover and RTL cases
and passes GoTo - precisely the regression described, which the old test would
have shipped green. The first attempt at that mutation used a FlightCommandType
member that does not exist and failed to compile; the result above is from the
one that actually applied.

Hover asserts the flag rather than a climb, because RTL rewrites to
GoTo(LaunchPosition) and has somewhere to go while Hover holds station.
Asserting a climb for Hover would assert the wrong contract.

1378 tests green. dotnet format clean. Submodule restored after each mutation.
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