Files
michaelschiemer/src/Framework/Exception/Http/MalformedJsonException.php
Michael Schiemer fc3d7e6357 feat(Production): Complete production deployment infrastructure
- Add comprehensive health check system with multiple endpoints
- Add Prometheus metrics endpoint
- Add production logging configurations (5 strategies)
- Add complete deployment documentation suite:
  * QUICKSTART.md - 30-minute deployment guide
  * DEPLOYMENT_CHECKLIST.md - Printable verification checklist
  * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle
  * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference
  * production-logging.md - Logging configuration guide
  * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation
  * README.md - Navigation hub
  * DEPLOYMENT_SUMMARY.md - Executive summary
- Add deployment scripts and automation
- Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment
- Update README with production-ready features

All production infrastructure is now complete and ready for deployment.
2025-10-25 19:18:37 +02:00

195 lines
6.5 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Framework\Exception\Http;
use App\Framework\Exception\Core\HttpErrorCode;
use App\Framework\Exception\ExceptionContext;
use App\Framework\Exception\FrameworkException;
/**
* Ausnahme für fehlerhaft formatiertes JSON
*
* Verwendet OWASP-konforme Nachrichten für JSON-Parsing-Fehler
*/
final class MalformedJsonException extends FrameworkException
{
/**
* @param string $clientIp Client-IP für Security-Tracking
* @param string $jsonError JSON-Parser-Fehlermeldung
* @param int $jsonErrorCode JSON-Error-Code
* @param string $jsonData Ursprüngliche JSON-Daten (gekürzt)
* @param \Throwable|null $previous Vorherige Ausnahme
*/
public function __construct(
public readonly string $clientIp,
public readonly string $jsonError,
public readonly int $jsonErrorCode = JSON_ERROR_SYNTAX,
public readonly string $jsonData = '',
?\Throwable $previous = null
) {
// OWASP-konforme Nachricht mit Platzhaltern
$message = "Client {$this->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,
]);
}
}