passbolt/passbolt_api · error · InternalErrorException

Failed extended user control. Invalid IP Address.

Error message

Failed extended user control. Invalid IP Address.

What it means

The ExtendedUserAccessControl constructor validates the optional $userIp argument with CakePHP's Validation::ip() and throws an InternalErrorException when it is not a valid IPv4/IPv6 address. This immutable value object wraps user identity plus request metadata, so an invalid IP means the caller passed missing, empty, or malformed input (Validation::ip() also rejects null). The exception is thrown before the object is ever constructed, so no partially-initialized instance leaks.

Solutions

  1. Ensure a valid IP string is passed: read it from $request->clientIp() (or getServerParams()['REMOTE_ADDR']) and default to a safe value like '127.0.0.1' when the request has no IP (CLI context).
  2. If behind a proxy, configure passbolt to trust the proxy and pass the first entry of X-Forwarded-For, trimmed and validated before constructing the object.
  3. Guard before construction: if ($userIp === null || !Validation::ip($userIp)) { $userIp = '127.0.0.1'; } for CLI or header-less contexts.
  4. Catch InternalErrorException around construction in controllers/services and convert it to a 400-level response instead of a 500.

Example fix

// before
$uac = new ExtendedUserAccessControl(
    Role::USER,
    $user->id,
    $user->username,
    $_SERVER['REMOTE_ADDR'] ?? null,
    $_SERVER['HTTP_USER_AGENT'] ?? null
);
// after
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? '';
$ip = trim(explode(',', $ip)[0]);
if (!Validation::ip($ip)) {
    $ip = $_SERVER['REMOTE_ADDR'] ?? '127.0.0.1';
}
$uac = new ExtendedUserAccessControl(
    Role::USER,
    $user->id,
    $user->username,
    $ip,
    $_SERVER['HTTP_USER_AGENT'] ?? 'unknown'
);
Defensive patterns

Strategy: validation

Validate before calling

use Cake\Validation\Validation;
$ip = trim(explode(',', (string)($request->getHeaderLine('X-Forwarded-For') ?: $_SERVER['REMOTE_ADDR'] ?? '')))[0] ?? '';
if (!Validation::ip($ip)) {
    $ip = '127.0.0.1'; // safe fallback for CLI/header-less requests
}

Type guard

/** @param mixed $ip @phpstan-assert non-empty-string $ip */
function isValidIp(mixed $ip): bool
{
    return is_string($ip) && \Cake\Validation\Validation::ip($ip);
}

Try / catch

try {
    $uac = new ExtendedUserAccessControl($role, $userId, $username, $ip, $ua);
} catch (\Cake\Http\Exception\InternalErrorException $e) {
    // log $e->getMessage(), fall back to a 4xx client error response
}

Prevention

When it happens

Trigger: Calling new ExtendedUserAccessControl($role, $userId, $username, $userIp, $userAgent) with $userIp = null, an empty string, a hostname, an IPv6 with bad syntax, or an IP read from a spoofed/absent header like Client-Ip or X-Forwarded-For that contains garbage or a comma-separated list.

Common situations: Running behind a reverse proxy or load balancer where REMOTE_ADDR is empty or the forwarded header is missing/malformed; CLI commands (recovery, emails queue) constructing the object without request context so no IP exists; integration tests sending requests without REMOTE_ADDR; proxies appending multiple IPs ('1.2.3.4, 10.0.0.1') which fails Validation::ip().

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17). Data as JSON: /api/errors/c1fd53b5c9610a47. Report an issue: GitHub.

Appendix: source

Thrown at src/Utility/ExtendedUserAccessControl.php:62

     * UserAccessControl constructor.
     *
     * @param string $roleName The role name
     * @param string|null $userId the user uuid
     * @param string|null $username the user email
     * @param string|null $userIp the user ip
     * @param string|null $userAgent the user agent
     */
    public function __construct(
        string $roleName,
        ?string $userId = null,
        ?string $username = null,
        ?string $userIp = null,
        ?string $userAgent = null
    ) {
        parent::__construct($roleName, $userId, $username);

        if (!Validation::ip($userIp)) {
            throw new InternalErrorException('Failed extended user control. Invalid IP Address.');
        }
        $this->userIp = $userIp;

        if (!UserAgentValidation::isValid($userAgent)) {
            throw new InternalErrorException('Failed extended user control. Invalid user agent.');
        }
        $this->userAgent = $userAgent;
    }

    /**
     * Get the user ip address
     *
     * @return string
     */
    public function getUserIp(): string
    {
        return $this->userIp;
    }

View on GitHub (pinned to 31c1bbc10f)