Every CVE Search/ Get API key

CVE-2026-71322

Lemur: Missing authorization check on POST /certificates/<id>/export for plugins with requires_key = False

4.3 MediumFix available

Lemur manages TLS certificate creation. Prior to 1.9.3, CertificateExport placed its CertificatePermission ownership check inside the plugin.requires_key branch for POST /api/1/certificates//export. A plugin declaring requires_key false bypassed that check, and the handler still passed cert.private_key as an argument and recorded a key_view audit event. The bundled JavaTruststoreExportPlugin ignored the key, so the immediate exposure was limited to public certificate material and misleading audit entries, but a future plugin could have consumed the supplied key. The fix passes no private key to plugins that do not require one and confines ownership checks and key_view logging to actual private-key exports. This issue is fixed in version 1.9.3.

Attack profile

How easy it is to exploit

Attack vector
Network
Complexity
Low
Privileges needed
Low
User interaction
None

What an attacker can affect

Confidentiality
Low
Integrity
None
Availability
None

Exploit likelihood

0.23%Up 0.1 points since Aug 19, 2026
90%50%10%1%

What each source reports

CVE List Score used 4.3

Lemur: Missing authorization check on POST /certificates/<id>/export for plugins with requires_key = False

Lemur manages TLS certificate creation. Prior to 1.9.3, CertificateExport placed its CertificatePermission ownership check inside the plugin.requires_key branch for POST /api/1/certificates//export. A plugin declaring requires_key false bypassed that check, and the handler still passed cert.private_key as an argument and recorded a key_view audit event. The bundled JavaTruststoreExportPlugin ignored the key, so the immediate exposure was limited to public certificate material and misleading audit entries, but a future plugin could have consumed the supplied key. The fix passes no private key to plugins that do not require one and confines ownership checks and key_view logging to actual private-key exports. This issue is fixed in version 1.9.3.

Record
CVE-2026-71322
CVSS 3.1
4.3 MediumCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N
Weakness
CWE-862
Updated
2026-11-02
EUVDEUVD-2026-61158 4.3

Lemur manages TLS certificate creation. Prior to 1.9.3, CertificateExport placed its CertificatePermission ownership check inside the plugin.requires_key branch for POST /api/1/certificates//export. A plugin declaring requires_key false bypassed that check, and the handler still passed cert.private_key as an argument and recorded a key_view audit event. The bundled JavaTruststoreExportPlugin ignored the key, so the immediate exposure was limited to public certificate material and misleading audit entries, but a future plugin could have consumed the supplied key. The fix passes no private key to plugins that do not require one and confines ownership checks and key_view logging to actual private-key exports. This issue is fixed in version 1.9.3.

Record
EUVD-2026-61158
CVSS 3.1
4.3 MediumCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N
Updated
2026-11-02
GitHub AdvisoriesGHSA-4h97-p9wq-chqj 4.3

Lemur: Missing authorization check on POST /certificates/<id>/export for plugins with requires_key = False

Summary

The CertificateExport handler in lemur/certificates/views.py nests its entire ownership / CertificatePermission check inside an if plugin.requires_key: branch. When the selected export plugin advertises requires_key = False, the authorization check is skipped entirely and any authenticated user can invoke plugin.export(cert.body, cert.chain, cert.private_key, options) against a certificate they do not own. The handler additionally writes a "key_view" audit-log event for every call, regardless of whether the plugin actually accessed the private key, polluting the audit trail with false positives.

Root Cause

lemur/certificates/views.py:1573:

if plugin.requires_key:
    if not cert.private_key:
        return (..., 400)
    else:
        if g.current_user != cert.user:
            owner_role = role_service.get_by_name(cert.owner)
            permission = CertificatePermission(owner_role, [x.name for x in cert.roles])
            if not permission.can():
                return (..., 403)
 
log_service.create(g.current_user, "key_view", certificate=cert)   # always logged
extension, passphrase, data = plugin.export(
    cert.body, cert.chain, cert.private_key, options
)

