Release 0.1.3 — relicense from Apache-2.0 to Proprietary

Reverts the earlier Apache-2.0 change in favour of a proprietary licence
matching the other internal ATT plugins (att-consent). Grants use only to
authorised sites; all other rights reserved. MIT carve-out preserved for
vendored PUC.
This commit is contained in:
2026-07-16 12:56:06 +01:00
parent 51436dba6b
commit 3159ca7999
4 changed files with 47 additions and 199 deletions

View File

@@ -15,6 +15,7 @@
{"_type":"issue","id":"hc-5ix.4","title":"Session data model: option-backed in-progress healthcheck record","description":"One in-progress session per site at a time. Stored in a custom option (or a CPT — pick during implementation). Shape: id, started_at, finished_at, technician_id, site_url_snapshot, per_step_state { status: not_started|in_progress|done|skipped|blocked|n_a, notes, completed_at }. Designed so phase-3 automation can attach structured findings later.","notes":"Built in phase-1 scaffold; passing lint + WP-eval end-to-end smoke test on testsite.","status":"closed","priority":1,"issue_type":"task","owner":"steve@hanlon.co.uk","created_at":"2026-06-11T14:36:11Z","created_by":"Steve Hanlon","updated_at":"2026-06-11T14:49:27Z","started_at":"2026-06-11T14:41:27Z","closed_at":"2026-06-11T14:49:27Z","labels":["phase-1"],"dependencies":[{"issue_id":"hc-5ix.4","depends_on_id":"hc-5ix","type":"parent-child","created_at":"2026-06-11T15:36:11Z","created_by":"Steve Hanlon","metadata":"{}"}],"dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"hc-5ix.4","title":"Session data model: option-backed in-progress healthcheck record","description":"One in-progress session per site at a time. Stored in a custom option (or a CPT — pick during implementation). Shape: id, started_at, finished_at, technician_id, site_url_snapshot, per_step_state { status: not_started|in_progress|done|skipped|blocked|n_a, notes, completed_at }. Designed so phase-3 automation can attach structured findings later.","notes":"Built in phase-1 scaffold; passing lint + WP-eval end-to-end smoke test on testsite.","status":"closed","priority":1,"issue_type":"task","owner":"steve@hanlon.co.uk","created_at":"2026-06-11T14:36:11Z","created_by":"Steve Hanlon","updated_at":"2026-06-11T14:49:27Z","started_at":"2026-06-11T14:41:27Z","closed_at":"2026-06-11T14:49:27Z","labels":["phase-1"],"dependencies":[{"issue_id":"hc-5ix.4","depends_on_id":"hc-5ix","type":"parent-child","created_at":"2026-06-11T15:36:11Z","created_by":"Steve Hanlon","metadata":"{}"}],"dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"hc-5ix.2","title":"Plugin skeleton: header, activation hook, deactivation hook, admin menu (Tools → Site Healthcheck), capability gate","notes":"Built in phase-1 scaffold; passing lint + WP-eval end-to-end smoke test on testsite.","status":"closed","priority":1,"issue_type":"task","owner":"steve@hanlon.co.uk","created_at":"2026-06-11T14:36:10Z","created_by":"Steve Hanlon","updated_at":"2026-06-11T14:49:26Z","started_at":"2026-06-11T14:41:27Z","closed_at":"2026-06-11T14:49:26Z","labels":["phase-1"],"dependencies":[{"issue_id":"hc-5ix.2","depends_on_id":"hc-5ix","type":"parent-child","created_at":"2026-06-11T15:36:09Z","created_by":"Steve Hanlon","metadata":"{}"}],"dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"hc-5ix.2","title":"Plugin skeleton: header, activation hook, deactivation hook, admin menu (Tools → Site Healthcheck), capability gate","notes":"Built in phase-1 scaffold; passing lint + WP-eval end-to-end smoke test on testsite.","status":"closed","priority":1,"issue_type":"task","owner":"steve@hanlon.co.uk","created_at":"2026-06-11T14:36:10Z","created_by":"Steve Hanlon","updated_at":"2026-06-11T14:49:26Z","started_at":"2026-06-11T14:41:27Z","closed_at":"2026-06-11T14:49:26Z","labels":["phase-1"],"dependencies":[{"issue_id":"hc-5ix.2","depends_on_id":"hc-5ix","type":"parent-child","created_at":"2026-06-11T15:36:09Z","created_by":"Steve Hanlon","metadata":"{}"}],"dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"hc-5ix.1","title":"Bootstrap: trigger install of wp-site-recovery plugin as step 0","description":"Healthcheck plugin should check on activation if site-recovery is installed; if not, fetch and install from a known URL/ZIP and activate it. Block stepper from starting until recovery is in place.","notes":"Detection half done (status panel + manual link). Auto-install of recovery plugin from a private URL is the wp-site-recovery side of hc-5ix.27 — closing this as 'detection complete'.","status":"closed","priority":1,"issue_type":"task","owner":"steve@hanlon.co.uk","created_at":"2026-06-11T11:26:40Z","created_by":"Steve Hanlon","updated_at":"2026-06-11T15:03:02Z","closed_at":"2026-06-11T15:03:02Z","labels":["phase-1"],"dependencies":[{"issue_id":"hc-5ix.1","depends_on_id":"hc-5ix","type":"parent-child","created_at":"2026-06-11T12:26:39Z","created_by":"Steve Hanlon","metadata":"{}"}],"dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"hc-5ix.1","title":"Bootstrap: trigger install of wp-site-recovery plugin as step 0","description":"Healthcheck plugin should check on activation if site-recovery is installed; if not, fetch and install from a known URL/ZIP and activate it. Block stepper from starting until recovery is in place.","notes":"Detection half done (status panel + manual link). Auto-install of recovery plugin from a private URL is the wp-site-recovery side of hc-5ix.27 — closing this as 'detection complete'.","status":"closed","priority":1,"issue_type":"task","owner":"steve@hanlon.co.uk","created_at":"2026-06-11T11:26:40Z","created_by":"Steve Hanlon","updated_at":"2026-06-11T15:03:02Z","closed_at":"2026-06-11T15:03:02Z","labels":["phase-1"],"dependencies":[{"issue_id":"hc-5ix.1","depends_on_id":"hc-5ix","type":"parent-child","created_at":"2026-06-11T12:26:39Z","created_by":"Steve Hanlon","metadata":"{}"}],"dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"hc-adg","title":"Recovery installer pulls unpinned 'latest' from Gitea + auto-activates without signature check","description":"`recovery-installer.php` (`resolve_latest_ref()` L67-92, install L148) fetches the recovery plugin from Gitea using an unpinned reference (releases → tags → `main` branch archive fallback), then feeds the downloaded ZIP straight to WP's `Plugin_Upgrader::install()` which activates the code.\n\nTrust anchor is Gitea repo integrity: whoever controls the recovery repo (or a leaked Gitea deploy token) controls what runs on every install at next tech click.\n\nTLS verification is on (default `wp_remote_get` behaviour, not disabled anywhere), so MITM is out of scope — but supply-chain compromise of the Gitea repo is not.\n\nNote: this is the same trust model as the new PUC auto-update flow (hc-8gb) — so it's not a new risk introduced by that change, but it's worth naming and mitigating in the same pass.","design":"Options:\n\n**a) Pin to signed tags only.** Require the recovery installer to reject anything that isn't a signed tag (verify via Gitea API's tag object). Only works if we start GPG-signing releases.\n\n**b) Verify a SHA-256 hash from a second source.** Ship an expected-hash constant in the plugin (updated at release time). Installer fetches ZIP + verifies hash before install. Simple, effective, but couples plugin releases to recovery releases.\n\n**c) Detached signature file alongside the ZIP.** e.g. `recovery-1.2.3.zip.sig` verified against a public key baked into the healthcheck plugin. Best long-term option, most setup.\n\n**d) Reduce blast radius via read-only, single-repo Gitea tokens.** Doesn't stop repo compromise but shrinks the credential footprint.\n\nRecommendation: start with **(b)** — quick win. Consider **(c)** if we ever distribute recovery outside the internal network.","acceptance_criteria":"- [ ] Recovery installer refuses to install a ZIP whose SHA-256 doesn't match an expected value shipped in the plugin (or a similar integrity check)\n- [ ] Recovery repo Gitea token is read-only + scoped to just that repo\n- [ ] README documents the release-integrity flow","notes":"Reported in 2026-07-16 security review. Flagged as risk #2 of 3.\n\nRelated: hc-8gb introduced PUC-based auto-updates for THIS plugin with the same trust model. Any signing/hashing scheme we build for recovery should be reusable for the healthcheck plugin's own updates.","status":"open","priority":2,"issue_type":"bug","owner":"steve@hanlon.co.uk","created_at":"2026-07-16T10:14:40Z","created_by":"Steve Hanlon","updated_at":"2026-07-16T10:14:40Z","dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"hc-gp3","title":"Harden API key handling — remove settings-page echo, add DB encryption or force constants","description":"Central-server API key is currently exposed in two ways that widen the blast radius of a compromise:\n\n1. `admin-page.php:749` renders the key back into an `\u003cinput type=\"password\"\u003e` on the settings page — trivially unmasked via DOM inspector by any user with `manage_options`.\n2. When set via the settings UI (not a wp-config constant), the key is stored plaintext in `wp_options` under `att_hc_api_key` — readable by any user or process with DB access.\n\nBecause the same key is shared across every WP install that talks to the central server, a leak from one site gives read/write on every other site's healthcheck history.","design":"Options (pick one or combine):\n\n**a) Never render the key back into the input.** Show only a masked placeholder like `••••••••abc3` (last 4 chars) with a 'change' button that swaps in an empty input. Won't help against DB reads but blocks the easiest exfiltration path.\n\n**b) Force wp-config constants only.** Delete the settings-page input for the key entirely; require `ATT_HC_API_KEY` in `wp-config.php`. Simple, most secure, but loses the managed-host convenience the original design called out (`class-att-hc-api.php:14-19`).\n\n**c) Encrypt the option at rest.** Use a key derived from `AUTH_KEY` / `SECURE_AUTH_KEY` (already in wp-config) to encrypt the DB value. Still readable by anyone who can read both DB + wp-config, but that's a higher bar than DB-only.\n\n**d) Per-site keys instead of a shared key.** Bigger change on the server side (would need a keys table + rotation). Correct long-term fix. Files as a follow-up if we do (a)/(b) now.\n\nRecommendation: **(a) + prefer (b)** for new installs, document (b) as the strong default in README, keep (c)/(d) as future work.","acceptance_criteria":"- [ ] Settings page no longer echoes the key value into the DOM\n- [ ] README + wp-config docs recommend the constant path as the strong default\n- [ ] Existing option-based configs continue to work (no breakage on client sites)\n- [ ] Consider filing a follow-up for (d) per-site keys","notes":"Reported in 2026-07-16 security review. Flagged as risk #1 of 3.\n\nRelated risks (separate issues):\n- Recovery installer unpinned 'latest' pull\n- No rate limiting on server API + dashboard","status":"open","priority":2,"issue_type":"bug","owner":"steve@hanlon.co.uk","created_at":"2026-07-16T10:14:21Z","created_by":"Steve Hanlon","updated_at":"2026-07-16T10:14:21Z","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"hc-gp3","title":"Harden API key handling — remove settings-page echo, add DB encryption or force constants","description":"Central-server API key is currently exposed in two ways that widen the blast radius of a compromise:\n\n1. `admin-page.php:749` renders the key back into an `\u003cinput type=\"password\"\u003e` on the settings page — trivially unmasked via DOM inspector by any user with `manage_options`.\n2. When set via the settings UI (not a wp-config constant), the key is stored plaintext in `wp_options` under `att_hc_api_key` — readable by any user or process with DB access.\n\nBecause the same key is shared across every WP install that talks to the central server, a leak from one site gives read/write on every other site's healthcheck history.","design":"Options (pick one or combine):\n\n**a) Never render the key back into the input.** Show only a masked placeholder like `••••••••abc3` (last 4 chars) with a 'change' button that swaps in an empty input. Won't help against DB reads but blocks the easiest exfiltration path.\n\n**b) Force wp-config constants only.** Delete the settings-page input for the key entirely; require `ATT_HC_API_KEY` in `wp-config.php`. Simple, most secure, but loses the managed-host convenience the original design called out (`class-att-hc-api.php:14-19`).\n\n**c) Encrypt the option at rest.** Use a key derived from `AUTH_KEY` / `SECURE_AUTH_KEY` (already in wp-config) to encrypt the DB value. Still readable by anyone who can read both DB + wp-config, but that's a higher bar than DB-only.\n\n**d) Per-site keys instead of a shared key.** Bigger change on the server side (would need a keys table + rotation). Correct long-term fix. Files as a follow-up if we do (a)/(b) now.\n\nRecommendation: **(a) + prefer (b)** for new installs, document (b) as the strong default in README, keep (c)/(d) as future work.","acceptance_criteria":"- [ ] Settings page no longer echoes the key value into the DOM\n- [ ] README + wp-config docs recommend the constant path as the strong default\n- [ ] Existing option-based configs continue to work (no breakage on client sites)\n- [ ] Consider filing a follow-up for (d) per-site keys","notes":"Reported in 2026-07-16 security review. Flagged as risk #1 of 3.\n\nRelated risks (separate issues):\n- Recovery installer unpinned 'latest' pull\n- No rate limiting on server API + dashboard","status":"open","priority":2,"issue_type":"bug","owner":"steve@hanlon.co.uk","created_at":"2026-07-16T10:14:21Z","created_by":"Steve Hanlon","updated_at":"2026-07-16T10:14:21Z","dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"hc-8gb","title":"Add auto-updates from Gitea via Plugin Update Checker","description":"Enable this plugin (and eventually the two other in-house plugins) to auto-update when a new version is pushed to Gitea's main branch, so field techs pick up fixes without manual re-installs.\n\nApproach: vendor YahnisElsts/plugin-update-checker (PUC) as a git submodule under `vendor/plugin-update-checker`, wire it up in `att-site-healthcheck.php` to poll a `updates.json` (or equivalent metadata file) served from the Gitea repo's main branch.\n\nOnce wired, WP's normal update flow surfaces new versions in Dashboard → Updates and honours site auto-update settings.","design":"**Library**: YahnisElsts/plugin-update-checker (PUC) v5.x — vendored as git submodule at `vendor/plugin-update-checker`.\n\n**Metadata source**: static `updates.json` committed to the plugin repo main branch, fetched via Gitea raw URL. Chosen over Gitea's Releases API because:\n- No auth needed if repo is readable from client sites' networks\n- Simple to bump in a release commit (version + download_url)\n- Avoids coupling to Gitea-specific API quirks\n\n**Wiring** (approx, in main plugin file after ABSPATH guard):\n```php\nrequire_once ATT_HC_PLUGIN_DIR . 'vendor/plugin-update-checker/plugin-update-checker.php';\n$updater = YahnisElsts\\PluginUpdateChecker\\v5\\PucFactory::buildUpdateChecker(\n 'https://\u003cgitea-host\u003e/steve/att-site-healthcheck/raw/branch/main/updates.json',\n ATT_HC_PLUGIN_FILE,\n 'att-site-healthcheck'\n);\n```\n\n**Release flow**:\n1. Bump `Version:` in plugin header + `ATT_HC_VERSION` constant\n2. Bump `version` + `download_url` in `updates.json` (pointing at a zip — either a Gitea release asset or a raw branch zipball)\n3. Commit + push to main\n4. Client sites pick it up on next WP update check (twice-daily) or manual 'Check again'\n\n**Later**: replicate the same submodule + updates.json pattern in the two sibling plugins.","acceptance_criteria":"- [ ] PUC vendored as git submodule under `vendor/plugin-update-checker`\n- [ ] Plugin bootstrap wires PUC against a Gitea raw URL for `updates.json`\n- [ ] `updates.json` template committed to repo main branch with current version\n- [ ] Bumping version + updates.json + pushing to main causes a test WP site to see the update in Dashboard → Updates\n- [ ] One-click update from WP admin installs the new version and the plugin remains active\n- [ ] README documents the release flow (bump header version, bump updates.json, push)\n- [ ] Pattern documented well enough to replicate for the other two in-house plugins","notes":"Implementation notes (2026-07-16):\n\nDeviated from original design: PUC is vendored directly under `vendor/plugin-update-checker/` (not a git submodule).\n\nReason: Gitea's `archive/main.zip` endpoint does NOT include submodule contents. A submodule-based approach would mean auto-downloaded update ZIPs are missing PUC entirely and fatal on activation. User confirmed vendored approach (2026-07-16).\n\n**Files added/changed:**\n- `vendor/plugin-update-checker/` — vendored PUC v5.7 (trimmed: no build/, vendor/, examples/, composer.json, phpcs.xml)\n- `att-site-healthcheck.php` — loads PUC and calls `PucFactory::buildUpdateChecker()` pointing at Gitea raw `updates.json`\n- `updates.json` — metadata file at repo root, served via Gitea raw URL\n- `README.md` — release flow + PUC-update instructions\n\n**Metadata URL**: https://git.h12e.com/steve/wp-healthcheck/raw/branch/main/updates.json\n**Download URL**: https://git.h12e.com/steve/wp-healthcheck/archive/main.zip\n\n**Folder rename**: PUC's base `UpdateChecker::fixDirectoryName()` handles the mismatch between Gitea's archive folder (`wp-healthcheck`) and the WP plugin slug (`att-site-healthcheck`) automatically via the `upgrader_source_selection` filter.\n\n**PUC v5 alias**: verified — `YahnisElsts\\PluginUpdateChecker\\v5\\PucFactory` resolves to v5p7's implementation.\n\n**Testing needed on real WP site:**\n1. Install this version, confirm no PHP errors on activation\n2. Bump version to 0.1.1 in header + updates.json, push\n3. Wait 12h OR click 'Check again' on Dashboard → Updates\n4. Confirm update offered, install it, confirm plugin still active + slug preserved\n\nOnce tested and confirmed working, replicate the same three-file pattern (vendor/, updates.json, main-plugin PUC wiring block) in the other two in-house plugins.","status":"closed","priority":2,"issue_type":"feature","assignee":"Steve Hanlon","owner":"steve@hanlon.co.uk","created_at":"2026-07-16T08:44:49Z","created_by":"Steve Hanlon","updated_at":"2026-07-16T08:52:50Z","started_at":"2026-07-16T08:47:36Z","closed_at":"2026-07-16T08:52:50Z","close_reason":"Implemented and pushed as commit 19d726f. Awaiting real-WP-site verification per the release-flow test in the notes before replicating to the two sibling plugins.","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"hc-8gb","title":"Add auto-updates from Gitea via Plugin Update Checker","description":"Enable this plugin (and eventually the two other in-house plugins) to auto-update when a new version is pushed to Gitea's main branch, so field techs pick up fixes without manual re-installs.\n\nApproach: vendor YahnisElsts/plugin-update-checker (PUC) as a git submodule under `vendor/plugin-update-checker`, wire it up in `att-site-healthcheck.php` to poll a `updates.json` (or equivalent metadata file) served from the Gitea repo's main branch.\n\nOnce wired, WP's normal update flow surfaces new versions in Dashboard → Updates and honours site auto-update settings.","design":"**Library**: YahnisElsts/plugin-update-checker (PUC) v5.x — vendored as git submodule at `vendor/plugin-update-checker`.\n\n**Metadata source**: static `updates.json` committed to the plugin repo main branch, fetched via Gitea raw URL. Chosen over Gitea's Releases API because:\n- No auth needed if repo is readable from client sites' networks\n- Simple to bump in a release commit (version + download_url)\n- Avoids coupling to Gitea-specific API quirks\n\n**Wiring** (approx, in main plugin file after ABSPATH guard):\n```php\nrequire_once ATT_HC_PLUGIN_DIR . 'vendor/plugin-update-checker/plugin-update-checker.php';\n$updater = YahnisElsts\\PluginUpdateChecker\\v5\\PucFactory::buildUpdateChecker(\n 'https://\u003cgitea-host\u003e/steve/att-site-healthcheck/raw/branch/main/updates.json',\n ATT_HC_PLUGIN_FILE,\n 'att-site-healthcheck'\n);\n```\n\n**Release flow**:\n1. Bump `Version:` in plugin header + `ATT_HC_VERSION` constant\n2. Bump `version` + `download_url` in `updates.json` (pointing at a zip — either a Gitea release asset or a raw branch zipball)\n3. Commit + push to main\n4. Client sites pick it up on next WP update check (twice-daily) or manual 'Check again'\n\n**Later**: replicate the same submodule + updates.json pattern in the two sibling plugins.","acceptance_criteria":"- [ ] PUC vendored as git submodule under `vendor/plugin-update-checker`\n- [ ] Plugin bootstrap wires PUC against a Gitea raw URL for `updates.json`\n- [ ] `updates.json` template committed to repo main branch with current version\n- [ ] Bumping version + updates.json + pushing to main causes a test WP site to see the update in Dashboard → Updates\n- [ ] One-click update from WP admin installs the new version and the plugin remains active\n- [ ] README documents the release flow (bump header version, bump updates.json, push)\n- [ ] Pattern documented well enough to replicate for the other two in-house plugins","notes":"Implementation notes (2026-07-16):\n\nDeviated from original design: PUC is vendored directly under `vendor/plugin-update-checker/` (not a git submodule).\n\nReason: Gitea's `archive/main.zip` endpoint does NOT include submodule contents. A submodule-based approach would mean auto-downloaded update ZIPs are missing PUC entirely and fatal on activation. User confirmed vendored approach (2026-07-16).\n\n**Files added/changed:**\n- `vendor/plugin-update-checker/` — vendored PUC v5.7 (trimmed: no build/, vendor/, examples/, composer.json, phpcs.xml)\n- `att-site-healthcheck.php` — loads PUC and calls `PucFactory::buildUpdateChecker()` pointing at Gitea raw `updates.json`\n- `updates.json` — metadata file at repo root, served via Gitea raw URL\n- `README.md` — release flow + PUC-update instructions\n\n**Metadata URL**: https://git.h12e.com/steve/wp-healthcheck/raw/branch/main/updates.json\n**Download URL**: https://git.h12e.com/steve/wp-healthcheck/archive/main.zip\n\n**Folder rename**: PUC's base `UpdateChecker::fixDirectoryName()` handles the mismatch between Gitea's archive folder (`wp-healthcheck`) and the WP plugin slug (`att-site-healthcheck`) automatically via the `upgrader_source_selection` filter.\n\n**PUC v5 alias**: verified — `YahnisElsts\\PluginUpdateChecker\\v5\\PucFactory` resolves to v5p7's implementation.\n\n**Testing needed on real WP site:**\n1. Install this version, confirm no PHP errors on activation\n2. Bump version to 0.1.1 in header + updates.json, push\n3. Wait 12h OR click 'Check again' on Dashboard → Updates\n4. Confirm update offered, install it, confirm plugin still active + slug preserved\n\nOnce tested and confirmed working, replicate the same three-file pattern (vendor/, updates.json, main-plugin PUC wiring block) in the other two in-house plugins.","status":"closed","priority":2,"issue_type":"feature","assignee":"Steve Hanlon","owner":"steve@hanlon.co.uk","created_at":"2026-07-16T08:44:49Z","created_by":"Steve Hanlon","updated_at":"2026-07-16T08:52:50Z","started_at":"2026-07-16T08:47:36Z","closed_at":"2026-07-16T08:52:50Z","close_reason":"Implemented and pushed as commit 19d726f. Awaiting real-WP-site verification per the release-flow test in the notes before replicating to the two sibling plugins.","dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"hc-dy9","title":"Plugin: Notes for next time → seed prereq notes on next session","description":"Add a 'Notes for next time' step at the end of the flow as a handover for whoever picks up the next engagement. On starting a new session for the same site_key, the server's step history for the handover step is pulled and used to pre-populate the 'Before You Start' (before) step's notes — prefixed with the prior session's date so the carry-over is obvious.\n\nImplementation:\n- New step file includes/steps/125-handover.php using the existing step pattern (id=handover, standard notes field). No schema or API change required since this is just another step.\n- ATT_HC_Session::start() does a best-effort step_history(handover, site_key, limit=1, exclude_id=current) and on success calls update_step('before', NOT_STARTED, prefix + notes) on the new session. Silent degrade if API unreachable — start should still succeed.\n- Same auto-seed for resume()? Probably not — resuming an existing session would clobber whatever the tech had already typed. Only seed on a genuinely new start.\n\nUser asked for this 2026-06-29 — they want a low-friction handover so engagement context survives between visits.","status":"closed","priority":2,"issue_type":"feature","assignee":"Steve Hanlon","owner":"steve@hanlon.co.uk","created_at":"2026-06-30T09:14:53Z","created_by":"Steve Hanlon","updated_at":"2026-06-30T09:17:44Z","started_at":"2026-06-30T09:15:12Z","closed_at":"2026-06-30T09:17:44Z","close_reason":"Shipped. New step file includes/steps/125-handover.php (id=handover, title='Notes for next time') with blurb + sub_items prompting the tech for what to flag. ATT_HC_Session::start() now ends with a best-effort seed_before_notes_from_prior_handover() call that pulls the prior session's handover notes via step_history(handover, site_key, 1, current_id) and writes them into this session's 'before' step notes prefixed with 'From previous session (YYYY-MM-DD):'. Silent degrade on API failure so the session is never blocked from being created. Verified end-to-end against the live server: handover-test-XXXX site exercises full flow, no-handover-XXXX site confirms no false-positive seed. Plugin-only change — no server deploy needed. Test rows purged from MySQL.","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"hc-dy9","title":"Plugin: Notes for next time → seed prereq notes on next session","description":"Add a 'Notes for next time' step at the end of the flow as a handover for whoever picks up the next engagement. On starting a new session for the same site_key, the server's step history for the handover step is pulled and used to pre-populate the 'Before You Start' (before) step's notes — prefixed with the prior session's date so the carry-over is obvious.\n\nImplementation:\n- New step file includes/steps/125-handover.php using the existing step pattern (id=handover, standard notes field). No schema or API change required since this is just another step.\n- ATT_HC_Session::start() does a best-effort step_history(handover, site_key, limit=1, exclude_id=current) and on success calls update_step('before', NOT_STARTED, prefix + notes) on the new session. Silent degrade if API unreachable — start should still succeed.\n- Same auto-seed for resume()? Probably not — resuming an existing session would clobber whatever the tech had already typed. Only seed on a genuinely new start.\n\nUser asked for this 2026-06-29 — they want a low-friction handover so engagement context survives between visits.","status":"closed","priority":2,"issue_type":"feature","assignee":"Steve Hanlon","owner":"steve@hanlon.co.uk","created_at":"2026-06-30T09:14:53Z","created_by":"Steve Hanlon","updated_at":"2026-06-30T09:17:44Z","started_at":"2026-06-30T09:15:12Z","closed_at":"2026-06-30T09:17:44Z","close_reason":"Shipped. New step file includes/steps/125-handover.php (id=handover, title='Notes for next time') with blurb + sub_items prompting the tech for what to flag. ATT_HC_Session::start() now ends with a best-effort seed_before_notes_from_prior_handover() call that pulls the prior session's handover notes via step_history(handover, site_key, 1, current_id) and writes them into this session's 'before' step notes prefixed with 'From previous session (YYYY-MM-DD):'. Silent degrade on API failure so the session is never blocked from being created. Verified end-to-end against the live server: handover-test-XXXX site exercises full flow, no-handover-XXXX site confirms no false-positive seed. Plugin-only change — no server deploy needed. Test rows purged from MySQL.","dependency_count":0,"dependent_count":0,"comment_count":0}
@@ -24,6 +25,7 @@
{"_type":"issue","id":"hc-5ix.9","title":"Smoke test on testsite: full end-to-end run through the checklist","notes":"Built in phase-1 scaffold; passing lint + WP-eval end-to-end smoke test on testsite.","status":"closed","priority":2,"issue_type":"task","owner":"steve@hanlon.co.uk","created_at":"2026-06-11T14:36:15Z","created_by":"Steve Hanlon","updated_at":"2026-06-11T14:49:29Z","closed_at":"2026-06-11T14:49:29Z","labels":["phase-1"],"dependencies":[{"issue_id":"hc-5ix.9","depends_on_id":"hc-5ix","type":"parent-child","created_at":"2026-06-11T15:36:14Z","created_by":"Steve Hanlon","metadata":"{}"}],"dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"hc-5ix.9","title":"Smoke test on testsite: full end-to-end run through the checklist","notes":"Built in phase-1 scaffold; passing lint + WP-eval end-to-end smoke test on testsite.","status":"closed","priority":2,"issue_type":"task","owner":"steve@hanlon.co.uk","created_at":"2026-06-11T14:36:15Z","created_by":"Steve Hanlon","updated_at":"2026-06-11T14:49:29Z","closed_at":"2026-06-11T14:49:29Z","labels":["phase-1"],"dependencies":[{"issue_id":"hc-5ix.9","depends_on_id":"hc-5ix","type":"parent-child","created_at":"2026-06-11T15:36:14Z","created_by":"Steve Hanlon","metadata":"{}"}],"dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"hc-5ix.8","title":"README + install instructions + screenshots placeholder","notes":"Built in phase-1 scaffold; passing lint + WP-eval end-to-end smoke test on testsite.","status":"closed","priority":2,"issue_type":"chore","owner":"steve@hanlon.co.uk","created_at":"2026-06-11T14:36:14Z","created_by":"Steve Hanlon","updated_at":"2026-06-11T14:49:29Z","closed_at":"2026-06-11T14:49:29Z","labels":["phase-1"],"dependencies":[{"issue_id":"hc-5ix.8","depends_on_id":"hc-5ix","type":"parent-child","created_at":"2026-06-11T15:36:14Z","created_by":"Steve Hanlon","metadata":"{}"}],"dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"hc-5ix.8","title":"README + install instructions + screenshots placeholder","notes":"Built in phase-1 scaffold; passing lint + WP-eval end-to-end smoke test on testsite.","status":"closed","priority":2,"issue_type":"chore","owner":"steve@hanlon.co.uk","created_at":"2026-06-11T14:36:14Z","created_by":"Steve Hanlon","updated_at":"2026-06-11T14:49:29Z","closed_at":"2026-06-11T14:49:29Z","labels":["phase-1"],"dependencies":[{"issue_id":"hc-5ix.8","depends_on_id":"hc-5ix","type":"parent-child","created_at":"2026-06-11T15:36:14Z","created_by":"Steve Hanlon","metadata":"{}"}],"dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"hc-5ix","title":"Build WordPress Healthcheck plugin (stepper through steps.md)","description":"Plugin that walks a technician through the WordPress healthcheck steps documented in steps.md. Independent of the recovery plugin (which it depends on as step 0).","status":"open","priority":2,"issue_type":"epic","owner":"steve@hanlon.co.uk","created_at":"2026-06-11T11:26:32Z","created_by":"Steve Hanlon","updated_at":"2026-06-11T11:26:32Z","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"hc-5ix","title":"Build WordPress Healthcheck plugin (stepper through steps.md)","description":"Plugin that walks a technician through the WordPress healthcheck steps documented in steps.md. Independent of the recovery plugin (which it depends on as step 0).","status":"open","priority":2,"issue_type":"epic","owner":"steve@hanlon.co.uk","created_at":"2026-06-11T11:26:32Z","created_by":"Steve Hanlon","updated_at":"2026-06-11T11:26:32Z","dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"hc-ufl","title":"Server: add rate limiting + lockout on API + /dashboard, rotate error_log","description":"Central healthcheck history server has no rate limiting or account lockout:\n\n- `server/src/Auth.php` — unlimited Bearer-key attempts, no delay, no lockout\n- `server/src/Dashboard.php:46-71` — unlimited login attempts on the /dashboard cookie-key form\n\nCombined with the shared-API-key exposure (see companion issue), a leaked or brute-forced key can be abused indefinitely with no signal.\n\nAlso: `server/src/bootstrap.php:25` calls `error_log()` on every uncaught exception with no rotation — the log grows unbounded on a busy or attacked server.","design":"**Rate limiting**: simplest workable option is a small in-DB counter table (key = client IP + endpoint, value = attempt count + window start). Reject when \u003e N attempts / window. No Redis dependency needed at current scale.\n\n**Lockout on /dashboard**: after e.g. 5 failed attempts from one IP within 15 min, return 429 with Retry-After. Keep it IP-scoped (not global) so a random attacker can't lock out real users.\n\n**Auth on API endpoints**: constant-time compare is already correct (`Auth.php:20` uses `hash_equals`). Add: increment failed-attempt counter, refuse to serve after threshold. Consider structured logging of each rejection with IP + user-agent for later review.\n\n**Log rotation**: either delegate to logrotate on the host (document in DEPLOY.md) OR self-rotate via a size check + rename in `bootstrap.php`'s handler.\n\nNice-to-have: session hardening on /dashboard — call `session_regenerate_id(true)` after successful auth (currently missing at `Dashboard.php:53-70`).","acceptance_criteria":"- [ ] API + /dashboard rate-limited per IP with a documented threshold\n- [ ] 429 with Retry-After returned when limit hit\n- [ ] error_log rotation strategy in place (either OS-level via DEPLOY.md docs, or in-app)\n- [ ] session_regenerate_id after /dashboard auth\n- [ ] Rejections logged with enough context to spot abuse patterns","notes":"Reported in 2026-07-16 security review. Flagged as risk #3 of 3 — lower priority than the API-key and recovery-installer issues but worth tackling in the same sweep since it's server-side and cheap.\n\nAlso noted (not required for close): session fixation on /dashboard is theoretically possible but requires attacker to first set a victim's PHPSESSID — high bar. Adding session_regenerate_id closes it cheaply.","status":"open","priority":3,"issue_type":"bug","owner":"steve@hanlon.co.uk","created_at":"2026-07-16T10:14:51Z","created_by":"Steve Hanlon","updated_at":"2026-07-16T10:14:51Z","dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"hc-u5c","title":"Add Google Analytics and Search Console healthcheck steps","description":"Add two new healthcheck steps between performance (Step 7) and security (Step 8): a Google Analytics check that sniffs the homepage for GA4/GTM/UA snippets and detects known analytics plugins, and a Google Search Console check that looks for verification meta tags, sitemap reachability, robots.txt Disallow rules, and the WP 'Discourage search engines' setting. Updates steps.md to match.","status":"closed","priority":3,"issue_type":"feature","assignee":"Steve Hanlon","owner":"steve@hanlon.co.uk","created_at":"2026-07-16T08:44:39Z","created_by":"Steve Hanlon","updated_at":"2026-07-16T08:44:56Z","started_at":"2026-07-16T08:44:44Z","closed_at":"2026-07-16T08:44:56Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"hc-u5c","title":"Add Google Analytics and Search Console healthcheck steps","description":"Add two new healthcheck steps between performance (Step 7) and security (Step 8): a Google Analytics check that sniffs the homepage for GA4/GTM/UA snippets and detects known analytics plugins, and a Google Search Console check that looks for verification meta tags, sitemap reachability, robots.txt Disallow rules, and the WP 'Discourage search engines' setting. Updates steps.md to match.","status":"closed","priority":3,"issue_type":"feature","assignee":"Steve Hanlon","owner":"steve@hanlon.co.uk","created_at":"2026-07-16T08:44:39Z","created_by":"Steve Hanlon","updated_at":"2026-07-16T08:44:56Z","started_at":"2026-07-16T08:44:44Z","closed_at":"2026-07-16T08:44:56Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"hc-5ix.27","title":"Self-hosted update channel: plugin checks a private URL for new releases and offers one-click update from wp-admin","description":"Lightweight wrapper around the WP plugin update transient: site_transient_update_plugins filter that adds an entry for ourselves if a newer release exists at a configured URL. URL hosts a JSON manifest + zip. Lets technicians keep clients up to date without manually re-uploading the zip every check.","status":"open","priority":3,"issue_type":"task","owner":"steve@hanlon.co.uk","created_at":"2026-06-11T14:40:30Z","created_by":"Steve Hanlon","updated_at":"2026-06-11T14:40:30Z","labels":["phase-3"],"dependencies":[{"issue_id":"hc-5ix.27","depends_on_id":"hc-5ix","type":"parent-child","created_at":"2026-06-11T15:40:29Z","created_by":"Steve Hanlon","metadata":"{}"}],"dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"hc-5ix.27","title":"Self-hosted update channel: plugin checks a private URL for new releases and offers one-click update from wp-admin","description":"Lightweight wrapper around the WP plugin update transient: site_transient_update_plugins filter that adds an entry for ourselves if a newer release exists at a configured URL. URL hosts a JSON manifest + zip. Lets technicians keep clients up to date without manually re-uploading the zip every check.","status":"open","priority":3,"issue_type":"task","owner":"steve@hanlon.co.uk","created_at":"2026-06-11T14:40:30Z","created_by":"Steve Hanlon","updated_at":"2026-06-11T14:40:30Z","labels":["phase-3"],"dependencies":[{"issue_id":"hc-5ix.27","depends_on_id":"hc-5ix","type":"parent-child","created_at":"2026-06-11T15:40:29Z","created_by":"Steve Hanlon","metadata":"{}"}],"dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"hc-5ix.25","title":"Step 12 (Wrap-up): pre-fill report with all automated findings, attach diff vs. previous session, optionally push summary to ManageWP/WP Umbrella","notes":"wrap_up step's autocheck walks all other steps' stored findings, counts bad/warn separately, lists the top 6-8 examples, and identifies any blocked steps. Gives the technician a quick 'roll-up' before they finish. The report itself already embeds every finding verbatim.","status":"closed","priority":3,"issue_type":"task","owner":"steve@hanlon.co.uk","created_at":"2026-06-11T14:36:26Z","created_by":"Steve Hanlon","updated_at":"2026-06-11T15:13:15Z","started_at":"2026-06-11T15:05:01Z","closed_at":"2026-06-11T15:13:15Z","labels":["phase-3"],"dependencies":[{"issue_id":"hc-5ix.25","depends_on_id":"hc-5ix","type":"parent-child","created_at":"2026-06-11T15:36:25Z","created_by":"Steve Hanlon","metadata":"{}"}],"dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"hc-5ix.25","title":"Step 12 (Wrap-up): pre-fill report with all automated findings, attach diff vs. previous session, optionally push summary to ManageWP/WP Umbrella","notes":"wrap_up step's autocheck walks all other steps' stored findings, counts bad/warn separately, lists the top 6-8 examples, and identifies any blocked steps. Gives the technician a quick 'roll-up' before they finish. The report itself already embeds every finding verbatim.","status":"closed","priority":3,"issue_type":"task","owner":"steve@hanlon.co.uk","created_at":"2026-06-11T14:36:26Z","created_by":"Steve Hanlon","updated_at":"2026-06-11T15:13:15Z","started_at":"2026-06-11T15:05:01Z","closed_at":"2026-06-11T15:13:15Z","labels":["phase-3"],"dependencies":[{"issue_id":"hc-5ix.25","depends_on_id":"hc-5ix","type":"parent-child","created_at":"2026-06-11T15:36:25Z","created_by":"Steve Hanlon","metadata":"{}"}],"dependency_count":0,"dependent_count":0,"comment_count":0}

