security-vite

Vite security audit patterns. Load when reviewing Vite apps (vite.config.ts present). Covers VITE_* exposure, build-time secrets, dev server security, and SPA-specific issues.

$ Installieren

git clone https://github.com/IgorWarzocha/Opencode-Workflows /tmp/Opencode-Workflows && cp -r /tmp/Opencode-Workflows/security-reviewer/.opencode/skill/security-vite ~/.claude/skills/Opencode-Workflows

// tip: Run this command in your terminal to install the skill


name: security-vite description: Vite security audit patterns. Load when reviewing Vite apps (vite.config.ts present). Covers VITE_* exposure, build-time secrets, dev server security, and SPA-specific issues.

Security audit patterns for Vite applications focusing on environment variable exposure, build-time secrets, and SPA-specific vulnerabilities.

Environment Variable Exposure

The VITE_ Footgun

VITE_*    → Bundled into client JavaScript → Visible to everyone
No prefix → Only available in vite.config.ts → Safe for secrets

Audit steps:

  1. grep -r "VITE_" . -g "*.env*"
  2. Check import.meta.env.VITE_* usage in source
  3. Common mistakes:
    • VITE_API_SECRET (SHOULD be server-only)
    • VITE_DATABASE_URL (MUST NOT use)
    • VITE_STRIPE_SECRET_KEY (only publishable keys)

Env Files Priority

Vite loads in this order (later overrides earlier):

.env                # Always loaded
.env.local          # Always loaded, gitignored
.env.[mode]         # e.g., .env.production
.env.[mode].local   # e.g., .env.production.local, gitignored

Check: Are .env.local and .env.*.local in .gitignore?

envPrefix Overrides

If envPrefix is configured, Vite exposes any variables with those prefixes. Treat envPrefix as a security-sensitive setting.

Build-Time vs Runtime

Dangerous: Secrets in vite.config.ts

// ❌ Secret in config (ends up in bundle)
export default defineConfig({
  define: {
    'process.env.API_KEY': JSON.stringify(process.env.API_KEY),
  },
});

// The above makes API_KEY available in client code!

Safe Pattern

// Only use VITE_ prefix for truly public values
export default defineConfig({
  define: {
    '__APP_VERSION__': JSON.stringify(process.env.npm_package_version),
  },
});

// Keep secrets on server (use a backend API)

Dev Server Security

Open to Network

// ❌ Exposes dev server to network
export default defineConfig({
  server: {
    host: '0.0.0.0',  // or host: true
  },
});

This is dangerous on shared networks. Check if intentional.

Proxy Misconfiguration

export default defineConfig({
  server: {
    proxy: {
      '/api': {
        target: 'http://localhost:3000',
        changeOrigin: true,
        // ❌ Missing secure options for production-like setup
      },
    },
  },
});

SPA Security Issues

Client-Side Auth Only

// ❌ "Protection" only in React Router
const ProtectedRoute = ({ children }) => {
  const { user } = useAuth();
  if (!user) return <Navigate to="/login" />;
  return children;
};

// API calls still need server-side auth!
// This is UI convenience, not security.

Secrets in Bundle

# Check the built bundle for secrets
rg -a "(sk_live|sk_test|AKIA|api[_-]?key)" dist/

Source Maps in Production

// Check vite.config.ts
export default defineConfig({
  build: {
    sourcemap: true,  // ❌ Exposes source code in production
  },
});

<severity_table>

Common Vulnerabilities

IssueWhere to LookSeverity
VITE_* secrets.env*, source filesCRITICAL
Secrets in definevite.config.tsCRITICAL
Source maps in prodvite.config.tsMEDIUM
Dev server exposedvite.config.ts server.hostMEDIUM
Client-only authRoute guards without API authHIGH
API keys in bundledist/ directoryCRITICAL

</severity_table>

Quick Audit Commands

# Find VITE_ secrets
grep -r "VITE_" . -g "*.env*"

# Find import.meta.env usage
rg 'import\.meta\.env' . -g "*.ts" -g "*.tsx" -g "*.vue"

# Check define in config
rg 'define:' vite.config.*

# Scan built bundle for secrets
rg -a "(sk_live|AKIA|ghp_|api[_-]?key['\"]?\s*[:=])" dist/

# Check for source maps
fd '\.map$' dist/

Hardening Checklist

  • No secrets in VITE_* variables
  • .env.local and .env.*.local in .gitignore
  • sourcemap: false in production build
  • server.host is not 0.0.0.0 or true (unless intentional)
  • All sensitive API calls go through a backend (not direct from browser)
  • No secrets in vite.config.ts define block

Repository

IgorWarzocha
IgorWarzocha
Author
IgorWarzocha/Opencode-Workflows/security-reviewer/.opencode/skill/security-vite
21
Stars
4
Forks
Updated5d ago
Added1w ago