Skip to content

v4.6.7

Choose a tag to compare

@mrflos mrflos released this 31 Aug 10:42
· 26 commits to doryphore since this release

YesWiki 4.6.7

This is first of all a security release. 29 advisories are fixed here, and several of the holes need no account at all: SQL injection through the search box and through tag names, protected page content leaking into feeds and listings, and a contact form that would send mail to any address a visitor named. Upgrade.

The other half of the release is restore. YesWiki could make a backup and never put one back. Now it can, from the admin page, from the installer, or over the network from another wiki.

Security

SQL injection

  • newtextsearch and nuagetag built their REGEXP and LIKE clauses out of the search phrase and the tag names. Both escape now, and nuagetag became a proper action class (GHSA-4333-x9p4-8xj7).
  • filtertags put tag names straight into an IN clause (GHSA-p87r-f4x4-8r4g).
  • The ACL condition that AclService appends to a query interpolated the ACL names unescaped (GHSA-6rw5-62xf-q5xp).

Arbitrary file write

  • A CSV import could download a remote "image" and keep whatever extension the URL carried, writing any file it liked into files/. The destination extension is now checked against the allowed image extensions (GHSA-v3gq-c7c6-mxw3).

Cross-site scripting

  • Markdown image syntax accepted javascript: and data: sources. Only relative paths and http(s) get through now (GHSA-7pv9-pwm8-cwwr).
  • Uploaded SVG files skipped sanitising whenever htmlPurifierActivated was off, which was the default. The SVG sanitiser always runs now, and htmlPurifier is on by default (GHSA-67v5-7pfv-pp9m).

Access control and content leaks

  • filtertags counted, and includepages included, pages the visitor is not allowed to read (GHSA-hx4v-hjvg-9p6w).
  • recentchangesrssplus published the body of read-protected pages in the feed (GHSA-89vc-h4r4-h25q).
  • The attachment download handler served files without checking read access on the page holding them (GHSA-7cj2-9pgf-vcw2).
  • The triples API matched the user name with a LIKE pattern, so a crafted name read other people's triples (GHSA-9j7h-ccj2-jxv6).
  • Any key in api_allowed_keys reached API routes reserved for @admins. Group-restricted routes now check real membership (GHSA-v527-6r4j-j2w2).
  • Editing a comment trusted the parent page named in the request instead of the comment being edited, and never checked write access on the comment (GHSA-894w-63wr-8x5r).
  • Posting id_fiche while creating a bazar entry overwrote the entry already using that id (GHSA-4388-phmh-jrw8).
  • The page listings pageindex, pageonlyindex, mychanges and bazarrecordsindex listed pages the visitor cannot read. They filter on read access in the query now.
  • Three jsonp handlers edited pages, deleted pages and posted comments straight from a GET request. They are deleted, not patched (GHSA-hqmg-843g-pp28).

Cross-site request forgery

Five places did their work on a GET, so a link was enough to make an administrator do it. They are POST with a CSRF token now.

  • Restoring a revision, which also accepted a revision id belonging to another page (GHSA-vj5g-974q-7ff3).
  • Deleting tags from admintag, with the ids interpolated into the DELETE (GHSA-5c27-6gcm-hc7x).
  • The file manager operations del, erase, restore and emptytrash (GHSA-7m4h-m7qm-hc32).
  • The forced update button in the backups page (GHSA-jp5c-grgj-624x).
  • pointimage, which also appended posted markers to any page named in the request and ignored its own readonly parameter (GHSA-7547-q56p-h99v).

Server-side request forgery

  • The syndication action fetched whatever URL it was handed (GHSA-jwh5-j4f4-c6xp).
  • External bazar lists and imports followed redirects without checking where they landed (GHSA-gw65-cpc7-hww6).
  • The valeur action fetched an arbitrary URL server-side. It is now ValeurAction and validates the address first (GHSA-6rvf-7pwm-6j44).
  • The same action built a regular expression out of its champ parameter without quoting it (GHSA-pghp-rrp4-q666).

Behind these, SsrfUrlValidator was strengthened and is now shared by every service that follows a URL somebody else chose. It resolves the host once and refuses private, loopback, reserved and link-local addresses, so a DNS rebind cannot slip past between the check and the request.

Accounts and sessions

  • The session id survived login, so a session id planted beforehand kept working as the user who logged in (GHSA-7fvc-v2hp-5pwh).
  • The lost password page said whether an address had an account, and answered faster when it did. It now gives one answer, in constant time (GHSA-892r-45m6-45xc).
  • Password recovery keys never expired and were never cleaned up. They last an hour and maintenance purges them (GHSA-x3xh-4hx3-rgm7).

