Skip to content
Merged
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
34 changes: 32 additions & 2 deletions StarResponseController.php
Original file line number Diff line number Diff line change
Expand Up @@ -218,8 +218,14 @@ public static function reset(): void
*/
private static function sendPublicCacheHeaders(): void
{
$maxAge = (int) apply_filters('starcache_max_age', self::DEFAULT_MAX_AGE);
$swr = (int) apply_filters('starcache_stale_while_revalidate', self::DEFAULT_STALE_WHILE_REVALIDATE);
$maxAge = self::sanitizeDirectiveSeconds(
apply_filters('starcache_max_age', self::DEFAULT_MAX_AGE),
self::DEFAULT_MAX_AGE
);
$swr = self::sanitizeDirectiveSeconds(
apply_filters('starcache_stale_while_revalidate', self::DEFAULT_STALE_WHILE_REVALIDATE),
self::DEFAULT_STALE_WHILE_REVALIDATE
);
Comment thread
MaximillianGroup marked this conversation as resolved.
Comment thread
MaximillianGroup marked this conversation as resolved.

header('Cache-Control: public, max-age=' . $maxAge . ', stale-while-revalidate=' . $swr);

Expand All @@ -231,6 +237,30 @@ private static function sendPublicCacheHeaders(): void
header('X-StarCache-Context: ' . StarCacheContext::hash());
}

/**
* Normalize cache directive durations to a safe, non-negative integer.
*
* Negative or non-sensical values from third-party filters can result in
* invalid Cache-Control headers. This guard keeps header output valid and
* predictable while still allowing plugin-level TTL customization.
*
* @param mixed $value Requested TTL value from filter callbacks.
* @param int $fallback Default value used when the request is invalid.
*/
private static function sanitizeDirectiveSeconds(mixed $value, int $fallback): int
{
if (!is_numeric($value)) {
return max(0, $fallback);
}

$seconds = (int) $value;
if ($seconds < 0) {
return max(0, $fallback);
}

return $seconds;
}

/**
* Emit no-cache headers.
*/
Expand Down
22 changes: 22 additions & 0 deletions tests/UnitTests.php
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,28 @@ public function testResponseControllerNotEligibleWhenAuthenticated(): void
$this->assertFalse(StarResponseController::isEligible());
}

public function testResponseControllerSanitizeDirectiveSecondsFallsBackForNegativeValue(): void
{
$method = new \ReflectionMethod(StarResponseController::class, 'sanitizeDirectiveSeconds');
$method->setAccessible(true);

$maxAge = $method->invoke(null, -15, StarResponseController::DEFAULT_MAX_AGE);
$swr = $method->invoke(null, -3, StarResponseController::DEFAULT_STALE_WHILE_REVALIDATE);

$this->assertSame(StarResponseController::DEFAULT_MAX_AGE, $maxAge);
$this->assertSame(StarResponseController::DEFAULT_STALE_WHILE_REVALIDATE, $swr);
}

public function testResponseControllerSanitizeDirectiveSecondsFallsBackForInvalidType(): void
{
$method = new \ReflectionMethod(StarResponseController::class, 'sanitizeDirectiveSeconds');
$method->setAccessible(true);

$this->assertSame(120, $method->invoke(null, null, 120));
$this->assertSame(90, $method->invoke(null, 'not-a-number', 90));
$this->assertSame(0, $method->invoke(null, false, -10));
}

// =========================================================================
// StarVersionStore — renamed groups
// =========================================================================
Expand Down