forked from BassPago/leeds_backend
238 lines
5.2 KiB
PHP
238 lines
5.2 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace Bass\Webclient\Libs;
|
|
|
|
class GuardLib
|
|
{
|
|
public static function requireJsonObject(mixed $data): array
|
|
{
|
|
// JSON precisa ser um objeto (array associativo)
|
|
if (!is_array($data)) {
|
|
return [
|
|
false,
|
|
[
|
|
'code' => '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}"
|
|
]
|
|
];
|
|
}
|
|
|
|
}
|