'INVALID_JSON', 'message' => 'JSON payload must be an object' ] ]; } // Bloqueia array indexado [] if (array_is_list($data)) { return [ false, [ 'code' => 'INVALID_JSON_STRUCTURE', 'message' => 'JSON payload must be an object, not a list' ] ]; } // Payload vazio {} if ($data === []) { return [ false, [ 'code' => 'EMPTY_PAYLOAD', 'message' => 'JSON payload cannot be empty' ] ]; } return [true, []]; } public static function validateClientRequest(array $data): array { foreach (['name', 'email', 'company_name', 'sector', 'revenue'] as $field) { if (!isset($data[$field]) || trim((string) $data[$field]) === '') { return [ false, [ 'code' => 'MISSING_FIELD', 'field' => $field, 'message' => "Field {$field} is required" ] ]; } } if (!filter_var((string) $data['email'], FILTER_VALIDATE_EMAIL)) { return [ false, [ 'code' => 'INVALID_EMAIL', 'message' => 'Invalid email' ] ]; } if ((int) ($data['number_of_employees'] ?? 0) < 1) { return [ false, [ 'code' => 'INVALID_EMPLOYEES', 'message' => 'Invalid number_of_employees' ] ]; } return [true, []]; } public static function allowOnlyFields(array $data, array $allowed): array { $extra = array_diff(array_keys($data), $allowed); if (!empty($extra)) { return [ false, [ 'code' => 'UNEXPECTED_FIELDS', 'fields' => array_values($extra), 'message' => 'Payload contains unexpected fields' ] ]; } return [true, []]; } public static function maxPayloadFields(array $data, int $limit = 15): array { if (count($data) > $limit) { return [ false, [ 'code' => 'PAYLOAD_TOO_LARGE', 'message' => 'Too many fields in payload' ] ]; } return [true, []]; } public static function blockDangerousPatterns(array $data): array { $patterns = [ '/\$\(/', // command substitution '/`/', // backticks '/\|/', // pipe '/&&|\|\|/', // logical chaining '/--/', // SQL comment '/;/' // statement break ]; foreach ($data as $key => $value) { if (!is_scalar($value)) { continue; } foreach ($patterns as $pattern) { if (preg_match($pattern, (string) $value)) { return [ false, [ 'code' => 'DANGEROUS_INPUT', 'field' => $key, 'message' => 'Suspicious input detected' ] ]; } } } return [true, []]; } public static function requiredBySchema(array $data, array $schema): array { foreach ($schema as $field => $rules) { if (($rules['required'] ?? false) === true) { if (!isset($data[$field]) || trim((string) $data[$field]) === '') { return [ false, [ 'code' => 'MISSING_FIELD', 'field' => $field, 'message' => "Field {$field} is required" ] ]; } } } return [true, []]; } public static function validateBySchema(array $data, array $schema): array { foreach ($schema as $field => $rules) { if (!isset($data[$field])) { continue; } $value = $data[$field]; switch ($rules['type']) { case 'string': if (!is_scalar($value)) { return self::typeError($field); } if (isset($rules['max']) && mb_strlen((string) $value) > $rules['max']) { return self::limitError($field); } break; case 'int': if (!is_numeric($value)) { return self::typeError($field); } if (isset($rules['min']) && (int) $value < $rules['min']) { return self::limitError($field); } break; case 'email': if (!filter_var((string) $value, FILTER_VALIDATE_EMAIL)) { return [ false, [ 'code' => 'INVALID_EMAIL', 'field' => $field, 'message' => 'Invalid email' ] ]; } break; } } return [true, []]; } private static function typeError(string $field): array { return [ false, [ 'code' => 'INVALID_TYPE', 'field' => $field, 'message' => "Invalid type for {$field}" ] ]; } private static function limitError(string $field): array { return [ false, [ 'code' => 'INVALID_VALUE', 'field' => $field, 'message' => "Invalid value for {$field}" ] ]; } }