Skip to content

Commit 2334a9c

Browse files
Merge pull request #113 from julienloizelet/feat/various-fix-and-update
Feat/various fix and update
2 parents a98b983 + 7d42d80 commit 2334a9c

File tree

8 files changed

+56
-27
lines changed

8 files changed

+56
-27
lines changed

CHANGELOG.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,22 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/)
55
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
66

77

8+
## [0.34.0](https://github.com/crowdsecurity/php-cs-bouncer/releases/tag/v0.34.0) - 2022-11-24
9+
[_Compare with previous release_](https://github.com/crowdsecurity/php-cs-bouncer/compare/v0.33.0...v0.34.0)
10+
11+
### Changed
12+
- Do not cache bypass decision in stream mode
13+
- Replace unauthorized chars by underscore `_` in cache key
14+
15+
### Added
16+
- Add compatibility with PHP 8.2
17+
18+
### Fixed
19+
- Fix decision duration parsing when it uses milliseconds
20+
21+
---
22+
23+
824
## [0.33.0](https://github.com/crowdsecurity/php-cs-bouncer/releases/tag/v0.33.0) - 2022-11-10
925
[_Compare with previous release_](https://github.com/crowdsecurity/php-cs-bouncer/compare/v0.32.0...v0.33.0)
1026

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
"ext-json": "*"
5151
},
5252
"require-dev": {
53-
"phpunit/phpunit": "8.5.27",
53+
"phpunit/phpunit": "^8.5.30 || ^9.3",
5454
"ext-curl": "*"
5555
},
5656
"suggest": {

src/AbstractCache.php

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -145,17 +145,24 @@ protected function getCacheKey(string $scope, string $value): string
145145
if (!isset($this->cacheKeys[$scope][$value])) {
146146
switch ($scope) {
147147
case Constants::SCOPE_RANGE:
148-
$this->cacheKeys[$scope][$value] = Constants::SCOPE_IP . self::CACHE_SEP . $value;
148+
$result = Constants::SCOPE_IP . self::CACHE_SEP . $value;
149149
break;
150150
case Constants::SCOPE_IP:
151151
case Constants::CACHE_TAG_GEO . self::CACHE_SEP . Constants::SCOPE_IP:
152152
case Constants::CACHE_TAG_CAPTCHA . self::CACHE_SEP . Constants::SCOPE_IP:
153153
case Constants::SCOPE_COUNTRY:
154-
$this->cacheKeys[$scope][$value] = $scope . self::CACHE_SEP . $value;
154+
$result = $scope . self::CACHE_SEP . $value;
155155
break;
156156
default:
157157
throw new BouncerException('Unknown scope:' . $scope);
158158
}
159+
160+
/**
161+
* Replace unauthorized symbols.
162+
*
163+
* @see https://symfony.com/doc/current/components/cache/cache_items.html#cache-item-keys-and-values
164+
*/
165+
$this->cacheKeys[$scope][$value] = preg_replace('/[^A-Za-z0-9_.]/', self::CACHE_SEP, $result);
159166
}
160167

161168
return $this->cacheKeys[$scope][$value];
@@ -285,7 +292,8 @@ protected function addRemediationToCacheItem(
285292
]; // erase previous decision with the same id
286293

287294
// Build the item lifetime in cache and sort remediations by priority
288-
$maxLifetime = max(array_column($remediations, 1));
295+
$exps = array_column($remediations, 1);
296+
$maxLifetime = $exps ? max($exps) : 0;
289297
$prioritizedRemediations = Remediation::sortRemediationByPriority($remediations);
290298

291299
$item->set($prioritizedRemediations);
@@ -341,20 +349,14 @@ protected function defferUpdateCacheConfig(array $config): void
341349
protected function formatRemediationFromDecision(?array $decision): array
342350
{
343351
if (!$decision) {
344-
/**
345-
* In stream mode we consider a clean IP forever... until the next resync.
346-
* in this case, forever is 10 years as PHP_INT_MAX will cause trouble with the Memcached Adapter
347-
* (int to float unwanted conversion)
348-
*
349-
*/
350-
$duration = $this->streamMode ? 315360000 : $this->cacheExpirationForCleanIp;
352+
$duration = $this->cacheExpirationForCleanIp;
351353

352354
return [Constants::REMEDIATION_BYPASS, time() + $duration, 0];
353355
}
354356

355357
$duration = self::parseDurationToSeconds($decision['duration']);
356358

357-
// Don't set a max duration in stream mode to avoid bugs. Only the stream update has to change the cache state.
359+
// In stream mode, only the stream update has to change the cache state.
358360
if (!$this->streamMode) {
359361
$duration = min($this->cacheExpirationForBadIp, $duration);
360362
}
@@ -425,6 +427,10 @@ protected function miss(string $value, string $cacheScope): string
425427
]);
426428
}
427429
}
430+
// In stream mode, we do not save bypass decision in cache
431+
if ($this->streamMode && !$decisions) {
432+
return Constants::REMEDIATION_BYPASS;
433+
}
428434

