Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions backend/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ enum PageStatus {

model Episode {
id String @id @default(uuid())
userId String // Owner of this episode
seedInput Json
outline Json?
rendererModel String?
Expand All @@ -24,12 +25,15 @@ model Episode {
pages Page[]
characters Character[]

@@index([userId])
@@index([createdAt])
@@index([updatedAt])
@@index([userId, createdAt])
}

model Page {
id String @id @default(uuid())
userId String // Owner of this page
episodeId String
pageNumber Int
status PageStatus
Expand All @@ -44,9 +48,11 @@ model Page {
canvas Canvas?

@@unique([episodeId, pageNumber])
@@index([userId])
@@index([episodeId])
@@index([status])
@@index([episodeId, status])
@@index([userId, episodeId])
}

model Character {
Expand All @@ -67,6 +73,7 @@ model Character {

model Canvas {
id String @id @default(uuid())
userId String // Owner of this canvas
pageId String @unique
canvasData Json? // Serialized Fabric.js canvas data
thumbnailUrl String?
Expand Down
15 changes: 14 additions & 1 deletion backend/src/planner/planner.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
import { generateStubOutline, mergeWithStub } from './planner.fallback';
import { LoggerService } from '../observability/logger.service';
import { TracingService } from '../observability/tracing.service';
import { validateAndSanitizeSeed, containsInjectionPatterns } from './prompt-sanitizer';

/**
* Error types for better error handling
Expand Down Expand Up @@ -130,14 +131,26 @@ export class PlannerService {
*/
private validateInput(seed: EpisodeSeed): void {
try {
// First validate structure with Zod
EpisodeSeedSchema.parse(seed);
this.logger.debug('Input validation passed');

// Then sanitize and validate content
const sanitized = validateAndSanitizeSeed(seed);

// Check for injection patterns in original seed (for monitoring)
const seedString = JSON.stringify(seed);
if (containsInjectionPatterns(seedString)) {
this.logger.warn('Potential prompt injection pattern detected in seed input');
}

this.logger.debug('Input validation and sanitization passed');
} catch (error) {
if (error instanceof ZodError) {
const details = formatZodError(error);
this.logger.error('Input validation failed', undefined, { details });
throw new PlannerValidationError('Invalid episode seed data', details);
}
this.logger.error('Input sanitization failed', error instanceof Error ? error.stack : undefined);
throw error;
}
}
Expand Down