{"record":{"id":"d5af11c0b7b257cf","repo":"passbolt/passbolt_api","slug":"no-openpgp-marker-found","errorCode":null,"errorMessage":"No OpenPGP marker found.","messagePattern":"No OpenPGP marker found\\.","errorType":"exception","errorClass":"Cake\\Core\\Exception\\CakeException","httpStatus":null,"severity":"error","filePath":"src/Utility/OpenPGP/Traits/OpenPGPBackendArmoredParseTrait.php","lineNumber":34,"sourceCode":" */\nnamespace App\\Utility\\OpenPGP\\Traits;\n\nuse Cake\\Core\\Exception\\CakeException;\n\ntrait OpenPGPBackendArmoredParseTrait\n{\n    /**\n     * Get the gpg marker.\n     *\n     * @param string $armored ASCII armored gpg data\n     * @return mixed\n     * @throws \\Cake\\Core\\Exception\\CakeException\n     */\n    protected function getGpgMarker(string $armored): mixed\n    {\n        $isMarker = preg_match('/-(BEGIN )*([A-Z0-9 ]+)-/', $armored, $values);\n        if (!$isMarker || !isset($values[2])) {\n            throw new CakeException(__('No OpenPGP marker found.'));\n        }\n\n        return $values[2];\n    }\n\n    /**\n     * Forked from OpenPGP::unarmor\n     * Fail if key doesn't contain CRC instead of triggering php error\n     *\n     * @param string $text key\n     * @param string $header header\n     * @return string|false\n     */\n    private function unarmor(string $text, string $header = 'PGP PUBLIC KEY BLOCK'): false|string\n    {\n        // @codingStandardsIgnoreStart\n        $header = \\OpenPGP::header($header);\n        $text = str_replace([\"\\r\\n\", \"\\r\"], [\"\\n\", ''], $text);","sourceCodeStart":16,"sourceCodeEnd":52,"githubUrl":"https://github.com/passbolt/passbolt_api/blob/31c1bbc10f32808a607fa9bd81891e898779c0bc/src/Utility/OpenPGP/Traits/OpenPGPBackendArmoredParseTrait.php#L16-L52","documentation":"getGpgMarker() extracts the ASCII-armored OpenPGP marker (the text after '-----BEGIN ', e.g. 'PGP PRIVATE KEY BLOCK') with a regex. If no marker pattern matches or the capture group is missing, it throws a CakeException('No OpenPGP marker found.'). The input string is therefore not an ASCII-armored OpenPGP structure at all.","triggerScenarios":"Called (directly or via assertGpgMarker / isParsableArmoredSignedMessage / getKeyInfo / getMessageInfo) with a string containing no '-(BEGIN )*([A-Z0-9 ]+)-' match: empty string, plain text, binary key data, or an armored block whose header is mangled (e.g. 'BEGIN PGP' without dashes).","commonSituations":"Users pasting a key without the BEGIN/END armor lines; uploading a binary .gpg file instead of an armored .asc; form input truncated by length limits cutting off the header; whitespace/encoding mangling from copy-paste.","solutions":["Validate the input starts with '-----BEGIN PGP' before calling, and reject it early with a user-friendly message.","Ask users to export armored keys: `gpg --armor --export <fingerprint>` (produces .asc, not binary .gpg).","Trim whitespace/BOM from the input; check for truncation if the string comes from a form or DB column.","If you have binary key data, armor it first (enigmail/gpg --enarmor) rather than passing raw bytes."],"exampleFix":"// before\n$info = $this->getKeyInfo($armoredKey); // throws on non-armored input\n// after\nif (!is_string($armoredKey) || strpos($armoredKey, '-----BEGIN PGP') !== 0) {\n    throw new \\InvalidArgumentException('Please provide an ASCII-armored key (BEGIN PGP ... block).');\n}\n$info = $this->getKeyInfo($armoredKey);","handlingStrategy":"validation","validationCode":"if (!is_string($armored) || preg_match('/-----BEGIN PGP [A-Z0-9 ]+-----/', $armored) !== 1) {\n    throw new \\InvalidArgumentException('Input is not an ASCII-armored OpenPGP block.');\n}","typeGuard":"function isArmoredOpenPgp(mixed $input): bool {\n    return is_string($input) && strpos($input, '-----BEGIN PGP') !== false;\n}","tryCatchPattern":"try {\n    $marker = $backend->getKeyInfo($armored);\n} catch (\\Cake\\Core\\Exception\\CakeException $e) {\n    if ($e->getMessage() === 'No OpenPGP marker found.') {\n        // surface 'please paste the full armored key including BEGIN/END lines'\n    }\n    throw $e;\n}","preventionTips":["Validate user-provided keys client-side for the BEGIN/END armor lines before submitting.","Reject binary .gpg uploads; require .asc armored exports.","Beware input truncation: check the string ends with the expected END line."],"tags":["openpgp","armored-parsing","invalid-input","validation"],"backgroundTag":"invalid-key-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"}