History log of /dokuwiki/ (Results 1 – 25 of 10912)
Revision Date Author Comments
(<<< Hide modified files)
(Show modified files >>>)
e014d4c820-Jul-2026 Andreas Gohr <gohr@cosmocode.de>

docs: updated the security policy

More details for out-of-scope issue including previous rejected
examples.

fe58309e16-Jul-2026 Andreas Gohr <gohr@cosmocode.de>

extension: don't crash on malformed conflict/dependency ids

The conflicts and depends fields in the repository metadata are free
form text entered by extension authors and may contain values that ar

extension: don't crash on malformed conflict/dependency ids

The conflicts and depends fields in the repository metadata are free
form text entered by extension authors and may contain values that are
not valid extension ids, such as "sprintdoc template". Since 9af82229f
routed every Extension construction through the strict setBase()
validation, such a value made Extension::createFromId() throw an
uncaught RuntimeException while building the notices, taking down the
whole extension manager.

Skip entries that cannot be parsed into an extension id when checking
for missing dependencies and conflicts.

As an immeadiate fix this will also be fixed in the repository API to
avoid sending invalid extension IDs to the extension manager.

Fixes #4691

show more ...

6d10fb8614-Jul-2026 Andreas Gohr <gohr@cosmocode.de>

style(changelog): fix overlong line

057ab5a014-Jul-2026 Andreas Gohr <gohr@cosmocode.de>

ci: update workflow actions to their latest releases

Bumps actions/checkout to v7, actions/cache to v6, actions/github-script
to v9, peter-evans/create-pull-request to v8 and softprops/action-gh-rel

ci: update workflow actions to their latest releases

Bumps actions/checkout to v7, actions/cache to v6, actions/github-script
to v9, peter-evans/create-pull-request to v8 and softprops/action-gh-release
to v3. The cache bump is required since the v3 cache service backend was
retired. All existing inputs and outputs remain compatible.

show more ...

a37b7ba514-Jul-2026 Andreas Gohr <gohr@cosmocode.de>

fix(parser): guard row_counter in tablerow_open()

