Repository: xbmc/xbmc
File: xbmc/cores/VideoPlayer/DVDSubtitles/DVDSubtitlesLibass.cpp (C++)

Code before:
  StringUtils::Replace(escapedSystemFontsDir, "'", "&apos;");

  std::string escapedCacheDir = fontconfigCacheDir;
  StringUtils::Replace(escapedCacheDir, "&", "&amp;");
  StringUtils::Replace(escapedCacheDir, "<", "&lt;");
  StringUtils::Replace(escapedCacheDir, ">", "&gt;");
  StringUtils::Replace(escapedCacheDir, "\"", "&quot;");
  StringUtils::Replace(escapedCacheDir, "'", "&apos;");

  std::string fontconfigXml = "<?xml version=\"1.0\"?>\n"
                              "<!DOCTYPE fontconfig SYSTEM \"fonts.dtd\">\n"
                              "<fontconfig>\n"
                              "  <dir>" +
                              escapedFontsDir +
                              "</dir>\n"
                              "  <dir>" +
                              escapedSystemFontsDir +
                              "</dir>\n"
                              "  <dir>/system/fonts</dir>\n"
                              "  <dir>/product/fonts</dir>\n"
                              "  <dir>/system_ext/fonts</dir>\n"
                              "  <dir>/vendor/fonts</dir>\n"
                              "  <cachedir>" +
                              escapedCacheDir +
                              "</cachedir>\n"
                              "</fontconfig>\n";

  // Write fontconfig configuration to temp directory (only if it doesn't exist)
  fontconfigPath = CSpecialProtocol::TranslatePath("special://temp/fonts.conf");

  if (!XFILE::CFile::Exists(fontconfigPath))
  {
    XFILE::CFile fontconfigFile;
    if (fontconfigFile.OpenForWrite(fontconfigPath, true))
    {
      fontconfigFile.Write(fontconfigXml.c_str(), fontconfigXml.length());
      fontconfigFile.Close();
      fontconfigConfig = fontconfigPath.c_str();
      CLog::Log(LOGINFO, "CDVDSubtitlesLibass: Created fontconfig configuration at {}",
                fontconfigPath);
    }
    else
    {
      CLog::Log(LOGERROR, "CDVDSubtitlesLibass: Failed to create fontconfig configuration, falling "
                          "back to AUTODETECT");
      fontProvider = ASS_FONTPROVIDER_AUTODETECT;
      fontconfigConfig = nullptr;
    }
  }
  else
  { 

Review comment (security):
all XML code should be created by using the appropriate XML library and so escaping should not required (and in any case, an escape function should not be implemented here)

Code after:
    tinyxml2::XMLElement* eRoot = xmlDoc.NewElement("fontconfig");
    xmlDoc.InsertEndChild(eRoot);

    auto addRootChildText = [&](const char* name, const std::string& value)
    {
      tinyxml2::XMLElement* elem = xmlDoc.NewElement(name);
      elem->SetText(value.c_str());
      eRoot->InsertEndChild(elem);
    };

    // Add Android system font directories
    // Note: USER and SYSTEM dirs are handled by ass_set_fonts_dir below
    const std::vector<std::string> fontDirs = {
        "/system/fonts", // AOSP standard
        "/product/fonts", // AOSP vendor-specific
        "/system_ext/fonts" // OEM-specific (may not exist on all devices)
    };

    for (const auto& dir : fontDirs)
    {
      if (XFILE::CDirectory::Exists(dir))
        addRootChildText("dir", dir);
    }

    addRootChildText("cachedir", fontconfigCacheDir);

    if (xmlDoc.SaveFile(fontconfigPath))
    {
      fontconfigConfig = fontconfigPath.c_str();
      CLog::Log(LOGINFO, "CDVDSubtitlesLibass: Created fontconfig configuration at {}",
                fontconfigPath);
    }
    else
    {
      CLog::LogF(LOGERROR, "Failed to create fontconfig configuration, falling back to "
                           "ASS_FONTPROVIDER_AUTODETECT");
      fontProvider = ASS_FONTPROVIDER_AUTODETECT;
      fontconfigConfig = nullptr;
    }
  }
  else
  {
    fontconfigConfig = fontconfigPath.c_str();
  }
#endif

  // Set additional fonts directory for libass to scan. This provides font lookup
  // on platforms without a font provider, and supplements fontconfig on platforms that use it.
  ass_set_fonts_dir(m_library, CSpecialProtocol::TranslatePath(FONT::FONTPATH::USER).c_str());

  // Load additional fonts into Libass memory