| Version | Supported |
|---|---|
| 1.x.x | ✅ |
We take security seriously at Mova Store. If you discover a security vulnerability, please report it responsibly.
- Do NOT open a public GitHub issue for security vulnerabilities
- Email security concerns to: security@movalabs.dev
- Include a detailed description of the vulnerability
- Provide steps to reproduce if possible
- Acknowledgment within 48 hours
- Regular updates on our progress
- Credit in the security advisory (if desired)
All sensitive configuration is stored in environment variables. Never commit secrets to the repository.
Required environment variables are documented in .env.local.example. Copy this file to .env.local and fill in your values.
cp .env.local.example .env.local| Data Type | Storage | Notes |
|---|---|---|
| API Keys | Environment variables | Never hardcode |
| Supabase Config | Environment variables | Client-side anon key is safe; never commit service role key |
| EmailJS Credentials | Environment variables | Required for email functionality |
| Stellar Contract IDs | Environment variables | Public but environment-specific |
| User Passwords | Supabase Auth | Handled by Supabase, never stored locally |
| Payment Data | On-chain (Stellar) | Non-custodial, no card data stored |
The Soroban escrow contract follows security best practices:
- Non-custodial: Funds flow directly from buyer to contract to merchant
- Access Control: Only authorized parties can dispatch/refund orders
- Token Whitelist: Only approved tokens can be used for payment
- Event Emission: All state changes emit events for transparency
- Error Handling: Typed errors prevent unexpected failures
- Input Validation: All user inputs are validated and sanitized
- XSS Prevention: HTML entities are escaped before rendering
- HTTPS Only: All API calls use HTTPS
- No Sensitive Data in URLs: Sensitive data is sent via POST body
- Content Security Policy: Configured in Next.js headers
- Supabase Authentication handles user sessions.
- Client-side admin UI access is controlled via
NEXT_PUBLIC_ADMIN_EMAILSor theis_adminJWT claim inapp_metadata. - Server-side database and storage writes are enforced via PostgreSQL Row Level Security (RLS) policies in
supabase/schema.sql. - No passwords or private secrets are stored in the application repository.
To authorize an admin user for product mutations and storage uploads in Supabase, use one of the following methods:
-
Method 1:
public.admin_userstable (Recommended) Insert the admin's email address into theadmin_userstable:insert into public.admin_users (email) values ('admin@example.com') on conflict (email) do nothing;
-
Method 2: Custom JWT App Metadata (
is_admin: true) Assign theis_admin: trueflag to the user'sapp_metadatain Supabase Auth:update auth.users set raw_app_meta_data = coalesce(raw_app_meta_data, '{}'::jsonb) || '{"is_admin": true}'::jsonb where email = 'admin@example.com';
Or via the Supabase Admin API / Dashboard:
await supabase.auth.admin.updateUserById(userId, { app_metadata: { is_admin: true }, });
-
Method 3: Whitelist Environment Variable (
NEXT_PUBLIC_ADMIN_EMAILS) Include the admin email in.env.local:NEXT_PUBLIC_ADMIN_EMAILS=admin@example.com,lead@example.com
- Dependencies are regularly updated for security patches
- Use
npm auditto check for vulnerabilities - CI pipeline fails on high-severity vulnerabilities
Before submitting a PR, ensure:
- No secrets or API keys are committed
- User inputs are validated and sanitized
- No
eval()ordangerouslySetInnerHTMLusage - Error messages don't expose sensitive information
- Dependencies are from trusted sources
- No console.log with sensitive data
The current OTP implementation generates codes client-side for demonstration purposes. In production:
- Move OTP generation to a server-side API route
- Implement rate limiting on OTP requests
- Add OTP expiration (recommended: 5 minutes)
The card number, expiry and CVV fields on /checkout are a UI-only demo
affordance. No card data may be transmitted, persisted or processed by this
application:
- Card values live only in React state for the lifetime of the tab. They are never
sent to an API, logged, written to
localStorage, placed in a URL, or handed to a third party. - Because no card value leaves the browser, Mova Store stays outside PCI DSS scope for cardholder data.
- The UI labels the fields as demo-only so a shopper cannot mistake them for a real payment form; the sanctioned path is the Stellar (Soroban) checkout.
Do not "finish" this form in place. If real card payments are ever required, integrate a PCI-compliant processor (Stripe, etc.) and let it collect card data in its own iframe/tokenization flow — never handle raw card data on our servers.
Apply supabase/schema.sql. The shipped policies are already the hardened,
admin-only model — there is no outstanding "tighten later" step:
- Products —
public.productsand theproductsstorage bucket are publicly readable;insert,updateanddeleteon both requirepublic.is_admin()("Admins can insert/update/delete products","Admins can upload/update/delete product images"). - Orders — buyers may read only their own rows
(
auth.uid() = user_id or auth.email() = user_email); status changes and deletions are admin-only. - Allowlist —
public.admin_users, which backspublic.is_admin(), is readable by admins only.
public.is_admin() (defined next to the admin_users table in
supabase/schema.sql) is the single source of truth for admin access: it returns
true when the caller's JWT carries app_metadata.is_admin = true, or when the
caller's email is in public.admin_users. Grant admin rights through one of those
two mechanisms; never widen a policy to work around a missing admin entry.
Never expose the Supabase service role key in the browser.
- Testnet: Used for development, tokens have no real value
- Mainnet: Real value transactions, requires additional auditing
Before mainnet deployment:
- Smart contract security audit
- Penetration testing
- Rate limiting implementation
- Monitoring and alerting setup
- Users connect their own Freighter wallet
- Private keys never leave the user's browser
- Transactions require explicit user approval
For security-related inquiries:
- Email: security@movalabs.dev
- Response time: Within 48 hours