Please do not open a public issue for security problems.
Report privately through GitHub: go to the repository's Security tab → Report a vulnerability (this opens a private advisory). If you can't use that, email daveproxy80@gmail.com.
Include what you can: affected component, steps to reproduce, and impact. We aim to acknowledge within a few days and will coordinate a fix and disclosure with you.
This is testnet, pre-production software. The smart contracts have not yet been audited. Treat anything on-chain as experimental until a release notes otherwise.
The app signs transactions in the browser, so it ships a strict policy (#601):
Content-Security-Policyis set per request insrc/proxy.tswith a nonce. Scripts run only from'self'plus that nonce ('strict-dynamic'); the inline theme bootstrap insrc/app/layout.tsxcarries the nonce.connect-srcis limited to the configured Horizon, Soroban RPC, backend and error-report origins plus WalletConnect.frame-ancestors 'none'protects the signing flow from clickjacking. Styles keep'unsafe-inline'because React inlinestyleattributes are used throughout.Strict-Transport-Security,X-Content-Type-Options: nosniff,Referrer-Policy: strict-origin-when-cross-origin,Permissions-PolicyandX-Frame-Options: DENYare set for every route innext.config.ts.- Rollout: the policy is sent as
Content-Security-Policy-Report-OnlyuntilCSP_MODE=enforceis set. SetNEXT_PUBLIC_CSP_REPORT_URIto collect violations, watch them through a full connect → deposit → withdraw session with each supported wallet (Freighter, xBull, Lobstr, WalletConnect), then enforce. - If you add a third-party origin (RPC, API, analytics), it must be configured
through the env vars in
.env.exampleor added tosrc/lib/securityHeaders.ts.
Client error reporting and web-vitals (#609) go to NEXT_PUBLIC_ERROR_REPORT_URL
when it is set; nothing is sent otherwise.
- Collected: error kind (render, route, transaction, RPC timeout, unhandled), the scrubbed message and stack, the Soroban contract error code, the operation name (deposit/withdraw), page path, release tag, and LCP/INP/CLS/FCP/TTFB.
- Never collected: wallet addresses, transaction hashes, emails or XDR — these
are redacted from messages, stacks and caller-supplied
contextvalues before sending (scrub()insrc/lib/errorReporting.ts) — and no cookies or persistent identifiers. - Opt-in (#658): telemetry is off until the visitor agrees. A first visit
shows a consent banner (
src/components/TelemetryConsent.tsx); the choice is stored ashb-telemetry-consent=granted|deniedand can be changed at any time from the privacy control in the footer. With no recorded choice — including a visitor who never sees the banner — nothing is sent (isTelemetryAllowed()requiresgranted). - Browser signals override consent: nothing is sent when the browser sends Do-Not-Track or Global Privacy Control, even if consent was granted.
- Source maps: build with
SOURCE_MAPS=trueto publish browser source maps so the sink can symbolicate stacks.
Backend-authenticated features use a wallet signature (SEP-53 message signing, or a SEP-10 challenge transaction) exchanged for a short-lived JWT bound to the G-address (#603). The token is kept in memory only; the session is restored through an HttpOnly refresh cookie. See docs/AUTH.md.