# Snippet JS template allows attacker to read a user's private snippets

GitLab served a JS embed template for private snippets via /snippets/<id>.js, letting any site read private snippet contents through a cross-origin script tag.

- Payout: $300
- Severity: low
- Program: GitLab
- Credited: jobert
- Disclosed: 2019-03-03
- Write-up quality: 7/10
- Original: https://hackerone.com/reports/348443
- Library page: https://bugbountylibrary.com/r/h1%3A348443
- Tags: domain:web impact:data-leak kind:bounty platform:hackerone sev:low tech:ruby technique:source-review vuln:access-control vuln:idor vuln:info-disclosure

**Target:** Snippet embed JS template endpoint (/snippets/:id.js)
**Root cause:** The embed link was only rendered for public snippets in the view, but the controller did not enforce the public_snippet? check when serving the .js template, so private snippets returned their contents as JavaScript.
**Steps:**
1. Create a private (global) snippet on gitlab.com, note its auto-incrementing ID
2. Host an HTML page with `<script src="https://gitlab.com/snippets/<ID>.js"></script>`
3. Hide the injected `.gitlab-embed-snippets` div via CSS
4. Read the rendered snippet content from the DOM (e.g. `document.getElementById('LC1').innerHTML`) and exfiltrate
5. Enumerate IDs with multiple script tags to brute-force private snippet contents
**How found:** Noticed the embed link is only shown for public snippets in the view, then hypothesized the controller might not enforce the same check and tested appending .js to a private snippet URL.
**Exploitation:** Attacker embeds the victim's private snippet ID as a cross-origin script tag on an attacker-controlled page; the .js template renders the private snippet content into the DOM, which attacker JS reads and exfiltrates. Requires guessing/enumerating the sequential snippet ID.
**Insight:** When a feature is gated in the view layer (public_snippet? check in HAML), test the underlying controller/format variants (.js, .json, .atom) directly — authorization enforced only in templates is bypassable via alternate response formats.
