Commit 19e9b49
authored
* fix(security): bump phpcsstandards/phpcsutils to 1.2.3 for CVE-2026-65954
phpcsstandards/phpcsutils < 1.2.3 carries CVE-2026-65954 (arbitrary code
execution, GHSA-r6hr-vr92-vv28, affected >=1.0.0-alpha1,<1.2.3). The advisory
was published today, so composer audit turns red on a lock file that has not
changed. The Security (composer) job in run 31516037194 passed at 17:25Z only
because it ran before the advisory landed.
Negative control before the bump, on this tree:
composer audit --locked
-> Found 1 security vulnerability advisory affecting 1 package
phpcsstandards/phpcsutils / CVE-2026-65954 / exit 1
After 'composer update phpcsstandards/phpcsutils --no-install --no-scripts'
(1.2.2 => 1.2.3, a lock-only change, 0 installs 0 removals):
composer audit --locked
-> No security vulnerability advisories found / exit 0
The bump is exercised rather than merely locked: phpcs runs green against the
new library on PHP 8.4 --
0 ERRORS AND 468 WARNINGS IN 211 FILES, exit 0
so no sniff regressed on the upgrade.
* refactor: retire the inert groupfolder-storage-backend capability (closes #87, gate-57 1 -> 0)
gate-57 (orphaned-write-capability) reported exactly one finding on this repo:
lib/Service/DashboardService.php:3189 method=writeDashboardContent
rule=orphaned-write-capability class=DashboardService
It is a true positive and the real scope is larger than one method. Task 5 of
the original change -- the wiring that would have routed dashboard
get/create/update/delete through the storage factory -- was ticked in tasks.md
but never performed. Everything around it landed, which is why the capability
looked complete from every angle except the one that mattered.
Measured on this tree, every absence claim taken with a positive control (the
same search resolves callers for getUserDashboards and ->findByUuid):
DashboardService::writeDashboardContent() 0 callers
DashboardService::readDashboardContent() 0 callers
DashboardService::deleteDashboardContent() 0 callers
DashboardContentStorageFactory::getStorage() 0 (only the three above)
DashboardApiController::storageUnavailableResponse() 0 callers
DashboardMapper::findAll() 1 -- the migration command
So nothing ever populated oc_launchpad_dashboards.content, Dashboard::
jsonSerialize() emitted a content key that was always null, launchpad.
content_storage was a no-op setting, and launchpad:storage:migrate-to-groupfolder
migrated nothing.
Retired rather than completed, per Ruben's decision on #87: finishing it needs
an ADR-level ruling on whether the content blob or the WidgetPlacement rows are
the source of truth for a dashboard's layout. The spec asserts the blob, the
code has used the rows since long before this capability existed, and wiring
the blob in as a second source of truth invites divergence. Nobody uses it, so
retiring costs nothing and removes that risk permanently.
Removed: lib/Service/DashboardContentStorage/ (interface, both backends, three
exceptions), DashboardContentStorageFactory, the three DashboardService facade
methods and the factory constructor parameter, storageUnavailableResponse(),
DashboardMapper::findAll(), both CLI commands and their <command> registrations,
the $content/$locale properties with decodeContent() and their jsonSerialize()
keys, and the five PHPUnit classes covering the removed tree.
Version002009Date20260811000000 drops the content and locale columns. That loses
no data: the only writer, DbContentStorage::write(), was reachable only through
the factory, which was reachable only through the three orphaned facade methods,
so the columns were NULL on every row of every install. The step is idempotent
(hasColumn-guarded). Version002001Date20260603000000 and
DashboardTableBuilder::addContentStorageColumns() are deliberately KEPT -- a
shipped migration is part of the version ledger and deleting it would
desynchronise instances that have already run it.
Deliberately NOT removed, and tracked as Task 8 on #87: launchpad.content_storage,
AdminSettingKey::CONTENT_STORAGE, SetupWizardService::{get,set}ContentStorage /
hasGroupfolderApp, AdminController::setWizardStorage and setup-wizard step 2.
Those belong to the setup-wizard capability, which WRITES the setting; this
capability was the READER. The setting was already inert before this change, so
removing the reader does not make it worse, and retiring the wizard step edits a
different spec and deserves its own review.
The spec is marked withdrawn rather than deleted, which also retires its 16
@e2e exclude markers -- their stated reasons cited DbContentStorageTest.php, a
file this change deletes, so they would otherwise have become sixteen
exemptions resting on a class that no longer exists.
Verification, all on this tree:
gate-57 over lib/Service/*.php 1 -> 0
planted true positive (postGate57Probe, a tracked write method with no
caller) 0 -> 1, naming exactly the plant
plant removed 1 -> 0
PHPUnit 1570 tests, 4032 assertions, 0 failures,
3 pre-existing skips
phpcs 0 errors, 468 warnings, 211 files --
identical to the pre-change baseline
check_spec_anchors.py over lib/ 0 dangling anchors
gate-19 unchanged at 77 (base origin/beta)
NOTE for anyone reproducing gate-57: its checker takes a FILE LIST, not a
directory. `check_orphaned_write_capability.py .` prints nothing and exits 0
whatever the tree contains, which reads exactly like a clean repo.
* chore(openspec): add the missing .openspec.yaml to the retirement change
Every other active change dir under openspec/changes/ carries one
(schema/created/status); the retirement change was missing it.
* docs(spec): annotate the 84 changed frontend methods gate-16 reports (84 -> 0)
gate-16 (spec-coverage) reported 84 changed methods without an @SPEC tag
against base origin/beta. All 84 were in src/; zero were PHP -- the 273
lib/**/*.php files in the same diff are fully annotated already, established
with a positive control (WeatherService.php carries 2 tags, HealthPingController
3) so that "all in one layer" is a measurement rather than a failed lookup.
Closed with 84 real @SPEC anchors and exactly 2 reason-bearing exclusions.
The anchors point at the specs the methods actually implement -- principally
clock-weather-widgets, iframe-embed-widget, live-data-tile-widget,
service-health-ping and tile-quick-search. Every one resolves: the canonical
check_spec_anchors.py reports 0 dangling anchors across all of src/. That
matters because a dangling anchor fails SILENTLY in this gate family, so an
unresolvable tag would have made the gate green while asserting nothing.
The two exclusions, both verified against the source rather than argued from
the scenario text:
ClockWidget.vue::pointOnCircle -- pure polar-to-cartesian trigonometry. The
body reads neither this.now nor this.content, so it encodes no clock,
timezone, format or accessibility behaviour. Verified its only two callers
are tickCoords and handCoords, and both carry the REQ-CLOCK-003 tag.
useTileClickTracking.js::__resetTileClickTrackingForTest -- test-only harness
hook. Verified by grep that its only importer anywhere in the repo is
src/composables/__tests__/useTileClickTracking.spec.js; it implements no
product behaviour, only clearing cachedEnabled / inflightConfigPromise so
each test starts from a cold cache.
Both reasons name a concrete artifact (a caller, an importing test file) rather
than a state of the world, which is the shape that survives re-checking.
This diff is comment-only and provably so: 0 added and 0 removed lines fall
outside a docblock. No runtime behaviour can change.
Verification:
gate-16 (base origin/beta) 84 -> 0
check_spec_anchors.py 0 dangling anchors across src/
tools/check-spec-annotations.php unchanged at 12 pre-existing PHP findings
(TileClickMapper, WidgetPlacement, TileClick,
ImportLaunchpadRegister, Version002007) -- none
introduced here, none in a file this PR touches
phpcs 0 errors, 468 warnings, 211 files (baseline)
No gate was weakened: no baseline, no allowlist widening, no skipped test.
* docs(spec): anchor WeatherWidget::temperatureAriaLabel to REQ-WEATHER-003
Landed a moment after the main annotation commit. gate-16 was already 0
without it, so this adds no gate movement -- but the method builds the
accessible label that spells out the temperature unit, which is exactly what
REQ-WEATHER-003 ("Locale-aware units and language, WCAG AA") governs, so the
tag belongs there.
Verified: check_spec_anchors.py reports 0 dangling anchors for the file, and
REQ-WEATHER-003 exists at openspec/specs/clock-weather-widgets/spec.md:119.
* docs: make the placementItemKey NOTE precise and point it at #101
The note claimed a 'REQ-GRID-010 "Placement key regeneration"' requirement.
REQ-GRID-010 is titled **Grid Styling**; the stale scenario merely sits inside
it, at spec.md:405. Reworded to say exactly that, to name the line, and to
record that getPlacementKey() -- the method the scenario requires -- no longer
exists.
A wrong diagnosis written into a code comment is worse than no comment: the
next reader inherits it as established fact and nobody re-measures it. Filed
the substance as #101 rather than leaving it only in a docblock.
1 parent e24334b commit 19e9b49
50 files changed
Lines changed: 836 additions & 3049 deletions
File tree
- appinfo
- lib
- Command
- Controller
- Db
- Migration
- Service
- DashboardContentStorage
- openspec
- changes
- archive/2026-06-14-groupfolder-storage-backend
- retire-groupfolder-storage-backend
- specs/groupfolder-storage-backend
- src
- components
- Widgets/Renderers
- admin
- composables
- dialogs
- modals
- services
- stores
- views
- tests/Unit
- Command
- Service
- DashboardContentStorage
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
157 | 157 | | |
158 | 158 | | |
159 | 159 | | |
160 | | - | |
161 | | - | |
162 | | - | |
163 | | - | |
164 | 160 | | |
165 | 161 | | |
166 | 162 | | |
| |||
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
This file was deleted.
0 commit comments