| fe58309e | 16-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 ...
|
| 1ed5d398 | 08-Jul-2026 |
splitbrain <86426+splitbrain@users.noreply.github.com> |
Rector and PHPCS fixes |
| 2d05a06d | 08-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 ...
|
| 6a8e48ed | 07-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 ...
|
| aa331cf2 | 29-Jun-2026 |
Martin Růžička <martinr@post.cz> |
Translation update (cs) |
| 946266a3 | 26-Jun-2026 |
Schplurtz le Déboulonné <Schplurtz@laposte.net> |
Translation update (fr) |
| 2848662b | 25-Jun-2026 |
splitbrain <86426+splitbrain@users.noreply.github.com> |
Rector and PHPCS fixes |
| 9af82229 | 25-Jun-2026 |
Andreas Gohr <gohr@cosmocode.de> |
extension: validate base name to prevent path traversal on install
The extension base name flowed unsanitized from an uploaded archive's plugin.info.txt (or an extension id) into getInstallDir(), wh
extension: validate base name to prevent path traversal on install
The extension base name flowed unsanitized from an uploaded archive's plugin.info.txt (or an extension id) into getInstallDir(), where fullpath() collapses '..' segments. A crafted base such as '../../../../evil' therefore resolved outside lib/plugins/, letting dircopy() write the archive contents to an arbitrary web-writable path.
Route every base assignment through a new setBase() that rejects anything other than a bare extension name.
Note: this is not really a security concern since plugins can execute arbitrary code by design. The is mostly to make sure, plugin code is not installed accidentally at the wrong location.
show more ...
|
| 195a3a7f | 25-Jun-2026 |
Andreas Gohr <gohr@cosmocode.de> |
make getUserData() fail safely on non-string input
When removing multiple users at once, plugins listening on the AUTH_USER_CHANGE event may pass the array of user names (params[0] for 'delete') to
make getUserData() fail safely on non-string input
When removing multiple users at once, plugins listening on the AUTH_USER_CHANGE event may pass the array of user names (params[0] for 'delete') to getUserData(), which crashed authplain with an "Array to string conversion" / illegal offset error.
Guard the bundled auth backends so a non-string user fails safely by returning false, and document that AUTH_USER_CHANGE params[0] is a string for create/modify but a string[] for delete.
Fixes #4567
show more ...
|
| 6436b0ab | 25-Jun-2026 |
Schplurtz le Déboulonné <Schplurtz@laposte.net> |
Translation update (fr) |
| 75364f13 | 25-Jun-2026 |
Marek Adamski <fevbew@wp.pl> |
Translation update (pl) |
| 5d71952d | 16-Jun-2026 |
Andreas Gohr <andi@splitbrain.org> |
Translation update (de) |
| 17acbd0f | 06-Jun-2026 |
Andreas Gohr <andi@splitbrain.org> |
Cast the ACL permission to an integer when saving the rule table
The bulk update path pulled the permission map straight from input and wrote each value verbatim into a tab-separated ACL line, while
Cast the ACL permission to an integer when saving the rule table
The bulk update path pulled the permission map straight from input and wrote each value verbatim into a tab-separated ACL line, while the single-rule path already read it through an integer filter. A non-integer value could carry a newline or tab and inject extra rules into the file. Cast to int so the value can only ever be a single permission number.
This was not exploitable: the rule table is reachable only through the ACL admin plugin, which is superuser-only and CSRF-protected, and a superuser can already edit the ACL file directly. The cast is hardening for consistency with the single-rule path.
show more ...
|
| fa03edf3 | 06-Jun-2026 |
Andreas Gohr <andi@splitbrain.org> |
(security) Enforce per-page edit permission in the revert plugin
The revert manager is accessible to managers, not just admins. Its reversion loop called saveWikiText() for every submitted page id w
(security) Enforce per-page edit permission in the revert plugin
The revert manager is accessible to managers, not just admins. Its reversion loop called saveWikiText() for every submitted page id without checking the per-page ACL, relying only on the manager role. A manager denied edit on a namespace could therefore revert those pages to an older revision or blank them entirely (low severity).
The page listing already hid unreadable pages, but offered every readable page for reversion, including ones the manager could not edit. A hand-crafted POST could also target arbitrary ids regardless of what the listing showed.
Each id is now cleaned and checked for edit permission before it is reverted; ids that fail the check are silently skipped. The listing additionally only offers pages the manager actually has edit permission on, so the form and the action agree.
show more ...
|
| aabc4782 | 06-Jun-2026 |
Andreas Gohr <andi@splitbrain.org> |
(security) Add CSRF protection and use Form class in popularity plugin
The plugin is accessible to managers, not just admins, and had two separate issues.
1. Missing CSRF token (low severity) Th
(security) Add CSRF protection and use Form class in popularity plugin
The plugin is accessible to managers, not just admins, and had two separate issues.
1. Missing CSRF token (low severity) The handler accepted the posted data and toggled autosubmit without checking a security token. A cross-site forged POST against a logged-in manager could enable autosubmit and trigger a submission of the wiki's data to the popularity server. 2. Reflected XSS (low severity) When a submission failed, the posted data was reflected back into a readonly textarea without escaping. A value closing the textarea early (eg. </textarea><script>...) could inject script into the manager's browser. To exploit this, not only a CSRF attack against an authenticated manager was needed, also the connection to the DokuWiki popularity server needed to fail.
The popularity plugin now verifies the security token before it sends data or toggles the autosubmit option.
The form is now built via the the Form API so the textarea value is escaped automatically. The security token is emitted automatically. The fallback browser-submission form posts to an external server and is built without the security token.
show more ...
|
| 3e427828 | 02-Jun-2026 |
Wizzard <wizzardsk@gmail.com> |
Translation update (sk) |
| 1dbc46ac | 27-May-2026 |
Tzipirigu <tzipirigu.radu25683@gmail.com> |
Translation update (ro) |
| 08b5f5eb | 14-May-2026 |
Andreas Gohr <andi@splitbrain.org> |
fix(authad): avoid warning when initAdLdap is called before opts is populated
$this->opts is initialized as an empty array, so the is_array() guard in initAdLdap() passed even when no domain had bee
fix(authad): avoid warning when initAdLdap is called before opts is populated
$this->opts is initialized as an empty array, so the is_array() guard in initAdLdap() passed even when no domain had been loaded yet, producing an "Undefined array key 'domain'" warning on PHP 8 (e.g. from getUserCount() called by the Popularity admin page).
Fixes #4501
show more ...
|
| 73dc0a89 | 06-May-2026 |
Andreas Gohr <andi@splitbrain.org> |
fix(mail): keep '&' intact in mailto links with multiple query params
Move the email-handling helpers (obfuscate, mail_isvalid, mail_quotedprintable_encode, mail_setup) out of the procedural inc/mai
fix(mail): keep '&' intact in mailto links with multiple query params
Move the email-handling helpers (obfuscate, mail_isvalid, mail_quotedprintable_encode, mail_setup) out of the procedural inc/mail.php into a namespaced dokuwiki\MailUtils class plus a new Mailer::configInit(), and add a separate MailUtils::obfuscateUrl() for the mailto-href context.
The xhtml renderer and PluginTrait now build the link label and the href separately: the address half is run through the mailguard obfuscation, the query string is preserved verbatim with only HTML escaping applied. This fixes #1690 — in 'visible' mode the previous code rawurlencoded the entire address+query, turning '?' into '%3F' and breaking multi-parameter mailto links; in all modes the query string is no longer mangled by the [at]/[dot] substitution.
Core call sites (Mailer, auth, LegacyApiCore, common, the xhtml renderer, the parser, the bundled config/styling/usermanager plugins) are migrated to MailUtils directly. The old top-level functions and PREG_PATTERN_VALID_EMAIL constant remain as deprecated shims with rector mappings.
Tests for obfuscate / mail_isvalid / mail_quotedprintable_encode are consolidated into a single _test/tests/MailUtilsTest.php and extended with regression coverage for the multi-parameter, double-escape and URL-shape cases.
Closes #1690 Replaces #1964
show more ...
|
| 8788dbbd | 06-May-2026 |
splitbrain <86426+splitbrain@users.noreply.github.com> |
Rector and PHPCS fixes |
| 560c6061 | 06-May-2026 |
Andreas Gohr <andi@splitbrain.org> |
use new ModeRegistry constant in info plugin
Basically all syntax plugins could now return the constants instead of plain string, but the info plugin is the only syntax plugin we have directly in co
use new ModeRegistry constant in info plugin
Basically all syntax plugins could now return the constants instead of plain string, but the info plugin is the only syntax plugin we have directly in core.
show more ...
|
| 13a62f81 | 04-May-2026 |
Andreas Gohr <andi@splitbrain.org> |
rename syntax flavors 'dokuwiki' / 'markdown' to 'dw' / 'md'
Symmetry with the existing 'dw+md' / 'md+dw' setting values. |
| 59768247 | 02-May-2026 |
Kerem ATA <contact@zoda-service.web.tr> |
Translation update (tr) |
| 50ca5d06 | 29-Apr-2026 |
Schplurtz le Déboulonné <Schplurtz@laposte.net> |
Translation update (fr) |
| 17c6179b | 20-Apr-2026 |
Andreas Gohr <gohr@cosmocode.de> |
add $conf['syntax'] setting and conditional mode loading in ModeRegistry
Introduce a new 'syntax' configuration setting (dokuwiki, markdown, dw+md, md+dw) that controls which parser modes are loaded
add $conf['syntax'] setting and conditional mode loading in ModeRegistry
Introduce a new 'syntax' configuration setting (dokuwiki, markdown, dw+md, md+dw) that controls which parser modes are loaded. Built-in modes are split into always-loaded (no Markdown equivalent), DW-only, and MD-only groups. Refactor getModes() into focused sub-methods for each group.
No Gfm mode classes exist yet, so only 'dokuwiki' is functional. The change is a strict no-op for existing behavior.
show more ...
|