Plugins: names are assumed unique across kinds, but nothing enforces it and the reference plugin breaks it #593
Labels
No labels
accessibility
authentication
breaking change
bug
documentation
enhancement
interface
internationalisation
observability
performance
security
tier
1
tier
2
tier
3
tier/4
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Postulo/postulo#593
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Found on 2026-09-29 in a read-only review of postulo at
95348908d, by checking what the policy, the logo cache and the plugin forms key on, and rendering a source and a notifier with the same name in a test.Checked again on 2026-10-01 against
mainat0a5cad587: still present. The code is now atplugins/policy.py:158-164(decide) andplugins/policy.py:277-305(overviewrows),accounts/settings_views.py:251-256(set_choice) andcore/server_views.py:1045-1101(_policy_rows);plugins/registry.py:314-325,plugins/logos.py:120-142, the template and postulo-helloworld are where they were.Policy rows, memoised decisions, the logo cache and URL, and both plugin forms key on
plugin.namealone, andregistry.find_any()says "A name is unique across the instance". Nothing checks it: the registry and the publishedpostulo.plugins.testingcheck accept two plugins of different kinds with the same name.postulo-helloworld, the reference plugin third parties are told to copy, registers a source and a notifier both calledhelloworld.plugins/registry.py:314-325(find_anyand its docstring);plugins/policy.py:167-173(decide, memoised on(plugin_name, person));plugins/policy.py:286-314(overviewrows);accounts/settings_views.py:249-254(set_choice(request.user, row["name"], on=row["name"] in wanted));templates/settings/plugins.html:64and:87(id="plugin-{{ row.name }}",for="plugin-{{ row.name }}");core/server_views.py:1044-1100(_policy_rows);plugins/logos.py:120-142(_cachekeyed by name). In postulo-helloworld:src/postulo_helloworld/source.py:92andsrc/postulo_helloworld/notifier.py:105, bothname = "helloworld".helloworld, with two checkboxes sharingid="plugin-helloworld"(the second label switches the first box). Unticking one of them does nothing: the other still postson=helloworld, and both rows are saved as on. A test with a source and a notifier both nameddupgot two rows, and after ticking only one,decide("dup", user).onwasTruefor both. One policy row, one instance-level switch and one logo govern both plugins, and the logo URL always resolves to whichever kindfind_anymeets first.postulo.plugins.testingfail on it; or key policy rows, form fields and logo URLs on kind and name together. Either way, rename one of postulo-helloworld's two plugins so the reference shows the rule.Done when
tests/test_plugin_policy.pythat registers a source and a notifier with the same name and asserts the second is refused (or that the person's page renders two distinct ids and switching one leaves the other on).