Skip to content

security: Round 2 full-stack audit — 4 medium findings (contracts + backend + frontend)#6

Open
Godbrand0 wants to merge 1 commit into
jayteemoney:mainfrom
Godbrand0:security/deep-audit-2
Open

security: Round 2 full-stack audit — 4 medium findings (contracts + backend + frontend)#6
Godbrand0 wants to merge 1 commit into
jayteemoney:mainfrom
Godbrand0:security/deep-audit-2

Conversation

@Godbrand0
Copy link
Copy Markdown

Summary

This PR adds SECURITY_REVIEW_2.md, a second security audit that covers the OpenClaw backend service and Next.js frontend for the first time, plus a fresh pass on both Clarity contracts. The prior review (SECURITY_REVIEW.md, April 2026) only audited the contracts.

4 medium-severity findings, 0 critical/high.

ID Layer Finding
M-1 Contract top-up-stream accepts top-up of an already-elapsed ACTIVE stream, locking top-up funds in an orphaned stream
M-2 Backend error-handler.ts exposes raw err.message to API clients (file paths, SDK internals)
M-3 Frontend buildClaimTx, buildClaimAllTx, buildCancelStreamTx use PostConditionMode.Allow with zero post-conditions
M-4 Frontend AssistantWidget interpolates raw user input into fetch paths without encodeURIComponent

All existing contract security properties (reentrancy, contract-caller auth, token substitution prevention, fund conservation) were re-verified and remain sound.

Fixes Required Before Mainnet

  • M-1: Add (asserts! (< stacks-block-height end-block) ERR-STREAM-ENDED) to top-up-stream in stream-manager.clar
  • M-2: Replace message: err.message with opaque error reference UUIDs in openclaw-service/src/middleware/error-handler.ts
  • M-3: Add Pc.* post-conditions to buildClaimTx, buildClaimAllTx, buildCancelStreamTx in frontend/src/lib/stacks.ts
  • M-4: Wrap query input with encodeURIComponent() and add client-side regex validation in frontend/src/components/openclaw/assistant-widget.tsx

Test plan

  • Read SECURITY_REVIEW_2.md and verify all 4 findings with code references
  • After fixes are applied, re-run clarinet check to confirm the M-1 contract change compiles
  • Manually test the AssistantWidget with path-containing inputs (e.g. 0/status, ../health) to confirm M-4 fix
  • Verify Leather/Xverse wallet shows specific token amounts in signing dialog after M-3 fix

Audits the OpenClaw backend and Next.js frontend for the first time,
plus a second pass on both Clarity contracts. Finds 4 medium-severity
issues not caught in the prior contract-only review:

M-1 (contract): top-up-stream allows extending an already-elapsed ACTIVE
stream, silently locking top-up funds if the sender uses it on a dead stream.

M-2 (backend): error-handler.ts exposes raw err.message to API clients,
leaking internal file paths and SDK details on 500/502 responses.

M-3 (frontend): buildClaimTx, buildClaimAllTx, and buildCancelStreamTx
use PostConditionMode.Allow with zero post-conditions — wallet cannot
enforce expected token amounts at signing time.

M-4 (frontend): AssistantWidget interpolates raw user input directly into
fetch paths without encodeURIComponent, allowing path manipulation to hit
unintended API routes.

No critical or high findings. All fund-conservation properties of the
contracts are confirmed sound.
@vercel
Copy link
Copy Markdown

vercel Bot commented May 18, 2026

@Godbrand0 is attempting to deploy a commit to the dev_jaytee's projects Team on Vercel.

A member of the Team first needs to authorize it.

jayteemoney added a commit that referenced this pull request May 19, 2026
M-2 (backend): openclaw-service error handler forwarded raw err.message to
clients, leaking file paths, SDK versions, and upstream API details. Rewrote
to generate opaque randomUUID() reference IDs; full errors logged
server-side only.

M-3 (frontend): buildClaimTx, buildClaimAllTx, and buildCancelStreamTx used
PostConditionMode.Allow with zero post-conditions, so tampered functionArgs
would produce wallet prompts indistinguishable from legitimate ones. All
three rewritten to PostConditionMode.Deny with explicit
Pc.principal(contract).willSendLte(amount).ft(token, ftName) constraints.
Function signatures extended to take ftName and amount upper bound; all
four frontend call sites updated to pass these from
getTokenConfigByContractId() (new helper in constants.ts).

M-4 (frontend): AssistantWidget interpolated raw user input into fetch paths
without encodeURIComponent, letting slashes silently redirect requests to
different routes. Added client-side regex validation (^\\d+$ for stream IDs,
^S[A-Z0-9]{38,40}$ for Stacks addresses) plus encodeURIComponent on all four
interpolated paths.

PR #6 M-1 contract finding was rejected as a false positive - the L-10
end-block guard exists on main at stream-manager.clar:693.
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