2026.09.71 — 2026-09-30
Breaking
Refuse unsigned and untrusted plugins unless allowed (a95d6f55)
The plugin plan now reports who signed a jar (fingerprint, subject, trusted key name, the installed version's signer) and which confirmations an activation needs: permissions-added on an update, signer-changed when the signer differs from the installed one, unverified when the allowance lets an untrusted or unsigned jar through. Activation, update, rollback and enable refuse an untrusted or unsigned jar (plugin-untrusted, plugin-unsigned) unless unverified plugins are allowed, and refuse a plan that needs confirmation unless the caller acknowledged it (acknowledgement-required). An untrusted or unsigned upload stays pending so its key can be trusted.
The signer is recorded on the install row of the version actually activated, and the plugin summary reports it with a verified flag computed from the current keys. A new pluginTrust health contributor joins the studio group and reports DEGRADED, naming the plugins, while any installed plugin is not signed by a trusted key.
For upgraders: sign plugin builds and add the publisher key to the trusted keys, or allow unverified plugins. Plugins already installed keep running and show as unverified until then.
Added
Show publisher and trust in the review, manage trusted keys, badge unverified plugins (428a1faa)
Sign plugins from the template and check them against the publisher's key (f8d9a903)
The plugin template gains a
signprofile, active when-Dplugin.signing.keystoreis set, that signs the jar with maven-jarsigner-plugin at package (alias fromplugin.signing.alias, store password from the PLUGIN_SIGNING_STOREPASS environment variable). The README explains creating an EC key, publishing the certificate withkeytool -exportcert -rfcand checking a build against it.PluginVerifier now prints
Signed by <subject>, key <fingerprint>, or warns[plugin-unsigned]that Studio refuses an unsigned plugin unless an installer allows unverified plugins. With-Dartemis-studio.plugin.certificate=<pem>it exits 1 unless that key signed the jar, and also when the jar is unsigned.CI's plugin-template job signs the template with a throwaway key and verifies it against that key's exported certificate.
Trusted-key and trust-policy admin API with acknowledged activation (13b21ef8)
Installers can now manage publisher trust over the admin API. GET /api/v1/admin/plugins/keys lists the trusted keys, the unverified allowance and the installed plugins each key signed. POST /keys trusts a key given as exactly one of a pending upload, whose key is read from the stored jar and never from the client, or a PEM. DELETE /keys/{fingerprint} removes one, and PUT /trust-policy turns the unverified allowance on or off. Each change needs a sign-in or step-up in the last five minutes and is audited (PLUGIN_KEY_ADD, PLUGIN_KEY_REMOVE, PLUGIN_TRUST_POLICY); the audit row is opened before the installer and step-up checks, so a refused attempt is recorded as failed.
Activate, enable and rollback take ?acknowledge=true. A plan that lists acknowledgements is refused with 409 acknowledgement-required without it, even when the UI is bypassed. An activation of a jar that is not signed by a trusted key records trust=unverified and the signer fingerprint in its audit event.
The plan view reports the signer (trust) and the acknowledgements it needs, and the plugin view reports signerFingerprint, signerSubject and verified.
Refuse unsigned and untrusted plugins unless allowed (a95d6f55)
The plugin plan now reports who signed a jar (fingerprint, subject, trusted key name, the installed version's signer) and which confirmations an activation needs: permissions-added on an update, signer-changed when the signer differs from the installed one, unverified when the allowance lets an untrusted or unsigned jar through. Activation, update, rollback and enable refuse an untrusted or unsigned jar (plugin-untrusted, plugin-unsigned) unless unverified plugins are allowed, and refuse a plan that needs confirmation unless the caller acknowledged it (acknowledgement-required). An untrusted or unsigned upload stays pending so its key can be trusted.
The signer is recorded on the install row of the version actually activated, and the plugin summary reports it with a verified flag computed from the current keys. A new pluginTrust health contributor joins the studio group and reports DEGRADED, naming the plugins, while any installed plugin is not signed by a trusted key.
For upgraders: sign plugin builds and add the publisher key to the trusted keys, or allow unverified plugins. Plugins already installed keep running and show as unverified until then.
Trusted publisher keys and the unverified allowance (ffc4e2c0)
Add the trust store for plugin signing: pinned publisher keys, a single-row policy holding the off-by-default allowance for unverified plugins, and the signer columns on plugin_install. PluginTrust computes the decision (trusted, untrusted, unsigned) from the current keys on every call, and PublisherKeys parses a PEM certificate or public key.
Verify plugin jar signatures and report the signer (7b9b288c)
The validator now opens plugin jars with signature verification and reports the signer (SHA-256 fingerprint of its public key) in the validation report. A jar with a changed entry or manifest, an added or unsigned entry, an entry the signature lists but the jar lacks, or more than one signer is refused. An unsigned jar stays valid with no signer; whether it may run is decided later. Standard jarsigner metadata under META-INF is now on the allowlist.
Fixed
- Wrap long key fingerprints and say why an unsigned plugin cannot continue (72074b17)
Security
Check the signer again when a restart starts a waiting version (806fa9dd)
A restart-class activation records the new version and waits for the restart. If its signing key is removed in between, the restart now marks the plugin failed instead of starting a version nobody verified. A directory entry with content is refused like any other unsigned entry, and the key-add audit row records where the key came from rather than the PEM a client sent.