The row counter used for row CSS classes was only initialized in
table_open(). Renderers that open a row without a preceding table_open()
call (such

fix(parser): guard row_counter in tablerow_open()

The row counter used for row CSS classes was only initialized in
table_open(). Renderers that open a row without a preceding table_open()
call (such as the data plugin's empty-result list) triggered an
'Undefined array key row_counter' warning on PHP 8. Initialize it
defensively, mirroring the existing cell_counter handling.

show more ...

b22130d208-Jul-2026 Andreas Gohr <gohr@cosmocode.de>

fix(parser): stop group-quantifier body patterns from exhausting memory

Several patterns repeat a group — (?:…)* / (?:…)+ — over a body that can
span a large region. On the non-JIT PCRE engine each

fix(parser): stop group-quantifier body patterns from exhausting memory

Several patterns repeat a group — (?:…)* / (?:…)+ — over a body that can
span a large region. On the non-JIT PCRE engine each iteration of a
group repetition keeps its own backtracking frame, so a big body retains
one frame per byte: a linear, unbounded memory spike that can fatal a
request (and, with JIT on, trips pcre.jit_stacklimit so the lexer dumps
the rest of the document as literal text). A repetition over a single
item (.* , [^x]+) is optimized by PCRE and unaffected.

Make each body possessive: the repeated group cannot match the delimiter
that follows it, so it always stops at the first closer and never needs
to backtrack.

- media {{…}}, windowssharelink, gfm_quote, gfm_link: possessive body.
- email validation (used by emaillink and mail_isvalid): possessive
local-part and domain groups; a long dotted `a.a.a…` local part or
domain was a ReDoS vector. Match results are unchanged.

show more ...

3a56ec9b08-Jul-2026 Andreas Gohr <gohr@cosmocode.de>

fix(parser): stop unclosed GFM fenced code and emphasis from exhausting memory

Both modes repeat a group over their body, and on the non-JIT PCRE
engine each iteration of a group repetition keeps it

fix(parser): stop unclosed GFM fenced code and emphasis from exhausting memory

Both modes repeat a group over their body, and on the non-JIT PCRE
engine each iteration of a group repetition keeps its own backtracking
frame. On unclosed input the body runs to end of input (or a paragraph
break), the required closer fails, and the engine unwinds frame by
frame: a linear, unbounded memory spike that can fatal a request once
the body grows past the memory limit (and, with JIT on, trips
pcre.jit_stacklimit so the lexer dumps the rest of the document as
literal text).

gfm_code/gfm_file: the fence body (?:(?!CLOSE).)* already stops at the
first valid closer, so no backtracking into it is ever legitimate. Make
it possessive so the no-closer case fails immediately.

gfm_emphasis: its closer lookahead ended with a character the body had
to backtrack to reach (the [^\s*] before the closing *). Verify that
flanking-closer rule with a lookbehind instead, so the [^*] body — which
cannot match the * it stops at — can be possessive too.

Matching is unchanged for well-formed input. The fenced-code patterns
were introduced in b1c59bed2.

show more ...

1c00c02109-Jul-2026 Andreas Gohr <gohr@cosmocode.de>

fix(parser): validate inline formatting closers with a single memoized scan

The inline formatting modes only open a span when a valid closer exists
ahead. That check was a lookahead built on CONTENT

fix(parser): validate inline formatting closers with a single memoized scan

The inline formatting modes only open a span when a valid closer exists
ahead. That check was a lookahead built on CONTENT_UNTIL_PARA, tested
character by character up to the next paragraph break and re-evaluated
from scratch for every opener candidate — openers times paragraph
length. With pcre.jit=0 a crafted 32KB page took 16s and an ordinary
34KB page with long paragraphs 37s; with the JIT on (the PHP default)
the per-character lookahead exhausted the JIT stack, the match silently
failed, and the formatting — or everything after it — rendered as plain
text.

The check also decided the wrong thing. It scanned raw text, so a closer
lookalike inside content the lexer consumes atomically — a nowiki or %%
span, a backtick code span, a link, a URL — counted as a real closer
even though the mode's exit pattern can never fire there. And it ignored
the enclosing span: an inner delimiter whose only closer lay past the
closer of the mode it sits in was entered anyway, so a stray delimiter
paired with one in a following sibling span and dragged the boundary
along — the `*` in ''glob/*.conf'' joined the `*` of the next ''...''
span and corrupted the paragraph; the same held for //, ** and __ inside
monospace, and an emphasis opened inside ((...)) ran past the footnote's
)) and the enclosing bold's **.

Each formatting mode now declares its closer through
Lexer::addCloserPattern(), mirroring addExitPattern(), and the lexer
answers "does a valid closer exist ahead" with one anchored possessive
scan per range instead of a lookahead per opener:

- The scan runs left to right from the opener, hopping over opaque spans
derived from already-registered patterns — a plain or special match is
consumed in one step, an entry into a verbatim mode (nowiki, the
backtick code spans) extends to that mode's first exit — so a closer
lookalike inside consumed content is never mistaken for a closer. Each
hop finds the earliest of boundary, closer, or opaque span in a single
leftmost search, keeping the check linear.
- An opener is rejected when the nearest enclosing mode that has a closer
of its own would close before the opener's own closer, so a delimiter
that can never close within its span stays literal. That ancestor is
found by walking the mode stack past modes that declare no closer
(plugins, footnotes); the nearest guarded ancestor suffices, as it was
itself validated against its own when it opened.
- Both verdicts are memoized and reset per parse() run: a proven closer
validates every earlier candidate, and a proven closer-free range
rejects every later candidate before the next boundary. With the lexer
consuming each opened span, the whole parse is linear in document size.

Closer patterns match the closing delimiter itself with flanking context
in lookarounds — the convention exit patterns already follow — so closer
positions compare exactly across modes and a closer directly after an
inner opener is seen. AbstractFormatting derives the closer from the exit
pattern and registers it with the paragraph break as the boundary,
preserving the rule that formatting never spans paragraphs; a mode with
other needs can pass a different boundary or none.

Footnote declares its )) as a closer rather than guarding its (( entry
with a (?=.*)) lookahead, so the footnote becomes a boundary the scan
sees and formatting inside it no longer pairs across the )); its closer
takes no paragraph boundary, as footnotes are block-level. GfmEmphasis
gains a closer pattern so single * emphasis is validated the same way,
while its entry lookahead still enforces CommonMark nearest-delimiter
pairing. GfmEmphasis and GfmStrong span bodies cannot contain their
delimiter, so their in-pattern lookaheads stay linear on their own; the
GFM backtick span bodies get deterministic alternatives with possessive
quantifiers, removing their per-character backtracking.

CONTENT_UNTIL_PARA is removed: any entry pattern built with it recreates
the quadratic scan. ParallelRegex gains escapePattern() so embedded
closer fragments follow the lexer's bare-parenthesis convention, reports
PREG_JIT_STACKLIMIT_ERROR so a future JIT exhaustion surfaces instead of
silently truncating, and no longer rewrites its registered patterns in
place while compiling the compound regex.

The adversarial 32KB page drops to 0.1s, the 37-second benign page to
0.1s, and a 128KB variant stays under 0.6s.

show more ...

1ed5d39808-Jul-2026 splitbrain <86426+splitbrain@users.noreply.github.com>

�� Rector and PHPCS fixes

846f53c108-Jul-2026 Andreas Gohr <gohr@cosmocode.de>

fix(search): lock the metadata registry read-modify-write

updateMetadataRegistry() read metadata.idx, merged in new keys and wrote
the file back without holding a lock across the sequence. Two index

fix(search): lock the metadata registry read-modify-write

updateMetadataRegistry() read metadata.idx, merged in new keys and wrote
the file back without holding a lock across the sequence. Two indexer
processes each registering a different new key could both read the same
registry and the later writer would clobber the other's key, dropping it.
A dropped key is not cleared from its collection on deletePage(), leaving
orphaned index entries.

Guard the whole read-merge-write with a dedicated metadata lock so
concurrent registrations can no longer lose a key.

show more ...

d9043e7808-Jul-2026 Andreas Gohr <gohr@cosmocode.de>

fix(search): make DirectCollection::getEntitiesWithData() linear

The method resolved each entity name with a separate retrieveRow() call,
each of which rescans the entity index from the start, makin

fix(search): make DirectCollection::getEntitiesWithData() linear

The method resolved each entity name with a separate retrieveRow() call,
each of which rescans the entity index from the start, making the whole
operation quadratic in the number of entities. This runs on
MetadataSearch::getPages('title') and hurts large wikis. Collect the entity
IDs in one pass and resolve their names with a single batched retrieveRows()
read instead.

show more ...

45fa8bb208-Jul-2026 Andreas Gohr <gohr@cosmocode.de>

fix(search): stop FileIndex::retrieveRows() scanning past the last row

The early-exit check compared the array_shift() result against false, but
array_shift() returns null on an empty array, so the

fix(search): stop FileIndex::retrieveRows() scanning past the last row

The early-exit check compared the array_shift() result against false, but
array_shift() returns null on an empty array, so the break never fired and
every call read the index file to EOF after collecting the last requested
row. Compare against null and skip the file entirely when nothing is
requested.

show more ...

61dea71008-Jul-2026 Andreas Gohr <gohr@cosmocode.de>

fix(search): wait for a contended index lock and apply dperm

The Lock rewrite (c66b5ec65) made Lock::acquire() fail immediately when the
lock directory already existed, where the old indexer lock re

fix(search): wait for a contended index lock and apply dperm

The Lock rewrite (c66b5ec65) made Lock::acquire() fail immediately when the
lock directory already existed, where the old indexer lock retried until the
holder released it. Concurrent saves or indexer runs then aborted indexing
instead of serializing (recoverable only via the .indexed tag on the next
edit), and the lock directory was created without the configured directory
permissions, unlike every other directory DokuWiki creates.

Mirror the io_lock() convention: wait for a contended lock, bounded by a
tunable wait timeout, before throwing; keep clearing locks older than five
minutes as stale; and chmod the new lock directory to $conf['dperm']. The
give-up path still throws IndexLockException, so the self-healing retry via
the .indexed tag is unchanged.

show more ...

7ec464d908-Jul-2026 Andreas Gohr <gohr@cosmocode.de>

fix(remote): make aclCheck self-check case-insensitive-backend aware

The check gating aclCheck() for other users compared the requested user
against REMOTE_USER verbatim. On a case-insensitive auth

fix(remote): make aclCheck self-check case-insensitive-backend aware

The check gating aclCheck() for other users compared the requested user
against REMOTE_USER verbatim. On a case-insensitive auth backend a user
naming themselves in a different case than their login was treated as a
different user and wrongly denied checking their own ACL. Normalize both
names the way auth_isMember() does before comparing.

The self-check was introduced in 884caed92.

show more ...

e5358e0d08-Jul-2026 Andreas Gohr <gohr@cosmocode.de>

fix(infoutils): use git --pretty=reference to avoid Windows escaping

On Windows escapeshellarg() replaces percent signs with spaces, so the
--pretty=format:%h %cd argument reached git as literal tex

fix(infoutils): use git --pretty=reference to avoid Windows escaping

On Windows escapeshellarg() replaces percent signs with spaces, so the
--pretty=format:%h %cd argument reached git as literal text. Git then
echoed the format string verbatim, a truthy value that made the version
display report an empty hash and the date "h" while skipping the working
.git-directory fallback.

The named --pretty=reference format carries no percent signs on the
command line, so the shell path works on Windows too. Its "hash
(subject, date)" output is validated before being trusted.

Introduced in b9e35b2f0.

show more ...

9255329a08-Jul-2026 Andreas Gohr <gohr@cosmocode.de>

fix(feed): surface real fetch error instead of "unsupported status code"

DokuHTTPClient reports transport failures with negative pseudo statuses
while keeping the descriptive message in its error fi

fix(feed): surface real fetch error instead of "unsupported status code"

DokuHTTPClient reports transport failures with negative pseudo statuses
while keeping the descriptive message in its error field. SimplePie only
turns a file error into a reported error when the status code is zero, so
connection, SSL and timeout failures surfaced as "unsupported status code
-100" under allowdebug, hiding the real cause. Clamp the pseudo statuses
to zero so the descriptive error reaches the feed output.

show more ...

12ead38a08-Jul-2026 Andreas Gohr <gohr@cosmocode.de>

fix(httputils): canonicalize paths when building X-Accel-Redirect URLs

http_xaccel_url() compared the fullpath()-collapsed target file against a
non-canonicalized DOKU_INC and the raw savedir config

fix(httputils): canonicalize paths when building X-Accel-Redirect URLs

http_xaccel_url() compared the fullpath()-collapsed target file against a
non-canonicalized DOKU_INC and the raw savedir config value. The lib/exe entry
points define DOKU_INC as __DIR__.'/../../' and savedir defaults to the relative
'./data', so the in-tree prefix check never matched: nginx installs using
X-Accel-Redirect served in-tree files behind the /_x_accel_redirect/ escape
hatch and returned 404 without extra location config.

Canonicalize DOKU_INC and every configured root with fullpath() before the
prefix comparisons so they match the already-canonicalized file path.

The comparison lost its fullpath() wrapping in b8c2692f7, which replaced the
earlier substr($file, strlen(fullpath(DOKU_INC)) + 1) with a raw DOKU_INC
prefix check.

show more ...

65ba535c08-Jul-2026 Andreas Gohr <gohr@cosmocode.de>

fix(media): suppress exif_read_data() warnings on malformed JPEGs

Reading the EXIF orientation of a JPEG called exif_read_data() without
error suppression, so a file with a broken EXIF block emitted

fix(media): suppress exif_read_data() warnings on malformed JPEGs

Reading the EXIF orientation of a JPEG called exif_read_data() without
error suppression, so a file with a broken EXIF block emitted a warning
that the error handler logged on every media manager, detail page and
media diff render.

Bump slika to 1.2.1, which suppresses the warning at the source.

show more ...

63b2278608-Jul-2026 Andreas Gohr <gohr@cosmocode.de>

docs(parser): fix GfmListblock sub-parser docblock

The class docblock claimed the per-item loop runs inside a
ModeRegistry::withSubParser() callback, but the code acquires a pooled
sub-parser direct

docs(parser): fix GfmListblock sub-parser docblock

The class docblock claimed the per-item loop runs inside a
ModeRegistry::withSubParser() callback, but the code acquires a pooled
sub-parser directly and releases it after the loop. Describe the actual
mechanism.

show more ...

6f10c54408-Jul-2026 Andreas Gohr <gohr@cosmocode.de>

chore(changelog): rename $clogRev to $recordedRev

It's a bit easier to decipher

3a5ba39508-Jul-2026 Andreas Gohr <gohr@cosmocode.de>

fix(parser): treat quotes next to bold/underline markup as smart quotes

With typography=2 a single-quoted phrase wrapped in strong or underline
markup (**'x'** / __'x'__) rendered apostrophes instea

fix(parser): treat quotes next to bold/underline markup as smart quotes

With typography=2 a single-quoted phrase wrapped in strong or underline
markup (**'x'** / __'x'__) rendered apostrophes instead of opening and
closing quotes: the lexer matches against the full subject at a byte
offset, so the singlequoteopening lookbehind sees the consumed * or _
delimiter and fails, letting apostrophe win. Add * and _ to the quote
boundary set, as / (emphasis) already was, so the surrounding markup is
recognised as a word boundary.

show more ...

2d05a06d08-Jul-2026 Andreas Gohr <gohr@cosmocode.de>

fix(info): read parser modes from the registry, not the deprecated global

The info plugin's ~~INFO:syntaxtypes~~ read the $PARSER_MODES global
directly. Since the mode taxonomy moved into ModeRegist

fix(info): read parser modes from the registry, not the deprecated global

The info plugin's ~~INFO:syntaxtypes~~ read the $PARSER_MODES global
directly. Since the mode taxonomy moved into ModeRegistry (c8dd1b9d2),
that global is only populated as a side effect of a parse, so on a warm
instruction cache with no parse in the request it is unset and the
syntax-types table renders empty. ~~INFO:syntaxmodes~~ used the
deprecated p_get_parsermodes(), logging a deprecation on every render.

Both now build a ModeRegistry and read the categories and modes from it
directly, independent of parse or cache state.

show more ...

18cdf10207-Jul-2026 Andreas Gohr <gohr@cosmocode.de>

fix(editor): give toolbar and picker buttons an explicit type

Toolbar and picker buttons are created as <button> elements with no type
attribute, so they default to type="submit". When a plugin rend

fix(editor): give toolbar and picker buttons an explicit type

Toolbar and picker buttons are created as <button> elements with no type
attribute, so they default to type="submit". When a plugin renders the
toolbar into a container inside its own form, clicking any button (e.g. a
smiley picker) submits that form. Set type="button" so the buttons never
trigger a submit.

show more ...

6a8e48ed07-Jul-2026 Andreas Gohr <gohr@cosmocode.de>

fix(extension): skip invalid directories when listing extensions

A leftover directory whose name is not a valid extension base (e.g.
"myplugin (copy)") made Extension::setBase() throw, which propaga

fix(extension): skip invalid directories when listing extensions

A leftover directory whose name is not a valid extension base (e.g.
"myplugin (copy)") made Extension::setBase() throw, which propagated
out of the unguarded getPlugins()/getTemplates() calls and fataled the
whole Extension Manager page and the extension list CLI.

Catch the exception per-directory in readExtensionsFromDirectory() so a
single stray directory is skipped and logged instead of breaking every
caller. The throw is kept for the install path.

show more ...

85a452e207-Jul-2026 Andreas Gohr <gohr@cosmocode.de>

feat(admin): show template and syntax on admin info

two more values we always ask for when debugging

12345678910>>...437