@kieransimkin/dancemoves 3.1.21 → 3.1.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # DanceMoves
2
2
 
3
+ **3.1.23** secures WordPress motion-capture writes with authenticated
4
+ `manage_options` authorization. The private capture administration UI uses the
5
+ same capability. Published tokens no longer authorize uploads; cookie clients
6
+ send a fresh WordPress REST nonce and same-origin credentials, and administrators
7
+ have separate bounded upload quotas. Local recording/download stays available
8
+ without upload authorization. See the [capture contract](docs/api/wordpress.md#motion-capture-rest-api).
9
+ The real WordPress integration test reports its executed assertion count;
10
+ CI rejects a missing or zero-count summary as well as any failed check.
11
+
12
+ **3.1.22** fixes WordPress.org submission metadata: the Author URI points to
13
+ [Kieran's website](https://kieransimkin.co.uk/), separately from the plugin
14
+ homepage. The release checker rejects duplicate plugin/author URLs before
15
+ packaging. The plugin name remains **DanceMoves**.
16
+
3
17
  **3.1.20** adds a shared playback transport for explicitly verified native and
4
18
  YouTube players. Full-master players drive the existing musical effects; Shorts
5
19
  map their offset and duration onto the master clock. See
@@ -597,7 +611,8 @@ const example = {schema:'ks-epk-motion-recording/v1', samples:[{t:0,beta:10,gamm
597
611
  inspect(example);
598
612
  // The local simulator uses /examples/wordpress/api/capture, never /wp-json/… .
599
613
  // For actual captures, follow docs/api/wordpress.md and the private sensor harness.
600
- // The existing source token is not strong authentication; never embed it in public examples.
614
+ // Uploads require administrator authorization and a fresh WordPress REST nonce.
615
+ // Keep real session nonces out of static examples and source control.
601
616
  ```
602
617
 
603
618
  [Run the example](examples/wordpress/capture.html) · [Read its complete source](examples/wordpress/demos/capture.mjs).
@@ -7,6 +7,16 @@
7
7
  "effects_version": "3.1.13",
8
8
  "effects_reviewed_on": "2026-10-08",
9
9
  "inventory_method": "Manual comparison with the pinned source exports and PHP declarations; symbol-presence validation is not runtime coverage.",
10
+ "wordpress_capture_security": {
11
+ "version": "3.1.23",
12
+ "reviewed_on": "2026-10-15",
13
+ "scope": "Current capture contract supersedes the historical 2.8.0 capture authorization, quota and post-type capability description; pinned baseline symbols and source hashes below remain historical evidence.",
14
+ "authorization": "WordPress-authenticated current_user_can('manage_options'); cookie REST nonces are checked by WordPress core",
15
+ "removed_constants": ["KS_EPK_MOTION_CAPTURE_TOKEN"],
16
+ "quota": "ks_motion_user_<authenticated user ID>, eight accepted checks per ten-minute expiry",
17
+ "private_capture_capability": "manage_options",
18
+ "reference": "wordpress.md#motion-capture-rest-api"
19
+ },
10
20
  "source_blobs": {
11
21
  "kieran-epk-device-orientation.php": "b374690a0c141aa1f870bf1084a0a0b34c25abe5",
12
22
  "assets/dance-moves-core.js": "3d975c7a3b380f9c99a62f7deade14df4b746929",
@@ -15,7 +15,7 @@ registration, rendering or save callbacks manually.
15
15
  | --- | --- |
16
16
  | `DANCE_MOVES_VERSION` | `2.8.0` |
17
17
  | `KS_EPK_ORIENTATION_VERSION` | Alias of `DANCE_MOVES_VERSION`. |
18
- | `KS_EPK_MOTION_CAPTURE_TOKEN` | Existing fixed capture submission token; deliberately not reproduced here. It is in public source and is **not a deployment secret**. See the capture security section. |
18
+ | `KS_EPK_MOTION_CAPTURE_TOKEN` | Removed in 3.1.23. No fixed/published token authorizes capture writes. See the current capture security section. |
19
19
  | `DANCE_MOVES_META_BPM` | `_dance_moves_bpm` |
20
20
  | `DANCE_MOVES_META_LYRIC_TIMING` | `_dance_moves_lyric_timing_id` |
21
21
  | `DANCE_MOVES_META_CUE_TIMING` | `_dance_moves_cue_timing_id` |
@@ -172,7 +172,7 @@ The full validator runs when the Page editor saves the selected attachment.
172
172
  ```text
173
173
  POST <WordPress REST root>/ks-epk-motion/v1/capture
174
174
  Content-Type: application/json
175
- X-KS-Motion-Token: <operator-supplied value; not reproduced here>
175
+ X-WP-Nonce: <fresh wp_rest nonce for the signed-in administrator session>
176
176
  ```
177
177
 
178
178
  Implemented by `ks_epk_motion_register_rest_route()` using
@@ -180,23 +180,48 @@ Implemented by `ks_epk_motion_register_rest_route()` using
180
180
  `ks_epk_motion_capture_permission(WP_REST_Request $request)` and handler
181
181
  `ks_epk_motion_store_capture(WP_REST_Request $request)`.
182
182
 
183
- Permission processing, in order:
183
+ In 3.1.23, permission processing is:
184
+
185
+ 1. WordPress authenticates the request before the permission callback. Cookie
186
+ clients send same-origin credentials and a fresh `wp_rest` nonce in
187
+ `X-WP-Nonce`; WordPress validates that nonce. Missing/expired cookie nonces
188
+ cannot establish administrator authorization. Standard WordPress Application
189
+ Password authentication over HTTPS may also establish the current user;
190
+ this plugin does not create or store those credentials.
191
+ 2. Require `current_user_can('manage_options')`. Anonymous requests return 401;
192
+ authenticated callers lacking that capability return 403. The former
193
+ `X-KS-Motion-Token` header is ignored and its public constant is removed.
194
+ 3. When `get_http_origin()` supplies an origin, require it to equal `home_url()`
195
+ after trailing-slash removal. A missing Origin is allowed only after genuine
196
+ WordPress authentication/authorization, for example an authorized API client.
197
+ 4. Rate-limit by authenticated user ID, using `ks_motion_user_<id>`. Eight
198
+ accepted permission checks are allowed before 429. Each allowed check writes
199
+ a ten-minute expiry; a later-invalid payload still consumes a check. Denied
200
+ callers and other administrators cannot exhaust an administrator's quota.
201
+
202
+ The development lab's static HTML carries no real nonce. Populate its
203
+ `ksMotionCaptureConfig.restNonce` from the current administrator session, not a
204
+ committed value. A registered harness on an authenticated WordPress admin screen
205
+ can use the normal script configuration route:
184
206
 
185
- 1. Compare the supplied header with the fixed token using `hash_equals()`.
186
- 2. When `get_http_origin()` returns an origin, require it to equal `home_url()`
187
- after trailing-slash removal. A missing Origin is **not rejected**.
188
- 3. Rate-limit by a transient keyed from MD5 of `REMOTE_ADDR` (or `'unknown'`).
189
- Eight accepted permission checks are allowed before a 429. Each allowed check
190
- writes a new ten-minute expiry; this is not a rigorously sliding-window quota.
191
- A later-invalid payload still consumes a permission-check attempt.
207
+ ```php
208
+ if (current_user_can('manage_options')) {
209
+ // Run after registering/enqueuing your own capture harness script.
210
+ wp_localize_script('your-capture-harness', 'ksMotionCaptureConfig', array(
211
+ 'endpoint' => esc_url_raw(rest_url('ks-epk-motion/v1/capture')),
212
+ 'restNonce' => wp_create_nonce('wp_rest'),
213
+ ));
214
+ }
215
+ ```
192
216
 
193
- **Security limitation:** the token is embedded in this public repository. The
194
- endpoint does not require a logged-in user, a WordPress capability or a WordPress
195
- nonce. Origin checking and a shared IP transient do not turn a published constant
196
- into authentication. Do not document this as a secure secret-protected upload
197
- service. Restrict/disable access or implement genuine authenticated capture
198
- submission before relying on it in a new deployment. This documentation patch
199
- does not change the endpoint's security model or rotate its constant.
217
+ The capture client sends `credentials: 'same-origin'` and rejects a different
218
+ endpoint origin before transmitting a nonce or recording. A missing nonce leaves
219
+ local recording/download available and displays an accurate upload hold. After
220
+ a session/nonce expires, reload the authorized configuration before retrying.
221
+ Sensor consent and the explicit upload button remain separate requirements.
222
+ See WordPress's [REST authentication](https://developer.wordpress.org/rest-api/using-the-rest-api/authentication/)
223
+ and [permission callback](https://developer.wordpress.org/rest-api/extending-the-rest-api/adding-custom-endpoints/#permissions-callback)
224
+ documentation. No custom authentication mechanism is added.
200
225
 
201
226
  ### Request shape
202
227
 
@@ -275,9 +300,9 @@ Success returns a normal REST response (no explicit 201 status is set):
275
300
 
276
301
  | HTTP status | Error code | Meaning |
277
302
  | --- | --- | --- |
278
- | 403 | `ks_motion_forbidden` | Missing/wrong token. |
303
+ | 401 / 403 | `ks_motion_forbidden` | WordPress caller is anonymous / lacks `manage_options`. |
279
304
  | 403 | `ks_motion_origin` | Supplied origin does not match the site. |
280
- | 429 | `ks_motion_rate` | Permission-check quota exhausted. |
305
+ | 429 | `ks_motion_rate` | This administrator's permission-check quota exhausted. |
281
306
  | 400 | `ks_motion_schema` | Missing/wrong schema or nonarray JSON payload. |
282
307
  | 400 | `ks_motion_samples` | Missing/nonarray samples or input count outside 1–500. |
283
308
  | 400 | `ks_motion_invalid_samples` | At least one required sample field is invalid. |
@@ -292,9 +317,12 @@ A successful record is a `private` post of type `ks_motion_capture`, titled
292
317
  `Phone motion capture <site-local date/time>`, with the cleaned JSON in
293
318
  `post_content`. Extra metadata: `_ks_motion_passed` (`'1'`/`'0'`),
294
319
  `_ks_motion_sample_count`, and `_ks_motion_sha256` (hash of stored encoded JSON).
295
- The post type has a Tools-menu UI, title/editor support, `public: false`,
296
- `show_in_rest: false`, and default mapped post capabilities; no custom
297
- administrator-only capability scheme is defined.
320
+ The post author is the authenticated administrator ID. The post type has a
321
+ Tools-menu UI, title/editor support, `public: false`, and `show_in_rest: false`.
322
+ In 3.1.23 its read, create, edit and delete capabilities all require
323
+ `manage_options`, with generic post meta-capability mapping disabled. Ordinary
324
+ editors/authors cannot acquire access to private sensor captures through their
325
+ normal post-editing capabilities. Retention remains an explicit operator duty.
298
326
 
299
327
  There is **no plugin GET/list/delete capture REST route, retention job, automatic
300
328
  capture upload, or cryptographic proof of native sensor provenance**. Private
@@ -323,6 +351,24 @@ available. Do not assume a root exists merely because the shared script loaded.
323
351
 
324
352
  ## Registered hooks and callback reference
325
353
 
354
+ ### Project prefixes and compatibility
355
+
356
+ Production PHP declarations use `dance_moves_` / `ks_epk_` functions and
357
+ `DANCE_MOVES_` / `KS_EPK_` constants. Plugin-owned metadata, transients, post
358
+ types, REST namespaces and asset handles use the existing `dance_moves`,
359
+ `dance-moves`, `ks_motion`, `ks-epk-motion` or `ks-epk-device-orientation`
360
+ prefixes. Localized JavaScript configuration uses the named `danceMoves*` and
361
+ `ksEpkOrientationConfig` objects. WordPress core hook names in the table below
362
+ are consumed hooks; they must retain their core names.
363
+
364
+ These names remain for installed-site compatibility. In 3.1.23 the obsolete
365
+ public token constant is removed and the authenticated quota uses the prefixed
366
+ `ks_motion_user_<id>` transient. The declaration-prefix regression and capture
367
+ authorization/client tests run in the existing legacy test gate. Hosted CI also
368
+ exercises real WordPress core nonce validation, roles, REST dispatch, database
369
+ storage and capability mapping on a disposable site. It does not use a live
370
+ installation or claim physical phone acceptance.
371
+
326
372
  These are WordPress hooks **consumed** by the plugin, not plugin-defined extension
327
373
  hooks. Default priority is 10 unless shown. No `apply_filters('dance_moves_...')`
328
374
  or `do_action('dance_moves_...')` extension contract is exposed in this baseline.
@@ -1,5 +1,11 @@
1
1
  # WordPress.org publication
2
2
 
3
+ The plugin and author URI headers must identify different pages. The author
4
+ header uses https://kieransimkin.co.uk/; the plugin retains its published My Songs
5
+ homepage. `npm run check:release` rejects duplicate URLs (including a trailing
6
+ slash variation), matching the directory upload validation. Keep the public
7
+ plugin name **DanceMoves** and its existing compatibility identifiers.
8
+
3
9
  Run the official Plugin Check against the exact production ZIP with the intended directory slug. Do not scan tests as installed runtime code, ignore an entire category or treat a passing static scan as a security audit. Review all warnings and document each contextual finding. Keep the readme stable tag, main PHP version and GitHub release aligned. After directory approval, publish the same verified source through the assigned SVN repository; verify the downloaded directory ZIP independently. No directory password belongs in source.
4
10
 
5
11
  ## Potential problems
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schema": "dancemoves-library-build/v1",
3
3
  "name": "@kieransimkin/dancemoves",
4
- "version": "3.1.21",
4
+ "version": "3.1.23",
5
5
  "homepage": "https://kieransimkin.co.uk/my-songs/",
6
6
  "files": [
7
7
  {
@@ -32,12 +32,12 @@
32
32
  {
33
33
  "path": "dancemoves.min.js",
34
34
  "bytes": 212321,
35
- "sha256": "540af619a286599f2340cb35f70294a70e6899d834b24d0c18a5cbd6b1d1c023"
35
+ "sha256": "2d7a7ec476e32d695fd88277967009b3e10a2ef889e0ecafa59ac4eaf6e15f2a"
36
36
  },
37
37
  {
38
38
  "path": "index.cjs",
39
39
  "bytes": 361185,
40
- "sha256": "8c5b70cafdeb0135a8d4d4afd03a4a72064f71f75443bb253cace82de639bd1a"
40
+ "sha256": "cf41f6c466acbb9f170ca37258fdf15ef7695457543e3bc29dc8b38ff38fed54"
41
41
  },
42
42
  {
43
43
  "path": "index.d.ts",
@@ -47,7 +47,7 @@
47
47
  {
48
48
  "path": "index.mjs",
49
49
  "bytes": 360040,
50
- "sha256": "c23dfac191d589e317d65ec0c56beacf1f02129c1c78e37493a6d6c3a33dc6db"
50
+ "sha256": "abd302d298b2c808d83507eed49235602a4c7530dd3771083e4dfe16d84997ba"
51
51
  },
52
52
  {
53
53
  "path": "react.cjs",
@@ -107,7 +107,7 @@
107
107
  {
108
108
  "path": "wordpress.js",
109
109
  "bytes": 211126,
110
- "sha256": "f7c6f992c84282032e9a9b4257e2538c2424ee41aba07daf6a8957352c4f0f80"
110
+ "sha256": "dfb55567a063115518847ab70485924fed0bd4d64e790f9d659be97e1f0f08f5"
111
111
  }
112
112
  ]
113
113
  }