clientIp} sent malformed JSON: {$this->jsonError}"; $context = ExceptionContext::forOperation('http.json_parsing', 'Http') ->withData([ 'client_ip' => $this->clientIp, 'json_error' => $this->jsonError, 'json_error_code' => $this->jsonErrorCode, 'json_data_length' => strlen($this->jsonData), 'event_identifier' => "http_malformed_json:{$this->clientIp}", 'category' => 'http_validation', 'requires_alert' => false, ]) ->withDebug([ 'json_data_sample' => substr($this->jsonData, 0, 100), // Erste 100 Zeichen für Debug ]) ->withMetadata([ 'security_event' => true, 'owasp_compliant' => true, 'log_level' => 'INFO', 'http_error' => true, ]); parent::__construct( message: $message, context: $context, code: 400, // Bad Request previous: $previous, errorCode: HttpErrorCode::BAD_REQUEST ); } // === Factory Methods für verschiedene JSON-Fehler === public static function syntaxError(string $clientIp, string $jsonData = ''): self { return new self($clientIp, 'Syntax error', JSON_ERROR_SYNTAX, $jsonData); } public static function depthExceeded(string $clientIp, string $jsonData = ''): self { return new self($clientIp, 'Maximum stack depth exceeded', JSON_ERROR_DEPTH, $jsonData); } public static function ctrlCharError(string $clientIp, string $jsonData = ''): self { return new self($clientIp, 'Unexpected control character found', JSON_ERROR_CTRL_CHAR, $jsonData); } public static function utf8Error(string $clientIp, string $jsonData = ''): self { return new self($clientIp, 'Malformed UTF-8 characters', JSON_ERROR_UTF8, $jsonData); } public static function fromJsonLastError(string $clientIp, string $jsonData = ''): self { $error = json_last_error_msg(); $code = json_last_error(); return new self($clientIp, $error, $code, $jsonData); } /** * Gibt OWASP-konforme Event-Daten zurück */ public function getSecurityEventData(): array { return [ 'event_identifier' => "http_malformed_json:{$this->clientIp}", 'description' => "Client {$this->clientIp} sent malformed JSON: {$this->jsonError}", 'category' => 'http_validation', 'log_level' => 'INFO', 'requires_alert' => false, 'client_ip' => $this->clientIp, 'json_error' => $this->jsonError, 'json_error_code' => $this->jsonErrorCode, ]; } /** * Gibt benutzerfreundliche Fehlermeldung zurück */ public function getUserMessage(): string { return match ($this->jsonErrorCode) { JSON_ERROR_SYNTAX => 'Invalid JSON format. Please check your JSON syntax.', JSON_ERROR_DEPTH => 'JSON structure too complex. Please reduce nesting depth.', JSON_ERROR_CTRL_CHAR => 'Invalid characters in JSON. Please check for control characters.', JSON_ERROR_UTF8 => 'Invalid UTF-8 encoding in JSON. Please check character encoding.', default => 'Invalid JSON format. Please check your request body.', }; } /** * Gibt detaillierte Fehlerbeschreibung für Entwickler zurück */ public function getDeveloperMessage(): string { $position = $this->findErrorPosition(); $positionInfo = $position ? " at position {$position}" : ''; return "JSON parsing failed: {$this->jsonError}{$positionInfo}"; } /** * Versucht die Position des JSON-Fehlers zu finden */ private function findErrorPosition(): ?int { if (empty($this->jsonData)) { return null; } // Vereinfachte Positionsbestimmung // In einer echten Implementierung könnte hier ein detaillierterer Parser verwendet werden $testData = $this->jsonData; $position = 0; while ($position < strlen($testData)) { $substr = substr($testData, 0, $position + 1); json_decode($substr); if (json_last_error() !== JSON_ERROR_NONE) { return $position; } $position++; } return null; } /** * Gibt JSON-Error-Code als String zurück */ public function getJsonErrorName(): string { return match ($this->jsonErrorCode) { JSON_ERROR_NONE => 'JSON_ERROR_NONE', JSON_ERROR_DEPTH => 'JSON_ERROR_DEPTH', JSON_ERROR_STATE_MISMATCH => 'JSON_ERROR_STATE_MISMATCH', JSON_ERROR_CTRL_CHAR => 'JSON_ERROR_CTRL_CHAR', JSON_ERROR_SYNTAX => 'JSON_ERROR_SYNTAX', JSON_ERROR_UTF8 => 'JSON_ERROR_UTF8', JSON_ERROR_RECURSION => 'JSON_ERROR_RECURSION', JSON_ERROR_INF_OR_NAN => 'JSON_ERROR_INF_OR_NAN', JSON_ERROR_UNSUPPORTED_TYPE => 'JSON_ERROR_UNSUPPORTED_TYPE', default => 'UNKNOWN_JSON_ERROR', }; } /** * Prüft ob der Fehler ein häufiger Entwicklerfehler ist */ public function isCommonDeveloperError(): bool { return in_array($this->jsonErrorCode, [ JSON_ERROR_SYNTAX, JSON_ERROR_CTRL_CHAR, JSON_ERROR_UTF8, ]); } }