The authorization gate is structurally inside the if plugin.requires_key: block. With requires_key = False, control falls straight through to plugin.export(...) with no ownership check. The cert.private_key is passed to the plugin regardless of the flag — the flag only describes what the plugin advertises it needs, not what it actually receives.

The only currently shipping ExportPlugin with requires_key = False is JavaTruststoreExportPlugin (lemur/plugins/lemur_jks/plugin.py), whose export() ignores the key argument and emits a public-only Java truststore. The present-day data exposure is therefore limited to public certificate material. The bug is nonetheless filed as a real authorization gap because:

  1. The structural defect is latent and silent - any future requires_key = False ExportPlugin that does read cert.private_key will inherit the bypass with no test or code-review signal.
  2. The unconditional log_service.create(..., "key_view", ...) call falsely records key-view events for callers who never viewed a key, weakening incident-response signal.

Affected Endpoints

| Method | Path | Source | |---|---|---| | POST | /api/1/certificates/<id>/export | lemur/certificates/views.py:1573 |

Impact

In the current codebase:

  • Any authenticated user can mint a Java truststore (java-truststore-jks plugin) containing any certificate's public body and chain, without owning the certificate or holding a role with permission over it.
  • The audit log records a key_view event for the calling user against that certificate, despite no private key having been accessed. Defenders investigating apparent key-view events will encounter false positives that they cannot distinguish from genuine accesses.

Latent risk:

  • A future ExportPlugin author who sets requires_key = False because their plugin can operate without a key (e.g., for a fall-back code path) but still uses the key when one is provided will silently leak private keys to any authenticated user. The same code review that approves the plugin will not flag this — the authorization invariant is held by a structurally distant if-branch in the view, not by the plugin itself.

Remediation

Lift the authorization check out of the if plugin.requires_key: block so it runs for every export call:

# Authorization first, unconditionally.
if g.current_user != cert.user:
    owner_role = role_service.get_by_name(cert.owner)
    permission = CertificatePermission(owner_role, [x.name for x in cert.roles])
    if not permission.can():
        return (dict(message="You are not authorized to export this certificate."), 403)
 
if plugin.requires_key:
    if not cert.private_key:
        return…
Record
GHSA-4h97-p9wq-chqj
CVSS 3.1
4.3 MediumCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N
Weakness
CWE-862
Updated
2026-11-02
NVD 4.3

Lemur manages TLS certificate creation. Prior to 1.9.3, CertificateExport placed its CertificatePermission ownership check inside the plugin.requires_key branch for POST /api/1/certificates//export. A plugin declaring requires_key false bypassed that check, and the handler still passed cert.private_key as an argument and recorded a key_view audit event. The bundled JavaTruststoreExportPlugin ignored the key, so the immediate exposure was limited to public certificate material and misleading audit entries, but a future plugin could have consumed the supplied key. The fix passes no private key to plugins that do not require one and confines ownership checks and key_view logging to actual private-key exports. This issue is fixed in version 1.9.3.

Record
CVE-2026-71322
CVSS 3.1
4.3 MediumCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N
Weakness
CWE-862
Updated
2026-11-02
OSV 4.3

Lemur: Missing authorization check on POST /certificates/<id>/export for plugins with requires_key = False

Lemur manages TLS certificate creation. Prior to 1.9.3, CertificateExport placed its CertificatePermission ownership check inside the plugin.requires_key branch for POST /api/1/certificates//export. A plugin declaring requires_key false bypassed that check, and the handler still passed cert.private_key as an argument and recorded a key_view audit event. The bundled JavaTruststoreExportPlugin ignored the key, so the immediate exposure was limited to public certificate material and misleading audit entries, but a future plugin could have consumed the supplied key. The fix passes no private key to plugins that do not require one and confines ownership checks and key_view logging to actual private-key exports. This issue is fixed in version 1.9.3.

