{"record":{"id":"afdee6429923340b","repo":"passbolt/passbolt_api","slug":"a-valid-openpgp-key-must-be-provided-afdee6","errorCode":null,"errorMessage":"A valid OpenPGP key must be provided.","messagePattern":"A valid OpenPGP key must be provided\\.","errorType":"validation","errorClass":"CustomValidationException","httpStatus":422,"severity":"error","filePath":"src/Service/OpenPGP/PublicKeyValidationService.php","lineNumber":154,"sourceCode":"     * @param string $armoredKey user provided data\n     * @param array|null $rules to override default rules\n     * @throws \\App\\Error\\Exception\\CustomValidationException If parsing public key fails\n     * @throws \\App\\Error\\Exception\\CustomValidationException Validation rules fails\n     * @throws \\Cake\\Http\\Exception\\InternalErrorException No public key validation rules\n     * @throws \\Cake\\Http\\Exception\\InternalErrorException Unknown key validation rule\n     * @return array key information (see OpenPGPBackendInterface::getKeyInfo)\n     */\n    public static function parseAndValidatePublicKey(string $armoredKey, ?array $rules = null): array\n    {\n        $rules = $rules ?? self::getDefaultRules();\n        if (!count($rules)) {\n            throw new InternalErrorException('Invalid public key validation rules are missing.');\n        }\n\n        // Parsing check is mandatory and always done first\n        // We don't even try the other rules if this one fails\n        if (!self::isParsableArmoredPublicKey($armoredKey)) {\n            throw new CustomValidationException(__('A valid OpenPGP key must be provided.'), [\n                'armored_key' => [\n                    self::IS_PARSABLE_ARMORED_KEY_RULE => __('The public key could not be parsed.'),\n                ],\n            ]);\n        }\n\n        // Other rules are recommended but not mandatory\n        // As one may want to see what's inside the key info for debugging purpose\n        $keyInfo = self::getPublicKeyInfo($armoredKey);\n        $validationErrors = [];\n        foreach ($rules as $ruleName) {\n            switch ($ruleName) {\n                case self::IS_VALID_ALGORITHM_RULE:\n                    if (!self::isValidAlgorithm($keyInfo['type'], false)) {\n                        $validationErrors[$ruleName] = __('The algorithm is invalid.');\n                    }\n                    break;\n                case self::IS_VALID_ALGORITHM_STRICT_RULE:","sourceCodeStart":136,"sourceCodeEnd":172,"githubUrl":"https://github.com/passbolt/passbolt_api/blob/31c1bbc10f32808a607fa9bd81891e898779c0bc/src/Service/OpenPGP/PublicKeyValidationService.php#L136-L172","documentation":"CustomValidationException with message 'A valid OpenPGP key must be provided.' thrown when isParsableArmoredPublicKey fails — the armored string cannot be parsed as an OpenPGP key at all. This mandatory check always runs first in parseAndValidatePublicKey; other rules are skipped when it fails.","triggerScenarios":"Any call to parseAndValidatePublicKey (from assertMetadataKey, buildPublicKeyEntityFromDataOrFail, buildRevokedKeyEntityFromDataOrFail, create, rule, canValidate) with input missing the PGP armored block headers, malformed ASCII armor, or binary garbage.","commonSituations":"Users pasting a private key, a plain text file, or a truncated key; missing BEGIN PGP PUBLIC KEY BLOCK header due to copy/paste; client sending an empty string.","solutions":["Provide a complete, valid OpenPGP public key ASCII armor including the BEGIN/END PGP PUBLIC KEY BLOCK lines.","Pre-validate with PublicKeyValidationService::isParsableArmoredPublicKey before submitting to give a friendlier error.","Ensure the client sends the key as a plain string without HTML-escaping or base64 wrapping.","Catch CustomValidationException to read the structured validation errors (armored_key.IS_PARSABLE_ARMORED_KEY_RULE)."],"exampleFix":"// before\n$this->assertMetadataKey($armoredKey); // throws on bad paste\n\n// after\nif (!PublicKeyValidationService::isParsableArmoredPublicKey($armoredKey)) {\n    throw new BadRequestException(__('Please paste a complete OpenPGP public key block starting with -----BEGIN PGP PUBLIC KEY BLOCK-----'));\n}\n$this->assertMetadataKey($armoredKey);","handlingStrategy":"validation","validationCode":"// PHP\nif (!PublicKeyValidationService::isParsableArmoredPublicKey($armoredKey)) {\n    throw new BadRequestException(__('Provide a complete OpenPGP public key armored block.'));\n}","typeGuard":"function looksLikeArmoredPublicKey(?string $key): bool {\n    return is_string($key)\n        && str_contains($key, '-----BEGIN PGP PUBLIC KEY BLOCK-----')\n        && str_contains($key, '-----END PGP PUBLIC KEY BLOCK-----');\n}","tryCatchPattern":"try {\n    PublicKeyValidationService::parseAndValidatePublicKey($armoredKey);\n} catch (\\App\\Error\\Exception\\CustomValidationException $e) {\n    return $this->respondWithError(400, __('The provided key is not a parsable OpenPGP key.'));\n}","preventionTips":["Pre-check armor headers client-side before submitting the key.","Strip whitespace/BOM and ensure no HTML escaping of the key payload.","Instruct users to export the PUBLIC key, never the private one."],"tags":["openpgp","invalid-key","user-input","validation"],"backgroundTag":"invalid-argument-format","analyzedSha":"31c1bbc10f32808a607fa9bd81891e898779c0bc","analyzedAt":"2026-09-17T00:04:38.960Z","contentChangedAt":"2026-09-17T00:04:38.960Z","schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}