Protokoll #1696
| ID | 1696 |
|---|---|
| Zeitstempel | 2025-12-20 19:42:30.618315 |
| Client | root |
| IP | Warning: Undefined array key "request_ip" in /var/www/dev.campus.systemische-tools.de/src/View/protokoll/show.php on line 13 Deprecated: htmlspecialchars(): Passing null to parameter #1 ($string) of type string is deprecated in /var/www/dev.campus.systemische-tools.de/src/View/protokoll/show.php on line 13 |
| Modell | claude-sonnet-4-20250514 |
| Status | completed |
| Tokens | 4,792 (Input: 0, Output: 0) |
| Dauer | 94 ms |
| Request-Zeit | Warning: Undefined array key "request_timestamp" in /var/www/dev.campus.systemische-tools.de/src/View/protokoll/show.php on line 18 |
| Response-Zeit | - |
Warning: Undefined array key "error_message" in /var/www/dev.campus.systemische-tools.de/src/View/protokoll/show.php on line 22
Request
{
"event": "PreToolUse",
"tool_name": "Edit",
"tool_input": {
"file_path": "\/var\/www\/dev.campus.systemische-tools.de\/src\/Controller\/ChatController.php",
"old_string": " \/**\n * Validate and sanitize system prompt\n *\/\n private function validateSystemPrompt(string $prompt): string\n {\n \/\/ Max length: 2000 characters\n $prompt = mb_substr($prompt, 0, 2000);\n\n \/\/ Remove potentially dangerous patterns\n $dangerousPatterns = [\n '\/ignore\\s+(all\\s+)?previous\\s+instructions\/i',\n '\/forget\\s+(all\\s+)?previous\/i',\n '\/disregard\\s+(all\\s+)?instructions\/i',\n '\/system\\s*:\\s*override\/i',\n ];\n\n foreach ($dangerousPatterns as $pattern) {\n $prompt = preg_replace($pattern, '[removed]', $prompt) ?? $prompt;\n }\n\n return trim($prompt);\n }",
"new_string": " \/**\n * Validate system prompt input.\n *\n * NOTE: Pattern-based prompt injection filtering was intentionally removed.\n * Such filtering provides false security since attackers can easily rephrase\n * malicious instructions. LLM prompt injection cannot be reliably prevented\n * through input sanitization alone.\n *\n * Instead, we apply:\n * - Length limits (to prevent resource abuse)\n * - Logging (for monitoring and incident response)\n *\n * True prompt injection mitigation requires architectural measures like\n * sandboxing LLM outputs and limiting LLM capabilities.\n *\/\n private function validateSystemPrompt(string $prompt): string\n {\n \/\/ Length limit: prevent resource abuse\n $maxLength = 2000;\n $wasTruncated = mb_strlen($prompt) > $maxLength;\n $prompt = mb_substr($prompt, 0, $maxLength);\n\n \/\/ Log custom system prompts for monitoring\n $this->logSystemPromptChange($prompt, $wasTruncated);\n\n return trim($prompt);\n }\n\n \/**\n * Log system prompt changes for security monitoring.\n *\/\n private function logSystemPromptChange(string $prompt, bool $wasTruncated): void\n {\n $logData = [\n 'prompt_length' => mb_strlen($prompt),\n 'truncated' => $wasTruncated,\n 'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown',\n 'timestamp' => date('c'),\n ];\n\n \/\/ Log to database via ki_dev.mcp_log\n try {\n $devDb = \\Infrastructure\\Config\\DatabaseFactory::dev();\n $stmt = $devDb->prepare(\n \"INSERT INTO mcp_log (tool, operation, parameters, result, logged_at)\n VALUES ('chat', 'system_prompt_change', :params, 'logged', NOW())\"\n );\n $stmt->execute(['params' => json_encode($logData)]);\n } catch (\\Exception $e) {\n \/\/ Silently fail - logging should not break functionality\n }\n }"
}
}
Response
{
"tool_response": {
"filePath": "\/var\/www\/dev.campus.systemische-tools.de\/src\/Controller\/ChatController.php",
"oldString": " \/**\n * Validate and sanitize system prompt\n *\/\n private function validateSystemPrompt(string $prompt): string\n {\n \/\/ Max length: 2000 characters\n $prompt = mb_substr($prompt, 0, 2000);\n\n \/\/ Remove potentially dangerous patterns\n $dangerousPatterns = [\n '\/ignore\\s+(all\\s+)?previous\\s+instructions\/i',\n '\/forget\\s+(all\\s+)?previous\/i',\n '\/disregard\\s+(all\\s+)?instructions\/i',\n '\/system\\s*:\\s*override\/i',\n ];\n\n foreach ($dangerousPatterns as $pattern) {\n $prompt = preg_replace($pattern, '[removed]', $prompt) ?? $prompt;\n }\n\n return trim($prompt);\n }",
"newString": " \/**\n * Validate system prompt input.\n *\n * NOTE: Pattern-based prompt injection filtering was intentionally removed.\n * Such filtering provides false security since attackers can easily rephrase\n * malicious instructions. LLM prompt injection cannot be reliably prevented\n * through input sanitization alone.\n *\n * Instead, we apply:\n * - Length limits (to prevent resource abuse)\n * - Logging (for monitoring and incident response)\n *\n * True prompt injection mitigation requires architectural measures like\n * sandboxing LLM outputs and limiting LLM capabilities.\n *\/\n private function validateSystemPrompt(string $prompt): string\n {\n \/\/ Length limit: prevent resource abuse\n $maxLength = 2000;\n $wasTruncated = mb_strlen($prompt) > $maxLength;\n $prompt = mb_substr($prompt, 0, $maxLength);\n\n \/\/ Log custom system prompts for monitoring\n $this->logSystemPromptChange($prompt, $wasTruncated);\n\n return trim($prompt);\n }\n\n \/**\n * Log system prompt changes for security monitoring.\n *\/\n private function logSystemPromptChange(string $prompt, bool $wasTruncated): void\n {\n $logData = [\n 'prompt_length' => mb_strlen($prompt),\n 'truncated' => $wasTruncated,\n 'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown',\n 'timestamp' => date('c'),\n ];\n\n \/\/ Log to database via ki_dev.mcp_log\n try {\n $devDb = \\Infrastructure\\Config\\DatabaseFactory::dev();\n $stmt = $devDb->prepare(\n \"INSERT INTO mcp_log (tool, operation, parameters, result, logged_at)\n VALUES ('chat', 'system_prompt_change', :params, 'logged', NOW())\"\n );\n $stmt->execute(['params' => json_encode($logData)]);\n } catch (\\Exception $e) {\n \/\/ Silently fail - logging should not break functionality\n }\n }",
"originalFile": "<?php\n\nnamespace Controller;\n\nuse Framework\\Controller;\nuse Infrastructure\\AI\\AIConfig;\nuse Infrastructure\\AI\\ChatService;\nuse Infrastructure\\AI\\QdrantService;\nuse Infrastructure\\Persistence\\SystemPromptRepository;\n\nclass ChatController extends Controller\n{\n private ChatService $chatService;\n private QdrantService $qdrantService;\n private \\PDO $db;\n private SystemPromptRepository $systemPromptRepo;\n\n \/** @var array<string>|null Cached collections list *\/\n private ?array $collectionsCache = null;\n\n public function __construct()\n {\n $config = AIConfig::fromCredentialsFile();\n $this->chatService = $config->createChatService();\n $this->qdrantService = $config->createQdrantService();\n $this->db = $this->initializeDatabase();\n $this->systemPromptRepo = new SystemPromptRepository();\n }\n\n \/**\n * GET \/chat\n * Show chat interface with session list, create new session if none\n *\/\n public function index(): void\n {\n $sessions = $this->getSessions();\n\n \/\/ Create new session and redirect\n $uuid = $this->createSession();\n header('Location: \/chat\/' . $uuid);\n exit;\n }\n\n \/**\n * GET \/chat\/{uuid}\n * Show specific chat session\n *\/\n public function show(string $uuid): void\n {\n $session = $this->getSession($uuid);\n\n if ($session === null) {\n header('Location: \/chat');\n exit;\n }\n\n $messages = $this->getMessages($session['id']);\n $sessions = $this->getSessions();\n $authorProfiles = $this->getAuthorProfiles();\n $systemPrompts = $this->systemPromptRepo->findAllActive();\n $collections = $this->getAvailableCollections();\n\n $this->view('chat.index', [\n 'title' => $session['title'] ?? 'KI-Chat',\n 'session' => $session,\n 'messages' => $messages,\n 'sessions' => $sessions,\n 'authorProfiles' => $authorProfiles,\n 'systemPrompts' => $systemPrompts,\n 'collections' => $collections,\n ]);\n }\n\n \/**\n * GET \/chat\/sessions (HTMX partial)\n * Return session list HTML\n *\/\n public function sessionList(): void\n {\n $sessions = $this->getSessions();\n $this->view('chat.partials.session-list', [\n 'sessions' => $sessions,\n 'currentUuid' => $_GET['current'] ?? null,\n ]);\n }\n\n \/**\n * POST \/chat\/{uuid}\/message (HTMX)\n * Process message and return HTML response\n *\/\n public function message(string $uuid): void\n {\n $session = $this->getSession($uuid);\n\n if ($session === null) {\n echo '<div class=\"chat-error\">Session nicht gefunden.<\/div>';\n\n return;\n }\n\n $question = trim($_POST['message'] ?? '');\n $model = $this->validateModel($_POST['model'] ?? $session['model']);\n $sessionCollections = json_decode($session['collections'] ?? '[\"documents\"]', true) ?: ['documents'];\n $collections = $this->validateCollections($_POST['collections'] ?? $sessionCollections);\n $contextLimit = $this->validateContextLimit((int) ($_POST['context_limit'] ?? $session['context_limit'] ?? 5));\n $authorProfileId = $this->validateAuthorProfileId((int) ($_POST['author_profile_id'] ?? $session['author_profile_id'] ?? 0));\n $systemPromptId = (int) ($_POST['system_prompt_id'] ?? $session['system_prompt_id'] ?? 1);\n $temperature = $this->validateTemperature((float) ($_POST['temperature'] ?? $session['temperature'] ?? 0.7));\n $maxTokens = $this->validateMaxTokens((int) ($_POST['max_tokens'] ?? $session['max_tokens'] ?? 4096));\n\n \/\/ Update session if settings changed\n $currentLimit = (int) ($session['context_limit'] ?? 5);\n $currentProfileId = (int) ($session['author_profile_id'] ?? 0);\n $currentTemperature = (float) ($session['temperature'] ?? 0.7);\n $currentMaxTokens = (int) ($session['max_tokens'] ?? 4096);\n $collectionsJson = json_encode($collections);\n if ($model !== $session['model'] || $collectionsJson !== ($session['collections'] ?? '[\"documents\"]') || $contextLimit !== $currentLimit || $authorProfileId !== $currentProfileId || $temperature !== $currentTemperature || $maxTokens !== $currentMaxTokens) {\n $this->updateSessionSettings($session['id'], $model, $collections, $contextLimit, $authorProfileId, $temperature, $maxTokens);\n }\n\n if ($question === '') {\n echo '<div class=\"chat-error\">Bitte gib eine Frage ein.<\/div>';\n\n return;\n }\n\n try {\n \/\/ Save user message\n $this->saveMessage($session['id'], 'user', $question, $model);\n\n \/\/ Auto-set title from first message\n if ($session['title'] === null) {\n $title = mb_substr($question, 0, 50) . (mb_strlen($question) > 50 ? '...' : '');\n $this->updateSessionTitle($session['id'], $title);\n }\n\n \/\/ Get style prompt from author profile\n $stylePrompt = $this->getStylePromptFromProfile($authorProfileId);\n\n \/\/ Get system prompt from database\n $systemPromptData = $this->systemPromptRepo->findById($systemPromptId);\n $customSystemPrompt = $systemPromptData['content'] ?? null;\n\n \/\/ Track timing\n $startMicrotime = microtime(true);\n\n \/\/ Get response from AI\n $result = $this->askChat(\n $question,\n $model,\n $collections,\n $contextLimit,\n $stylePrompt,\n $customSystemPrompt,\n $temperature,\n $maxTokens\n );\n\n $endMicrotime = microtime(true);\n\n if (isset($result['error'])) {\n echo '<div class=\"chat-error\">' . htmlspecialchars($result['error']) . '<\/div>';\n\n return;\n }\n\n \/\/ Save assistant message with full tracking\n $tokensInput = $result['usage']['input_tokens'] ?? null;\n $tokensOutput = $result['usage']['output_tokens'] ?? null;\n $sources = $result['sources'] ?? [];\n\n $this->saveMessage(\n $session['id'],\n 'assistant',\n $result['answer'],\n $model,\n $tokensInput,\n $tokensOutput,\n $sources,\n $startMicrotime,\n $endMicrotime,\n $authorProfileId > 0 ? $authorProfileId : null,\n $systemPromptId > 0 ? $systemPromptId : null,\n $collectionsJson,\n $contextLimit\n );\n\n $this->renderResponse($question, $result, $model);\n } catch (\\Exception $e) {\n echo '<div class=\"chat-error\">Fehler: ' . htmlspecialchars($e->getMessage()) . '<\/div>';\n }\n }\n\n \/**\n * POST \/chat\/{uuid}\/title (HTMX)\n * Update session title\n *\/\n public function updateTitle(string $uuid): void\n {\n $session = $this->getSession($uuid);\n\n if ($session === null) {\n http_response_code(404);\n echo '';\n\n return;\n }\n\n $title = trim($_POST['title'] ?? '');\n\n \/\/ Validate title\n if ($title === '') {\n $title = 'Neuer Chat';\n }\n\n \/\/ Max 100 characters\n $title = mb_substr($title, 0, 100);\n\n \/\/ Save to database\n $this->updateSessionTitle($session['id'], $title);\n\n \/\/ Return updated title HTML\n echo htmlspecialchars($title);\n }\n\n \/**\n * POST \/chat\/{uuid}\/system-prompt (HTMX)\n * Update session system prompt\n *\/\n public function systemPrompt(string $uuid): void\n {\n $session = $this->getSession($uuid);\n\n if ($session === null) {\n http_response_code(404);\n echo '<div class=\"chat-error\">Session nicht gefunden.<\/div>';\n\n return;\n }\n\n $systemPrompt = trim($_POST['system_prompt'] ?? '');\n\n \/\/ Validate and sanitize system prompt\n $systemPrompt = $this->validateSystemPrompt($systemPrompt);\n\n \/\/ Save to database\n $this->updateSystemPrompt($session['id'], $systemPrompt);\n\n \/\/ Return success message\n echo '<div class=\"chat-success\">System-Prompt gespeichert.<\/div>';\n }\n\n \/**\n * GET \/chat\/{uuid}\/system-prompt (HTMX)\n * Get system prompt form\n *\/\n public function getSystemPrompt(string $uuid): void\n {\n $session = $this->getSession($uuid);\n\n if ($session === null) {\n http_response_code(404);\n echo '<div class=\"chat-error\">Session nicht gefunden.<\/div>';\n\n return;\n }\n\n $defaultPrompt = $this->getDefaultSystemPrompt();\n $currentPrompt = $session['system_prompt'] ?? '';\n\n $this->view('chat.partials.system-prompt-modal', [\n 'session' => $session,\n 'currentPrompt' => $currentPrompt,\n 'defaultPrompt' => $defaultPrompt,\n ]);\n }\n\n \/**\n * DELETE \/chat\/{uuid}\n * Delete a session\n *\/\n public function delete(string $uuid): void\n {\n $session = $this->getSession($uuid);\n\n if ($session !== null) {\n $stmt = $this->db->prepare('DELETE FROM chat_sessions WHERE id = ?');\n $stmt->execute([$session['id']]);\n }\n\n \/\/ Return success for HTMX\n header('HX-Redirect: \/chat');\n echo 'OK';\n }\n\n \/\/ ========== Private Helper Methods ==========\n\n \/**\n * Initialize database connection\n *\/\n private function initializeDatabase(): \\PDO\n {\n return \\Infrastructure\\Config\\DatabaseFactory::content();\n }\n\n \/**\n * Create a new chat session\n *\/\n private function createSession(): string\n {\n $uuid = $this->generateUuid();\n\n $stmt = $this->db->prepare(\n 'INSERT I... [TRUNCATED-7181593d2c38a7ee]",
"structuredPatch": [
{
"oldStart": 609,
"oldLines": 26,
"newStart": 609,
"newLines": 56,
"lines": [
" }",
" ",
" \/**",
"- * Validate and sanitize system prompt",
"+ * Validate system prompt input.",
"+ *",
"+ * NOTE: Pattern-based prompt injection filtering was intentionally removed.",
"+ * Such filtering provides false security since attackers can easily rephrase",
"+ * malicious instructions. LLM prompt injection cannot be reliably prevented",
"+ * through input sanitization alone.",
"+ *",
"+ * Instead, we apply:",
"+ * - Length limits (to prevent resource abuse)",
"+ * - Logging (for monitoring and incident response)",
"+ *",
"+ * True prompt injection mitigation requires architectural measures like",
"+ * sandboxing LLM outputs and limiting LLM capabilities.",
" *\/",
" private function validateSystemPrompt(string $prompt): string",
" {",
"- \/\/ Max length: 2000 characters",
"- $prompt = mb_substr($prompt, 0, 2000);",
"+ \/\/ Length limit: prevent resource abuse",
"+ $maxLength = 2000;",
"+ $wasTruncated = mb_strlen($prompt) > $maxLength;",
"+ $prompt = mb_substr($prompt, 0, $maxLength);",
" ",
"- \/\/ Remove potentially dangerous patterns",
"- $dangerousPatterns = [",
"- '\/ignore\\s+(all\\s+)?previous\\s+instructions\/i',",
"- '\/forget\\s+(all\\s+)?previous\/i',",
"- '\/disregard\\s+(all\\s+)?instructions\/i',",
"- '\/system\\s*:\\s*override\/i',",
"+ \/\/ Log custom system prompts for monitoring",
"+ $this->logSystemPromptChange($prompt, $wasTruncated);",
"+",
"+ return trim($prompt);",
"+ }",
"+",
"+ \/**",
"+ * Log system prompt changes for security monitoring.",
"+ *\/",
"+ private function logSystemPromptChange(string $prompt, bool $wasTruncated): void",
"+ {",
"+ $logData = [",
"+ 'prompt_length' => mb_strlen($prompt),",
"+ 'truncated' => $wasTruncated,",
"+ 'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown',",
"+ 'timestamp' => date('c'),",
" ];",
" ",
"- foreach ($dangerousPatterns as $pattern) {",
"- $prompt = preg_replace($pattern, '[removed]', $prompt) ?? $prompt;",
"+ \/\/ Log to database via ki_dev.mcp_log",
"+ try {",
"+ $devDb = \\Infrastructure\\Config\\DatabaseFactory::dev();",
"+ $stmt = $devDb->prepare(",
"+ \"INSERT INTO mcp_log (tool, operation, parameters, result, logged_at)",
"+ VALUES ('chat', 'system_prompt_change', :params, 'logged', NOW())\"",
"+ );",
"+ $stmt->execute(['params' => json_encode($logData)]);",
"+ } catch (\\Exception $e) {",
"+ \/\/ Silently fail - logging should not break functionality",
" }",
"-",
"- return trim($prompt);",
" }",
" ",
" \/**"
]
}
],
"userModified": false,
"replaceAll": false
}
}