Skip to content
This repository was archived by the owner on May 29, 2026. It is now read-only.

Release: merge development into beta#176

Open
github-actions[bot] wants to merge 103 commits into
betafrom
development
Open

Release: merge development into beta#176
github-actions[bot] wants to merge 103 commits into
betafrom
development

Conversation

@github-actions

Copy link
Copy Markdown
Contributor

Automated PR to sync development changes to beta for beta release.

Merging this PR will trigger the beta release workflow.

Reminder: Add a major, minor, or patch label to this PR to control the version bump. Default is patch.

WilcoLouwerse and others added 23 commits March 24, 2026 21:14
- Merge openspec/README.md: keep Planix goal section, add artifact
  progression diagram, workflow steps, and full commands table
- Add openspec/ROADMAP.md for feature tracking
- Add openspec/architecture/README.md with ADR format guide
- Add openspec/specs/README.md with feature spec format and lifecycle
…uestions

- Resolve all 6 open research questions in ARCHITECTURE.md:
  CalDAV one-way export (V1), 1-level sub-tasks, configurable Procest
  bridge (Procest UI decides), per-task-only time tracking, GitHub sync
  via OpenConnector (V1), soft WIP limits
- Update ADR-003 (Procest bridge) and ADR-004 (time tracking scope)
- Create 8 OpenSpec changes with full artifacts (proposal, design, specs, tasks):
  register-schemas, projects, tasks, kanban-board, time-tracking,
  dashboard-my-work, admin-user-settings, procest-integration
- Update all 7 feature specs to in-progress status with change links
- Add conduction schema symlink (openspec/schemas/conduction)
- Sync app description in info.xml and README.md with app-config.json goal
Replace example schema with 5 production schemas (task, project, column,
timeEntry, label), add seed data (5 labels, 3 projects, 12 columns,
5 tasks, 3 time entries), register deep links for all schemas, and
bump register version to 0.2.0.
…x data model

Replace placeholder example schema with 5 production schemas (task, project,
column, timeEntry, label) and 28 seed data objects. Fix repair step registration
(info.xml), loadConfiguration() argument passing, and register definition fields
required by ImportHandler. All quality checks pass (PHPCS, Psalm, PHPStan).
Implement register-schemas — 5 schemas with seed data for Planix data model
…, and l10n

Add ProjectList, ProjectBoard, and ProjectBacklog views with supporting
store, router entries, and MainMenu navigation. Include l10n strings,
webpack config, and openspec plan/test-plan artifacts for the projects
change and all related MVP changes.
Adds full project management surface to Planix — list, create, settings
sidebar, member management, archive, and delete. All 47 tasks complete,
verified by automated functional tests (all 11 TC pass).

Key fixes applied during implementation:
- webpack publicPath overridden to /apps-extra/planix/js/ (chunk 404s)
- settings sidebar PATCH now includes members to prevent data loss on save
- NcChip imported via component path (not available in @nextcloud/vue main index v8.16.0)
- publicWrite/publicRead enabled on planix register via repair step migration
Add project management UI — list, create, settings, member management, archive and delete
…acking, settings, and procest integration