ActivityPub

  • The inbox went on processing an activity after signature verification failed, and never tied the activity to the verified actor. It answers 401 and 403 now (GHSA-rm6r-grfg-4v78).

Denial of service

  • The markdown link pattern in the wiki formatter could backtrack catastrophically on crafted page content, burning CPU on every render. The pattern is bounded (GHSA-gf57-wg5m-h34q).

Mail

  • The contact handler let an anonymous visitor pick the recipient, which made any wiki a relay. Naming a recipient now requires an account, and read access is checked before a page is mailed (GHSA-36fx-49jj-57rw).

Restoring a backup

  • Restore from the backups admin page. Pick an archive, choose the files, the database or both, and let it run. The work is cut into slices short enough to finish inside whatever time limit the host allows, so a large wiki no longer needs a generous max_execution_time.
  • Restore during installation. The installer offers the archives it finds, renames the tables to the new prefix, and rewrites the links stored in the backup to the new base URL. Moving a wiki to another address is a restore now.
  • Fetch a backup from another wiki. Give the address and an administrator account of a remote YesWiki, and this wiki asks it for a full archive, downloads it, resuming if the transfer breaks, then deletes it there. The remote has to be able to run its backup in the background.
  • SQL dumps are streamed and restored statement by statement, so the database no longer has to fit in memory.
  • A backup and a restore touch the seven tables of this wiki only. Another wiki sharing the same database is left alone, and a dump that YesWiki did not write is refused.
  • Archives record the base URL they were taken from, which is what makes the link rewriting possible.
  • A failed restore no longer leaves an empty wiki. Replacing the database means dropping the tables first, so the content being replaced is kept aside and put back if the new dump does not import.

Bazar

  • New checkcontent action. It walks through entries and reports content that no longer matches its form, with the option to repair it. Two parameters do the boring part: forcevalues takes field=value pairs and pre-fills every empty required field it names, textreplace gives the stand-in value for an empty required text field. It also percent-encodes addresses before testing them, so a URL carrying accents or emoji is no longer called broken.
  • Conditional fields are checked on the server. A field hidden by a condition no longer blocks a save, an import or a content check, and a condition can no longer be bypassed from the browser.
  • Conditions also work on image and file fields.
  • The date pickers keep an event's end after its start.
  • Better CSV model and exports. Imports keep the URLs they are given and no longer time out on large files.
  • Fixed: recurring dates in the calendar, geojson output, searches on != queries, empty queries throwing, tags reported as broken, JSON imports.

Other fixes

  • Removing a user could leave an empty entry in the groups it belonged to.
  • API routes made of several path segments were mis-parsed when they arrived through the wiki parameter.
  • Saving a page whose ACLs hide it from its own author no longer breaks link registration.
  • Advanced facet options were hidden.
  • The opening hours template works with Vue 3, and its form builder label is capitalised properly.
  • Contact accepts a list as mail_receiver again, and escapes the field parameter it takes from the URL.
  • Text search styling, trailing newlines in templates, a regression in the panel action, and the address the login action sends you back to.
  • Installation hashes the first password with password_hash directly.
  • Backup errors say what actually went wrong instead of "no process created".

Under the hood

  • The layout actions are real PHP classes: section, grid, col, label, accordion, panel, tab, tabs, buttondropdown. They share one end-tag check that reports a clear error instead of rendering broken HTML.
  • Every class reads request data through the Symfony request instead of $_POST, $_GET, $_REQUEST and $_COOKIE.
  • Prettier owns JavaScript formatting, the airbnb style is gone, and the tree was reformatted in one pass.
  • deleteAll on the triple store.
  • Dependency bumps: dompurify, mermaid, postcss, js-yaml, nanoid, brace-expansion.

Upgrade notes

  • htmlPurifier is now on by default. The new htmlPurifierSafeIframeRegexp setting says which iframe sources survive it, and its default ~^https://.*~ is permissive on purpose. Tighten it on a public wiki.
  • The jsonp handlers ajaxedit, ajaxdeletepage and ajaxaddcomment are gone. Anything calling them gets a 404.
  • An API key belonging to a user who is not in @admins no longer opens @admins routes.
  • Tag deletion, revision restore, file manager operations and pointimage markers are POST with a CSRF token. A custom theme or template that links to them has to be updated.
  • Password recovery links expire after an hour.
  • The syndication and valeur actions refuse addresses that resolve to a private or loopback host. A wiki that fetched from a neighbour on the same private network needs another way in.
  • Fetching a backup from a remote wiki needs that wiki to archive in the background. On a host where canExec is false, the fetch stops and says so.

Full changelog: v4.6.6...v4.6.7