{"record":{"id":"e857e00d67d38ccf","repo":"ellite/Wallos","slug":"issuer-must-not-contain-a-colon","errorCode":null,"errorMessage":"Issuer must not contain a colon.","messagePattern":"Issuer must not contain a colon\\.","errorType":"exception","errorClass":"InvalidArgumentException","httpStatus":null,"severity":"error","filePath":"libs/OTPHP/ParameterTrait.php","lineNumber":177,"sourceCode":"            },\n            'secret' => static fn (string $value): string => strtoupper(trim($value, '=')),\n            'algorithm' => static function (string $value): string {\n                $value = strtolower($value);\n                in_array($value, hash_algos(), true) || throw new InvalidArgumentException(sprintf(\n                    'The \"%s\" digest is not supported.',\n                    $value\n                ));\n\n                return $value;\n            },\n            'digits' => static function ($value): int {\n                $value > 0 || throw new InvalidArgumentException('Digits must be at least 1.');\n\n                return (int) $value;\n            },\n            'issuer' => function (string $value): string {\n                assert($value !== '');\n                $this->hasColon($value) === false || throw new InvalidArgumentException(\n                    'Issuer must not contain a colon.'\n                );\n\n                return $value;\n            },\n        ];\n    }\n\n    /**\n     * @param non-empty-string $value\n     */\n    private function hasColon(string $value): bool\n    {\n        $colons = [':', '%3A', '%3a'];\n        foreach ($colons as $colon) {\n            if (str_contains($value, $colon)) {\n                return true;\n            }","sourceCodeStart":159,"sourceCodeEnd":195,"githubUrl":"https://github.com/ellite/Wallos/blob/52820e87ca5a6e105fdbb7f1c0c681bc0cfee2fd/libs/OTPHP/ParameterTrait.php#L159-L195","documentation":"Like the label, the 'issuer' parameter is validated by the parameter map: setParameter('issuer', ...) rejects values containing a colon because the issuer occupies the provider part of an otpauth:// URI where colons are structural separators.","triggerScenarios":"Calling setIssuer('My:Company') or setParameter('issuer', ...) with any string containing ':'; often occurs when the issuer string was assembled from 'brand:region' style identifiers.","commonSituations":"Company display names copied from internal naming conventions that include colons; storing the full 'Issuer:account' string in the issuer field by mistake.","solutions":["Remove the colon from the issuer name, e.g. 'My Company' instead of 'My:Company'","Keep issuer and label separate: issuer is the provider, label is the account","Sanitize the configured issuer with str_replace(':', '', $value) before assigning"],"exampleFix":"// before\n$otp->setIssuer('Acme:EU');\n// after\n$otp->setIssuer('Acme EU');","handlingStrategy":"validation","validationCode":"$issuer = preg_replace('/[:]/', ' ', $config['issuer'] ?? '');\nif ($issuer !== '') {\n    $otp->setIssuer($issuer);\n}","typeGuard":null,"tryCatchPattern":"try {\n    $otp->setIssuer($issuer);\n} catch (\\InvalidArgumentException $e) {\n    $otp->setIssuer(str_replace(':', '', $issuer));\n}","preventionTips":["Strip colons from issuer names at config load time","Keep issuer (provider) and label (account) concepts separate","Warn users if their brand name contains a colon"],"tags":["php","otp","provisioning-uri"],"backgroundTag":"invalid-argument-value","analyzedSha":"52820e87ca5a6e105fdbb7f1c0c681bc0cfee2fd","analyzedAt":"2026-09-13T14:09:30.873Z","contentChangedAt":"2026-09-13T14:09:30.873Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}