Ampersand in groups and subgroups name

Summary

Groups and subgroups created before GitLab 16.11 that contain ampersands in their names now display the HTML entity code & instead of rendering the ampersand character & properly.

Steps to reproduce

  1. Have a group created before GitLab 16.11 with an ampersand in the name (e.g., "R&D Team")
  2. Upgrade GitLab to version 17.5 or later
  3. View the group name in the GitLab UI
  4. Observe that & displays as &

Note: New groups cannot be created with ampersands - validation now prevents this. This issue only affects legacy groups.

What is the current bug behavior?

Group names containing ampersands display the HTML entity & instead of the actual & character:

image

This is a cosmetic issue with no functional impact - the groups work correctly, but the display is incorrect.

What is the expected correct behavior?

Legacy group names containing ampersands should display the & character properly, just as they did in GitLab 16.11 and earlier versions.

Relevant logs and/or screenshots

See screenshot above showing & displaying literally instead of being rendered as &.

Implementation Guide for Contributors

Problem: HTML entities in group names are being double-escaped or not decoded properly during display.

Files to investigate:

  • app/views/groups/_group.html.haml (group display partials)
  • app/helpers/groups_helper.rb (name rendering methods)
  • Vue components displaying group names in app/assets/javascripts/groups/
  • API serializers returning group names

Likely causes:

  • Group name is already HTML-escaped in the database (stored as &)
  • Display code is applying additional escaping via h() or html_escape
  • Double-escaping: & → & (in DB) → & (displayed as &)

Changes needed:

  • Identify where group.name rendering applies HTML escaping
  • Check if names in database are already escaped (query a legacy group)
  • If already escaped in DB: Use .html_safe carefully or decode before display
  • If not escaped in DB: Ensure proper single-pass escaping
  • Consider using sanitize() with a whitelist (likely empty for names)

Critical security consideration:

  • Must maintain XSS protection while fixing display
  • Test with malicious input: <script>alert(1)</script>, <img src=x onerror=alert(1)>
  • Ensure fix doesn't enable script injection
  • Consult security team before merging if uncertain

Testing approach:

  1. Create test fixtures with groups named: R&D, Sales & Marketing, Q&A
  2. Verify correct display in: group headers, breadcrumbs, dropdowns, admin lists, API responses
  3. Test XSS protection still works with dangerous characters
  4. Test both Haml templates and Vue.js components
  5. Verify behavior on both legacy groups and newly created groups (if validation is removed)

Areas to test:

  • Group navigation sidebar
  • Group settings pages
  • Breadcrumb navigation
  • Admin area group lists
  • API endpoints returning group data
  • Group selection dropdowns

Possible fixes

This is likely a double-escaping issue introduced during an upgrade. Check where group names are being HTML-escaped during rendering and ensure it only happens once. The name may already be escaped in the database for legacy groups.

Edited by Christina Lohr