Skip to content
CVE-2026-115453 ·
High · October 22, 2026

Cross user deletion and modification of cart and wishlist rows in Ecommerce Template through unscoped primary key queries

S0
Secur0 CNA
CVE-2026-115453

Description

Authorization Bypass Through User-Controlled Key (CWE-639) in the cart and wishlist repositories of MarcosCamara01 Ecommerce Template, in cartRepository.delete(), cartRepository.updateQuantityInternal() and wishlistRepository.delete() (src/lib/db/drizzle/repositories/). Any authenticated user can delete and modify the cart and wishlist rows of any other user.

The three queries match rows by primary key alone, with no predicate on the owning user. The per user authorization for them rests on PostgreSQL row level security: the user context is set per transaction with set_config('app.current_user_id', userId, true) in connection.ts, and the policies compare app.current_user_id() with user_id.

That control does not operate. The migrations run ALTER TABLE ... ENABLE ROW LEVEL SECURITY but never ALTER TABLE ... FORCE ROW LEVEL SECURITY, and PostgreSQL does not apply policies to the role that owns a table. The template uses one DATABASE_URL both to run the migrations, which create the tables and so make that role the owner, and for the application at runtime, so the application connects as the owner and the policies do not apply to it.

cart_items.id and wishlist.id are sequential bigserial values, so the identifiers of other users' rows can be enumerated rather than guessed, and the attack can be run across every account of the installation. The update path returns the modified row, which carries the identifier of the user it belongs to.

Only these three operations are reachable. The remaining repository queries carry an explicit user_id predicate of their own and are unaffected, so this record covers the three operations that were demonstrated and not the whole of the authorization model.

Vulnerability type (CWE)

  • CWE-639: Authorization Bypass Through User-Controlled Key

Affected versions

Affected from commit b139a11c of 24 December 2025, where these repositories were introduced with primary key only predicates, and fixed in commit adf7dedb of 27 July 2026. Default status: unaffected.

At the lower bound the migrations contained no row level security policies at all, so the three operations carried no per user authorization of any kind. The policies arrive later, in commit 63f94f5 of 1 April 2026, and are inert for the reason above, so the exposure is continuous across the whole range.

The project publishes no versioned releases and the repository carries no git tags, and the version field in package.json has remained at 0.1.0, so an affected revision can only be identified by commit.

Score (CVSS 4.0)

High (7.1)

CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:L/VI:H/VA:N/SC:N/SI:N/SA:N

Privileges required are Low because the attacker needs an ordinary account on the store and nothing more. User interaction is None because the attacker acts alone against the API. Confidentiality on the vulnerable system is Low because the update response returns the victim's row, which discloses their user identifier and the variant and size they had selected, and nothing beyond that. Integrity on the vulnerable system is High because the delete and update reach rows that belong to another account and destroy or alter them, and a write is scored Low only where it is confined to something of no direct consequence, which a user's own cart and wishlist contents are not. Availability is None because the store continues to trade and the victim is not denied use of it. The subsequent system metrics are None, since the absent check is authorization internal to the application.

Solution

Update to a revision that includes commit adf7dedb of 27 July 2026.

An operator who cannot update can obtain the same effect by running the application as a database role that does not own the tables, or by applying ALTER TABLE ... FORCE ROW LEVEL SECURITY to the tables that carry policies, which makes the existing policies apply to the owner.

Patch

Commit adf7dedb. It fixes the problem at both layers. The three queries no longer depend on row level security: cartRepository.delete() and wishlistRepository.delete() now filter on the userId they already received, and updateQuantityInternal() takes a userId and filters on it, threaded through from upsert() and updateQuantity(). A new migration 0004_force_rls.sql then applies FORCE ROW LEVEL SECURITY to every table carrying policies, so the policies apply to the owning role as well, and each layer blocks the attack on its own.

Credits

  • Darío Rivas - finder
  • Gerard Martinez Guillamon - finder
  • Cristian Fernández Cornejo - analyst
  • Xoán M. Otero Jorge - analyst
  • Secur0 CNA - coordinator
  • MarcosCamara01 - remediation developer

Discovery source: Internal

Official record: CVE-2026-115453

Related records

  • CVE-2026-115450 - price manipulation in the checkout flow of the same application