mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-09-25 01:53:03 +02:00
Reorganise docs: move historical files to archive/, merge overlapping docs
- Move 12 historical/superseded docs + 1 session log + 1 PDF + 1 HTML plan to archive/ - Merge 4 VM-crash docs into archive/vm-crash-incident.md - Merge LDAP plan + spec into ldap.md - Merge FilePond race investigation into filepond-crash-analysis.md - Merge SPECS.md client notes into spec-sheet.md appendix - Update README index and security.md cross-reference
This commit is contained in:
@@ -264,3 +264,114 @@ The `destroyFilePondsIn()` function in `file-upload-filepond.js` should abort in
|
||||
- Added pre-destroy abort in `destroyFilePondsIn()`
|
||||
|
||||
These changes address server response format and cleanup ordering, but **do not bypass the buggy `load-file-error` → `action.status` path inside FilePond's internal code**. The crash still reproduces.
|
||||
|
||||
---
|
||||
|
||||
# HTMX/destroy race investigation (merged from filepond-race-investigation.md)
|
||||
|
||||
This section narrows the crash's trigger. It was formerly a separate doc
|
||||
(`filepond-race-investigation.md`).
|
||||
|
||||
## HTMX destroy triggers
|
||||
|
||||
The only code path that destroys FilePond instances is `destroyFilePondsIn(el)`, called by the `htmx:beforeSwap` listener:
|
||||
|
||||
```js
|
||||
window.htmx.on("htmx:beforeSwap", onHtmxBeforeSwap);
|
||||
// → onHtmxBeforeSwap(evt) { destroyFilePondsIn(evt.detail.target); }
|
||||
```
|
||||
|
||||
On the **edit page** (`/admin/edit.php`), the HTMX targets on page load are:
|
||||
|
||||
| Element | Trigger | Target selector | Scope |
|
||||
|---------|---------|-----------------|-------|
|
||||
| `#toast-region` | `load` | `#toast-region` | Footer `<aside>` |
|
||||
| `.licence-license-choice` (hidden input) | `load` | `.licence-license-choice` | Licence fieldset |
|
||||
| Language checkboxes | `change` | `#languages-required-asterisk` | A `<span>` |
|
||||
| File browser buttons | `click` | `#relink-modal-body` | Modal body |
|
||||
| Jury autocomplete | `change` | small targets | Form field |
|
||||
| Tag search input | `input` | pill list container | Form field |
|
||||
| Licence radio buttons | `change` | `.licence-license-choice` | Licence fieldset |
|
||||
|
||||
**None of these targets are ancestors of the `#format-fichiers-block` div**
|
||||
(which contains all FilePond inputs including the cover queue). Therefore **no
|
||||
HTMX swap on the edit page can trigger `destroyFilePondsIn` on the FilePond
|
||||
container during normal operation.**
|
||||
|
||||
The `htmx:targetError` in the crash log is confirmed noise: `targetError` does
|
||||
**not** fire `htmx:beforeSwap`, so no DOM swap occurs.
|
||||
|
||||
**Verdict: HTMX does NOT swap the FilePond container. The race hypothesis as stated is refuted.**
|
||||
|
||||
## In-flight state at file-pick time
|
||||
|
||||
No HTMX request is in flight when the crash occurs: the toast-region's
|
||||
`hx-get` completes quickly (sub-second, 204 or small fragment) long before a
|
||||
human clicks "Parcourir" and selects a file. Other triggers require explicit
|
||||
user interaction; the native file picker is modal and blocks the main thread.
|
||||
|
||||
## `znunoqpw` abort analysis
|
||||
|
||||
Commit `znunoqpw` added a pre-destroy abort in `destroyFilePondsIn`:
|
||||
|
||||
```js
|
||||
for (var i = 0; i < files.length; i++) {
|
||||
var f = files[i];
|
||||
if (f.status === 4 || f.status === 2 || f.status === 3) {
|
||||
try { pond.removeFile(f); } catch (_abort) {}
|
||||
}
|
||||
}
|
||||
pond.destroy();
|
||||
```
|
||||
|
||||
**The status check is incorrect.** FilePond 4.32.12 internal status constants
|
||||
are `INIT:1`, `IDLE:2`, `PROCESSING:3`, `PROCESSING_COMPLETE:5`, `LOADING:7`,
|
||||
`LOAD_ERROR:8`, `PROCESSING_QUEUED:9`. The check catches `2` (IDLE, no-op),
|
||||
`3` (PROCESSING), and `4` (does not exist). Status `7` (LOADING) is **not
|
||||
caught**, so a file in the LOAD_FILE filter chain is never removed.
|
||||
|
||||
However, `pond.destroy()` → `ABORT_ALL` freezes items and calls `abortLoad()`.
|
||||
Since `activeLoader` is null during the LOAD_FILE chain, the else branch sets
|
||||
status INIT + fires `load-abort`; the chain Promise still runs but the freeze
|
||||
gate (`i.frozen`) suppresses event dispatch. **So the abort mechanism prevents
|
||||
the crash after destroy, but only when `destroyFilePondsIn` is actually called
|
||||
— which it never is in the standard repro (HTMX never swaps the container).**
|
||||
|
||||
## Line 6878 catch reachability
|
||||
|
||||
Two paths dispatch `DID_THROW_ITEM_INVALID` to the `file-status` view writer:
|
||||
|
||||
```js
|
||||
Wt = function(e) {
|
||||
var t = e.root, n = e.action;
|
||||
Nt(t.ref.main, n.status.main); // ← crashes if n.status undefined
|
||||
Nt(t.ref.sub, n.status.sub);
|
||||
};
|
||||
```
|
||||
|
||||
- **Path A — `load-request-error` → SAFE.** Both branches wrap rejection in
|
||||
`{ status: { main, sub } }`. No crash.
|
||||
- **Path B — `load-file-error` → VULNERABLE.** Passes `t.status` through
|
||||
unguarded. For local files, LOAD_FILE plugins (`FileValidateType`/`FileValidateSize`)
|
||||
reject with a proper `{ status: { main, sub } }`. For server-loaded files with
|
||||
error responses, `createResponse` has `.code` not `.status`, but the FileValidateType
|
||||
filter still wraps its rejection correctly → still safe.
|
||||
- **`.catch` handler (line 6878)** has an explicit `!t.status` guard → cannot crash.
|
||||
|
||||
## Verdict
|
||||
|
||||
- **HTMX race hypothesis: REFUTED** (no swap targets the FilePond container;
|
||||
freeze gate prevents post-destroy dispatch).
|
||||
- **Actual crash cause: INDETERMINATE (but narrowed).** The only vulnerable path
|
||||
is `load-file-error` → `DID_THROW_ITEM_INVALID` with `status: undefined`. For
|
||||
local file selection, the exact path to `undefined` status isn't identified.
|
||||
The most likely trigger is a **Firefox-specific XHR abort edge case** in the
|
||||
existing cover file's `server.load`, racing with adding a new local file.
|
||||
|
||||
## Recommended next step
|
||||
|
||||
Add `console.log` instrumentation to `server.load`'s onload/onerror and a global
|
||||
`FilePond:error` / `window.error` trap, then reproduce in Firefox. If
|
||||
`server.load onload` fires immediately before the crash, the race is confirmed
|
||||
and the fix is **Option B** (custom `fetch`-based `server.load` that never
|
||||
routes server responses through the LOAD_FILE filter chain).
|
||||
|
||||
Reference in New Issue
Block a user