Record
CVE-2026-71322
CVSS 3.1
4.3 MediumCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N
Weakness
CWE-862
Updated
2026-11-02
OSVGHSA-4h97-p9wq-chqj 4.3

Lemur: Missing authorization check on POST /certificates/<id>/export for plugins with requires_key = False

Summary

The CertificateExport handler in lemur/certificates/views.py nests its entire ownership / CertificatePermission check inside an if plugin.requires_key: branch. When the selected export plugin advertises requires_key = False, the authorization check is skipped entirely and any authenticated user can invoke plugin.export(cert.body, cert.chain, cert.private_key, options) against a certificate they do not own. The handler additionally writes a "key_view" audit-log event for every call, regardless of whether the plugin actually accessed the private key, polluting the audit trail with false positives.

Root Cause

lemur/certificates/views.py:1573:

if plugin.requires_key:
    if not cert.private_key:
        return (..., 400)
    else:
        if g.current_user != cert.user:
            owner_role = role_service.get_by_name(cert.owner)
            permission = CertificatePermission(owner_role, [x.name for x in cert.roles])
            if not permission.can():
                return (..., 403)
 
log_service.create(g.current_user, "key_view", certificate=cert)   # always logged
extension, passphrase, data = plugin.export(
    cert.body, cert.chain, cert.private_key, options
)

The authorization gate is structurally inside the if plugin.requires_key: block. With requires_key = False, control falls straight through to plugin.export(...) with no ownership check. The cert.private_key is passed to the plugin regardless of the flag — the flag only describes what the plugin advertises it needs, not what it actually receives.

The only currently shipping ExportPlugin with requires_key = False is JavaTruststoreExportPlugin (lemur/plugins/lemur_jks/plugin.py), whose export() ignores the key argument and emits a public-only Java truststore. The present-day data exposure is therefore limited to public certificate material. The bug is nonetheless filed as a real authorization gap because:

  1. The structural defect is latent and silent - any future requires_key = False ExportPlugin that does read cert.private_key will inherit the bypass with no test or code-review signal.
  2. The unconditional log_service.create(..., "key_view", ...) call falsely records key-view events for callers who never viewed a key, weakening incident-response signal.

Affected Endpoints

| Method | Path | Source | |---|---|---| | POST | /api/1/certificates/<id>/export | lemur/certificates/views.py:1573 |

Impact

In the current codebase:

  • Any authenticated user can mint a Java truststore (java-truststore-jks plugin) containing any certificate's public body and chain, without owning the certificate or holding a role with permission over it.
  • The audit log records a key_view event for the calling user against that certificate, despite no private key having been accessed. Defenders investigating apparent key-view events will encounter false positives that they cannot distinguish from genuine accesses.

Latent risk:

  • A future ExportPlugin author who sets requires_key = False because their plugin can operate without a key (e.g., for a fall-back code path) but still uses the key when one is provided will silently leak private keys to any authenticated user. The same code review that approves the plugin will not flag this — the authorization invariant is held by a structurally distant if-branch in the view, not by the plugin itself.

Remediation

Lift the authorization check out of the if plugin.requires_key: block so it runs for every export call:

# Authorization first, unconditionally.
if g.current_user != cert.user:
    owner_role = role_service.get_by_name(cert.owner)
    permission = CertificatePermission(owner_role, [x.name for x in cert.roles])
    if not permission.can():
        return (dict(message="You are not authorized to export this certificate."), 403)
 
if plugin.requires_key:
    if not cert.private_key:
        return…
Record
GHSA-4h97-p9wq-chqj
CVSS 3.1
4.3 MediumCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N
Weakness
CWE-862
Updated
2026-11-02
OSVPYSEC-2026-3673 4.3

Lemur: Missing authorization check on POST /certificates/<id>/export for plugins with requires_key = False

Summary