232
LICENSE
View File

@@ -1,201 +1,47 @@
Apache License ATT Site Healthcheck — Proprietary Software License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION Copyright (c) 2026 Steve Hanlon. All rights reserved.
1. Definitions. This software and its source code are the confidential and proprietary
property of the copyright holder. No rights are granted except as
expressly set out below.
"License" shall mean the terms and conditions for use, reproduction, 1. Permitted use.
and distribution as defined by Sections 1 through 9 of this document. Sites that have been explicitly authorised in writing by the copyright
holder ("Authorised Sites") may install, run, and receive automatic
updates of this software solely for the operation of that Authorised
Site. No other use is permitted.
"Licensor" shall mean the copyright owner or entity authorized by 2. Restrictions.
the copyright owner that is granting the License. Except as permitted above, you may not:
(a) copy, modify, merge, publish, distribute, sublicense, sell,
rent, lease, or otherwise transfer the software or any portion
of it;
(b) reverse engineer, decompile, or disassemble the software,
except to the extent that such activity is expressly permitted
by applicable law notwithstanding this limitation;
(c) remove or alter any copyright, trademark, or other proprietary
notices in the software.
"Legal Entity" shall mean the union of the acting entity and all 3. Third-party components.
other entities that control, are controlled by, or are under common This distribution includes the Plugin Update Checker library by
control with that entity. For the purposes of this definition, Janis Elsts, which is licensed under the MIT License. See
"control" means (i) the power, direct or indirect, to cause the vendor/plugin-update-checker/license.txt for that license text. The
direction or management of such entity, whether by contract or present proprietary license applies only to the balance of the
otherwise, or (ii) ownership of fifty percent (50%) or more of the software authored by the copyright holder.
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity 4. No warranty.
exercising permissions granted by this License. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE
FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"Source" form shall mean the preferred form for making modifications, 5. Termination.
including but not limited to software source code, documentation Any use in breach of this license terminates the licence
source, and configuration files. automatically. On termination you must uninstall the software and
destroy all copies in your possession.
"Object" form shall mean any form resulting from mechanical For licensing enquiries, contact the copyright holder.
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of tracking or otherwise improving the Work,
but excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for describing the origin of the Work and
reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Support. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or support.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2026 Steve Hanlon
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied. See the License for the specific language governing
permissions and limitations under the License.

View File

@@ -2,10 +2,10 @@
/** /**
* Plugin Name: ATT Site Healthcheck * Plugin Name: ATT Site Healthcheck
* Description: Walks a technician through a structured WordPress site healthcheck. Steps are drop-in PHP files so adding/removing one is a single file change. * Description: Walks a technician through a structured WordPress site healthcheck. Steps are drop-in PHP files so adding/removing one is a single file change.
* Version: 0.1.2 * Version: 0.1.3
* Author: Steve Hanlon * Author: Steve Hanlon
* License: Apache-2.0 * License: Proprietary
* License URI: https://www.apache.org/licenses/LICENSE-2.0 * License URI: https://git.h12e.com/steve/wp-healthcheck/raw/branch/main/LICENSE
* Requires PHP: 7.4 * Requires PHP: 7.4
* *
* Internal/agency tool — not distributed via WP.org. See beads decision hc-5ix.26. * Internal/agency tool — not distributed via WP.org. See beads decision hc-5ix.26.
@@ -15,7 +15,7 @@ if (!defined('ABSPATH')) {
exit; exit;
} }
define('ATT_HC_VERSION', '0.1.2'); define('ATT_HC_VERSION', '0.1.3');
define('ATT_HC_PLUGIN_FILE', __FILE__); define('ATT_HC_PLUGIN_FILE', __FILE__);
define('ATT_HC_PLUGIN_DIR', plugin_dir_path(__FILE__)); define('ATT_HC_PLUGIN_DIR', plugin_dir_path(__FILE__));
define('ATT_HC_PLUGIN_URL', plugin_dir_url(__FILE__)); define('ATT_HC_PLUGIN_URL', plugin_dir_url(__FILE__));

View File

@@ -1,12 +1,12 @@
{ {
"name": "ATT Site Healthcheck", "name": "ATT Site Healthcheck",
"slug": "att-site-healthcheck", "slug": "att-site-healthcheck",
"version": "0.1.2", "version": "0.1.3",
"author": "Steve Hanlon", "author": "Steve Hanlon",
"requires": "6.0", "requires": "6.0",
"tested": "6.6", "tested": "6.6",
"requires_php": "7.4", "requires_php": "7.4",
"last_updated": "2026-07-16 11:15:00", "last_updated": "2026-07-16 13:00:00",
"download_url": "https://git.h12e.com/steve/wp-healthcheck/archive/main.zip", "download_url": "https://git.h12e.com/steve/wp-healthcheck/archive/main.zip",
"sections": { "sections": {
"description": "Walks a technician through a structured WordPress site healthcheck. Steps are drop-in PHP files so adding/removing one is a single file change.", "description": "Walks a technician through a structured WordPress site healthcheck. Steps are drop-in PHP files so adding/removing one is a single file change.",