Uploaded image for project: 'XWiki Platform'
  1. XWiki Platform
  2. XWIKI-24778

Loading a document with a context wiki different from the document's wiki silently drops all its xobjects

    XMLWordPrintable

Details

    • Bug
    • Resolution: Unresolved
    • Major
    • None
    • 17.10.12
    • Old Core
    • None
    • Unknown

    Description

      Problem

      XWikiHibernateStore#loadXWikiDoc resolves each xobject's document reference from the wiki in the context instead of from the wiki of the document being loaded, and then silently discards every xobject whose reference does not match the document:

      BaseObject object = it.next();
      ...
      // It seems to search before is case insensitive. And this would break the loading if we get an
      // object which doesn't really belong to this document
      if (!object.getDocumentReference().equals(doc.getDocumentReference())) {
          continue;
      }
      

      The mismatch comes from BaseObject#setName(String), the setter Hibernate calls when hydrating a row from xwikiobjects (the name column is mapped by property access in xwiki.hbm.xml). On a freshly hydrated object the document reference is still null, so the stored local name is resolved with the current mixed document reference resolver, that is against the wiki in the context:

      public void setName(String name)
      {
          DocumentReference reference = getDocumentReference();
      
          if (reference != null) {
              // ... keep the existing wiki, replace space and page ...
          } else {
              reference = getCurrentMixedDocumentReferenceResolver().resolve(name);
          }
          setDocumentReference(reference);
      }
      

      So any call that reaches the store with a context wiki different from the document's wiki gets back a document that exists, has its content, and has no xobjects at all, with nothing logged. XWikiCacheStore then stores that stripped document in the document cache, whose lifetime is unbounded, so it keeps being served until that document is next saved.

      This is not a cache key problem

      An earlier version of this issue blamed XWikiCacheStore#getKey(XWikiDocument, XWikiContext), which normalises the key to the context wiki. That behaviour is intended, it mirrors the Hibernate store, and changing it would break the API. Every XWiki API entry point into the store switches the context wiki to the document's own wiki before touching it, so the normalisation is a no-op and no cross-wiki key collision is reachable through the API:

      • XWiki#getDocument(XWikiDocument, XWikiContext)
      • XWiki#saveDocument(XWikiDocument, String, boolean, boolean, XWikiContext)
      • XWiki#deleteDocument(XWikiDocument, boolean, boolean, XWikiContext)
      • XWiki#exists(DocumentReference, XWikiContext)

      The uid serializer used for the key does include the wiki, so there is no blanket collision either. A search of the code base for callers that reach an XWikiStoreInterface directly without aligning the context found none: the direct getStore().loadXWikiDoc / saveXWikiDoc / deleteXWikiDoc uses outside XWiki are in the attachment stores and in the legacy Package, all inside an already switched save path, and the only getNotCacheStore() user, LazyXWikiDocument, switches the wiki itself.

      The point of this issue is therefore not that such a caller is known to exist, but that when one does the store answers with silent data loss instead of an error.

      User visible consequence

      A page renders as if it had never been configured: its xobjects are invisible, so sheets and administration sections that read them show empty fields, or leak raw Velocity where a property of the missing XClass is dereferenced. Meanwhile REST, Solr backed live tables and direct queries keep returning the correct data, and nothing shows up in the logs. On a multi wiki instance this typically hits configuration pages shipped by an extension that is installed on more than one wiki, since those exist at the same path in several wikis.

      Suggested fix

      loadXWikiDoc should not silently return a document stripped of its xobjects. Either align the context to the document's wiki for the duration of the load, or resolve each xobject's reference against the document being loaded rather than against the context. In addition, the continue that drops an object should at least log a warning, so that a caller which does not align the context produces a diagnosable message instead of an unexplained empty page.

      How it was found

      While investigating the flickering org.xwiki.image.style.test.ui.AllIT$NestedImageStyleIT#imageStyleAdministration test (see XWIKI-24720 for a different, already mitigated flicker of the same test). Its failure rate on the CI went from about 0.2% (June and July 2026) to about 2.8% (since mid August 2026).

      The failure is always NoSuchElementException: *[name='Image.Style.Code.ConfigurationClass_0_defaultStyle'] in ImageStyleAdministrationPage#getDefaultStyle. The archived screenshots show the Image Styles administration section rendered with two literal $escapetool.xml($property.translatedPrettyName) labels and without the "Default Style" select and "Force Default Style" checkbox, while the rest of the section renders normally. Frames extracted from the failure video show the section is already broken on the first visit to the administration, more than 20 seconds before the assertion fails, and that it stays broken across page loads, so it is not a UI or wait race and no page object change can work around it.

      The same rendering was reproduced on a local instance by deleting the Image.Style.Code.ConfigurationClass xobject of Image.Style.Code.Configuration over REST: $configurationDoc.display('defaultStyle', 'edit') then returns an empty string because XWikiDocument#display bails out on pclass == null, and $configurationObj.xWikiClass.get('defaultStyle') returns null, which is what leaks the two raw Velocity labels. A document loaded with a mismatched context wiki is in exactly that state.

      The test uses @WikisSource, which runs it once on the main wiki and once on a subwiki, so Image.Style.Code.Configuration exists at the same path in both wikis. The two parameterized runs fail independently of each other.

      Note that the code path which reaches the store with a mismatched context in that scenario has not been identified, so this issue describes a robustness defect in the store rather than a proven root cause for that test.

      Notes

      • Same family as XWIKI-13632 and XWIKI-24673.
      • XWikiDocument#getId() deliberately excludes the wiki from the Hibernate id as well, so the same page path in two wikis is indistinguishable at the Hibernate identifier level too.

      Attachments

        Issue Links

          Activity

            People

              Unassigned Unassigned
              vmassol Vincent Massol
              Votes:
              0 Vote for this issue
              Watchers:
              0 Start watching this issue

              Dates

                Created:
                Updated: