I started working with openemr a couple of weeks ago. I was using opus 4.6 for agentic coding and it may have discovered a bug. I had opus describe it for posting in case it is a true bug for community review:
E-signature verification always fails: Signature constructor is missing two parameters that getSignatures() passes
Summary
In the ESign subsystem, DbRow_Signable::getSignatures() constructs each ESign\Signature object with 13 arguments, but ESign\Signature::__construct() only declares 11 parameters. The two extra arguments (suffix and valedictory) are passed in the middle of the list, so every argument after them lands in the wrong parameter. As a result the signature object is built with mismatched fields, Signature::verify() can never succeed, and every locked form/encounter is reported as failing e-signature verification even when the stored data is completely intact.
The underlying esign_signatures rows are correct — both the content hash and the signature hash validate when computed from the correct columns. The defect is purely in how the row is loaded back into the Signature object.
Environment
• OpenEMR 7.0.2
• PHP 8.2.12
• E-signing enabled (lock_esign_individual)
Because PHP silently ignores surplus positional arguments to a non-variadic user function (no ArgumentCountError for too many args), this misalignment is latent — it produces wrong results instead of throwing, which is likely why it has gone unnoticed.
Affected files
• library/ESign/DbRow/Signable.php — getSignatures() (the call site)
• library/ESign/Signature.php — __construct() (the constructor)
Root cause
DbRow_Signable::getSignatures() selects the signer’s suffix and valedictory and passes them into the constructor: