Commit Graph

11 Commits

Author SHA1 Message Date
jmiller f9c1c6b186 @
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 16s
Generic: Project CI / Lint & Validate (pull_request) Has been cancelled
Universal: PR Check / Branch Policy (pull_request) Has been cancelled
Universal: PR Check / Require Docs Update (pull_request) Has been cancelled
Universal: PR Check / Wiki Update Reminder (pull_request) Has been cancelled
Universal: PR Check / Secret Scan (pull_request) Has been cancelled
Universal: PR Check / Validate PR (pull_request) Has been cancelled
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Has been cancelled
Generic: Project CI / Tests (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
fix(review): open-redirect, btoa UTF-8 safety, honest duplicate-mode txn

Pre-release review findings on dev -> main:

- Return-URL open-redirect (MED): a base64 returnurl like "/\evil.com"
  passed the root-relative allow-list (raw[1] is "\", not "/"), and a
  browser normalises leading "/\" to "//", yielding a protocol-relative
  redirect off-site. Reject any backslash or control char in the decoded
  return URL outright — legitimate Joomla admin URLs never contain them.

- installer-backup.js btoa() (LOW): btoa() throws on non-Latin1 input
  (e.g. a UTF-8 filter value in the list query string), and the throw was
  uncaught after preventDefault(), leaving the toolbar button dead. Build
  the redirect (UTF-8-safe base64) BEFORE swallowing the click; if it
  still fails, bail without preventing so Joomla proceeds normally.

- Snapshot duplicate-mode transaction (HIGH): restore() wrapped duplicate
  mode in transactionStart()/Rollback(), but duplicate mode writes via
  Joomla Table API whose Nested tables (categories/tags) issue LOCK
  TABLES, which implicitly COMMITS the open transaction in MySQL — so the
  rollback promise was illusory and a mid-inject failure left a slave
  half-updated. Run only the raw-DB modes (replace/create/merge) inside
  the transaction; duplicate mode runs outside it, matching its existing
  per-item defensive design (bad item skipped + logged, never fatal).

Claude-Session: https://claude.ai/code/session_01WbGBN9VyRK61zczYWcCQ2i
@
2026-07-12 17:14:10 -05:00
jmiller 4e07eabc0f feat(#237): implement duplicate snapshot mode (insert as new, remapped IDs)
Universal: Auto Version Bump / Version Bump (push) Successful in 7s
Finishes the deferred 'duplicate' conflict mode. Inserts snapshot content
as brand-new records with remapped IDs, leaving existing content
untouched — for master->slave 'push as new copies'.

- Uses Joomla's Table API (bootComponent MVCFactory) for Article,
  Category and Module so assets, UCM entries, category/tag nested-sets
  and alias uniqueness are handled by core rather than hand-rolled.
- Remaps category parent_id (parents first), article catid, module menu
  assignments, and custom-field-value item_id; re-features articles.
- Tags are reused/created by title (resolveTagIds) and assigned via the
  article table's newTags so UCM/tag-map stay correct.
- uniqueAlias() avoids alias collisions per scope.
- DEFENSIVE: every item is wrapped in try/catch — a bad item is skipped
  and logged, never fatal — so an untested/partial import degrades
  gracefully instead of corrupting content.

NOTE: still needs a real test pass on a dev site before relying on it.

Refs #237

Claude-Session: https://claude.ai/code/session_01WbGBN9VyRK61zczYWcCQ2i
2026-07-10 12:00:45 -05:00
jmiller 49fcd5e63c feat(#237): portable snapshot transfer + master->slave injection API
Universal: Auto Version Bump / Version Bump (push) Successful in 16s
Adds the first increment of snapshot transfer:

- SnapshotPortable engine: package a snapshot into a portable .msbsnap
  (zip of manifest.json + snapshot.json + sha256), and ingest one back
  (from a zip upload or an inline payload) by materialising a local
  snapshot record.
- Admin: per-row 'Download portable snapshot' + an Import modal
  (upload .msbsnap) on the Snapshots page.
- API: POST /snapshot/inject — master pushes a snapshot payload into a
  slave's Web Services API; gated by a new 'Allow Snapshot Injection'
  option. Route added to the webservices plugin.
- Restore engine: new 'create' mode (skip existing, insert only new) and
  mode-name normalisation (overwrite->replace, create, duplicate). The
  'duplicate' (insert-as-new-with-remapped-IDs) mode returns a clear
  'not yet available' — deferred to a dedicated, tested pass.
- Options: 'Snapshot Transfer' fieldset — default conflict mode +
  allow-inject toggle. Language keys added (en-GB fallback).

Reuses the existing mokosuitebackup.snapshot.manage ACL. NOTE: untested
end-to-end (no dev build this session) — verify download/import/inject
on a dev build.

Refs #237

Claude-Session: https://claude.ai/code/session_01WbGBN9VyRK61zczYWcCQ2i
2026-07-10 11:40:51 -05:00
Jonathan Miller 64ffbb9d61 feat: profiles UI, snapshot detail, progress warning, action logs (#100, #104, #108, #110)
Universal: PR Check / Branch Policy (pull_request) Failing after 1s
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 4s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: PR Check / Validate PR (pull_request) Failing after 4s
Generic: Repo Health / Access control (pull_request) Successful in 2s
Universal: PR Check / Secret Scan (pull_request) Successful in 6s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 10s
Branch Cleanup / Delete merged branch (pull_request) Successful in 1s
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 39s
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 18s
Universal: Workflow Sync Trigger / Sync workflows to live repos (pull_request) Failing after 4m10s
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
#100: Run Backup button on profiles list (per-row) and edit toolbar,
backup count badge linking to filtered backups view, View Backups
toolbar button on profile edit.

#101: Profile → filtered backup list link (included in #100).

#104: Snapshot browse modal now shows tabbed view (Articles,
Categories, Modules) with item counts. AjaxController returns
all content types. Categories show indented hierarchy.

#108: "Do not navigate away or close this window" warning banner
added to both backup and restore progress modals.

#110: Joomla Action Logs integration — RestoreEngine, SnapshotEngine,
and SnapshotRestoreEngine now dispatch events that the actionlog
plugin logs to #__action_logs.

Closes #100, closes #101, closes #104, closes #108, closes #110
2026-06-23 11:03:13 -05:00
Jonathan Miller 8a4ebe1bde feat: selective article restore from snapshot (#58)
Browse articles inside a snapshot and restore individual items:
- SnapshotRestoreEngine::restoreSelectedArticles() merges by ID
- AjaxController::browseSnapshot() returns article list as JSON
- SnapshotsController::restoreSelected() handles selective restore
- Browse modal with checkboxes + Restore Selected button

Closes #58
2026-06-22 22:18:48 -05:00
Jonathan Miller 391047d8e5 feat: email/ntfy notifications for restore operations (#60)
Send notifications when site restores and snapshot create/restore
complete. Uses sendRestoreNotification() with type-specific subjects.
All calls wrapped in try-catch to never break the actual operation.

Closes #60
2026-06-22 09:27:16 -05:00
Jonathan Miller ad1c0cf349 fix: scope #__fields_values dump and restore to com_content.article
Universal: PR Check / Branch Policy (pull_request) Failing after 1s
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 4s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Failing after 5s
Universal: PR Check / Secret Scan (pull_request) Successful in 7s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 11s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 28s
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
Branch Cleanup / Delete merged branch (pull_request) Successful in 1s
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 17s
Universal: Workflow Sync Trigger / Sync workflows to live repos (pull_request) Failing after 2m55s
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
The fields_values table is shared across all Joomla extensions.
Previously, dump captured ALL field values and restore deleted ALL
field values, destroying data for contacts, users, and other
extensions. Now scoped via subquery on field_id WHERE context =
'com_content.article'.
2026-06-21 19:32:23 -05:00
Jonathan Miller eb7f48d3a2 feat: extend snapshots to include custom fields and tags (#57)
When articles are included in a snapshot, now also captures:
- #__tags (tag definitions)
- #__fields (custom field definitions for com_content.article)
- #__fields_values (custom field values)
- #__fields_categories (field-to-category mappings)

Restore correctly scopes deletes to avoid touching non-content fields.

Closes #57
2026-06-21 19:08:53 -05:00
Jonathan Miller d5421738b7 fix: address PR review findings — error handling and safety
Universal: PR Check / Branch Policy (pull_request) Failing after 2s
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 4s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 9s
Universal: PR Check / Validate PR (pull_request) Failing after 6s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 2s
Universal: Auto Version Bump / Version Bump (push) Successful in 14s
Generic: Project CI / Lint & Validate (pull_request) Successful in 44s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 49s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 51s
Generic: Project CI / Tests (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
Fixes from code review and silent failure audit:

- SnapshotRestoreEngine: catch only duplicate key errors (MySQL 1062)
  in merge mode, re-throw all other exceptions instead of swallowing
- SnapshotRestoreEngine: add json_last_error() check for better error
  messages on corrupt snapshot files
- SnapshotRestoreEngine: log warnings when set_time_limit/ini_set fail
- SnapshotEngine: use strlen($json) instead of filesize() to avoid
  race conditions; catch \Exception instead of \Throwable
- SnapshotsController: remove @unlink suppression, add try-catch
  around delete loop with partial failure reporting
- script.php: add user-facing warning when webcron secret generation
  fails (was silently swallowed, inconsistent with other catch blocks)
2026-06-21 17:21:55 -05:00
Jonathan Miller 854383a899 fix: scope module DELETE to snapshot IDs in replace mode
The truncateFiltered() method ran unfiltered DELETE FROM #__modules
in replace mode, which would wipe ALL site modules (admin toolbar,
login, menus) — not just the ones in the snapshot. Now scoped to
only delete modules whose IDs exist in the snapshot data.

Also scopes #__modules_menu delete to snapshot module IDs, and adds
defense-in-depth validation of restore_mode in the controller.
2026-06-21 15:50:41 -05:00
Jonathan Miller ef31713029 feat: content snapshots, restore UI, and config hardening (v01.25.00)
Universal: Auto Version Bump / Version Bump (push) Successful in 10s
Add content snapshot system for lightweight article/category/module
versioning independent of full backups. Snapshots store as JSON files
with replace or merge restore modes, wrapped in DB transactions.

- SnapshotEngine: dumps articles, categories, modules + related tables
  (workflow_associations, tag maps, frontpage) to JSON
- SnapshotRestoreEngine: replace (clean slate) or merge (upsert) mode
- Full MVC: controller, models, view, template with create/restore modals
- New ACL permission: mokosuitebackup.snapshot.manage
- Submenu entry with camera icon, upgrade SQL for snapshots table

Improve full-site restore UI with confirmation modal offering options
for files, database, preserve config, and encryption password.

Config improvements:
- WebcronSecretField: CSPRNG generator, strength meter, rejects weak
  patterns (password, admin, secret), enforces min 16 chars
- IpWhitelistField: table-based management, current IP detection with
  one-click "Add my IP" button
- Default profile shows "Title (#ID)" format
- Default backup dir uses [DEFAULT_DIR] placeholder
- Install script generates random 32-char webcron secret
- Dashboard quick actions: full-width dropdown with button below
2026-06-21 15:25:53 -05:00