Skip to content

fix(wasteland): add .trim() to forceUnclaimWantedItem reason validation#3159

Open
jrf0110 wants to merge 4 commits intowasteland-staging-v3from
convoy/wasteland-claims-page/93e6710c/head
Open

fix(wasteland): add .trim() to forceUnclaimWantedItem reason validation#3159
jrf0110 wants to merge 4 commits intowasteland-staging-v3from
convoy/wasteland-claims-page/93e6710c/head

Conversation

@jrf0110
Copy link
Copy Markdown
Contributor

@jrf0110 jrf0110 commented May 9, 2026

Summary

Fix whitespace-only reason validation in forceUnclaimWantedItem:

  • Added .trim() to the reason Zod schema so whitespace-only strings like ' ' are properly rejected (previously passed .min(1) since length ≥ 1).
  • Updated the "rejects whitespace-only reason" test to assert on code: 'BAD_REQUEST' (Zod validation error) instead of generic toThrow(), which was passing for the wrong reason (TypeError from undefined mock return).

Verification

Ran pnpm --filter cloudflare-wasteland test — all 10 router tests pass, including the whitespace-only reason test now asserting on the correct error code.

Visual Changes

N/A

Reviewer Notes

Defense-in-depth fix — the UI already trims and disables the button, but the API must also reject whitespace-only reasons from direct API calls.

jrf0110 added 4 commits May 9, 2026 20:44
…Client (#3150)

* feat(wasteland): add Claims page with listClaims procedure and ClaimsClient UI

Add listClaims tRPC procedure that filters browseWantedBoard to claimed
items, optionally by rigHandle, and enriches each with pending PR info
from DoltHub open pulls. Build full ClaimsClient with filter bar, stats
strip, claims table, empty/error/loading states, and drawer integration.
Extract shared STATUS_COLORS/PRIORITY_COLORS/TYPE_COLORS to status-colors.ts.

* fix: address review feedback on PR #3150

- Dismiss toast on unmount in useSlowOperationToast cleanup
- Guard claimed_by Link against null (render plain span instead)
- Filter empty strings from claimedItemIds set
)

* WIP: container eviction save

* feat(wasteland): add force unclaim mutation for admin claims page

Admin-only forceUnclaimWantedItem tRPC mutation that writes directly
to DoltHub wanted table (scratch branch → PR → merge) since wl CLI
has no admin-unclaim command. Includes rate limiting, analytics event,
and billing meter event.

Frontend adds confirmation dialog with required reason textarea,
warning callout, and red destructive confirm button. Gated on
isOwner (wasteland owner or site admin) or isUpstreamAdmin.

* fix(wasteland): address PR #3153 review feedback

- Move forceUnclaimWantedItem from adminProcedure to procedure, relying
  on requireOwnerAccess + isUpstreamAdmin (matches setUpstreamRigTrust
  pattern and fixes frontend/backend auth mismatch)
- Track branchCreated flag and delete scratch branch on merge failure
  to prevent orphaned branches on DoltHub
- Wrap user-controlled reason in blockquote in DoltHub PR description
  to prevent misleading markdown formatting
- Align frontend canForceUnclaim gate with backend (isOwner && isUpstreamAdmin)
* feat(wasteland): add Claims page tRPC tests + docs

* fix: address review feedback - remove non-null assertions and as casts in test

- Replace pending_pr! with optional chaining (?.) after not.toBeNull() guards
- Replace fragile try/catch with .then() pattern that fails if unexpected resolve
- Remove 'as TRPCError' cast, use instanceof narrowing instead

* fix: collapse double-invocation test to rejects.toMatchObject per review
delivery: 'trpc',
userId: ctx.userId,
wastelandId: input.wastelandId,
label: `${input.itemId}:${targetHandle}:${adminHandle}:${input.reason}`,
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.

WARNING: Free-form user text in analytics label

The input.reason field (up to 500 chars of arbitrary user-provided text) is embedded verbatim in the Cloudflare Analytics Engine label blob. While reason is not a credential, it could contain sensitive information the admin typed (e.g. names, internal details). Consider hashing or truncating it, or omitting it from AE entirely — the reason is already preserved in the DoltHub PR description (line 1231), which is the durable audit trail. AE is better suited for structured, non-sensitive labels like itemId:targetHandle:adminHandle.

if (orphanedPullId) {
await doltApi.closePull(upstream, token, orphanedPullId).catch(() => {});
}
if (mergeConfirmed) {
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.

SUGGESTION: Redundant branch in finally cleanup

Both if (mergeConfirmed) and else if (branchCreated) call identical deleteBranch logic. Since the branch should be deleted in all cases where it was created (whether or not the merge succeeded), this can be simplified:

Suggested change
if (mergeConfirmed) {
if (branchCreated) {

This replaces both branches with a single unconditional delete-if-created, which is what both arms do today anyway.

@kilo-code-bot
Copy link
Copy Markdown
Contributor

kilo-code-bot Bot commented May 9, 2026

Code Review Summary

Status: 2 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 1
Issue Details (click to expand)

WARNING

File Line Issue
services/wasteland/src/trpc/router.ts 1272 Free-form reason text embedded verbatim in Cloudflare Analytics Engine label blob — could contain sensitive info typed by the admin

SUGGESTION

File Line Issue
services/wasteland/src/trpc/router.ts 1295 Both if (mergeConfirmed) and else if (branchCreated) in the finally block run identical deleteBranch — simplifiable to if (branchCreated)
Other Observations (not in diff)

listClaims has no rate limit entrylistClaims calls browseWantedBoard (DO RPC) plus up to 6 doltApi.getPull requests against DoltHub. The RATE_LIMITS map has wasteland.browseWantedBoard at 60/min but no entry for wasteland.listClaims. Since rate-limit checks pass through when no config is present, this endpoint is effectively unthrottled. Consider adding an entry like 'wasteland.listClaims': { maxRequests: 60, windowMs: 60_000 } in rate-limit.util.ts.

File Note
services/wasteland/src/util/rate-limit.util.ts No rate limit entry for wasteland.listClaims
Files Reviewed (6 files)
  • apps/web/src/app/(app)/wasteland/[wastelandId]/claims/ClaimsClient.tsx — no issues
  • apps/web/src/lib/wasteland/status-colors.ts — no issues
  • apps/web/src/lib/wasteland/types/router.d.ts — no issues
  • services/wasteland/docs/admin-mode.md — no issues
  • services/wasteland/src/trpc/init.ts — no issues
  • services/wasteland/src/trpc/router.test.ts — no issues
  • services/wasteland/src/trpc/router.ts — 2 issues
  • services/wasteland/src/trpc/schemas.ts — no issues
  • services/wasteland/src/util/rate-limit.util.ts — see Other Observations

Fix these issues in Kilo Cloud


Reviewed by claude-sonnet-4.6 · 1,128,310 tokens

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