Skip to content

Reject Acquire Lease without a usable duration - #2896

Open
compiler-segfault (compiler-segfault) wants to merge 1 commit into
Azure:mainfrom
compiler-segfault:fix/lease-duration-required
Open

compiler-segfault (compiler-segfault) wants to merge 1 commit into
Azure:mainfrom
compiler-segfault:fix/lease-duration-required

Conversation

@compiler-segfault

@compiler-segfault compiler-segfault (compiler-segfault) commented Nov 8, 2026 •

Copy link
Copy Markdown

Problem

Acquire Lease accepts a request that omits x-ms-lease-duration. The lease is
stored in an unusable state and from then on every request that touches the blob
or container returns 500, so the emulated resource is bricked until the
emulator data is discarded.

Reproduction from #2855, on main (716539c):

  1. PUT /container/b (block blob)
  2. PUT /container/b?comp=lease with x-ms-lease-action: acquire and no
    x-ms-lease-duration
  3. GET /container/b and GET /container?restype=container&comp=list

Before this change step 2 returns 201, and both reads in step 3 return 500
with LeaseExpiredState:constructor() error, incoming leaseDurationSeconds undefined in the log. The container and blob are stuck in that state.

Root cause

options.duration is undefined when the header is absent. The lease state
builders validated the value with

if ((duration < 15 || duration > 60) && duration !== -1) {

undefined < 15 and undefined > 60 are both false, so a missing duration
passed validation. LeaseLeasedState then stored leaseState: Leased with
leaseDurationSeconds: undefined and leaseExpireTime: new Date(NaN). On the
next request the lease cannot be represented by any state, the constructor
throws a RangeError, and the handler turns that into a 500.

Fix

  • BlobHandler.acquireLease and ContainerHandler.acquireLease now require the
    header and return 400 MissingRequiredHeader when it is absent, matching
    Azure Storage; both use a new StorageErrorFactory.getMissingRequiredHeader
    and an X_MS_LEASE_DURATION constant.
  • LeaseStateBase.validateLeaseDuration() rejects a duration that is not an
    integer (including a missing value) before the lease state is built, so the
    four acquire implementations share one check instead of four copies of the
    loose comparison.

Because validation happens before the lease is synced into the blob or
container record, a rejected acquire leaves no trace.

Tests

  • tests/blob/apis/blob.test.ts: acquire with no duration returns 400 MissingRequiredHeader; the blob stays readable and listable; a well formed
    acquire right after still succeeds.

  • tests/blob/apis/container.test.ts: the same for container leases.

  • npm run build

  • npx eslint "src/**/*.ts"

  • blob.test.ts + container.test.ts: 195 passing, 2 pending

ChangeLog.md updated under Upcoming Release / Blob.

Supersedes #2864: that PR handles blob leases only and has since gone stale and
conflicting with main; this one covers container leases too and makes the
invalid value impossible to persist from any caller.

Copilot AI balanced review requested due to automatic review settings November 8, 2026 12:30

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota. Try requesting a review at a lower effort level.

x-ms-lease-duration is mandatory for acquire, but a missing header parsed as
undefined and slipped through the range check: undefined compares false against
both bounds. The blob or container was then stored with leaseState Leased, no
leaseDurationSeconds, and an invalid leaseExpireTime, so every later request
failed with 500 and the resource stayed unusable until the emulator data was
discarded.

Require the header in both acquire handlers, returning 400
MissingRequiredHeader, and validate the duration in the lease state builders so
a missing or non-integer value can never be persisted.

Fixes Azure#2855
Copilot AI balanced review requested due to automatic review settings November 8, 2026 12:32

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota. Try requesting a review at a lower effort level.

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.

2 participants