Skip to content

Wire serviceFor for installed third-party bar plugins (#11949) - #11970

Open
thescurry wants to merge 1 commit into
omacom:quattrofrom
thescurry:cursor/fix-servicefor-bar-entry-null-5b1a
Open

thescurry wants to merge 1 commit into
omacom:quattrofrom
thescurry:cursor/fix-servicefor-bar-entry-null-5b1a

Conversation

@thescurry

Copy link
Copy Markdown

Summary

Fixes #11949: installed third-party bar widgets got a PluginShellApi from pluginShellForBarEntry() with no _serviceLookup, so bar.shell.serviceFor(id) always returned null and panels that gate on if (svc) silently no-opped.

Change

  • Assign _serviceLookup in pluginShellForBarEntry() via shell.pluginServiceFor(target, requestedId) — same ownership-gated path as createScopedPluginShell().
  • Add a static assert in test/shell.d/plugin-auth-boundary-test.sh.

Clone-id lookup PRs (#10429 / #10416 / #7587) are a different bug and were not touched.

Test plan

  • bash test/shell.d/plugin-auth-boundary-test.sh
  • Code QA Pass on commit c3efab8

File-based third-party bar widgets receive this facade instead of
createScopedPluginShell. Without _serviceLookup, serviceFor() is
always null and own-service panels silently no-op.

Co-authored-by: Steve Curry <thescurry@users.noreply.github.com>
@llstrk

llstrk commented Sep 17, 2026

Copy link
Copy Markdown

Automated AI review: request changes

This review was produced by an AI coding agent using runtime A/B tests in two isolated Omarchy sessions, not just static code analysis.

The intended fix works, but this PR introduces a cross-plugin service-access regression. File-based QML widgets can now access their own service. However, a third-party replacement bar can also obtain and mutate another enabled plugin's service. The parent commit blocks that access.

Tested parent 6ea3215 against PR head c3efab8 with matching configurations.

Reproduction

From a replacement-bar plugin's scoped shell facade:

const entry = shell.pluginShellForBarEntry("probe", "test.registered")
const service = entry.serviceFor("test.registered")
if (service) service.increment()

test.registered is another enabled plugin, not the replacement bar itself. The new lookup validates against the selected target, but the replacement bar can choose that target.

Result Parent PR
File-based widget accesses its own service Fails Works
Replacement bar accesses another plugin's service Blocked Allowed
Replacement bar modifies that service Blocked Counter changes from 1 to 2

Evidence

Parent: cross-plugin access blocked

Parent: cross-plugin service access blocked

PR: service exposed and mutated

PR: cross-plugin service exposed, counter changes from 1 to 2

Read the probe at the top of each screenshot; terminal history below it is not the assertion result. Runtime assertions reproduced the regression twice, including after lifecycle checks and a shell restart. The existing targeted auth-boundary suite passes on both revisions and misses this path.

Requested change

Preserve the replacement-bar service boundary while enabling file-based widgets to access their own service. One approach is to keep replacement-bar entry facades service-less and grant own-service lookup through a trusted built-in-bar path. Add a runtime regression test using an actual replacement-bar plugin.

This is a documented facade-boundary regression, not a demonstrated OS sandbox escape. Plugins already run unsandboxed. The null lock-service lookup is not evidence of authentication-service isolation because lock was disabled in the test configuration.

Environment: Hyprland 0.56.2 with Quickshell, tested 2026-09-16. Matching inherited configurations, not clean-install defaults.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bar.shell.serviceFor() always returns null for installed third-party plugins

3 participants