[Bug]: DAV Forbidden/InvalidPath responses break on "&" in the message (DOMDocument::createElementNS(): unterminated entity reference)
メンテナーはふだん 1 日以内に返信
評価
調査の方向性
問題は apps/dav/lib/Connector/Sabre/Exception/Forbidden.php の46行目付近にある serialize() にあります。ここでは createElementNS の値引数としてメッセージを o:reason に渡しています。同じパターンが InvalidPath.php の58行目付近にもあり、Server.php の247行目が s:message の正しいエスケープ方法を示しています。まず issue の短い PHP スニペットで再現させ、その後両方のクラスを変更して代わりにテキストノードを追加してください。o:reason 要素にエスケープ済みのメッセージが入り、PHP の警告が出ず、両方の例外クラスをユニットテストがカバーしていれば完了です。
索引モデルが issue の本文から書いたものです。
説明
⚠️ This issue respects the following points: ⚠️
- This is not a troubleshooting question, general support matter, or webserver/proxy problem, but likely a bug (if unsure, ask the Community Help Forum).
- This issue is not already reported on Github OR solved at the Community Help Forum (I've searched!).
- I'm using a maintained major version of Nextcloud Server and tested against the latest patch level. (Supported major versions and current patch levels).
- I agree to follow Nextcloud's Code of Conduct.
- I've tried my best to provide clear reproduction steps that someone unfamiliar with this bug could use to reproduce it.
Bug description
When a DAV request fails with OCA\DAV\Connector\Sabre\Exception\Forbidden and the exception message contains an & (e.g. a folder named Muster GmbH & Co. KG), the XML error response is not built correctly. With libxml2 2.9.14 (Debian-based PHP images) PHP logs a warning and <o:reason> is sent empty. With libxml2 2.13.9 (Alpine) there is no warning, but the & is silently dropped from the message.
Cause: Forbidden::serialize() passes the message as the value argument of DOMDocument::createElementNS():
$error = $errorNode->ownerDocument->createElementNS('o:', 'o:reason', $this->getMessage());
PHP does not escape & in that argument and treats it as the start of an entity reference (documented in the PHP manual, see also PHP bug #31191, "Won't fix"). The same pattern was reported for ownCloud's bundled Sabre back in ownCloud 4.5.5: owncloud/core#1223. apps/dav/lib/Connector/Sabre/Exception/InvalidPath.php#L58 has the same pattern. apps/dav/lib/Connector/Sabre/Server.php#L247 escapes the same message correctly for <s:message>, so only these two exception classes are affected.
Seen in practice with a desktop client that tried to move a received share into another share. The message comes from lib/private/Files/View.php#L923 (Moving a storage (%s) into a share (%s) is not allowed).
Suggested fix
Create the element empty and append a text node (escapes automatically), in both Forbidden.php and InvalidPath.php. The dav app already does this in apps/dav/lib/Exception/UidConflict.php#L76-L77. Verified with the PHP reproduction above on both libxml2 versions, and end to end on the instance above (both files patched, same steps). <o:reason> then carries the escaped message and no warning is logged:
<o:reason xmlns:o="o:">Moving a storage (Muster GmbH & Co. KG) into a share (Projekt) is not allowed</o:reason>
A PR with unit tests for both classes follows:
$error = $errorNode->ownerDocument->createElementNS('o:', 'o:reason');
$error->appendChild($errorNode->ownerDocument->createTextNode($this->getMessage()));
Side note: when the root of a received share is moved, View::rename() passes the parent folder's mount as $sourceMount to validateMountMove(), so the message says "Moving a storage" although a share is being moved.
Environment
- Nextcloud Server: 35.0.2 (Docker, Apache image)
- Database: MariaDB 11.8
- PHP: 8.5.12, libxml2 2.9.14
- Bug also present in current master (checked at 0554acf).
Steps to reproduce
Minimal reproduction in plain PHP, same calls as Forbidden::serialize():
<?php
$msg = 'Moving a storage (Muster GmbH & Co. KG) into a share (Projekt) is not allowed';
$DOM = new DOMDocument('1.0', 'utf-8');
$errorNode = $DOM->createElementNS('DAV:', 'd:error');
$DOM->appendChild($errorNode);
// current code
$old = $errorNode->ownerDocument->createElementNS('o:', 'o:reason', $msg);
echo $DOM->saveXML($old), PHP_EOL;
// proposed fix
$new = $errorNode->ownerDocument->createElementNS('o:', 'o:reason');
$new->appendChild($errorNode->ownerDocument->createTextNode($msg));
echo $DOM->saveXML($new), PHP_EOL;
Output with php:8.5-cli (PHP 8.5.12, libxml2 2.9.14):
Warning: DOMDocument::createElementNS(): unterminated entity reference Co. KG) into a share (Projekt) is not allowed in /d.php on line 8
<o:reason xmlns:o="o:"/>
<o:reason xmlns:o="o:">Moving a storage (Muster GmbH & Co. KG) into a share (Projekt) is not allowed</o:reason>
Output with php:8.5-cli-alpine (PHP 8.5.12, libxml2 2.13.9), no warning:
<o:reason xmlns:o="o:">Moving a storage (Muster GmbH Co. KG) into a share (Projekt) is not allowed</o:reason>
<o:reason xmlns:o="o:">Moving a storage (Muster GmbH & Co. KG) into a share (Projekt) is not allowed</o:reason>
End to end, verified on a Nextcloud 35.0.2 instance (PHP 8.5.12, libxml2 2.9.14):
- As
bugtest-alice, create the foldersMuster GmbH & Co. KGandProjektand share both withbugtest-bob. - As
bugtest-bob, moveMuster GmbH & Co. KGintoProjektvia WebDAV:
curl -u bugtest-bob:PASS -X MOVE "https://HOST/remote.php/dav/files/bugtest-bob/Muster%20GmbH%20%26%20Co.%20KG" \
-H "Destination: https://HOST/remote.php/dav/files/bugtest-bob/Projekt/Muster%20GmbH%20%26%20Co.%20KG"
- Check the response body and
nextcloud.log.
Expected behavior
HTTP 403, with the full message in <o:reason>, and no PHP warning.
Actual behavior
HTTP 403. Note that <s:message> (built in Server.php) is escaped correctly, while <o:reason> in the same response is empty:
<?xml version="1.0" encoding="utf-8"?>
<d:error xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns" xmlns:o="http://owncloud.org/ns">
<s:exception>OCA\DAV\Connector\Sabre\Exception\Forbidden</s:exception>
<s:message>Moving a storage (Muster GmbH & Co. KG) into a share (Projekt) is not allowed</s:message>
<o:retry xmlns:o="o:">false</o:retry>
<o:reason xmlns:o="o:"/>
</d:error>
The log gets this warning (from the reproduction run; first seen in production with the desktop client):
{
"reqId": "xp5UETOUhmbX1eFxZSwi",
"level": 2,
"time": "2026-11-02T10:14:09+00:00",
"remoteAddr": "fd00:beef:1337::1",
"user": "bugtest-bob",
"app": "PHP",
"method": "MOVE",
"url": "/remote.php/dav/files/bugtest-bob/Muster%20GmbH%20%26%20Co.%20KG",
"scriptName": "/remote.php",
"message": "DOMDocument::createElementNS(): unterminated entity reference Co. KG) into a share (Projekt) is not allowed at /var/www/html/apps/dav/lib/Connector/Sabre/Exception/Forbidden.php#46",
"userAgent": "curl/8.14.1",
"version": "35.0.2.2",
"data": {
"app": "PHP"
}
}
message in <o:reason>, and no PHP warning.
Nextcloud Server version
35
Operating system
Debian/Ubuntu
PHP engine version
PHP 8.5
Web server
Apache (supported)
Database engine version
MariaDB
Is this bug present after an update or on a fresh install?
Fresh Nextcloud Server install
Are you using the Nextcloud Server Encryption module?
Encryption is Disabled
What user-backends are you using?
- Default user-backend (database)
- LDAP/ Active Directory
- SSO - SAML
- Other
Configuration report
{
"system": {
"htaccess.RewriteBase": "\/",
"memcache.local": "\\OC\\Memcache\\APCu",
"apps_paths": [
{
"path": "\/var\/www\/html\/apps",
"url": "\/apps",
"writable": false
},
{
"path": "\/var\/www\/html\/custom_apps",
"url": "\/custom_apps",
"writable": true
}
],
"instanceid": "***REMOVED SENSITIVE VALUE***",
"passwordsalt": "***REMOVED SENSITIVE VALUE***",
"secret": "***REMOVED SENSITIVE VALUE***",
"trusted_domains": [
"example.com"
],
"trusted_proxies": "***REMOVED SENSITIVE VALUE***",
"datadirectory": "***REMOVED SENSITIVE VALUE***",
"dbtype": "mysql",
"version": "35.0.2.2",
"overwrite.cli.url": "https:\/\/example.com",
"overwriteprotocol": "https",
"dbname": "***REMOVED SENSITIVE VALUE***",
"dbhost": "***REMOVED SENSITIVE VALUE***",
"dbport": "",
"dbtableprefix": "oc_",
"default_phone_region": "DE",
"mysql.utf8mb4": true,
"dbuser": "***REMOVED SENSITIVE VALUE***",
"dbpassword": "***REMOVED SENSITIVE VALUE***",
"installed": true,
"mail_from_address": "***REMOVED SENSITIVE VALUE***",
"mail_smtpmode": "smtp",
"mail_sendmailmode": "smtp",
"mail_domain": "***REMOVED SENSITIVE VALUE***",
"mail_smtpsecure": "tls",
"mail_smtphost": "***REMOVED SENSITIVE VALUE***",
"mail_smtpauthtype": "LOGIN",
"mail_smtpauth": 1,
"mail_smtpname": "***REMOVED SENSITIVE VALUE***",
"mail_smtppassword": "***REMOVED SENSITIVE VALUE***",
"mail_smtpport": "587",
"loglevel": 2,
"maintenance": false,
"memcache.distributed": "\\OC\\Memcache\\Redis",
"memcache.locking": "\\OC\\Memcache\\Redis",
"redis": {
"host": "***REMOVED SENSITIVE VALUE***",
"password": "***REMOVED SENSITIVE VALUE***",
"port": 6379
},
"theme": "",
"maintenance_window_start": 3,
"upgrade.disable-web": true,
"forbidden_filename_basenames": [
"con",
"prn",
"aux",
"nul",
"com0",
"com1",
"com2",
"com3",
"com4",
"com5",
"com6",
"com7",
"com8",
"com9",
"com\u00b9",
"com\u00b2",
"com\u00b3",
"lpt0",
"lpt1",
"lpt2",
"lpt3",
"lpt4",
"lpt5",
"lpt6",
"lpt7",
"lpt8",
"lpt9",
"lpt\u00b9",
"lpt\u00b2",
"lpt\u00b3"
],
"forbidden_filename_characters": [
"<",
">",
":",
"\"",
"|",
"?",
"*",
"\\",
"\/"
],
"forbidden_filename_extensions": [
" ",
".",
".filepart",
".part"
],
"overwritehost": "example.com"
}
}
List of activated Apps
Enabled:
- activity: 8.0.0
- admin_audit: 2.0.0
- app_api: 35.0.0
- appstore: 2.0.0
- bruteforcesettings: 8.0.0
- calendar: 6.6.3
- circles: 35.0.0
- cloud_federation_api: 2.0.0
- comments: 2.0.0
- contactsinteraction: 2.0.0
- dashboard: 8.0.0
- dav: 2.0.0
- federatedfilesharing: 2.0.0
- federation: 2.0.0
- files: 3.0.0
- files_downloadlimit: 5.3.0
- files_fulltextsearch: 35.0.0
- files_lock: 35.0.1
- files_pdfviewer: 8.0.0
- files_reminders: 2.0.0
- files_sharing: 2.0.0
- files_trashbin: 2.0.0
- files_versions: 2.0.0
- firstrunwizard: 8.0.0
- fulltextsearch: 35.0.0
- fulltextsearch_elasticsearch: 35.0.1
- logreader: 8.0.0
- lookup_server_connector: 2.0.0
- nextcloud_announcements: 7.0.0
- notifications: 8.0.1
- oauth2: 2.0.0
- office: 1.1.0
- password_policy: 7.0.0
- photos: 8.0.0
- privacy: 7.0.0
- profile: 2.0.0
- provisioning_api: 2.0.0
- recommendations: 8.0.0
- related_resources: 6.0.0
- serverinfo: 7.0.0
- settings: 2.0.0
- sharebymail: 2.0.0
- sharing: 1.0.4
- spreed: 25.0.6
- support: 7.0.0
- survey_client: 7.0.0
- suspicious_login: 13.0.0
- systemtags: 2.0.0
- text: 9.0.1
- theming: 3.0.0
- twofactor_backupcodes: 2.0.0
- twofactor_totp: 17.1.0
- updatenotification: 2.0.0
- user_status: 2.0.0
- viewer: 8.0.0
- weather_status: 2.0.0
- webhook_listeners: 2.0.0
- workflowengine: 3.0.0
Disabled:
- cfg_share_links: 7.0.1 (installed 7.0.1)
- encryption: 3.0.1
- files_downloadactivity: 1.18.1 (installed 1.18.1)
- files_external: 2.0.0
- files_fulltextsearch_tesseract: 32.0.0 (installed 32.0.0)
- twofactor_nextcloud_notification: 9.0.0
- user_ldap: 2.0.0
- whiteboard: 2.1.0 (installed 2.1.0)
Nextcloud Signing status
No errors have been found.
Nextcloud Logs
{
"reqId": "xp5UETOUhmbX1eFxZSwi",
"level": 2,
"time": "2026-11-02T10:14:09+00:00",
"remoteAddr": "fd00:beef:1337::1",
"user": "bugtest-bob",
"app": "PHP",
"method": "MOVE",
"url": "/remote.php/dav/files/bugtest-bob/Muster%20GmbH%20%26%20Co.%20KG",
"scriptName": "/remote.php",
"message": "DOMDocument::createElementNS(): unterminated entity reference Co. KG) into a share (Projekt) is not allowed at /var/www/html/apps/dav/lib/Connector/Sabre/Exception/Forbidden.php#46",
"userAgent": "curl/8.14.1",
"version": "35.0.2.2",
"data": {
"app": "PHP"
}
}
- 主要言語
- PHP
- スター
- 37.4k
- フォーク
- 5.3k
- 平均マージ
- 2日 7時間
- マージ済み PR(30日)
- 744
環境構築
このプロジェクトの開発コンテナを、あなたの GitHub アカウントでブラウザ上に起動します。
- Dockerfile・Docker Compose ファイルなし
- プルリクエストのテンプレートあり
- コントリビューションガイドを読む
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
nextcloud/server のほかの issue
-
34-feedback
難易度 2/5 1〜3時間 初心者へのやさしさ 77/100
メンテナーはふだん 1 日以内に返信
-
[Bug]: Detection::detectString() / detectContent() never use finfo – missing use finfo; (regression from #54077)対応中かも @lazerg が 1 日前に担当しました。 オープン0. Needs triage 35-feedback bug
難易度 1/5 1時間未満 初心者へのやさしさ 62/100
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
メンテナーはふだん 1 日以内に返信
-
[Bug]: Files folder-tree API eagerly lists the children of every subfolder at the last requested depth, then discards them (N extra directory listings per request)対応中かも @lazerg が 5 日前に担当しました。 オープン0. Needs triage 34-feedback bug
難易度 2/5 1〜3時間 初心者へのやさしさ 77/100
nextcloud/server#65904 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
[Bug]: Contradictory notifications when cancelling welcome email confirmation対応中かも @Jashan32 が 6 日前に担当しました。 オープン0. Needs triage 36-feedback bug
難易度 2/5 1〜3時間 初心者へのやさしさ 67/100
メンテナーはふだん 1 日以内に返信
nextcloud/server の issue をすべて見る
似ている issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
-
Author mapping step: "create new user" and "assign to existing user" fields have no spacing between them対応中かも @umeshnevase が今日担当しました。 オープン
難易度 1/5 1〜3時間 初心者へのやさしさ 78/100
WordPress/wordpress-importer#275 ·
-
security/openvpn-legacy: read-only user can stop a running VPN対応中かも @besendorf が今日担当しました。 オープンincomplete
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
opnsense/plugins#5887 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
jira-created
難易度 2/5 1〜3時間 初心者へのやさしさ 77/100
-
No Code Attached Yet
難易度 1/5 1時間未満 初心者へのやさしさ 90/100
joomla/joomla-cms#48786 ·
メンテナーはふだん 1 日以内に返信