October 28, 2026
How a Forgotten Legacy JS File Landed Me My First Bug Bounty
Why you should never ignore legacy.js files during recon

By A. M. Alogo Situmorang
4 min read
Why you should never ignore legacy.js files during recon
Every bug hunter remembers their first valid bounty. Before that first payout, you spend hours staring at terminal screens, second-guessing your recon, and wondering if every finding is just a false positive or a duplicate.
In February 2026, I finally broke the seal. It wasn't a flashy remote code execution (RCE) or a complex account takeover. Instead, it came down to bug bounty basics: checking an open cloud storage bucket and digging through an old, forgotten JavaScript file.
Here is the story of how I found hardcoded environment variables in Vidio's Smart TV application, how my "High" severity report got downgraded to "Informational," and why the security team still rewarded me **Rp ….**for it.
The Recon: An Open Bucket and a "Legacy" Clue
While doing recon on Vidio.com, I stumbled across a subdomain used to host static assets for their Smart TV application: ......vidio.com.
When I opened the root URL in my browser, I was greeted by that classic XML tree every bug hunter loves to see Google Cloud Storage (GCS) bucket listing was enabled.
Instead of blindly running heavy automated scanners, I scrolled through the XML file list manually to understand what was hosted there. Among the usual static assets, one filename immediately caught my eye:
assets/assets/.....js
Whenever you see the word legacy, old, backup, or dev in a filename, pay attention. Developers often tighten security on their latest production builds, but old build artifacts left sitting in a storage bucket frequently contain mistakes from earlier versions.
Digging Into the Code
I downloaded the JavaScript bundle to my terminal and decided to check if any environment variables had been baked into the client-side code during the build process.
If you've ever worked with React (specifically Create React App), you know that any environment variable prefixed with REACT_APP_ gets embedded directly into the final static JavaScript bundle at build time. If a developer accidentally puts a sensitive key in a .env file with that prefix, it goes straight to the public.
I ran a quick curl and grep one-liner to see what was hiding inside:
Bash
curl -s "https:.....js" -o vidio_leaked.js
grep -o 'REACT_APP_[A-Z_]*:"[^"]*"' vidio_leaked.jscurl -s "https:.....js" -o vidio_leaked.js
grep -o 'REACT_APP_[A-Z_]*:"[^"]*"' vidio_leaked.jsSeconds later, my terminal spat out exactly what I was hoping for:
REACT_APP_FLAVOUR:"production"
REACT_APP_SENTRY_DSN:"https://7....."
REACT_APP_HMAC_SECRET:"V1..."
REACT_APP_MUX_API_KEY:"v..."
REACT_APP_SK_ID:"...."
REACT_APP_SK:"KS...."
REACT_APP_RELEASE_VERSION:""
REACT_APP_SHORT_HASH:""REACT_APP_FLAVOUR:"production"
REACT_APP_SENTRY_DSN:"https://7....."
REACT_APP_HMAC_SECRET:"V1..."
REACT_APP_MUX_API_KEY:"v..."
REACT_APP_SK_ID:"...."
REACT_APP_SK:"KS...."
REACT_APP_RELEASE_VERSION:""
REACT_APP_SHORT_HASH:""Seeing REACT_APP_HMAC_SECRET and a 32-byte Base64 string inside REACT_APP_SK (Secret Key) in a production build felt like hitting gold.
Writing the PoC and Submitting the Report
To make life as easy as possible for the triage team, I wrote a clean Node.js Proof-of-Concept (PoC) script that automatically fetched the JS bundle and parsed out the exposed keys using Regex:
const https = require('https');
const VULNERABLE_URL = 'https://.....js';
https.get(VULNERABLE_URL, (res) => {
let data = '';
res.on('data', chunk => data += chunk);
res.on('end', () => {
const secrets = {
'HMAC_SECRET': data.match(/REACT_APP_HMAC_SECRET:"([^"]+)"/),
'SK (Secret Key)': data.match(/REACT_APP_SK:"([^"]+)"/),
'SK_ID': data.match(/REACT_APP_SK_ID:"([^"]+)"/),
'MUX_API_KEY': data.match(/REACT_APP_MUX_API_KEY:"([^"]+)"/),
'SENTRY_DSN': data.match(/REACT_APP_SENTRY_DSN:"([^"]+)"/)
};
for (const [name, match] of Object.entries(secrets)) {
if (match) {
console.log(`${name}: ${match[1]}`);
}
}
});
}).on('error', console.error);const https = require('https');
const VULNERABLE_URL = 'https://.....js';
https.get(VULNERABLE_URL, (res) => {
let data = '';
res.on('data', chunk => data += chunk);
res.on('end', () => {
const secrets = {
'HMAC_SECRET': data.match(/REACT_APP_HMAC_SECRET:"([^"]+)"/),
'SK (Secret Key)': data.match(/REACT_APP_SK:"([^"]+)"/),
'SK_ID': data.match(/REACT_APP_SK_ID:"([^"]+)"/),
'MUX_API_KEY': data.match(/REACT_APP_MUX_API_KEY:"([^"]+)"/),
'SENTRY_DSN': data.match(/REACT_APP_SENTRY_DSN:"([^"]+)"/)
};
for (const [name, match] of Object.entries(secrets)) {
if (match) {
console.log(`${name}: ${match[1]}`);
}
}
});
}).on('error', console.error);In my report, I rated the severity as High. My reasoning at the time was that if the HMAC_SECRET and SK were used to sign API requests or tokens without strict server-side checks, an attacker could potentially forge signatures or manipulate requests.
On February 8, 2026, I hit send on my email to security@vidio.com and waited.
Expectation vs. Reality: The Triage
Two days later, on February 10, the Vidio Security team replied confirming they were validating the report.
Then, on February 19, the final verdict arrived. And honestly, it taught me a massive lesson about how real-world production architectures work:
- It was an unused legacy artifact: The JS bundle I found was from an older build that was no longer used in their active deployment. In their current production system, those variables were no longer exposed the same way.
- Defense-in-depth on the server side: Even with those keys exposed, Vidio had additional server-side validation layers. The keys themselves were considered public/semi-public client identifiers by design (like the Mux analytics key and Sentry DSN) and couldn't be used on their own to compromise user data or take over accounts.
At that point in the email, I thought, "Well, there goes my bounty. It's going to be closed as Informative with a pat on the back."
Then I read the last two paragraphs:
"However, this report still helped us identify legacy files that had not yet been cleaned up in our storage bucket. We are currently cleaning up old assets and rotating the keys as a preventive measure.
Referring to our reward policy, we categorize this report as Informational but Useful, and as a token of appreciation for your responsible disclosure, we would like to award you a bounty of Rp ….."
Key Takeaways for Fellow Beginner Hunters
Getting that first reward wasn't just about the money, it was proof that the methodology works. Looking back at this report, here are three things I learned:
- Don't sleep on legacy files: Cloud buckets often act like digital attics. Old deployments get left behind all the time. Even if a bug doesn't affect the latest v3.0 app, finding leftover v1.0 files can help a company clean up their attack surface.
- Client-side keys ≠ Instant Critical: Just because a variable is named
SECRETinside a.jsfile doesn't automatically mean it gives you keys to the kingdom. Always try to verify what a key actually does, though if you aren't sure and want to stay safe within scope rules, reporting it cleanly as a potential exposure is still the right call. - Write a clean, respectful report: I genuinely believe that structuring the report clearly, providing 3 different reproduction methods (
Browser,curl, andBucket Listing) plus a ready-to-run Node.js script made the triage team's job effortless and contributed to them rewarding an "Informational" finding.
Huge shoutout to the Vidio Security Team for the fast response, transparent explanation, and for running a beginner-friendly bug bounty program that rewards hunters even for hygiene and defense-in-depth findings.