Prevent authentication service capability downgrades

Co-Authored-By: GPT-5.6-Sol <noreply@openai.com>
This commit is contained in:
Ryan HughesandGPT-5.6-Sol committed 2026-09-06 20:20:30 -04:00
1 parent 203e1639c3
commit f2b4731bb3
5 files changed
+40 -7

No files matched your search

+6
View File
@@ -4,6 +4,7 @@
// a separate empty store rather than a shared path to credential-bearing QML.
var services = ({})
var trustedIds = ({})
function has(id) {
return services[String(id || "")] !== undefined
@@ -12,11 +13,16 @@ function has(id) {
function put(id, service) {
var key = String(id || "")
if (!key || !service) return
trustedIds[key] = true
if (services[key] && services[key] !== service && typeof services[key].destroy === "function")
services[key].destroy()
services[key] = service
}
function isTrusted(id) {
return trustedIds[String(id || "")] === true
}
function ids() {
return Object.keys(services)
}
+9 -7
View File
@@ -375,7 +375,7 @@ ShellRoot {
if (!shell.pluginHasBarCapabilities(manifest)) return false
var id = shell.pluginRegistry.resolveEnabledId(String(requestedId || ""))
var target = shell.pluginRegistry.installedPlugins[id]
if (!target || shell.isAuthenticationService(target)) return false
if (!target || shell.isAuthenticationService(target, id)) return false
if (shell.barEntryConfigured(id)) return true
var uiKinds = ["bar-widget", "panel", "overlay", "menu"]
for (var i = 0; i < uiKinds.length; i++)
@@ -842,9 +842,11 @@ ShellRoot {
return serviceFor(shell.pluginRegistry.resolveEnabledId(pluginId))
}
function isAuthenticationService(manifest) {
return !!manifest && Array.isArray(manifest.__hostCapabilities)
&& manifest.__hostCapabilities.indexOf("authentication") !== -1
function isAuthenticationService(manifest, pluginId) {
var key = String(pluginId || (manifest && manifest.id) || "")
return AuthServiceStore.isTrusted(key)
|| (!!manifest && Array.isArray(manifest.__hostCapabilities)
&& manifest.__hostCapabilities.indexOf("authentication") !== -1)
}
function ensureService(pluginId) {
@@ -857,7 +859,7 @@ ShellRoot {
if (!manifest.entryPoints || !manifest.entryPoints.service) return null
var url = pluginRegistry.entryPointUrl(manifest, "service")
if (!url) return null
var authenticationService = shell.isAuthenticationService(manifest)
var authenticationService = shell.isAuthenticationService(manifest, key)
if (authenticationService && AuthServiceStore.has(key)) return null
var comp = Qt.createComponent(url, Component.PreferSynchronous)
@@ -908,7 +910,7 @@ ShellRoot {
if (!Array.isArray(m.kinds) || m.kinds.indexOf("service") === -1) continue
if (!m.entryPoints || !m.entryPoints.service) continue
if (!pluginRegistry.isEnabled(id)) continue
var authenticationService = shell.isAuthenticationService(m)
var authenticationService = shell.isAuthenticationService(m, id)
if (_services[id]) {
if (authenticationService) {
// A service that gains a trusted authentication capability must move
@@ -965,7 +967,7 @@ ShellRoot {
&& authenticationManifest.entryPoints
&& authenticationManifest.entryPoints.service
if (stillAuthenticationService && pluginRegistry.isEnabled(authenticationId)
&& shell.isAuthenticationService(authenticationManifest)) continue
&& shell.isAuthenticationService(authenticationManifest, authenticationId)) continue
AuthServiceStore.destroy(authenticationId)
}
}
@@ -10,6 +10,10 @@ QtObject {
return AuthServiceStore.has(id)
}
function isTrusted(id) {
return AuthServiceStore.isTrusted(id)
}
function updateManifest(id, manifest) {
AuthServiceStore.updateManifest(id, manifest)
}
@@ -75,6 +75,7 @@ ShellRoot {
ownSettings: api.updateEntryInline(caller, {}) === true,
foreignSettings: api.updateEntryInline("omarchy.lock", {}) === false,
authStoreOwnerRetains: authStoreOwner.has("omarchy.lock") === true,
authStoreOwnerRemembersTrust: authStoreOwner.isTrusted("omarchy.lock") === true,
authStoreOwnerUpdatesManifest: root.ownService.manifest
&& root.ownService.manifest.version === "kept",
authStoreImportIsolated: authStoreReader.has("omarchy.lock") === false,
+20
View File
@@ -37,12 +37,32 @@ qml_matches "$shell_qml" 'comp\.createObject\( *manifest\.__isFirstParty *&& *!a
fail "third-party and authentication services are detached from the host object tree"
qml_matches "$shell_qml" 'AuthServiceStore\.put\( *key, *inst *\)' ||
fail "authentication services are retained outside the host service map"
qml_matches "$shell_qml" 'AuthServiceStore\.isTrusted\( *key *\)' ||
fail "live authentication classification survives public manifest mutation"
qml_matches "$shell_qml" 'AuthServiceStore\.updateManifest\( *id, *shell\.publicPluginManifest\( *m *\) *\)' ||
fail "kept authentication services receive only a public manifest snapshot"
qml_matches "$shell_qml" 'if *\( *!serviceKeepLoaded\( *authenticationId *\) *\) *AuthServiceStore\.destroy\( *authenticationId *\)' ||
fail "keepLoaded authentication services survive plugin rescans"
pass "third-party and authentication services are detached from the host object tree"
run_node_test <<'JS'
const fs = require('fs')
const vm = require('vm')
const store = {}
vm.createContext(store)
vm.runInContext(
fs.readFileSync(path.join(root, 'shell/services/AuthServiceStore.js'), 'utf8'),
store
)
const service = { destroy() {} }
store.put('omarchy.lock', service)
store.destroy('omarchy.lock')
assert(
!store.has('omarchy.lock') && store.isTrusted('omarchy.lock'),
'authentication classification survives service teardown'
)
JS
qml_matches "$shell_qml" 'inst\.shell *= *shell\.pluginShellFor\( *manifest *\)' ||
fail "service plugins receive a scoped shell facade"
qml_matches "$shell_qml" 'item\.shell *= *shell\.pluginShellFor\( *panelEntry\.manifest *\)' ||