Update README with restructured feature list, add six new feature docs
with standards references, and include project screenshots.
Add bug tasks discovered by automated test agents to three changes:
- admin-user-settings: health/metrics 500s, admin settings 404, label a11y
- dashboard-my-work: KPI card a11y roles, missing alt text
- tasks: status enum validation, PATCH support for partial updates
…Register init (#8)

Merged by Hydra pipeline (admin override — PHPUnit CI failures are environment bootstrap issues, not code issues). All code review and security checks passed after 3 fix iterations.
Add 6 openspec change proposals (admin-settings-mvp archive, dashboard-my-work,
kanban-mvp, label-crud, projects-crud, status-api) and 5 test scenarios for
backend settings, frontend admin root, and default columns editor.
@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/planix @ 2c3d9c4

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
composer ✅ 100/100
npm ✅ 215/215
PHPUnit ⏭️
Newman ⏭️
Playwright ⏭️

Quality workflow — 2026-04-14 05:30 UTC

Download the full PDF report from the workflow artifacts.

@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/planix @ 5a67a87

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
composer ✅ 100/100
npm ✅ 215/215
PHPUnit ⏭️
Newman ⏭️
Playwright ⏭️

Quality workflow — 2026-04-14 08:41 UTC

Download the full PDF report from the workflow artifacts.

@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/planix @ 205b614

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
composer ✅ 100/100
npm ✅ 215/215
PHPUnit ⏭️
Newman ⏭️
Playwright ⏭️

Quality workflow — 2026-04-14 09:21 UTC

Download the full PDF report from the workflow artifacts.

@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/planix @ 2abaff0

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
composer ✅ 100/100
npm ✅ 215/215
PHPUnit ⏭️
Newman ⏭️
Playwright ⏭️

Quality workflow — 2026-04-14 09:48 UTC

Download the full PDF report from the workflow artifacts.

github-actions Bot and others added 4 commits May 26, 2026 04:47
…omplete-translations

# Conflicts:
#	test-results/README.md
#	test-results/accessibility-results.md
#	test-results/api-results.md
#	test-results/performance-results.md
#	test-results/security-results.md
#	test-results/ux-results.md
- Add eslint-plugin-import-x and node-polyfill-webpack-plugin overrides/pins
  so the lockfile resolves without --legacy-peer-deps (lint-check ran
  'npm ci' which failed on pre-existing peer-dependency conflicts).
- Regenerate package-lock.json under strict peer resolution.
- Declare the ADR-008 @SPEC JSDoc tag in eslint.config.js so the
  spec-coverage annotations stop tripping jsdoc/check-tag-names.
- Add missing @param JSDoc lines and fix stylelint rule-empty-line-before
  errors in Settings.vue surfaced by the development merge.
…lations

chore: kanban app icons, remove stale test-results, coverage report
@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/planix @ 70a0c2f

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
composer ✅ 100/100
npm ✅ 426/426
PHPUnit
Newman ⏭️
Playwright ⏭️

Coverage: 0% (0/3 statements)


Quality workflow — 2026-05-26 05:25 UTC

Download the full PDF report from the workflow artifacts.

@github-actions

Copy link
Copy Markdown
Contributor Author

Quality Report — ConductionNL/planix @ 2157e42

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
composer ✅ 100/100
npm ✅ 426/426
PHPUnit
Newman ⏭️
Playwright ⏭️

Coverage: 0% (0/3 statements)


Quality workflow — 2026-05-26 06:10 UTC

Download the full PDF report from the workflow artifacts.

)

Gate-5 route-auth: create HealthController and MetricsController for the
/api/health and /api/metrics routes that were registered in routes.php but
had no backing controller files, leaving those routes unreachable.

Gate-7 no-admin-idor: add IUserSession 401 guard to SettingsController::index()
so that the @NoAdminRequired READ endpoint explicitly returns 401 for
unauthenticated callers rather than relying on implicit middleware.

All 18 hydra gates green; phpstan no errors; php -l clean.

Refs #250
All 16 scenarios in openspec/specs/register-schemas/spec.md are pure
backend: PHP file content inspection, OpenRegister API validation (HTTP
400 responses), install-time repair steps, and version-skip logic.
None have a browser UI surface, so the spec is excluded from
gate-19 e2e-coverage enforcement with an explicit reason.

Gate-19 result: uncovered=0, excluded=16, coverage_pct=null (no UI scenarios).
test(e2e): gate-19 e2e-coverage — 0 uncovered (register-schemas whole-spec excluded)
…alignment

fix(phpcs): correct equals sign alignment in HealthController
…ross-tenant IDOR (#276)

Fixes #257 (IDOR: any authenticated user could read/write all projects/tasks),
#258 (missing owner field blocked creator-based RBAC), and #259 (migration
docblock claimed publicWrite:true contradicting the membership-access spec).

Changes:
- planix_register.json: add `authorization` blocks to project, task, column,
  and timeEntry schemas enforcing member-based row-level read/write filtering;
  project delete restricted to owner or admin; timeEntry write restricted to
  the logging user. Add explicit `publicRead: false` and `publicWrite: false`
  to the register block. Add `owner` field to project schema for creator RBAC.
  Bump register version 0.2.0 -> 0.2.1, info version -> 0.2.3.
