| #
49d7ec0a |
| 03-Jun-2026 |
tracker-user <82045103+tracker-user@users.noreply.github.com> |
Fix stored XSS in JSINFO embed and self-deadlocking write lock
Two serious bugs found during review, plus three minor fixes:
- Stored XSS: the inline-embedded annotation payload was encoded with
Fix stored XSS in JSINFO embed and self-deadlocking write lock
Two serious bugs found during review, plus three minor fixes:
- Stored XSS: the inline-embedded annotation payload was encoded with JSON_UNESCAPED_SLASHES and appended into the page's inline <script>. A body containing "</script>" closed the script element and injected arbitrary HTML, executing in every viewer's browser. Anyone who can annotate (AUTH_READ + login) could exploit it. Add JSON_HEX_TAG so < and > are escaped, neutralising every tag-based breakout.
- Write lock: mutate() held io_lock($file) and then called io_saveFile($file), which takes io_lock($file) again internally. The inner lock collided with the outer, busy-waiting ~3s for the stale-lock timeout on every create/edit/delete/resolve/clear and defeating mutual exclusion. Lock on a sentinel key ($file.lock) instead, leaving io_saveFile's own lock uncontended (matches DokuWiki TaskRunner idiom).
- style.css: the :root colour fallbacks were self-referential (var(--x) of themselves), i.e. cyclic and invalid, not a fallback. Replace with the literal config-default triplets.
- action.php: drop hsc() from a JSON error string (wrong context).
- script.js: refresh the open orphan drawer after a thread mutation so an edited body shows there too.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
show more ...
|
| #
86c7806d |
| 03-Jun-2026 |
tracker-user <82045103+tracker-user@users.noreply.github.com> |
Add config, selection guards, annotation overlap detection, and glow button
- Config: color_open / color_resolved (hex, drives CSS vars); embed_max_bytes, context_length, body_cap move from consta
Add config, selection guards, annotation overlap detection, and glow button
- Config: color_open / color_resolved (hex, drives CSS vars); embed_max_bytes, context_length, body_cap move from constants to conf/default + metadata. action.php injects --ann-open-rgb / --ann-resolved-rgb as CSS custom props; style.css escapes every rgba(var(…)) with LESS ~"…" so lesserphp doesn't bake them to #000000 at compile time. - Selection guards: Annotate button suppressed inside .ann-* UI, #dw__toc, .docInfo, .secedit; the old endpoint-only isInsideHighlight is replaced by selectionHitsHighlight (range.intersectsNode over all highlight spans) so a selection overrunning an existing annotation on either side opens it instead of offering a new overlapping one. - Glow: static amber box-shadow on .ann-tooltip .ann-btn (no animation). - Removed email line from plugin.info.txt; updated GeneralTest accordingly. - context_length fed to JS via JSINFO.annotations.contextLen so capture and PHP trimming stay in sync. - conf/ + lang/*/settings.php + README/DESIGN updated.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
show more ...
|
| #
108f92bd |
| 03-Jun-2026 |
tracker-user <82045103+tracker-user@users.noreply.github.com> |
Eliminate load round-trip by embedding annotations in JSINFO
action.php now reads the annotation list once during page render and ships it inline as JSINFO.annotations.annotations, so script.js rend
Eliminate load round-trip by embedding annotations in JSINFO
action.php now reads the annotation list once during page render and ships it inline as JSINFO.annotations.annotations, so script.js renders immediately at DOMContentLoaded with no second AJAX bootstrap (~300 ms saved per view). The embedded list also removes the double file read that existed before (getStats called getAnnotations internally, then the client fetched the same file again).
helper.php adds statsFor(array) so action.php can derive stats from the already-loaded list instead of re-reading through getStats(). getStats now delegates to it.
script.js uses the embedded list when present; falls back to the GET load endpoint only when the list was too large to embed (>128 KB cap). Adds a window.load repositionMarkers call so gutter markers re-align after late-loading images shift the layout.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
show more ...
|
| #
ee9dbf15 |
| 03-Jun-2026 |
tracker-user <82045103+tracker-user@users.noreply.github.com> |
Add nested reply threading; sync docs
Replies can now be threaded under one another instead of only sitting flat under their annotation.
Backend: - helper.php: addReply() takes an optional parentId
Add nested reply threading; sync docs
Replies can now be threaded under one another instead of only sitting flat under their annotation.
Backend: - helper.php: addReply() takes an optional parentId, stored on each reply after sanitising it to hex; an empty or unknown parent simply falls back to a top-level reply. Replies remain a flat list, with the parent link carried as data. - action.php: the reply action forwards parentId and returns the full updated annotation (not just the new reply) so the client re-renders the whole thread in one round-trip. Also fix a stray-indented docblock and drop the inaccurate "and exit" wording on the JSON responders.
Docs / manifest: - DESIGN.md: document the parentId field, the flat-storage-plus-client-tree model, and the full-annotation reply response. - README.md: note that replies can nest. - plugin.info.txt: bump the date.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
show more ...
|
| #
da56206c |
| 02-Jun-2026 |
tracker-user <82045103+tracker-user@users.noreply.github.com> |
Fix highlight off-by-one, localise the UI, add tests
Anchoring (the core bug): - Replace buildNormToRaw with normalizeWithMap so the normalised search string and the normalised->raw index map are
Fix highlight off-by-one, localise the UI, add tests
Anchoring (the core bug): - Replace buildNormToRaw with normalizeWithMap so the normalised search string and the normalised->raw index map are built in one pass and share the same trimming. DokuWiki indents its content markup, so the collected text starts with a whitespace text node; the old untrimmed map shifted every highlight by a character. Verified on the live wiki: a quote now re-anchors exactly, including across element boundaries. - Locate all matches first, then wrap last-to-first, so wrapping (which splits text nodes) never disturbs a not-yet-wrapped offset. - Fix a pre-existing orphan double-count: renderAll passed an orphan total into updateCounter, which then recounted it from the _orphaned flags.
Localisation: - Move front-end strings to $lang['js'] (exposed as LANG.plugins.annotations) and read them via t()/fmt() with English fallbacks. - Add de, ru and ja translations alongside en.
Conventions / cleanup: - action.php: read request data via $INPUT; pass the CSRF token straight to checkSecurityToken($token) instead of poking $_POST/$_REQUEST; unify admin detection on auth_isadmin(). - helper.php: write annotation files with JSON_UNESCAPED_UNICODE | _SLASHES. - Drop the dead ann-highlight-orphaned constant; set panel data-status so the resolved accent in style.css applies.
Tests: - Add _test/GeneralTest.php (manifest + conf invariant) and _test/HelperTest.php (permission rules, CRUD, input cleaning, findOrphaned). 15 tests pass.
Docs: sync DESIGN.md and README.md; bump plugin.info.txt date.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
show more ...
|
| #
f58805fb |
| 23-May-2026 |
tracker-user <82045103+tracker-user@users.noreply.github.com> |
Accept the CSRF token from the JSON request body
checkSecurityToken() reads the token from $_REQUEST, which is empty when the AJAX request body is application/json. Copy the payload's sectok into $_
Accept the CSRF token from the JSON request body
checkSecurityToken() reads the token from $_REQUEST, which is empty when the AJAX request body is application/json. Copy the payload's sectok into $_POST / $_REQUEST before the check so state-changing JSON requests validate instead of being rejected as forged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
show more ...
|
| #
7d2714c7 |
| 23-May-2026 |
tracker-user <82045103+tracker-user@users.noreply.github.com> |
Expose current user, admin flag and CSRF token to the front-end
DokuWiki's JSINFO carries no user identity, so script.js could not tell who was logged in and could not gate the edit/delete/resolve U
Expose current user, admin flag and CSRF token to the front-end
DokuWiki's JSINFO carries no user identity, so script.js could not tell who was logged in and could not gate the edit/delete/resolve UI. Inject user, isAdmin and the security token into JSINFO.annotations from action.php, and read them from there instead of the non-existent JSINFO.userinfo object and the #dw__token hidden field.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
show more ...
|
| #
b8076f00 |
| 23-May-2026 |
tracker-user <82045103+tracker-user@users.noreply.github.com> |
Fix JSINFO injection timing and gutter marker positioning
handleMetaHeader() wrote the annotation payload to $JSINFO, but tpl_metaheaders() has already serialised JSINFO into the inline <script> by
Fix JSINFO injection timing and gutter marker positioning
handleMetaHeader() wrote the annotation payload to $JSINFO, but tpl_metaheaders() has already serialised JSINFO into the inline <script> by the time TPL_METAHEADER_OUTPUT fires, so the data never reached the page. Append a `JSINFO.annotations = {...}` statement to that inline block instead, and only inject it on show / export_xhtml views.
Anchor the gutter markers to .page (the article column) rather than #dokuwiki__content: the latter's position:relative extended over the sidebar and swallowed its navigation clicks.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
show more ...
|
| #
43d2073c |
| 23-May-2026 |
tracker-user <82045103+tracker-user@users.noreply.github.com> |
Initial annotations plugin
Word- and sentence-level annotations for DokuWiki pages: text-quote anchoring, threaded replies, open/resolved status, gutter markers, client- and server-side orphan detec
Initial annotations plugin
Word- and sentence-level annotations for DokuWiki pages: text-quote anchoring, threaded replies, open/resolved status, gutter markers, client- and server-side orphan detection, a per-user on/off toggle, and admin per-page bulk-clear. Annotations are stored out-of-band in a per-page JSON file, so the wiki changelog is never touched.
Built with Claude Opus 4.7 and Sonnet 4.6.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
show more ...
|