fix: address critical security issues in template metadata

- Fix SQL injection vulnerability in template-repository.ts
  - Use proper parameterization with SQLite concatenation operator
  - Escape JSON strings correctly for LIKE queries
  - Prevent malicious SQL through filter parameters

- Add input sanitization for OpenAI API calls
  - Sanitize template names and descriptions before sending to API
  - Remove control characters and prompt injection patterns
  - Limit input length to prevent token abuse

- Lower temperature to 0.3 for consistent structured outputs

- Add comprehensive test coverage
  - 100+ new tests for metadata functionality
  - Security-focused tests for SQL injection prevention
  - Integration tests with real database operations

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
czlonkowski
2025-09-15 00:51:41 +02:00
parent 1e586c0b23
commit c18c4e7584
9 changed files with 2257 additions and 21 deletions

View File

@@ -111,10 +111,15 @@ export class MetadataGenerator {
// Extract node information for analysis
const nodesSummary = this.summarizeNodes(template.nodes);
// Build context for the AI
// Sanitize template name and description to prevent prompt injection
const sanitizedName = this.sanitizeInput(template.name, 200);
const sanitizedDescription = template.description ?
this.sanitizeInput(template.description, 500) : '';
// Build context for the AI with sanitized inputs
const context = [
`Template: ${template.name}`,
template.description ? `Description: ${template.description}` : '',
`Template: ${sanitizedName}`,
sanitizedDescription ? `Description: ${sanitizedDescription}` : '',
`Nodes Used (${template.nodes.length}): ${nodesSummary}`,
template.workflow ? `Workflow has ${template.workflow.nodes?.length || 0} nodes with ${Object.keys(template.workflow.connections || {}).length} connections` : ''
].filter(Boolean).join('\n');
@@ -125,7 +130,7 @@ export class MetadataGenerator {
url: '/v1/chat/completions',
body: {
model: this.model,
temperature: 1,
temperature: 0.3, // Lower temperature for more consistent structured outputs
max_completion_tokens: 1000,
response_format: {
type: 'json_schema',
@@ -145,6 +150,27 @@ export class MetadataGenerator {
};
}
/**
* Sanitize input to prevent prompt injection and control token usage
*/
private sanitizeInput(input: string, maxLength: number): string {
// Truncate to max length
let sanitized = input.slice(0, maxLength);
// Remove control characters and excessive whitespace
sanitized = sanitized.replace(/[\x00-\x1F\x7F-\x9F]/g, '');
// Replace multiple spaces/newlines with single space
sanitized = sanitized.replace(/\s+/g, ' ').trim();
// Remove potential prompt injection patterns
sanitized = sanitized.replace(/\b(system|assistant|user|human|ai):/gi, '');
sanitized = sanitized.replace(/```[\s\S]*?```/g, ''); // Remove code blocks
sanitized = sanitized.replace(/\[INST\]|\[\/INST\]/g, ''); // Remove instruction markers
return sanitized;
}
/**
* Summarize nodes for better context
*/
@@ -243,7 +269,7 @@ export class MetadataGenerator {
try {
const completion = await this.client.chat.completions.create({
model: this.model,
temperature: 1,
temperature: 0.3, // Lower temperature for more consistent structured outputs
max_completion_tokens: 1000,
response_format: {
type: 'json_schema',