Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions src/Drivers/Mongo/MongoSecurityGuard.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,22 @@ private function assertCollections(): void
try {
$existing = [];

foreach ($this->db->listCollections() as $collectionInfo) {
if (method_exists($collectionInfo, 'getName')) {
$existing[] = $collectionInfo->getName();
/** @var mixed $cmd */
$cmd = [$this->db, 'command'];
if (is_callable($cmd)) {
/** @var iterable<mixed> $cursor */
$cursor = $cmd(['listCollections' => 1]);

foreach ($cursor as $collection) {
if (is_array($collection) && isset($collection['name']) && is_string($collection['name'])) {
$existing[] = $collection['name'];
} elseif (is_object($collection) && isset($collection->name) && is_string($collection->name)) {
$existing[] = $collection->name;
}
}
} else {
// Fallback for strict PHPStan analysis where command() is hidden
throw new RuntimeException('IntegrationV2 Mongo command not callable.');
}

$missing = array_values(array_diff($required, $existing));
Expand Down
2 changes: 2 additions & 0 deletions src/Drivers/MySQL/DbalMySQLDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ public function doGetActiveBlock(string $ip, string $subject): ?SecurityBlockDTO

/**
* @var array{
* ip:string,
* subject:string,
* type:string|int,
* expires_at:int|string,
* created_at:int|string
Expand Down
71 changes: 57 additions & 14 deletions src/Drivers/MySQL/MySQLSecurityGuard.php
Original file line number Diff line number Diff line change
Expand Up @@ -171,47 +171,90 @@
}
}

private function assertSchema(PDO|Connection $raw): void
{
$required = array_map('strtolower', ['sg_attempts', 'sg_blocks']);

try {
$missing = [];

if ($raw instanceof PDO) {
$placeholders = implode(', ', array_fill(0, count($required), '?'));
$stmt = $raw->prepare(
'SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES '
. 'WHERE TABLE_SCHEMA = DATABASE() '
. 'AND TABLE_NAME IN (' . $placeholders . ')'
);
$stmt->execute($required);
/** @var array<int,string> $present */
$present = $stmt->fetchAll(PDO::FETCH_COLUMN, 0);
$normalized = array_map('strtolower', $present);
$missing = array_values(array_diff($required, $normalized));
/** @var mixed $preparer */
$preparer = [$raw, 'prepare'];

if (is_callable($preparer)) {
$stmt = $preparer(
'SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES '
. 'WHERE TABLE_SCHEMA = DATABASE() '
. 'AND TABLE_NAME IN (' . $placeholders . ')'
);

if ($stmt !== false) {
/** @var \PDOStatement $stmt */
$stmt->execute($required);

/** @var mixed $fetcher */
$fetcher = [$stmt, 'fetchAll'];

if (is_callable($fetcher)) {
/** @var array<int,string>|false $present */
$present = $fetcher(7, 0); // 7 = \PDO::FETCH_COLUMN

if (is_array($present)) {
$normalized = array_map('strtolower', $present);
$missing = array_values(array_diff($required, $normalized));
} else {
throw new \RuntimeException('IntegrationV2 MySQL fetch failed.');
}
} else {
throw new \RuntimeException('IntegrationV2 MySQL fetchAll missing.');
}
} else {
throw new \RuntimeException('IntegrationV2 MySQL prepare failed.');
}
} else {
throw new \RuntimeException('IntegrationV2 MySQL prepare missing.');
}
} else {
$schemaManager = $raw->createSchemaManager();
$tables = $schemaManager->listTableNames();
$normalized = array_map('strtolower', $tables);
$missing = array_values(array_diff($required, $normalized));
/** @var mixed $smGetter */
$smGetter = [$raw, 'getSchemaManager'];

if (is_callable($smGetter)) {
$schemaManager = $smGetter();

/** @var mixed $lister */
$lister = [$schemaManager, 'listTableNames'];

if (is_callable($lister)) {
/** @var string[] $tables */
$tables = $lister();

$normalized = array_map('strtolower', $tables);
$missing = array_values(array_diff($required, $normalized));
} else {
throw new \RuntimeException('IntegrationV2 MySQL listTableNames missing.');
}
} else {
throw new \RuntimeException('IntegrationV2 MySQL getSchemaManager missing.');
}
}

if ($missing !== []) {
throw new RuntimeException('IntegrationV2 MySQL schema missing.');
}
} catch (RuntimeException $e) {
throw $e;
} catch (Throwable $e) {
throw new RuntimeException('IntegrationV2 MySQL connection failed.', 0, $e);
}
}

/**
* @template TReturn
* @param callable():TReturn $operation
* @return TReturn
*/

Check notice on line 257 in src/Drivers/MySQL/MySQLSecurityGuard.php

View check run for this annotation

codefactor.io / CodeFactor

src/Drivers/MySQL/MySQLSecurityGuard.php#L174-L257

Complex Method
private function executeSafely(callable $operation, string $context)
{
try {
Expand Down
18 changes: 15 additions & 3 deletions src/Drivers/MySQL/PdoMySQLDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,11 @@ public function doRecordFailure(LoginAttemptDTO $attempt): int
/** @var array{c:string}|false $row */
$row = $stmt->fetch(PDO::FETCH_ASSOC);

return isset($row['c']) ? (int)$row['c'] : 0;
if (is_array($row)) {
return (int)$row['c'];
}

return 0;
}

// ------------------------------------------------------------------------
Expand Down Expand Up @@ -115,10 +119,18 @@ public function doGetActiveBlock(string $ip, string $subject): ?SecurityBlockDTO
':now' => time(),
]);

/** @var array{type:string,expires_at:int|string,created_at:int|string}|false $row */
/**
* @var array{
* ip:string,
* subject:string,
* type:string,
* expires_at:int|string,
* created_at:int|string
* }|false $row
*/
$row = $stmt->fetch(PDO::FETCH_ASSOC);

if (! $row) {
if (! is_array($row)) {
return null;
}

Expand Down