Skip to content

fix: broken ownership check in environment secret delete/update (crash + authz bypass)#3729

Draft
cursor[bot] wants to merge 1 commit intodevelopfrom
cursor/critical-bug-inspection-db83
Draft

fix: broken ownership check in environment secret delete/update (crash + authz bypass)#3729
cursor[bot] wants to merge 1 commit intodevelopfrom
cursor/critical-bug-inspection-db83

Conversation

@cursor
Copy link
Copy Markdown

@cursor cursor bot commented Mar 29, 2026

Bug and Impact

The ownership guard in updateEnvironmentSecrets (used for both delete and update operations on environment secrets) contained a logically broken condition introduced in commit 84d9a735:

if key.EnvironmentID == nil && *key.EnvironmentID == env.ID {

This has two critical effects:

  1. Nil pointer dereference (server crash): When key.EnvironmentID is nil, the left side of && evaluates to true, causing Go to dereference the nil pointer on the right side. This panics and crashes the HTTP handler.

  2. Authorization bypass: When key.EnvironmentID is non-nil, the left side is false, so && short-circuits and the entire guard is never triggered. This means any project member can delete or modify access keys belonging to other environments in the same project by sending a crafted update/delete request with the target key ID.

Concrete trigger scenario

A user with access to Environment A sends an update-environment request with a secret operation referencing a key ID that belongs to Environment B. The ownership check (meant to reject this) is bypassed because the condition always evaluates to false. The key from Environment B is then deleted or overwritten.

Root Cause

The condition key.EnvironmentID == nil && *key.EnvironmentID == env.ID should have been key.EnvironmentID == nil || *key.EnvironmentID != env.ID. The original intent was "reject if the key has no environment or belongs to a different environment," but && with == inverts the logic entirely.

Additionally, the error-appending line errors = append(errors, err) was appending nil (since GetAccessKey had succeeded at that point).

Fix

  • Changed && to || and == to != in both the delete and update branches
  • Added a proper error message to the errors slice instead of appending nil
  • Added 4 unit tests covering: delete from other env, delete with nil env ID, delete with matching env (allowed), and update from other env

Validation

  • All new tests pass
  • All existing tests in api/projects/ pass
  • Package compiles cleanly
Open in Web View Automation 

The ownership check for environment secrets used a logically inverted
condition that was both a nil-pointer crash and an authorization bypass:

  if key.EnvironmentID == nil && *key.EnvironmentID == env.ID

Two critical issues:
1. Nil dereference panic: when EnvironmentID is nil, the left side of &&
   is true, so Go evaluates *key.EnvironmentID which crashes the server.
2. Authorization bypass: when EnvironmentID is non-nil, the left side is
   false, so && short-circuits and the entire guard is never triggered.
   This allows any user to delete or update access keys belonging to
   other environments within the same project.

Fix: change to 'key.EnvironmentID == nil || *key.EnvironmentID != env.ID'
which correctly rejects operations when the key has no environment or
belongs to a different environment.

Also fixed the error message that was appending nil to the errors slice.

Co-authored-by: Denis Gukov <fiftin@outlook.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant