vibes-plug 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- package/BLUEPRINT.md +125 -0
- package/CHANGELOG.md +195 -0
- package/CONTRIBUTING.md +199 -0
- package/LICENSE +21 -0
- package/README.md +263 -0
- package/SECURITY.md +21 -0
- package/banner.png +0 -0
- package/package.json +25 -0
- package/plugin.json +8 -0
- package/scripts/update_skills.js +75 -0
- package/skills/ai-llm-integration-expert/SKILL.md +162 -0
- package/skills/api-design-expert/SKILL.md +310 -0
- package/skills/app-analyzer-optimizer/SKILL.md +189 -0
- package/skills/asisten_ramah/SKILL.md +41 -0
- package/skills/authentication-identity-expert/SKILL.md +45 -0
- package/skills/auto-doc-updater/SKILL.md +204 -0
- package/skills/bootstrap-to-modern/SKILL.md +87 -0
- package/skills/brainstorming/SKILL.md +353 -0
- package/skills/bun-runtime-expert/SKILL.md +211 -0
- package/skills/ci-cd-devops-architect/SKILL.md +45 -0
- package/skills/cloud-hosting-expert/SKILL.md +244 -0
- package/skills/coderabbit/SKILL.md +192 -0
- package/skills/data-telemetry-expert/SKILL.md +213 -0
- package/skills/database-orm-expert/SKILL.md +294 -0
- package/skills/design-system-architect/SKILL.md +243 -0
- package/skills/e2e-testing-expert/SKILL.md +315 -0
- package/skills/event-driven-architect/SKILL.md +81 -0
- package/skills/firebase-security-expert/SKILL.md +195 -0
- package/skills/fullstack-expert/SKILL.md +202 -0
- package/skills/fullstack-expert/references/api_design_guide.md +466 -0
- package/skills/fullstack-expert/references/devops_infrastructure.md +477 -0
- package/skills/fullstack-expert/references/multi_language_backend.md +528 -0
- package/skills/fullstack-expert/references/system_design_patterns.md +358 -0
- package/skills/fullstack-expert/scripts/api_contract_validator.py +253 -0
- package/skills/fullstack-expert/scripts/architecture_analyzer.py +326 -0
- package/skills/gemini-agent-booster/SKILL.md +135 -0
- package/skills/global-a11y-i18n-expert/SKILL.md +81 -0
- package/skills/go-programming-expert/SKILL.md +295 -0
- package/skills/hig/SKILL.md +188 -0
- package/skills/js-backend-expert/SKILL.md +192 -0
- package/skills/mcp-server-architect/SKILL.md +194 -0
- package/skills/mobile-expo-expert/SKILL.md +186 -0
- package/skills/monday-design-aesthetic/SKILL.md +67 -0
- package/skills/monorepo-architect/SKILL.md +227 -0
- package/skills/mpa-orchestrator/SKILL.md +101 -0
- package/skills/multi-agent-orchestration/SKILL.md +234 -0
- package/skills/multiple-entry-points/SKILL.md +55 -0
- package/skills/mvc-expert/SKILL.md +231 -0
- package/skills/payment-gateway-expert/SKILL.md +45 -0
- package/skills/performance-web-vitals/SKILL.md +332 -0
- package/skills/prd-architect/SKILL.md +191 -0
- package/skills/production-ready-hardener/SKILL.md +469 -0
- package/skills/production-ready-hardener/references/performance_optimization.md +441 -0
- package/skills/production-ready-hardener/references/production_checklist.md +161 -0
- package/skills/production-ready-hardener/references/security_hardening_guide.md +379 -0
- package/skills/production-ready-hardener/scripts/production_readiness_scanner.py +875 -0
- package/skills/python-programming-expert/SKILL.md +271 -0
- package/skills/realtime-collaboration-expert/SKILL.md +45 -0
- package/skills/rust-programming-expert/SKILL.md +235 -0
- package/skills/saas-billing/SKILL.md +377 -0
- package/skills/saas-multi-tenant/SKILL.md +237 -0
- package/skills/saas-mvp-launcher/SKILL.md +231 -0
- package/skills/saas-transformer/SKILL.md +446 -0
- package/skills/saas-transformer/references/billing_integration_guide.md +401 -0
- package/skills/saas-transformer/references/feature_gating_patterns.md +137 -0
- package/skills/saas-transformer/references/saas_transformation_checklist.md +121 -0
- package/skills/saas-transformer/scripts/saas_transformation_scanner.py +254 -0
- package/skills/scalability-clean-code/SKILL.md +229 -0
- package/skills/secure-fuzz-testing/SKILL.md +201 -0
- package/skills/senior-frontend/SKILL.md +161 -0
- package/skills/senior-frontend/references/frontend_best_practices.md +806 -0
- package/skills/senior-frontend/references/nextjs_optimization_guide.md +724 -0
- package/skills/senior-frontend/references/react_patterns.md +746 -0
- package/skills/senior-frontend/scripts/bundle_analyzer.py +407 -0
- package/skills/senior-frontend/scripts/component_generator.py +329 -0
- package/skills/senior-frontend/scripts/frontend_scaffolder.py +1005 -0
- package/skills/senior-fullstack/SKILL.md +167 -0
- package/skills/senior-fullstack/references/architecture_patterns.md +160 -0
- package/skills/senior-fullstack/references/development_workflows.md +222 -0
- package/skills/senior-fullstack/references/tech_stack_guide.md +190 -0
- package/skills/senior-fullstack/scripts/code_quality_analyzer.py +114 -0
- package/skills/senior-fullstack/scripts/fullstack_scaffolder.py +114 -0
- package/skills/senior-fullstack/scripts/project_scaffolder.py +114 -0
- package/skills/seo/SKILL.md +225 -0
- package/skills/seo/references/cwv-thresholds.md +108 -0
- package/skills/seo/references/eeat-framework.md +214 -0
- package/skills/seo/references/quality-gates.md +155 -0
- package/skills/seo/references/schema-types.md +118 -0
- package/skills/seo-aeo-landing-page-writer/SKILL.md +97 -0
- package/skills/seo-geo/SKILL.md +188 -0
- package/skills/session-handoff-resume/SKILL.md +158 -0
- package/skills/skill_baru/SKILL.md +147 -0
- package/skills/spa-orchestrator/SKILL.md +288 -0
- package/skills/state-management-expert/SKILL.md +272 -0
- package/skills/supabase-migration/SKILL.md +45 -0
- package/skills/supabase-security-expert/SKILL.md +243 -0
- package/skills/tailwind-expert/SKILL.md +188 -0
- package/skills/tanstack-query-expert/SKILL.md +199 -0
- package/skills/tauri-expert/SKILL.md +97 -0
- package/skills/token-saver/SKILL.md +111 -0
- package/skills/typescript-expert/SKILL.md +279 -0
- package/skills/ui-components-expert/SKILL.md +63 -0
- package/skills/ui-ux-pro-max/SKILL.md +201 -0
- package/skills/ui-ux-pro-max/data/charts.csv +26 -0
- package/skills/ui-ux-pro-max/data/colors.csv +97 -0
- package/skills/ui-ux-pro-max/data/icons.csv +101 -0
- package/skills/ui-ux-pro-max/data/landing.csv +31 -0
- package/skills/ui-ux-pro-max/data/products.csv +97 -0
- package/skills/ui-ux-pro-max/data/prompts.csv +24 -0
- package/skills/ui-ux-pro-max/data/react-performance.csv +45 -0
- package/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
- package/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
- package/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
- package/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
- package/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
- package/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
- package/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
- package/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
- package/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
- package/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
- package/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
- package/skills/ui-ux-pro-max/data/styles.csv +59 -0
- package/skills/ui-ux-pro-max/data/typography.csv +58 -0
- package/skills/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
- package/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
- package/skills/ui-ux-pro-max/data/web-interface.csv +31 -0
- package/skills/ui-ux-pro-max/scripts/__pycache__/core.cpython-310.pyc +0 -0
- package/skills/ui-ux-pro-max/scripts/__pycache__/core.cpython-312.pyc +0 -0
- package/skills/ui-ux-pro-max/scripts/__pycache__/design_system.cpython-310.pyc +0 -0
- package/skills/ui-ux-pro-max/scripts/__pycache__/design_system.cpython-312.pyc +0 -0
- package/skills/ui-ux-pro-max/scripts/core.py +257 -0
- package/skills/ui-ux-pro-max/scripts/design_system.py +493 -0
- package/skills/ui-ux-pro-max/scripts/search.py +81 -0
- package/skills/ui_ux_expert/SKILL.md +114 -0
- package/skills/vibe-code-gardener/SKILL.md +173 -0
- package/skills/web-scraper/SKILL.md +205 -0
- package/skills/web-scraper/references/data-transforms.md +397 -0
- package/skills/web-scraper/references/extraction-patterns.md +475 -0
- package/skills/web-scraper/references/output-templates.md +481 -0
- package/skills/zero-to-prod-orchestrator/SKILL.md +180 -0
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
# Security Hardening Guide
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
This guide provides concrete, implementable security patterns for production applications. It covers authentication, authorization, data protection, API security, and infrastructure hardening.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 1. Authentication & Session Security
|
|
9
|
+
|
|
10
|
+
### OAuth 2.0 / OIDC Setup (Next.js + Clerk)
|
|
11
|
+
|
|
12
|
+
```typescript
|
|
13
|
+
// middleware.ts — Protect all app routes
|
|
14
|
+
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server';
|
|
15
|
+
|
|
16
|
+
const isPublicRoute = createRouteMatcher([
|
|
17
|
+
'/',
|
|
18
|
+
'/sign-in(.*)',
|
|
19
|
+
'/sign-up(.*)',
|
|
20
|
+
'/api/webhooks(.*)',
|
|
21
|
+
'/api/health(.*)',
|
|
22
|
+
]);
|
|
23
|
+
|
|
24
|
+
export default clerkMiddleware(async (auth, request) => {
|
|
25
|
+
if (!isPublicRoute(request)) {
|
|
26
|
+
await auth.protect(); // Redirect unauthenticated users
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
export const config = {
|
|
31
|
+
matcher: ['/((?!_next|[^?]*\\.(?:html?|css|js|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)'],
|
|
32
|
+
};
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### JWT Token Validation (Edge Function / API Route)
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
// lib/auth.ts
|
|
39
|
+
import { jwtVerify, JWTVerifyResult } from 'jose';
|
|
40
|
+
|
|
41
|
+
const JWKS_URL = process.env.CLERK_JWKS_URL!;
|
|
42
|
+
const secret = new TextEncoder().encode(process.env.JWT_SECRET!);
|
|
43
|
+
|
|
44
|
+
export async function verifyToken(token: string): Promise<JWTVerifyResult> {
|
|
45
|
+
try {
|
|
46
|
+
return await jwtVerify(token, secret, {
|
|
47
|
+
algorithms: ['HS256'],
|
|
48
|
+
issuer: process.env.JWT_ISSUER,
|
|
49
|
+
audience: process.env.JWT_AUDIENCE,
|
|
50
|
+
});
|
|
51
|
+
} catch (error) {
|
|
52
|
+
throw new Error('Invalid or expired token');
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Usage in API route
|
|
57
|
+
export async function authenticateRequest(request: Request) {
|
|
58
|
+
const authHeader = request.headers.get('Authorization');
|
|
59
|
+
if (!authHeader?.startsWith('Bearer ')) {
|
|
60
|
+
throw new Error('Missing authorization header');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const token = authHeader.slice(7);
|
|
64
|
+
const { payload } = await verifyToken(token);
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
userId: payload.sub!,
|
|
68
|
+
role: payload.role as string,
|
|
69
|
+
email: payload.email as string,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## 2. Authorization — Row-Level Security (RLS)
|
|
77
|
+
|
|
78
|
+
### Supabase/PostgreSQL RLS Patterns
|
|
79
|
+
|
|
80
|
+
```sql
|
|
81
|
+
-- Enable RLS on all user-facing tables
|
|
82
|
+
ALTER TABLE profiles ENABLE ROW LEVEL SECURITY;
|
|
83
|
+
ALTER TABLE projects ENABLE ROW LEVEL SECURITY;
|
|
84
|
+
ALTER TABLE documents ENABLE ROW LEVEL SECURITY;
|
|
85
|
+
|
|
86
|
+
-- Pattern 1: Owner-only access
|
|
87
|
+
CREATE POLICY "Users can CRUD own profile"
|
|
88
|
+
ON profiles FOR ALL
|
|
89
|
+
USING (auth.uid() = id)
|
|
90
|
+
WITH CHECK (auth.uid() = id);
|
|
91
|
+
|
|
92
|
+
-- Pattern 2: Team-based access via junction table
|
|
93
|
+
CREATE POLICY "Team members can read projects"
|
|
94
|
+
ON projects FOR SELECT
|
|
95
|
+
USING (
|
|
96
|
+
EXISTS (
|
|
97
|
+
SELECT 1 FROM team_members
|
|
98
|
+
WHERE team_members.project_id = projects.id
|
|
99
|
+
AND team_members.user_id = auth.uid()
|
|
100
|
+
)
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
-- Pattern 3: Role-based access via JWT claims
|
|
104
|
+
CREATE POLICY "Admins can manage all projects"
|
|
105
|
+
ON projects FOR ALL
|
|
106
|
+
USING (
|
|
107
|
+
(auth.jwt() -> 'app_metadata' ->> 'role') = 'admin'
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
-- Pattern 4: Public read, authenticated write
|
|
111
|
+
CREATE POLICY "Anyone can read published posts"
|
|
112
|
+
ON posts FOR SELECT
|
|
113
|
+
USING (status = 'published');
|
|
114
|
+
|
|
115
|
+
CREATE POLICY "Authors can manage own posts"
|
|
116
|
+
ON posts FOR ALL
|
|
117
|
+
USING (auth.uid() = author_id)
|
|
118
|
+
WITH CHECK (auth.uid() = author_id);
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## 3. Security Headers Configuration
|
|
124
|
+
|
|
125
|
+
### Next.js Security Headers
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
// next.config.ts
|
|
129
|
+
import type { NextConfig } from 'next';
|
|
130
|
+
|
|
131
|
+
const securityHeaders = [
|
|
132
|
+
{
|
|
133
|
+
key: 'X-DNS-Prefetch-Control',
|
|
134
|
+
value: 'on',
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
key: 'Strict-Transport-Security',
|
|
138
|
+
value: 'max-age=63072000; includeSubDomains; preload',
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
key: 'X-Frame-Options',
|
|
142
|
+
value: 'SAMEORIGIN',
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
key: 'X-Content-Type-Options',
|
|
146
|
+
value: 'nosniff',
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
key: 'Referrer-Policy',
|
|
150
|
+
value: 'strict-origin-when-cross-origin',
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
key: 'Permissions-Policy',
|
|
154
|
+
value: 'camera=(), microphone=(), geolocation=(), interest-cohort=()',
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
key: 'Content-Security-Policy',
|
|
158
|
+
value: [
|
|
159
|
+
"default-src 'self'",
|
|
160
|
+
"script-src 'self' 'unsafe-eval' 'unsafe-inline'", // Tighten in production
|
|
161
|
+
"style-src 'self' 'unsafe-inline' https://fonts.googleapis.com",
|
|
162
|
+
"font-src 'self' https://fonts.gstatic.com",
|
|
163
|
+
"img-src 'self' data: https: blob:",
|
|
164
|
+
"connect-src 'self' https://*.supabase.co wss://*.supabase.co",
|
|
165
|
+
"frame-ancestors 'none'",
|
|
166
|
+
"base-uri 'self'",
|
|
167
|
+
"form-action 'self'",
|
|
168
|
+
].join('; '),
|
|
169
|
+
},
|
|
170
|
+
];
|
|
171
|
+
|
|
172
|
+
const config: NextConfig = {
|
|
173
|
+
async headers() {
|
|
174
|
+
return [
|
|
175
|
+
{
|
|
176
|
+
source: '/(.*)',
|
|
177
|
+
headers: securityHeaders,
|
|
178
|
+
},
|
|
179
|
+
];
|
|
180
|
+
},
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
export default config;
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## 4. Input Sanitization & Validation
|
|
189
|
+
|
|
190
|
+
### Defense-in-Depth Validation Pattern
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
// lib/validators/user-input.ts
|
|
194
|
+
import { z } from 'zod';
|
|
195
|
+
import DOMPurify from 'isomorphic-dompurify';
|
|
196
|
+
|
|
197
|
+
// Layer 1: Schema validation with Zod
|
|
198
|
+
export const UserInputSchema = z.object({
|
|
199
|
+
name: z.string()
|
|
200
|
+
.min(1, 'Name is required')
|
|
201
|
+
.max(100, 'Name must be under 100 characters')
|
|
202
|
+
.regex(/^[a-zA-Z\s\-']+$/, 'Name contains invalid characters'),
|
|
203
|
+
email: z.string().email('Invalid email address'),
|
|
204
|
+
bio: z.string()
|
|
205
|
+
.max(500, 'Bio must be under 500 characters')
|
|
206
|
+
.transform((val) => DOMPurify.sanitize(val, { ALLOWED_TAGS: [] })) // Layer 2: Strip all HTML
|
|
207
|
+
.optional(),
|
|
208
|
+
website: z.string()
|
|
209
|
+
.url('Invalid URL')
|
|
210
|
+
.refine(
|
|
211
|
+
(url) => url.startsWith('https://'), // Layer 3: Only allow HTTPS
|
|
212
|
+
'Only HTTPS URLs are allowed'
|
|
213
|
+
)
|
|
214
|
+
.optional(),
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
// Layer 4: Rate limiting on the endpoint (see middleware)
|
|
218
|
+
// Layer 5: CSRF token validation (see middleware)
|
|
219
|
+
// Layer 6: WAF rules (Cloudflare, AWS WAF)
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## 5. Webhook Security
|
|
225
|
+
|
|
226
|
+
### Stripe Webhook Signature Verification
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
// app/api/webhooks/stripe/route.ts
|
|
230
|
+
import { NextResponse } from 'next/server';
|
|
231
|
+
import Stripe from 'stripe';
|
|
232
|
+
|
|
233
|
+
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
|
|
234
|
+
const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET!;
|
|
235
|
+
|
|
236
|
+
export async function POST(request: Request) {
|
|
237
|
+
const body = await request.text();
|
|
238
|
+
const signature = request.headers.get('stripe-signature');
|
|
239
|
+
|
|
240
|
+
if (!signature) {
|
|
241
|
+
return NextResponse.json({ error: 'Missing signature' }, { status: 400 });
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
let event: Stripe.Event;
|
|
245
|
+
|
|
246
|
+
try {
|
|
247
|
+
// CRITICAL: Always verify webhook signatures
|
|
248
|
+
event = stripe.webhooks.constructEvent(body, signature, webhookSecret);
|
|
249
|
+
} catch (err) {
|
|
250
|
+
console.error('⚠️ Webhook signature verification failed:', err);
|
|
251
|
+
return NextResponse.json({ error: 'Invalid signature' }, { status: 400 });
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Process verified event
|
|
255
|
+
switch (event.type) {
|
|
256
|
+
case 'checkout.session.completed':
|
|
257
|
+
await handleCheckoutCompleted(event.data.object);
|
|
258
|
+
break;
|
|
259
|
+
case 'customer.subscription.deleted':
|
|
260
|
+
await handleSubscriptionCanceled(event.data.object);
|
|
261
|
+
break;
|
|
262
|
+
default:
|
|
263
|
+
console.log(`Unhandled event type: ${event.type}`);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return NextResponse.json({ received: true });
|
|
267
|
+
}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
## 6. Secrets Management
|
|
273
|
+
|
|
274
|
+
### Environment Variable Validation
|
|
275
|
+
|
|
276
|
+
```typescript
|
|
277
|
+
// lib/env.ts — Validate all required env vars at startup
|
|
278
|
+
import { z } from 'zod';
|
|
279
|
+
|
|
280
|
+
const envSchema = z.object({
|
|
281
|
+
// Database
|
|
282
|
+
DATABASE_URL: z.string().url(),
|
|
283
|
+
DATABASE_DIRECT_URL: z.string().url().optional(),
|
|
284
|
+
|
|
285
|
+
// Auth
|
|
286
|
+
CLERK_SECRET_KEY: z.string().startsWith('sk_'),
|
|
287
|
+
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: z.string().startsWith('pk_'),
|
|
288
|
+
|
|
289
|
+
// Supabase
|
|
290
|
+
NEXT_PUBLIC_SUPABASE_URL: z.string().url(),
|
|
291
|
+
NEXT_PUBLIC_SUPABASE_ANON_KEY: z.string().min(1),
|
|
292
|
+
SUPABASE_SERVICE_ROLE_KEY: z.string().min(1),
|
|
293
|
+
|
|
294
|
+
// Stripe
|
|
295
|
+
STRIPE_SECRET_KEY: z.string().startsWith('sk_'),
|
|
296
|
+
STRIPE_WEBHOOK_SECRET: z.string().startsWith('whsec_'),
|
|
297
|
+
|
|
298
|
+
// App
|
|
299
|
+
NODE_ENV: z.enum(['development', 'test', 'production']),
|
|
300
|
+
NEXT_PUBLIC_APP_URL: z.string().url(),
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
// Validate on import — fail fast if missing
|
|
304
|
+
export const env = envSchema.parse(process.env);
|
|
305
|
+
|
|
306
|
+
// Type-safe environment access
|
|
307
|
+
declare global {
|
|
308
|
+
namespace NodeJS {
|
|
309
|
+
interface ProcessEnv extends z.infer<typeof envSchema> {}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
## 7. Audit Logging
|
|
317
|
+
|
|
318
|
+
### Structured Audit Log Pattern
|
|
319
|
+
|
|
320
|
+
```typescript
|
|
321
|
+
// lib/audit.ts
|
|
322
|
+
import { db } from './db';
|
|
323
|
+
import { auditLogs } from './db/schema';
|
|
324
|
+
|
|
325
|
+
interface AuditEvent {
|
|
326
|
+
action: string;
|
|
327
|
+
userId: string;
|
|
328
|
+
resourceType: string;
|
|
329
|
+
resourceId: string;
|
|
330
|
+
metadata?: Record<string, unknown>;
|
|
331
|
+
ipAddress?: string;
|
|
332
|
+
userAgent?: string;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
export async function logAuditEvent(event: AuditEvent): Promise<void> {
|
|
336
|
+
await db.insert(auditLogs).values({
|
|
337
|
+
action: event.action,
|
|
338
|
+
userId: event.userId,
|
|
339
|
+
resourceType: event.resourceType,
|
|
340
|
+
resourceId: event.resourceId,
|
|
341
|
+
metadata: event.metadata ?? {},
|
|
342
|
+
ipAddress: event.ipAddress ?? 'unknown',
|
|
343
|
+
userAgent: event.userAgent ?? 'unknown',
|
|
344
|
+
timestamp: new Date(),
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// Usage
|
|
349
|
+
await logAuditEvent({
|
|
350
|
+
action: 'user.data_export',
|
|
351
|
+
userId: currentUser.id,
|
|
352
|
+
resourceType: 'user',
|
|
353
|
+
resourceId: currentUser.id,
|
|
354
|
+
metadata: { exportFormat: 'csv', recordCount: 1500 },
|
|
355
|
+
ipAddress: request.headers.get('x-forwarded-for') ?? undefined,
|
|
356
|
+
});
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
---
|
|
360
|
+
|
|
361
|
+
## Security Checklist Quick Reference
|
|
362
|
+
|
|
363
|
+
| Category | Check | Tool/Method |
|
|
364
|
+
|----------|-------|-------------|
|
|
365
|
+
| Auth | Token validation | Clerk/NextAuth middleware |
|
|
366
|
+
| Authz | RLS policies | `pg_policies` view |
|
|
367
|
+
| Headers | CSP, HSTS | securityheaders.com |
|
|
368
|
+
| Secrets | No hardcoded | `grep -rn` + git history |
|
|
369
|
+
| Input | Validation | Zod schemas |
|
|
370
|
+
| XSS | Output encoding | DOMPurify + CSP |
|
|
371
|
+
| CSRF | Token validation | SameSite cookies |
|
|
372
|
+
| SQLi | Parameterized | ORM (Drizzle/Prisma) |
|
|
373
|
+
| Dependencies | CVE scan | `npm audit`, Snyk |
|
|
374
|
+
| Webhooks | Signature verify | Provider SDK |
|
|
375
|
+
|
|
376
|
+
---
|
|
377
|
+
|
|
378
|
+
## Conclusion
|
|
379
|
+
Security is not a feature — it's a requirement. Implement defense-in-depth with multiple layers of protection. Validate inputs, sanitize outputs, verify identities, authorize access, log actions, and monitor anomalies.
|