From f7e98f37db57195d07247b95c30ecd9c2a8b1a64 Mon Sep 17 00:00:00 2001 From: Christoph Schaefer Date: Mon, 22 Jun 2026 16:32:15 +0200 Subject: [PATCH] fix(files): don't expose WebDAV XML-attribute artifacts as DOM attributes genFileInfo() flattens every DAV property and runs camelcase() on each key. Since Nextcloud 33, a file's nc:system-tags property contains elements that carry XML attributes (can-assign, id, user-visible, ...). The WebDAV parser represents those attributes with a leading "@", and camelcase() preserves it, so genFileInfo produced keys such as "@canAssign". When the resulting object is bound via v-bind in Viewer.vue, Vue calls setAttribute("@canAssign", ...), which throws "InvalidCharacterError: Invalid qualified name" on Firefox and Safari (Chrome silently ignores it). The result is that tagged office files cannot be opened in those browsers. Skip the structured system-tags subtree (it is not scalar file metadata) and, as a defensive backstop, drop any camelCased key that still starts with "@", so XML-attribute artifacts never reach the DOM. Ref: https://github.com/nextcloud/richdocuments/issues/5490 Assisted-by: ClaudeCode:Opus-4.8 Signed-off-by: Christoph Schaefer --- src/utils/fileUtils.ts | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/src/utils/fileUtils.ts b/src/utils/fileUtils.ts index 9d6653daf..c1026825f 100644 --- a/src/utils/fileUtils.ts +++ b/src/utils/fileUtils.ts @@ -133,23 +133,41 @@ export function genFileInfo(obj: FileStat): FileInfo { Object.keys(obj).forEach(key => { const data = obj[key] + // Skip structured DAV sub-trees that are not scalar file metadata + // (e.g. nc:system-tags). Flattening them would camelCase the parsed + // XML-attribute keys (prefixed with "@" by the WebDAV parser) into + // invalid attribute names like "@canAssign", which crash v-bind / + // setAttribute on Firefox and Safari. See richdocuments#5490. + if (key === 'system-tags') { + return + } + + const ccKey = camelcase(key) + + // Never expose XML-attribute artifacts: the WebDAV parser prefixes + // element attributes with "@", which camelcase preserves, yielding + // invalid DOM qualified names that throw in setAttribute. + if (ccKey.startsWith('@')) { + return + } + // flatten object if any if (!!data && typeof data === 'object' && !Array.isArray(data)) { Object.assign(fileInfo, genFileInfo(data)) } else { // format key and add it to the fileInfo if (data === 'false') { - fileInfo[camelcase(key)] = false + fileInfo[ccKey] = false } else if (data === 'true') { - fileInfo[camelcase(key)] = true + fileInfo[ccKey] = true } else { // preserve string typed properties as string (FileStat interface in webdav) const stringTypedProperties = ['filename', 'basename', 'owner-id'] if (stringTypedProperties.includes(key)) { - fileInfo[camelcase(key)] = String(data) + fileInfo[ccKey] = String(data) return } - fileInfo[camelcase(key)] = isNumber(data) + fileInfo[ccKey] = isNumber(data) ? Number(data) : data }