Following our discovery of authentication bypass in etcd, we turned our attention to Appsmith, a popular open-source platform for building internal tools with ~40k stars on GitHub.
TL;DR
- Strix found and verified a BOLA/IDOR in Appsmith’s snapshot deletion flow (GHSA-g2hc-wmw2-32jr).
- Any authenticated user could delete another tenant’s snapshots by supplying a target
applicationId.
Appsmith: Broken Object-Level Authorization (BOLA)
Appsmith is a popular open-source platform for building internal tools and custom portals.
Strix identified a BOLA/IDOR vulnerability within Appsmith's application snapshot system (GHSA-g2hc-wmw2-32jr). It discovered that the snapshot deletion endpoint accepted a user-controlled application identifier without properly validating that the authenticated user actually had access to that application.
The Impact:
An attacker with any authenticated account could delete the backup snapshots of applications they did not own. Because Application IDs (24-character hex strings) are trivially discoverable in the HTML source code or API traffic of public-facing applications, a malicious actor could easily scrape an ID and destroy another tenant's backup/restore points. This allows for cross-tenant sabotage, interrupting recovery workflows and destroying backup integrity.
ID Discoverability
While Application IDs are 24-character hex strings, they are still easy to obtain in real-world usage. Strix observed that IDs can be pulled from normal HTML source and routine background API traffic for published apps. In practice, that means the identifier is not a meaningful barrier for cross-tenant abuse if authorization is missing on the delete path.
How Strix Found It:
Strix analyzed the backend logic and realized the ApplicationSnapshotServiceCEImpl.deleteSnapshot method was directly deleting snapshot records by application ID without an authorization check.
Unlike the restoreSnapshot method, which correctly called applicationService.findById(..., applicationPermission.getEditPermission()) to verify edit rights, the delete path skipped this entirely. It just executed applicationSnapshotRepository.deleteAllByApplicationId().
| 1 | // delete path |
| 2 | applicationSnapshotRepository.deleteAllByApplicationId(); |
| 3 | |
| 4 | // restore path |
| 5 | applicationService.findById(..., applicationPermission.getEditPermission()); |
To validate exploitability end-to-end, Strix generated a two-user PoC and reproduced the issue in a realistic tenant scenario.
Proof of Concept
Preconditions
- Two authenticated users exist: User A (application owner) and User B (non-owner).
- A valid User A
applicationIdis known from normal product usage.
Reproduction Flow (Generated by Strix)
- Authenticate as User A and create a snapshot with
POST /api/v1/applications/snapshot/<APP_ID>. - Confirm snapshot exists with
GET /api/v1/applications/snapshot/<APP_ID>(updatedTimeis non-null). - Authenticate as User B and confirm lack of access:
POST /api/v1/applications/snapshot/<APP_ID>returns404.GET /api/v1/applications/export/<APP_ID>returns404.
- As User B, call
DELETE /api/v1/applications/snapshot/<APP_ID>. - Observe delete succeeds with
200anddata: true. - Re-check snapshot metadata and observe
updatedTime: null.
| 1 | POST /api/v1/applications/snapshot/<APP_ID> |
| 2 | X-Requested-By: Appsmith |
| 3 | |
| 4 | DELETE /api/v1/applications/snapshot/<APP_ID> |
| 5 | X-Requested-By: Appsmith |
Observed validation (sanitized):
- User A could create a snapshot and saw
updatedTimeset. - User B could not create/export the target app (
404) but could delete snapshot (200). - After delete,
updatedTimebecamenull.
| 1 | # exploit core: non-owner deletes owner snapshot |
| 2 | r_delete_b = req( |
| 3 | "DELETE", |
| 4 | f"/api/v1/applications/snapshot/{app_id}", |
| 5 | cookies=cookies_b, |
| 6 | headers={"X-Requested-By": "Appsmith"}, |
| 7 | ) |
| 8 | print("User B delete snapshot:", r_delete_b.status_code) |
Code Analysis
During root-cause analysis, Strix identified the vulnerable sink in:
app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/ApplicationSnapshotServiceCEImpl.java
| 1 | |
| 2 | public Mono<Boolean> deleteSnapshot(String branchedApplicationId) { |
| 3 | return applicationSnapshotRepository |
| 4 | .deleteAllByApplicationId(branchedApplicationId) |
| 5 | .thenReturn(Boolean.TRUE); |
| 6 | } |
The endpoint was publicly reachable through:
app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/ce/ApplicationControllerCE.java
| 1 | |
| 2 | |
| 3 | public Mono<ResponseDTO<Boolean>> deleteSnapshotWithoutApplicationJson( String branchedApplicationId) { |
| 4 | return applicationSnapshotService |
| 5 | .deleteSnapshot(branchedApplicationId) |
| 6 | .map(isDeleted -> new ResponseDTO<>(HttpStatus.OK, isDeleted)); |
| 7 | } |
Strix surfaced the fix pattern below to enforce object-level authorization before delete:
| 1 | - public Mono<Boolean> deleteSnapshot(String branchedApplicationId) { |
| 2 | - return applicationSnapshotRepository |
| 3 | - .deleteAllByApplicationId(branchedApplicationId) |
| 4 | - .thenReturn(Boolean.TRUE); |
| 5 | - } |
| 6 | + public Mono<Boolean> deleteSnapshot(String branchedApplicationId) { |
| 7 | + return applicationService |
| 8 | + .findById(branchedApplicationId, applicationPermission.getEditPermission()) |
| 9 | + .switchIfEmpty(Mono.error(new AppsmithException( |
| 10 | + AppsmithError.NO_RESOURCE_FOUND, FieldName.APPLICATION, branchedApplicationId))) |
| 11 | + .flatMap(application -> applicationSnapshotRepository.deleteAllByApplicationId(application.getId())) |
| 12 | + .thenReturn(Boolean.TRUE); |
| 13 | + } |
Remediation
Strix recommended:
- Enforce object-level authorization in snapshot deletion by resolving application access before delete.
- Align authorization checks across snapshot create/restore/delete paths.
- Add regression tests to ensure non-owners cannot delete another tenant's snapshots.
- Review whether unauthenticated snapshot metadata exposure is required for product behavior.
The Appsmith team quickly validated the issue and shipped a patch to enforce the getEditPermission() check.
Timeline
Initial Contact: On February 19, 2026, we submitted the report via Github Security Advisories.
March 19, 2026: Appsmith confirmed the vulnerability as a valid BOLA/IDOR issue, acknowledged the missing authorization check on deleteSnapshot, and confirmed a fix plan.
March 24, 2026: The advisory was published.
Conclusion
Strix found a BOLA/IDOR in Appsmith's snapshot deletion path, proved it with a two-user PoC, and gave the maintainers the exact sink and fix pattern needed to ship a patch.
These bugs were found where thousands of others missed them. Try Strix on your own apps now and see what it finds before attackers do.

