fix(plugin): keep certified marketplace packages installable on OSS

The certified-archive admission gate treated any declared certificate it could
not resolve as an untrusted archive and rejected the install with
CERTIFIED_PLUGIN_OSS_FORCE_REQUIRED. OSS ships an empty
plugin.certification.trusted_public_keys ring and the marketplace signs every
package with its own issuer key, so all certified marketplace packages (for
example langbot-team/RunnerDemo and langbot-team/LocalAgent) failed at the
'validating plugin package' step before artifact storage.

Admission now distinguishes an unresolvable declaration from a configured trust
decision that fails:

- record certificate_id only when the key_id is actually present in the
  configured ring, so an empty ring yields an unresolvable declaration;
- OSS degrades an unresolvable declaration to the existing oss_dev dedicated
  profile with CERTIFIED_PLUGIN_OSS_UNTRUSTED_DEDICATED instead of blocking;
- a resolvable declaration that still fails keeps requiring the explicit
  administrator force (CERTIFIED_PLUGIN_OSS_FORCE_REQUIRED);
- Cloud stays fail-closed and rejects before storage.

No shared-runtime privilege is granted when the issuer is not trusted, so this
withholds isolation rather than escalating it.
This commit is contained in:
TyperBody
2026-09-25 19:32:54 +08:00
parent 262980d612
commit afe700f1d8
4 changed files with 188 additions and 19 deletions
+42 -2
View File
@@ -47,6 +47,7 @@ class AdmissionCode(str, Enum):
OSS_FORCE_REQUIRED = 'CERTIFIED_PLUGIN_OSS_FORCE_REQUIRED'
OSS_FORCED_DEDICATED = 'CERTIFIED_PLUGIN_OSS_FORCED_DEDICATED'
OSS_CERTIFIED_DEDICATED = 'CERTIFIED_PLUGIN_OSS_CERTIFIED_DEDICATED'
OSS_UNTRUSTED_DEDICATED = 'CERTIFIED_PLUGIN_OSS_UNTRUSTED_DEDICATED'
class PluginLogVisibility(str, Enum):
@@ -147,10 +148,19 @@ def verify_plugin_archive_certificate(
) -> VerifiedArchiveCertificate:
"""Use the SDK ZIP-comment API and retain its normalized-digest binding."""
verification = verify_archive(archive, trusted_public_key_ring(trusted_public_keys).get)
key_ring = trusted_public_key_ring(trusted_public_keys)
verification = verify_archive(archive, key_ring.get)
envelope = verification.envelope
runtime_profile = envelope.shared_runtime if envelope is not None else None
certificate_id = envelope.key_id if envelope is not None else None
# Record the issuer identity only when this instance actually resolved it
# through the configured ring. When the ring is empty (for example a
# self-hosted deployment that never configured
# ``plugin.certification.trusted_public_keys``) the declaration is
# unresolvable rather than rejected, so ``certificate_id`` stays unset and
# admission can degrade to the dedicated profile instead of blocking.
certificate_id = (
envelope.key_id if envelope is not None and envelope.key_id in key_ring else None
)
state = {
'absent': CertificateVerification.ABSENT,
'malformed': CertificateVerification.MALFORMED,
@@ -212,6 +222,23 @@ def decide_plugin_admission(
DEDICATED_RUNTIME,
)
# A self-hosted deployment that has not configured the issuer key ring cannot
# verify a marketplace archive's declaration. Falling back to the dedicated
# runtime (rather than blocking the install) is not a privilege escalation:
# the certificate is signed by an issuer this instance does not declare
# trusted, so no shared-runtime privilege may be granted. Shared-runtime
# isolation is therefore refused while the existing OSS dedicated profile
# keeps the install working. Cloud remains fail-closed above.
if not trusted_issuer_configured(facts):
return PluginAdmissionDecision(
AdmissionDisposition.DEDICATED_ALLOWED,
AdmissionCode.OSS_UNTRUSTED_DEDICATED,
DEDICATED_RUNTIME,
)
# The key ring is configured, yet the declaration still failed to verify
# (malformed, signature mismatch, unsupported schema, ...). Surface that as
# an explicit decision instead of silently degrading.
if administrator_force:
return PluginAdmissionDecision(
AdmissionDisposition.DEDICATED_ALLOWED,
@@ -226,6 +253,19 @@ def decide_plugin_admission(
)
def trusted_issuer_configured(facts: PluginCertificationFacts) -> bool:
"""Report whether the deployment holds a trusted issuer key for this archive.
``CertificateFacts`` records the verifying key only when it was resolved
through the configured ring, so an unresolvable declaration (``unknown_key``)
leaves ``certificate_id`` unset. That distinguishes "this operator never
configured the issuer" from "a configured trust decision rejected the
archive".
"""
return bool((facts.certificate.certificate_id or '').strip())
def decide_plugin_log_visibility(facts: PluginCertificationFacts) -> PluginLogVisibility:
"""Select the minimum log visibility compatible with a verified shared runtime."""