July 18, 2026
It Worked, Then It Didn’t, Then I Understood Why: A Bug Bounty Story on Authorization Bypass
Hello seniors! I am Atharv Chawan, a cybersecurity enthusiast and bug bounty hunter. Today, I want to share a wild ride of a bug hunt on a…

By Atharv Chawan
5 min read
Hello seniors! I am Atharv Chawan, a cybersecurity enthusiast and bug bounty hunter. Today, I want to share a wild ride of a bug hunt on a popular platform where users generate frontend web applications dynamically through an AI assistant.
Every bug hunt starts the same way you're not looking for this bug, you're just poking at the flow and something feels slightly off. This is the story of how a routine "let me understand how this feature works" session turned into a full server-side cache authorization bypass.
Let's dive right in.
Phase 1: Mapping the Core Chat Flow
Every good bug hunt starts with mapping the application logic under the DevTools network tab. I started chatting with the AI assistant to see exactly how it processes a website build from start to finish. I observed three distinct sequences:
- The Prompt: The user inputs text instructing the AI to build a specific UI framework.
- The Generation Process: The engine processes the request and streams the output components.
- The Snapshot Upload: Once the website creation completes, the client automatically dispatches a final
POSTrequest to freeze the state.
This third request was where I found my very first clue. The application automatically captures a complete point-in-time snapshot of the generated source code output [ result website ], uploads it to the backend server, and maps it to a unique, tracking reference token called a blockId.
Visiting that returned snapshot_url directly loaded the generated website layout. The architecture was clear: to fetch any specific historical generation, the application expects a GET request matching that specific blockId.
Phase 2: The "Holy Fuck" Moment and the 15-Minute Mystery
I immediately began testing security hypotheses. The project was set to Public by default, allowing anyone to pull the data. What happens if the owner turns it private?
- While the project was Public, I simulated an attacker perspective by launching an unauthenticated browser window, viewing the frontend DOM source code, and scraping the active
blockId. - I then went back to the owner's account and switched the privacy status to Private.
- Returning to the attacker window, I queried the snapshot proxy endpoint using the stolen
blockId.
The result? The private project loaded completely unauthenticated. I literally smiled hard right there at my desk and thought to myself: Holy fuck, I found a BAC (Broken Access Control).
The Twist
Elated with the finding, I decided to take a quick 15-minute break to grab lunch. When I sat back down at my computer to document the bug, I hit refresh on the attacker window to grab screenshots.
It came up completely blank. The exploit no longer worked.
I instantly tensed up. What changed in those 15 minutes? Did the developers push a hotfix right as I ate lunch?
To troubleshoot, I started a fresh chat session and generated another layout. This time, I closely tracked the blockId lifecycle. That's when I uncovered the true behavior of the platform: the application treats snapshots as completely immutable logs. Every single time you type a new prompt or modify the code, the application creates a new snapshot and assigns a completely new blockId.
Initially, I thought, "Damn, this isn't a bug after all. If the user keeps chatting, the old block ID changes and I lose active access." But my gut kept telling me there was a deeper architectural reason why it worked before lunch but stopped working after.
Phase 3: The Second Clue (Unmasking the Edge Cache)
I completely restarted the testing pipeline from scratch, but this time, I kept the DevTools network tab pinned open during the exploitation phase. I sent consecutive GET requests to the snapshot endpoint with the active blockId and stared at the headers.
There it was the second, definitive clue. The requests were hitting a server-side CDN / Edge Proxy caching tier.
Everything suddenly clicked. The reason the exploit failed after my 15-minute lunch break was because the edge cache had naturally expired during that time window.
When the project was public, the unauthenticated request caused the CDN proxy to save a copy of the successful response payload globally to improve application loading speeds. When the owner flipped the toggle to Private, the backend database was updated, but no cache-invalidation command was sent to the CDN proxy layer this was the main root cause and bug.
Because the edge proxy sits physically in front of the application architecture, it happily intercepts incoming matching requests and hands over the cached public data. The backend permission checks are completely bypassed because the traffic never reaches the backend application logic.
Advanced Impact: Attacker-Controlled Longevity
During the final analysis stage, I verified two critical technical parameters that elevated the security severity of this architectural flaw:
- Sliding-Window Cache Refreshing: The caching architecture utilized a rolling/sliding expiration window. Every time I submitted a consecutive automated request to the endpoint as an attacker, the edge proxy registered a fresh cache hit and reset the object's expiration timer. This meant an attacker could keep the private data alive inside the server-side cache indefinitely via simple automated polling.
- Server-Side Isolation Proof: To completely rule out a false positive caused by a local client browser cache, I terminated all active local testing sessions. I then successfully requested the same private data URL from a brand-new Incognito/Private window and an entirely isolated multi-account container tab. The data still loaded with an
X-Cache: HITheader, proving a global server-side exposure.
Even if a user updates their project workspace to scrub API keys, hides proprietary designs, or deletes their account completely, that point-in-time snapshot remains pinned at the edge proxy network for as long as the attacker maintains their automated polling loops.
Takeaways for Other Hunters
- Don't abandon a hypothesis just because your first clean retest fails. My "bug" disappeared after 15 minutes and I almost wrote it off as user error. The reason it disappeared was the actual finding.
- Watch the network tab, not just the response body. The headers (
X-Cache,Age,Cache-Control) told the real story long before I understood what was happening. - Isolate your test environment properly. Incognito + a separate container tab is a cheap, fast way to rule out "is this just my browser" before you write up a report claiming a server-side issue.
- Caching layers are chronically under-tested for authorization. They sit at the edge, outside the application's normal auth code path, and teams often forget that a CDN doesn't automatically know when a permission changed three layers down.
The Outcome
I packaged the entire flow, my testing logs, and the cache-refresh mechanics into a detailed report and submitted it responsibly.
The security team evaluated the submission and confirmed the root cause. However, it was ultimately marked as a Duplicate of a high-severity report submitted exactly one month prior (June 16). Even though it didn't end in a direct payout for this specific path due to the timing, I was still incredibly satisfied. The original report targeted a completely different endpoint and user flow, but they shared the exact same core architectural root cause: a failure to clear the CDN edge cache during project visibility state transitions.
It was an awesome engineering logic puzzle to solve.
Thanks for reading! If you want to ask anything about web security, connect with me on LinkedIn Atharv Chawan | LinkedIn & X: https://x.com/0xatharv
…bye! Have A Nice Day ❤️