The CertificateExport handler in lemur/certificates/views.py nests its entire ownership / CertificatePermission check inside an if plugin.requires_key: branch. When the selected export plugin advertises requires_key = False, the authorization check is skipped entirely and any authenticated user can invoke plugin.export(cert.body, cert.chain, cert.private_key, options) against a certificate they do not own. The handler additionally writes a "key_view" audit-log event for every call, regardless of whether the plugin actually accessed the private key, polluting the audit trail with false positives.

Root Cause

lemur/certificates/views.py:1573:

if plugin.requires_key:
    if not cert.private_key:
        return (..., 400)
    else:
        if g.current_user != cert.user:
            owner_role = role_service.get_by_name(cert.owner)
            permission = CertificatePermission(owner_role, [x.name for x in cert.roles])
            if not permission.can():
                return (..., 403)
 
log_service.create(g.current_user, "key_view", certificate=cert)   # always logged
extension, passphrase, data = plugin.export(
    cert.body, cert.chain, cert.private_key, options
)

The authorization gate is structurally inside the if plugin.requires_key: block. With requires_key = False, control falls straight through to plugin.export(...) with no ownership check. The cert.private_key is passed to the plugin regardless of the flag — the flag only describes what the plugin advertises it needs, not what it actually receives.

The only currently shipping ExportPlugin with requires_key = False is JavaTruststoreExportPlugin (lemur/plugins/lemur_jks/plugin.py), whose export() ignores the key argument and emits a public-only Java truststore. The present-day data exposure is therefore limited to public certificate material. The bug is nonetheless filed as a real authorization gap because:

  1. The structural defect is latent and silent - any future requires_key = False ExportPlugin that does read cert.private_key will inherit the bypass with no test or code-review signal.
  2. The unconditional log_service.create(..., "key_view", ...) call falsely records key-view events for callers who never viewed a key, weakening incident-response signal.

Affected Endpoints

| Method | Path | Source | |---|---|---| | POST | /api/1/certificates/<id>/export | lemur/certificates/views.py:1573 |

Impact

In the current codebase:

  • Any authenticated user can mint a Java truststore (java-truststore-jks plugin) containing any certificate's public body and chain, without owning the certificate or holding a role with permission over it.
  • The audit log records a key_view event for the calling user against that certificate, despite no private key having been accessed. Defenders investigating apparent key-view events will encounter false positives that they cannot distinguish from genuine accesses.

Latent risk:

  • A future ExportPlugin author who sets requires_key = False because their plugin can operate without a key (e.g., for a fall-back code path) but still uses the key when one is provided will silently leak private keys to any authenticated user. The same code review that approves the plugin will not flag this — the authorization invariant is held by a structurally distant if-branch in the view, not by the plugin itself.

Remediation

Lift the authorization check out of the if plugin.requires_key: block so it runs for every export call:

# Authorization first, unconditionally.
if g.current_user != cert.user:
    owner_role = role_service.get_by_name(cert.owner)
    permission = CertificatePermission(owner_role, [x.name for x in cert.roles])
    if not permission.can():
        return (dict(message="You are not authorized to export this certificate."), 403)
 
if plugin.requires_key:
    if not cert.private_key:
        return…
Record
PYSEC-2026-3673
CVSS 3.1
4.3 MediumCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
lemur
PyPI
before 1.9.3
1.9.3
lemur
PyPI
before 1.9.3, plus listed versions
0.11.00.2.10.8.00.8.10.9.0
Show 15 moreShow less
1.0.01.1.01.2.01.3.11.3.21.4.01.5.01.6.01.7.01.8.01.8.11.8.21.9.01.9.11.9.2
1.9.3

History

History
RecordedChangedSources
first indexedCVE List, EUVD, GitHub Advisories, NVD, OSV

Dates are when Every CVE recorded each change. Upstream dates may differ.

In short

CVE-2026-71322 is a medium vulnerability (CVSS 4.3) in Netflix Lemur, published August 18, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.23%, the 14th percentile. Fixed in lemur 1.9.3.

References

This page doesn't list everything

Left out here: the rest of 3 source descriptions. The JSON record has them.

MoveEnter Open