- β
.envremoved from Git tracking - β
.envadded to.gitignore - β
.env.exampleprovides template with instructions - β README.md includes security warnings
MANDATORY SECURITY CHECKS:
-
Verify .env is NOT tracked:
git ls-files | grep -E '\.env$' # Should return NO results (only .env.example should exist)
-
Confirm .gitignore includes .env:
grep -E '\.env' .gitignore # Should show .env patterns
-
Check for accidental commits:
git log --all --full-history -- .env # If this shows results, see "π¨ If .env Was Already Committed" below
If your .env file with real credentials was ever committed to a repository:
-
Immediately rotate your Supabase keys:
- Go to Supabase Dashboard > Settings > API
- Reset your anon key
- Update your local
.envwith new keys
-
Remove from Git history (if repository is private):
git filter-branch --force --index-filter \ 'git rm --cached --ignore-unmatch .env' \ --prune-empty --tag-name-filter cat -- --all -
For public repositories:
- Create a new Supabase project
- Update all your environment variables
- Consider the old keys compromised
- β All Supabase tables have RLS enabled
- β Users can only access their own data
- β Anonymous users cannot access authenticated data
// In src/lib/supabase.ts
if (!SUPABASE_URL || !SUPABASE_ANON_KEY) {
console.warn('Supabase env vars not found. Supabase sync will be disabled.');
}- Set environment variables in dashboard (not in code)
- Use different Supabase project for production
- Never use development keys in production
- Use Docker secrets or environment files
- Never include
.envin Docker images - Use build-time arguments for public variables only
-
.envis in.gitignore -
.envis not tracked by Git - No credentials hardcoded in source files
-
.env.examplehas dummy values only - Production uses separate Supabase project
- RLS policies are properly configured
- Environment variables set in deployment platform
# Final check before pushing
git status
git ls-files | grep -E '\.env$' # Should be empty
# Safe to push
git add .
git commit -m "feat: add authentication and database integration"
git push origin mainIf credentials are accidentally exposed:
- Immediately rotate Supabase keys
- Check repository access logs
- Monitor Supabase usage for unusual activity
- Consider creating new Supabase project if exposure was public
Certain transitive dependencies are pinned as direct devDependencies to enforce a minimum safe version, because pnpm's overrides field does not force version bumps for packages already at the minimum satisfying version. These entries exist for security β not because the project uses these packages directly.
| Package | Pinned version | CVE / reason |
|---|---|---|
hono |
4.12.25 |
CORS credential reflection, path traversal, Lambda header issues |
form-data |
4.0.6 |
CRLF injection via unescaped multipart field names |
Major version upgrades performed to resolve known CVEs in dev toolchain (dev-only, not shipped to production):
| Package | From | To | Severity |
|---|---|---|---|
vitest |
1.6.1 | 3.2.6 | Critical β arbitrary file read/execute via UI server |
vite |
5.4.21 | 6.4.3 | High β server.fs.deny bypass on Windows |
react-router-dom |
^6.30.3 | ^6.30.4 | Medium β open redirect via protocol-relative URL |
.npmrc declares only-built-dependencies for packages allowed to run postinstall scripts: esbuild, @swc/core, electron-winstaller, msw. All other packages run without postinstall. This was moved from package.json#pnpm to .npmrc when pnpm v10 stopped reading the pnpm namespace in package.json.
User-supplied URLs (e.g. contactWebsite on client records) are validated with URL() before being rendered as href attributes. Only http: and https: schemes produce a clickable anchor β any other value (including javascript: URIs or malformed URLs) is rendered as plain text.
Location: src/components/ClientManagement.tsx
All target="_blank" links include rel="noopener noreferrer" to prevent the opened page from accessing window.opener and to suppress the Referer header.
Locations: src/components/AppSidebar.tsx, src/components/ClientManagement.tsx
The Electron build sets a Content-Security-Policy response header via session.defaultSession.webRequest.onHeadersReceived. Key directives:
default-src 'self' app: data: https://*.supabase.co
script-src 'self'
style-src 'self' 'unsafe-inline'
img-src 'self' app: data: blob: https:
connect-src 'self' https://*.supabase.co wss://*.supabase.co
'unsafe-inline' and 'unsafe-eval' are intentionally absent from script-src β the production build uses only <script type="module"> and requires neither.
Location: electron/main.ts
Remember: It's always better to be overly cautious with credentials than to deal with a security incident later!