429435
return $this->saveRemediationsForCacheKey($decisions, $cacheKey);
430436
}
@@ -464,7 +470,8 @@ protected function removeDecisionFromRemediationItem(string $cacheKey, int $deci
464470
return true;
465471
}
466472
// Build the item lifetime in cache and sort remediations by priority
467-
$maxLifetime = max(array_column($remediations, 1));
473+
$exps = array_column($remediations, 1);
474+
$maxLifetime = $exps ? max($exps) : 0;
468475
$cacheContent = Remediation::sortRemediationByPriority($remediations);
469476
$item->expiresAt(new DateTime('@' . $maxLifetime));
470477
$item->set($cacheContent);
@@ -576,7 +583,8 @@ private function configureAdapter(): void
576583
$this->adapter = new RedisTagAwareAdapter((RedisAdapter::createConnection($redisDsn)));
577584
} catch (Exception $e) {
578585
throw new BouncerException('Error when connecting to Redis.' .
579-
' Please fix the Redis DSN or select another cache technology.');
586+
' Please fix the Redis DSN or select another cache technology.' .
587+
' Initial error was: ' . $e->getMessage());
580588
}
581589
break;
582590

@@ -612,7 +620,7 @@ private static function parseDurationToSeconds(string $duration): int
612620
$re = '/(-?)(?:(?:(\d+)h)?(\d+)m)?(\d+).\d+(m?)s/m';
613621
preg_match($re, $duration, $matches);
614622
if (!\count($matches)) {
615-
throw new BouncerException("Unable to parse the following duration: ${$duration}.");
623+
throw new BouncerException('Unable to parse the following duration: ' . $duration);
616624
}
617625
$seconds = 0;
618626
if (isset($matches[2])) {
@@ -621,12 +629,14 @@ private static function parseDurationToSeconds(string $duration): int
621629
if (isset($matches[3])) {
622630
$seconds += ((int)$matches[3]) * 60; // minutes
623631
}
632+
$secondsPart = 0;
624633
if (isset($matches[4])) {
625-
$seconds += ((int)$matches[4]); // seconds
634+
$secondsPart += ((int)$matches[4]); // seconds
626635
}
627636
if ('m' === ($matches[5])) { // units in milliseconds
628-
$seconds *= 0.001;
637+
$secondsPart *= 0.001;
629638
}
639+
$seconds += $secondsPart;
630640
if ('-' === ($matches[1])) { // negative
631641
$seconds *= -1;
632642
}

src/Constants.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ class Constants
7575
/** @var string Path for html templates folder (e.g. ban and captcha wall) */
7676
public const TEMPLATES_DIR = __DIR__ . "/templates";
7777
/** @var string The last version of this library */
78-
public const VERSION = 'v0.33.0';
78+
public const VERSION = 'v0.34.0';
7979
/** @var string The "disabled" x-forwarded-for setting */
8080
public const X_FORWARDED_DISABLED = 'no_forward';
8181
}

src/Fixes/Gregwar/Captcha/CaptchaBuilder.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,22 @@
55
use Gregwar\Captcha\CaptchaBuilder as GregwarCaptchaBuilder;
66

77
/**
8-
* Override to fix "implicit conversion error on PHP 8.1"
8+
* Override to :
9+
* - fix "implicit conversion error on PHP 8.1"
10+
* - fix "creation of dynamic property $background error on PHP 8.2"
11+
*
12+
*
913
* @see https://github.com/crowdsecurity/php-cs-bouncer/issues/62 and
1014
* @see https://github.com/Gregwar/Captcha/pull/101/files
1115
* @SuppressWarnings(PHPMD.ElseExpression)
1216
*
1317
*/
1418
class CaptchaBuilder extends GregwarCaptchaBuilder
1519
{
20+
/**
21+
* @var false|int
22+
*/
23+
protected $background = false;
1624
/**
1725
* Writes the phrase on the image
1826
*/

src/Remediation.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,8 @@ public static function sortRemediationByPriority(array $remediations): array
5757
}
5858

5959
// Sort by priorities.
60-
/** @var callable $compareFunction */
61-
$compareFunction = 'self::comparePriorities';
60+
/** @var callable $compareFunction */
61+
$compareFunction = self::class . '::comparePriorities';
6262
usort($remediationsWithPriorities, $compareFunction);
6363

6464
return $remediationsWithPriorities;

src/RestClient/Curl.php

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,6 @@ public function request(
5151
}
5252

5353
/**
54-
* @param $handle
55-
*
5654
* @return bool|string
5755
*/
5856
protected function exec($handle)
@@ -61,8 +59,6 @@ protected function exec($handle)
6159
}
6260

6361
/**
64-
* @param $handle
65-
*
6662
* @return mixed
6763
*/
6864
protected function getResponseHttpCode($handle)

tests/end-to-end/package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,7 @@
2323
"jest-playwright-preset": "^1.4.3",
2424
"jest-runner": "^26.6.3",
2525
"lodash": "^4.17.21",
26-
"playwright-chromium": "1.22.2",
27-
"soap": "^0.42.0",
26+
"playwright-chromium": "^1.27.1",
2827
"ws": "^7.4.6"
2928
}
3029
}

0 commit comments

Comments
 (0)