Files
ignacionelson e187513cdd Stop a mistyped CAPTCHA flag from switching the CAPTCHA off
`env()` recognises the words "true" and "false" and returns everything
else as the string it was — and every non-empty string is truthy in PHP.
So `(bool) env('PROJECTSEND_CAPTCHA_DISABLED')` read all of these as "yes,
disabled":

    PROJECTSEND_CAPTCHA_DISABLED=no
    PROJECTSEND_CAPTCHA_DISABLED=off
    PROJECTSEND_CAPTCHA_DISABLED=fasle

An operator who meant to say no took the bot protection off their login
and registration forms and had nothing to tell them so — the setting
screen still shows the CAPTCHA configured, because this is the escape
hatch that runs ahead of it.

For most settings the cast is a shrug: somebody notices the feature is on
and fixes the line. It stops being a shrug when the wrong answer is the
unsafe one, and this is one of those. EnvFlag lists what counts as yes —
`true` and `1`, either case, either type — and reads everything else,
recognised or not, as no. A value typed as `disabled` turns nothing off:
a configuration mistake to be found rather than guessed at.

Found while fixing the same bug in a new cloud-modules flag, where the
unsafe direction was publishing a customer's files rather than dropping a
CAPTCHA. Two of the four remaining `(bool) env()` casts are left alone on
purpose: a wrong S3 path-style value breaks storage loudly, and the
migration tool's direct mode defaults to true anyway, so neither fails
into an unsafe state.
2026-09-08 17:05:30 -03:00

52 lines
1.5 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Support;
/**
* Reading an on/off environment variable strictly.
*
* `env()` recognises the words `true` and `false` and hands back
* everything else as the string it was — and every non-empty string is
* truthy in PHP. So the obvious `(bool) env(...)` reads `no`, `off` and
* a typo as **on**:
*
* SOMETHING=no -> on
* SOMETHING=off -> on
* SOMETHING=enabld -> on
*
* For most settings that is a shrug — somebody notices the feature is on
* and fixes the line. It stops being a shrug when the wrong answer is the
* unsafe one, which is every flag that switches a protection *off* or a
* disclosure *on*. Those have to fail the other way, so this lists what
* counts as yes and reads everything else — including anything it does
* not recognise — as no.
*
* The list is deliberately short. Nothing an operator might have meant as
* "no" is in it, which is the point; a value typed as `enabled` turns
* nothing on and is a configuration mistake to be found rather than
* guessed at.
*/
final class EnvFlag
{
private const TRUE_VALUES = ['1', 'true'];
/**
* @param mixed $value whatever `env()` returned
*/
public static function isTrue(mixed $value): bool
{
if (is_bool($value)) {
return $value;
}
if (is_int($value)) {
return $value === 1;
}
return is_string($value)
&& in_array(strtolower(trim($value)), self::TRUE_VALUES, true);
}
}