HTML injection in code navigation leads to ATO
HackerOne report #3297413 by joaxcar on 2025-08-13, assigned to @katwu:
Report | Attachments | How To Reproduce
Report
Summary
Hi I saw that there is a patch for #3247096 in version 18.2.2. See MR here https://gitlab.com/gitlab-org/security/gitlab/-/merge_requests/5203 (private)
and commit here: 61f1c480
The fix only sanitizes the data using the Gitlab default configuration for DOMPurify, which I have proven before can be bypassed. This fix will prevent full XSS that was previously possible but still allow for ATO using script gadgets through data attributes
See my comments in #3247096 on how the root cause for the issue here is that you take the textContent of one element and put this into innerHTML of another element
wrapNodes(elm.textContent ...)
...
wrapper.innerHTML = wrapSpacesWithSpans(text);
This is problematic as textContent will convert HTML entities to HTML. You should probably just do elm.innerHTML in the first step as well, this will keep all entities as entities
(NOTE see the previous report for technical info on how this could be abused without the "override" in the POC, I can copy paste it over here if needed, but I think this keeps it cleaner)
Steps to reproduce
see #3247096 for details on how the full flow works and why we need to use an override in devtools. This is just to be able to prove the point here
Do this in Chrome!
- Import this project
- Go to the project and trigger a new pipeline manually
- Go to the project and click on the
xss.pyfile. - now open devtools and go to the
Networktab - Refresh the page
- You will see a call to
*something*....?file_type=lsifright click this call and pick Overwrite Content - There will be a small pop-up asking you where to store the Chrome override files, pick any directory
- Now in the small editor that opens, delete all text that is there and replace it with
[{"start_line":0,"start_char":1,"definition_path":"hej.py#L5","hover":[{"value":""}]}]
Click ctrl-s to save
9. Refresh the page again (with devtools still open)
The string in the Python file should now disappear and be replaced by a big gray square.
10.Click the gray square
11. A new page will open, wait for about 15 seconds for the page to close again
12. Now click the gray square again, nothing will happen
13. Go to https://gitlab.com/-/profile/emails and see that you have a new email on your account.
14. Remove the email so that others can test it
<video redacted>
Impact
HTML injection leads to full ATO
What is the current bug behavior?
The fix use sanatize from DOMPurify which can still be bypassed in some situations
What is the expected correct behavior?
There is point in using textContent an the text should never contain HTML. Thus its better to use innerHTML to also get the text out of the element. This will prevent all injections
Output of checks
This bug happens on GitLab.com
Impact
HTML injection leads to full ATO
Attachments
Warning: Attachments received through HackerOne, please exercise caution!
- xss_bypas.tar.gz
- <redacted>
How To Reproduce
Please add reproducibility information to this section:
3.