passbolt/passbolt_api · error · Cake\Core\Exception\CakeException

No OpenPGP marker found.

Error message

No OpenPGP marker found.

What it means

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.

Solutions

  1. Validate the input starts with '-----BEGIN PGP' before calling, and reject it early with a user-friendly message.
  2. Ask users to export armored keys: `gpg --armor --export <fingerprint>` (produces .asc, not binary .gpg).
  3. Trim whitespace/BOM from the input; check for truncation if the string comes from a form or DB column.
  4. If you have binary key data, armor it first (enigmail/gpg --enarmor) rather than passing raw bytes.

Example fix

// before
$info = $this->getKeyInfo($armoredKey); // throws on non-armored input
// after
if (!is_string($armoredKey) || strpos($armoredKey, '-----BEGIN PGP') !== 0) {
    throw new \InvalidArgumentException('Please provide an ASCII-armored key (BEGIN PGP ... block).');
}
$info = $this->getKeyInfo($armoredKey);
Defensive patterns

Strategy: validation

Validate before calling

if (!is_string($armored) || preg_match('/-----BEGIN PGP [A-Z0-9 ]+-----/', $armored) !== 1) {
    throw new \InvalidArgumentException('Input is not an ASCII-armored OpenPGP block.');
}

Type guard

function isArmoredOpenPgp(mixed $input): bool {
    return is_string($input) && strpos($input, '-----BEGIN PGP') !== false;
}

Try / catch

try {
    $marker = $backend->getKeyInfo($armored);
} catch (\Cake\Core\Exception\CakeException $e) {
    if ($e->getMessage() === 'No OpenPGP marker found.') {
        // surface 'please paste the full armored key including BEGIN/END lines'
    }
    throw $e;
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


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

Appendix: source

Thrown at src/Utility/OpenPGP/Traits/OpenPGPBackendArmoredParseTrait.php:34

 */
namespace App\Utility\OpenPGP\Traits;

use Cake\Core\Exception\CakeException;

trait OpenPGPBackendArmoredParseTrait
{
    /**
     * Get the gpg marker.
     *
     * @param string $armored ASCII armored gpg data
     * @return mixed
     * @throws \Cake\Core\Exception\CakeException
     */
    protected function getGpgMarker(string $armored): mixed
    {
        $isMarker = preg_match('/-(BEGIN )*([A-Z0-9 ]+)-/', $armored, $values);
        if (!$isMarker || !isset($values[2])) {
            throw new CakeException(__('No OpenPGP marker found.'));
        }

        return $values[2];
    }

    /**
     * Forked from OpenPGP::unarmor
     * Fail if key doesn't contain CRC instead of triggering php error
     *
     * @param string $text key
     * @param string $header header
     * @return string|false
     */
    private function unarmor(string $text, string $header = 'PGP PUBLIC KEY BLOCK'): false|string
    {
        // @codingStandardsIgnoreStart
        $header = \OpenPGP::header($header);
        $text = str_replace(["\r\n", "\r"], ["\n", ''], $text);

View on GitHub (pinned to 31c1bbc10f)