- Version20260403000000.php: correct misleading docblock that claimed
  publicWrite:true — the intent is and has always been publicWrite:false.
- tests/unit/Settings/PlanixRegisterSchemaTest.php: 7 new tests that
  mechanically assert the security properties introduced above, acting as
  regression guards so the IDOR cannot silently reopen.
- #260 ProjectLeaveDialog.mounted() no longer calls leaveProject as a
  probe; it inspects the already-fetched members array via fetchProject
  so Cancel can never inadvertently mutate state.

- #261/#262 Split removeMember into a pure write action and a new
  getMemberTaskCount read action. confirmRemoveMember now queries the
  count first and only removes when count is zero, eliminating the
  remove-then-re-add race and the silent data-loss if the re-add fails.

- #264 ProjectDeleteDialog now checks project.owner === currentUid ||
  isAdmin before enabling the delete button. The canDelete guard is also
  enforced in confirm() as a server-side safety net. owner is now set
  on createProject so newly created projects are always gated correctly.
…eation policy (#278)

- #263 deleteProject cascade error messages now explicitly tell the user
  some data may remain and to retry, rather than silently halting.

- #265 allow_project_creation is now read by ProjectList: 'admins' policy
  hides the New project button and creation dialog from non-admin users.
  Added a UI control in Settings.vue so admins can configure the policy.

- #266 SettingsService.setAdminSettings validates default_columns as a
  non-empty JSON array of non-empty strings before persisting; malformed
  values are rejected with a logger warning instead of being stored verbatim.
  New validateDefaultColumns() method is public and testable.

- #267 DeepLinkRegistrationListener URL templates switched from hash-mode
  (#/tasks/{uuid}) to history-mode paths (/apps/planix/projects/{uuid})
  matching the router configuration. All five hardcoded 'planix' strings
  replaced with Application::APP_ID.
…, error handling (#279)

- #268 Remove the dead re-export of useObjectStore from store.js; the
  local modules/object.js is used only for boot-time configuration and
  no longer shadows the @conduction/nextcloud-vue store that projects.js
  actually imports.

- #269 Dashboard KPI widgets now show real counts from the projects store
  (active/archived/total projects) instead of hardcoded placeholder values.
  Added a recent-projects list and a 'Go to projects' quick-action button.

- #270/#271 MainMenu.openLink now passes 'noopener,noreferrer' to
  window.open. Documentation link updated from conduction.nl (marketing
  site) to planix.conduction.nl (app-specific docs site).

- #272 Align version strings: info.xml bumped 0.2.1 → 0.2.3; register
  block version bumped 0.2.1 → 0.2.3 to match info.version and prevent
  OR from treating an already-imported register as stale.

- #273/#267 DeepLinkRegistrationListener: all five hardcoded 'planix'
  literals replaced with Application::APP_ID. URL templates switched from
  hash-mode (#/path) to history-mode (/apps/planix/path) to match the
  Vue Router configuration.

- #274 loadConfiguration validates x-openregister.app matches APP_ID
  before calling importFromApp, preventing accidental cross-app imports.

- #275 MemberSearch.searchUsers now throws on non-2xx responses and
  surfaces all errors to the user via showError instead of silently
  logging to the console.
…add cross-project guards (C1 + H1) (#282)

C1: project.update now requires owner match instead of member match.
Any member could previously update owner/members fields, allowing a
non-owner to evict the project owner. Now only the owner (or admin)
may update a project.

H1: task.create and column.create add parent-project membership
predicate, preventing cross-project injection by an authenticated
user who is not a member of the target project.
timeEntry.create adds user:$userId match to prevent impersonation —
a user may only log time as themselves.

Bumps all affected schema versions (0.1.1 → 0.1.2) and register/
info.xml version (0.2.3 → 0.2.4).
…ion policy (C2 + H2) (#283)

C2: InitializeSettings::run uses force:false (not force:true) so the repair
step respects already-configured OR schema IDs and does not overwrite
operator customisations on every install/upgrade. This was regressed on
development when the retrofit spec added force:true; main was already correct.

H2: allow_project_creation is now enforced server-side:
- SettingsService::canCurrentUserCreateProject() reads the policy config key
  and returns false for non-admins when the policy is 'admins'.
- ProjectController::checkCreatePolicy() exposes GET /api/projects/check-create-policy
  returning 200/403; requires @NoAdminRequired (authenticated user call).
- ProjectCreationDialog::submit() calls the policy gate before delegating to
  OR, so a user who bypasses the client-side canCreateProject guard still
  receives a 403 from the server.
…ontroller, dashboard KPI (M1+M2+M3+L1+L2+L3)

- M1: last-member leave guard in removeMember() + ProjectLeaveDialog disabled state
- M2: owner field preserved in saveDetails() PUT payload
- M3: task deep-link stub reads ?task= query param on ProjectBoard mount
- L1: remove @NoAdminRequired from SettingsController admin write endpoints
- L2: AbortController in MemberSearch cancels in-flight OCS requests
- L3: remove hardcoded-0 open-tasks KPI tile from Dashboard, 4→3 column grid
CVE-2026-48808 — sandbox property allowlist bypass via column filter
CVE-2026-48805 — sandbox state regression in deprecated internal wrappers
CVE-2026-46636 — sandbox filter/tag/function allowlist bypass on re-render
CVE-2026-48806 — sandbox __toString() bypass via dynamic mapping keys
CVE-2026-48807 — sandbox __toString() bypass via Traversable in join/replace/in

twig/twig was a transitive dependency (via edgedesign/phpqa); updated
lock file from v3.26.0 to v3.27.0. No composer.json change required.
fix(deps): bump twig/twig to 3.27.0 (5 sandbox CVEs)
…ers, C3 leave endpoint

C1: ProjectController::create() enforces allow_project_creation policy server-side.
C2: addMember/removeMember use PATCH to prevent owner field null-wipe.
C3: ProjectController::leaveProject() with _rbac: false enables non-owner leave.
Removes 2 scaffold-only change dirs with no authored proposal.md:

- issue-180/ (only applier.json)
- p2-meeting-management/ (only builds/)

Created by bulk-scaffolding steps that didn't follow through to
author. Re-create via /opsx-new when ready.
Non-owner members were getting a 403 when trying to leave a project
because the dialog called removeMember (PATCH on the OR object, subject
to owner-only RBAC). Replace with leaveProject which POSTs to
/api/projects/{id}/leave with server-side _rbac:false handling.
SettingsService:220 — add named parameter `raw:` to internal call of
validateDefaultColumns() to satisfy the NamedParameters PHPCS sniff.

ProjectController::create() — delegate the allow_project_creation policy
check to checkCreatePolicy() instead of duplicating it inline.  This
removes the duplicated guard, gives checkCreatePolicy() a PHP caller so
gate-6 orphan-auth passes, and consolidates the 403 path in one place.

composer check:strict: exit 0.  All 19 Hydra gates GREEN.
fix: pass composer check:strict — named-arg PHPCS + orphan-auth gate-6
…Project owner-wipe, test alignment

C1 (schema layer): tighten project.create rule from ["authenticated"] to
["admin"] in planix_register.json. The C1 proxy endpoint (ProjectController::create)
already owns enforcement server-side, so direct OR POSTs without admin
rights are now rejected at the schema layer as well, closing the bypass.

H1 (archiveProject owner-wipe): archiveProject() called updateProject(id,
{ status: 'archived' }) which triggered OR PUT semantics — filling every
missing schema property (including `owner`) with null. Switch to patchProject()
so only `status` is sent, matching the C2 fix already applied to addMember
and removeMember.

Test: PlanixRegisterSchemaTest::testProjectAuthorizationEnforcesMembershipForReadAndUpdate
was asserting a members-based update rule that no longer exists (wave-3
changed update to owner-match). Split into two tests: one for read-membership,
one for update-owner, both aligned with current security intent.

Bump appinfo/info.xml to 0.2.6 (immutable cache-bust).
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants