security: fix trace ownership bypass and externalize secrets to .env

- Add userId guard in trace upsert to prevent cross-user overwrites
- Move AUTH_SECRET, STRIPE_WEBHOOK_SECRET, POSTGRES_PASSWORD to .env
- docker-compose.yml now references env vars instead of hardcoded secrets
- Add .env.example with placeholder values for documentation
This commit is contained in:
Vectry
2026-02-10 16:53:57 +00:00
parent 539d35b649
commit e9cd11735c
3 changed files with 31 additions and 10 deletions

16
.env.example Normal file
View File

@@ -0,0 +1,16 @@
# Authentication
AUTH_SECRET= # Generate with: openssl rand -base64 32
# Stripe
STRIPE_SECRET_KEY= # sk_live_... or sk_test_...
STRIPE_WEBHOOK_SECRET= # whsec_...
STRIPE_STARTER_PRICE_ID=price_1SzJUlR8i0An4Wz7gZeYgzBY
STRIPE_PRO_PRICE_ID=price_1SzJVWR8i0An4Wz755hBrxzn
# Database (optional — defaults to agentlens/agentlens/agentlens)
POSTGRES_USER=agentlens
POSTGRES_PASSWORD=
POSTGRES_DB=agentlens
# Email (optional — email features disabled if not set)
EMAIL_PASSWORD=

View File

@@ -241,9 +241,14 @@ export async function POST(request: NextRequest) {
for (const trace of body.traces) { for (const trace of body.traces) {
const existing = await tx.trace.findUnique({ const existing = await tx.trace.findUnique({
where: { id: trace.id }, where: { id: trace.id },
select: { id: true }, select: { id: true, userId: true },
}); });
// Security: prevent cross-user trace overwrite
if (existing && existing.userId !== userId) {
continue; // skip traces owned by other users
}
const traceData = { const traceData = {
name: trace.name, name: trace.name,
sessionId: trace.sessionId, sessionId: trace.sessionId,

View File

@@ -8,13 +8,13 @@ services:
environment: environment:
- NODE_ENV=production - NODE_ENV=production
- REDIS_URL=redis://redis:6379 - REDIS_URL=redis://redis:6379
- DATABASE_URL=postgresql://agentlens:agentlens@postgres:5432/agentlens - DATABASE_URL=postgresql://${POSTGRES_USER:-agentlens}:${POSTGRES_PASSWORD:-agentlens}@postgres:5432/${POSTGRES_DB:-agentlens}
- AUTH_SECRET=Ge0Gh6bObko0Gdrzv+l0qKHgvut3M7Av8mDFQG9fYzs= - AUTH_SECRET=${AUTH_SECRET}
- AUTH_TRUST_HOST=true - AUTH_TRUST_HOST=true
- STRIPE_SECRET_KEY=${STRIPE_SECRET_KEY:-} - STRIPE_SECRET_KEY=${STRIPE_SECRET_KEY:-}
- STRIPE_WEBHOOK_SECRET=whsec_ZGT3JCrEK6GWP3cIMvYfrfLplZ3rMn0m - STRIPE_WEBHOOK_SECRET=${STRIPE_WEBHOOK_SECRET}
- STRIPE_STARTER_PRICE_ID=price_1SzJUlR8i0An4Wz7gZeYgzBY - STRIPE_STARTER_PRICE_ID=${STRIPE_STARTER_PRICE_ID:-price_1SzJUlR8i0An4Wz7gZeYgzBY}
- STRIPE_PRO_PRICE_ID=price_1SzJVWR8i0An4Wz755hBrxzn - STRIPE_PRO_PRICE_ID=${STRIPE_PRO_PRICE_ID:-price_1SzJVWR8i0An4Wz755hBrxzn}
- EMAIL_PASSWORD=${EMAIL_PASSWORD:-} - EMAIL_PASSWORD=${EMAIL_PASSWORD:-}
depends_on: depends_on:
redis: redis:
@@ -45,9 +45,9 @@ services:
postgres: postgres:
image: postgres:16-alpine image: postgres:16-alpine
environment: environment:
- POSTGRES_USER=agentlens - POSTGRES_USER=${POSTGRES_USER:-agentlens}
- POSTGRES_PASSWORD=agentlens - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-agentlens}
- POSTGRES_DB=agentlens - POSTGRES_DB=${POSTGRES_DB:-agentlens}
volumes: volumes:
- agentlens_postgres_data:/var/lib/postgresql/data - agentlens_postgres_data:/var/lib/postgresql/data
healthcheck: healthcheck:
@@ -72,7 +72,7 @@ services:
target: builder target: builder
command: npx prisma db push --schema=packages/database/prisma/schema.prisma --skip-generate command: npx prisma db push --schema=packages/database/prisma/schema.prisma --skip-generate
environment: environment:
- DATABASE_URL=postgresql://agentlens:agentlens@postgres:5432/agentlens - DATABASE_URL=postgresql://${POSTGRES_USER:-agentlens}:${POSTGRES_PASSWORD:-agentlens}@postgres:5432/${POSTGRES_DB:-agentlens}
depends_on: depends_on:
postgres: postgres:
condition: service_healthy condition: service_healthy