Commit Graph

2077 Commits

Author SHA1 Message Date
Cacie Prins 473d4f8e26 refactor(network-policy): scaffold package and wire createProxyRuntime
Stage 0 (0a+0b): package skeleton and composition root facade.

- Scaffold @packages/network-policy with port stubs, NetworkExchange, and protocol types
- Add NetworkInterceptionRuntime interface and createProxyRuntime composition root
- Wire ServerBase.createNetworkProxy to createProxyRuntime

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-22 12:22:26 -04:00
Adam Alston 4dc390cd17 fix: remove reserved compressed video files (#33550) 2026-05-20 23:38:52 -06:00
Bill Glesias 21956f0860 dependency: bump esbuild for CVE-2025-68121 in bundled Go stdlib (#33816)
Upgrade esbuild to ^0.28.0 so the native binary uses a fixed Go toolchain
(TLS resumption). Set packherd transpile and bundle targets to node20 to
match the published CLI minimum, refresh related locks, and document the
follow-up checklist in the Electron upgrade guide.

Fixes https://github.com/cypress-io/cypress/issues/33599

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-19 18:54:35 -04:00
mabela416 ba1f4bade2 feat(frontend-shared): auto-start cloud auth from login modal (#33831)
* feat(cloud): add signup flow for desktop cloud auth

- Add GraphQL signup mutation and AuthActions.signup that reuse the same authenticate path as login while calling the cloud signUp API.
- Extend server cloud auth to start signup against the Cloud signup URL with Sign Up campaign UTM parameters, alongside existing login behavior.
- Drive Auth, LoginModal, and LoginConnectModalsContent from store authFlow so the modal shows signup copy and triggers signup when users open the signup connect flow.
- Add English strings and unit/component tests covering signup mutations and UI.

* update text for sign up errors

* Revert "update text for sign up errors"

This reverts commit 7b89edb589.

* update login modal copy

* update changelog

* update waiting text

* update cloud.graphql

* update e2e tests to match renamed login modal title

The login modal initial title changed from "Log in to Cypress" to
"Continue in your browser", so update all dialog role assertions
across runs, settings, and specs list latest runs specs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* add more tests for sign up in auth_spec

* add more tests for authactions

* feat(frontend-shared): auto-start cloud auth from login modal

- Start login or signup from the login modal as soon as it opens when online, instead of waiting for a second click on the primary CTA.
- Add an autoStartAuth flag on Auth (off by default) and default it on from LoginModal; defer focus to the CTA when auto-start is off and retry after the app comes back online.

Co-authored-by: Cursor <cursoragent@cursor.com>

* add tests

* add changelog entry

* remove duplicate test

* do not start login when browser is already open

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-19 16:09:21 -04:00
mabela416 b911a8d332 feat: add signup flow for desktop cloud auth (#33805)
* feat(cloud): add signup flow for desktop cloud auth

- Add GraphQL signup mutation and AuthActions.signup that reuse the same authenticate path as login while calling the cloud signUp API.
- Extend server cloud auth to start signup against the Cloud signup URL with Sign Up campaign UTM parameters, alongside existing login behavior.
- Drive Auth, LoginModal, and LoginConnectModalsContent from store authFlow so the modal shows signup copy and triggers signup when users open the signup connect flow.
- Add English strings and unit/component tests covering signup mutations and UI.

* update text for sign up errors

* Revert "update text for sign up errors"

This reverts commit 7b89edb589.

* update login modal copy

* update changelog

* update waiting text

* update cloud.graphql

* update e2e tests to match renamed login modal title

The login modal initial title changed from "Log in to Cypress" to
"Continue in your browser", so update all dialog role assertions
across runs, settings, and specs list latest runs specs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* add more tests for sign up in auth_spec

* add more tests for authactions

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 11:01:09 -04:00
Cacie Prins 39a7f2a743 fix: graceful teardown of file watchers and spawned processes when exiting (#33542)
* wip: graceful exit

* add types for signal-exit

* use graceful exit; properly forward signals to forked process from gulp

* rm extraneous

* wait for child processes to close before exiting parent processes

* do not teardown twice; allow for multiple will-quit messages

* wait for async child to exit, clean up

* bring back env vars rather than inlined undefines

* updated tests

* fix exit codes

* pingpong code

* system test

* rm extraneous debug entry

* fix various

* fix operator precedence w/ promise

* improper treekill usage, coerce null exitCode

* fix concurrent duplicate .close() on watchers

* changelog

* clear timeout

* refinements

* refine

* infer exit code from signal

* if graceful exit fails in before-quit handler, exit with code 1

* v8 build errors

* revert lockfile teardown to previous pattern, keep on graceful-exit

* exit code 1 no matter what the signal is

* system test snapshots

* snapshot

* integration test timing fixes, more robust teardown

* no eventmap type huh

* universal reset of graceful exit

* rm all listeners from studio lifecycle

* euid on windows

* fix posix code 112 exit criterion

* mock process.exit in interactive spec

* fix graceful exit step

* better handling of return types for exit codes

* fixes an issue where system tests were not tearing down correctly

* rm dead code

* 112

* Update record_spec.js

* better messaging, debugging

* additional debugging for child process; fix CT teardown

* changelog

* fix run child fixture failing to exit

* rm console

* add proper error listener so test does not fail prematurely

* system test snapshots; no longer emit err from async child

* add back snapshots that got removed?

* only resolve on signals, not reject

* shut down gql ws server correctly

* properly await project shutdown in unit test

* prevent repeated graphql-ws .dispose() calls

* fix spawn unit test

* debounce signals in graceful exit signal handlers to account for duplicate kills from signal-exit

* only emit sigint/term message once, in case signal-exit goes overboard

* changelog

* Apply suggestion from @cacieprins

* do not double-resolve

* rm unused types

* rm all listeners on child process even if already killed

* sigkill = exit with 137

* prevent zombie plugin child processes

* prevent morgan output during shutdown

* surround sigint messaging with newlines to improve visual experience

* correct changelog

* fix ci-only issues

* enable mockery for morgan in before each instead of outside of test closure

* changelog

* fix out of order

* ensure stdin is no longer set to raw mode when signals are received

* fix spawn unit tests for setRawMode(false)

* check tty in gulp script

* lock, cache

* wsp

* prevent orphaned steps in graceful shutdown

* dont stack sigint/term handlers in pkgs/electron open

* changelog
2026-05-15 12:36:16 -04:00
Ryan Manuel f2d8def94a fix: stream-verify cy-prompt/Studio bundles, move cache out of /tmp (#33748)
* chore(server): add bundle cache root resolver

First step of the cy-prompt/Studio bundle cache rework. Adds
getBundleCacheRoot() / getBundleCacheDir(kind) under
packages/server/lib/cloud/bundles/cache_root.ts, returning
<cypress-cache>/bundles[/<kind>] and honoring CYPRESS_CACHE_FOLDER
the same way the CLI does (untildify + path.resolve, falling back
to cachedir('Cypress')).

Adds cachedir@^2.4.0 and untildify@^4.0.0 to @packages/server deps
to match the versions already used by the CLI.

No callers wired yet — pure helper landing ahead of the streaming
verify+extract module.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore(server): extract_atomic: extract retry harness, add renameAtomicWithRetry

Lifts the EPERM/EACCES retry loop from writeFileAtomicWithRetry into
a generic retryOnRenameError<T>(op) helper, then adds
renameAtomicWithRetry(src, dst) using the same harness.

renameAtomicWithRetry will be used by the upcoming bundle-cache
publish step, which moves staged files into the shared <hash>/
directory via per-file atomic rename. This preserves the
cross-process safety property from PRs #33034 / #33330: a concurrent
reader always sees either the prior version's complete bytes or the
new version's complete bytes, never absent or partial.

Behavior of writeFileAtomicWithRetry is unchanged — same retries
(3), same delay (100ms), same error semantics. Existing extractAtomic
spec passes unmodified; new spec block covers renameAtomicWithRetry
(success, EPERM/EACCES retry, exhausted budget, non-retryable codes).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(server): ensureSignedBundle module for unified bundle download

Adds the streaming verify+extract module under
packages/server/lib/cloud/bundles/ that will replace the legacy
download-to-tar + re-open + verify + re-extract flow used by
ensureCyPromptBundle / ensureStudioBundle.

Modules:
- bundle_error.ts          : BundleError(kind, stage) for Sentry tagging
- parse_hash_from_bundle_url.ts : hash extraction shared with lifecycle managers
- sweep_orphan_staging.ts  : best-effort cleanup of crashed-process staging dirs
- stream_download_verify_extract.ts : single-pass fetch -> SHA256 verify tee
                                       -> tar.Parse, files written to a
                                       per-process .staging-<rand>/ dir.
                                       No on-disk tar artifact.
- publish_staging_to_final.ts : per-file renameAtomicWithRetry from staging
                                 into shared <hash>/, manifest.json last.
- ensure_signed_bundle.ts  : orchestrator. Both signatures must verify before
                             any byte lands in the shared <hash>/ dir.

encryption.ts: new createStreamingSignatureVerifier export so the streaming
tee can update a SHA256 verifier per chunk without buffering the tar.

Cross-process safety property from PR #33034 / #33330 is preserved: each
process has a private staging dir, publish is gated on signature verify,
and per-file atomic rename means a concurrent reader always sees either
prior-version or new-version bytes for any file -- never absent or partial.

Telemetry: errors carry a structured `stage` tag (network / signature /
extract / manifest / publish) so Sentry filtering reproduces the planned
`bundle.download.error.{...}` taxonomy. A net-new counter framework was
not built -- the existing telemetry surface is duration marks/measures
only, and the legacy `ENOENT bundle.tar-<rand>` Sentry signal disappears
structurally when step 7 deletes the legacy fetchers. Counters can be a
follow-up if Sentry filtering proves insufficient.

No callers wired yet -- pure new module landing ahead of caller swap.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* test(server): unit + cross-process tests for ensureSignedBundle module

Adds 20 tests covering the new bundles/ module:

- parse_hash_from_bundle_url_spec.ts (5)  : url -> hash extraction edge cases
- bundle_error_spec.ts (3)                : kind/stage tagging + isBundleError
- sweep_orphan_staging_spec.ts (4)        : age-threshold filtering, non-staging
                                             entries ignored, missing dir tolerated
- publish_staging_to_final_spec.ts (3)    : happy path, manifest renamed last,
                                             cross-process safety regression
- ensure_signed_bundle_spec.ts (5)        : orchestrator happy path, signature
                                             failure, missing manifest, network
                                             error propagation, staging cleanup
                                             on publish failure

The cross-process test is the load-bearing one: it spawns two real child
processes (via `node -r @packages/ts/register cross_process_publish_worker.ts`)
that each call publishStagingToFinal into a shared finalDir while the
parent runs a tight readFile loop. After both children exit, the test
asserts (a) zero ENOENT observations on the watched file, (b) zero
corrupt/partial reads, and (c) finalDir contains every file with
matching content. This directly guards the PR #33034 invariant the
rework had to preserve.

Worker script lives in test/support/ rather than test/unit/ so the
mocha glob doesn't try to load it as a spec.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(server): wire cy-prompt to ensureSignedBundle

ensure_cy_prompt_bundle.ts is now a thin wrapper around
ensureSignedBundle({ kind: 'cy-prompt' }) that returns
{ manifest, cyPromptPath } (where cyPromptPath is the bundleDir
the new helper computed under <cypress-cache>/bundles/cy-prompt/<hash>).

CyPromptLifecycleManager:
- Drops os.tmpdir()-based path construction.
- Hash extraction now uses parseHashFromBundleUrl (shared with the
  bundle helper).
- hashLoadingMap value type updated from Promise<Record<string,string>>
  to Promise<{ manifest, cyPromptPath }>; the path now flows out of
  ensureCyPromptBundle rather than being computed twice.

CYPRESS_LOCAL_CY_PROMPT_PATH bypass is preserved verbatim.

Existing CyPromptLifecycleManager_spec assertions on tmpdir-based paths
still pass because the test stub returns that exact path -- the legacy
path is still a valid string in the test, just no longer baked into the
production code.

ensure_cy_prompt_bundle_spec was rewritten as a delegation test (3
cases: forwarding, undefined projectId, error propagation) since all
the prior assertions on download/extract/verify mechanics now live in
the bundles/ unit tests added in the previous commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(server): wire studio to ensureSignedBundle

Symmetric to the cy-prompt wiring. ensure_studio_bundle.ts is now a
thin wrapper around ensureSignedBundle({ kind: 'studio' }) that
returns { manifest, studioPath } where studioPath is the bundleDir
under <cypress-cache>/bundles/studio/<hash>.

StudioLifecycleManager:
- Drops os.tmpdir()-based path construction.
- Hash extraction now uses parseHashFromBundleUrl (shared with the
  bundle helper and cy-prompt manager).
- hashLoadingMap value type updated; the path now flows out of
  ensureStudioBundle rather than being computed twice.

CYPRESS_LOCAL_STUDIO_PATH bypass + currentStudioHash retry-clear
behavior preserved verbatim.

ensure_studio_bundle_spec rewritten as a delegation test (3 cases).
StudioLifecycleManager_spec assertions updated for the new
{manifest, studioPath} return shape; the 'clears cached bundle promises
on retry' test's dummyPromise was updated to match the new shape so
the awaiting code path in the lifecycle manager still works.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(server): delete get_cy_prompt_bundle / get_studio_bundle

These two legacy fetchers (and their specs) are now subsumed by
streamDownloadVerifyExtract under packages/server/lib/cloud/bundles/.
The lifecycle managers and ensure_*_bundle wrappers have been wired
to the new helper in the previous two commits, so nothing else
imports these.

extract_atomic.ts is intentionally kept -- it remains the home for
renameAtomicWithRetry, which the bundle publish step uses.

This also eliminates the legacy 'ENOENT bundle.tar-<rand>' Sentry
signal structurally: there's no longer any code path in the runtime
that writes a bundle.tar file to disk.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(cli): cache prune ignores non-semver entries; cache clear/prune cover bundles/

cypress cache prune now filters by util.isSemver before removing entries,
matching the existing behavior of getCachedVersions (used by cache list).
Without this filter, prune would also remove the new bundles/ subdir
that the server uses to cache cy-prompt and Studio bundles.

cypress cache clear is unchanged (still fs.remove the entire cache root)
and removes bundles/ along with binary version dirs, which is the
documented behavior.

Adds two regression tests:
- cache clear removes bundles/cy-prompt/<hash>/ and bundles/studio/<hash>/
  alongside binary version dirs.
- cache prune preserves bundles/ while pruning old binary versions.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(changelog): note bundle cache rework + ENOENT fix

Bugfix entry under 15.15.0 calling out:
- The intermittent ENOENT bundle.tar failure class on Linux
- The streaming verify+extract approach (no on-disk tar artifact)
- The new <cypress-cache>/bundles/<kind>/<hash>/ layout
- CYPRESS_CACHE_FOLDER honored, cache clear/prune behavior

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(server): drop dead extractAtomic / writeFileAtomicWithRetry

After the cy-prompt and Studio call sites were migrated to
streamDownloadVerifyExtract, neither extractAtomic nor its supporting
buffer-based writeFileAtomicWithRetry has any callers left. Both
removed along with the unused tar / createReadStream / writeFileAtomic
imports.

extract_atomic.ts now contains only renameAtomicWithRetry plus its
shared retry harness -- the load-bearing primitive used during the
bundle publish step. extract_atomic_spec.ts trimmed to just the 6
renameAtomicWithRetry tests.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(cli): cache prune skips bundles/ by name, still removes beta/prerelease binaries

The previous fix used util.isSemver to gate prune, but state.getVersionDir()
formats non-stable builds as 'beta-<version>-<branch>-<sha>' which is not a
valid semver. That spelling would orphan beta/prerelease binary caches forever.

Switched to a targeted name-based exclusion (NON_BINARY_CACHE_ENTRIES, currently
just 'bundles'). Restores the original 'anything not the current binary version'
prune semantics for both stable and beta dirs, while still preserving the
bundles/ tree.

Adds a regression test that pre-populates two beta-prefixed binary dirs and
asserts they are pruned alongside the existing bundles-preservation test.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(server): bundle fetch timeout now actually retries

Wrapping a fetch AbortError as BundleError(stage='network') stopped the
retry harness in its tracks: asyncRetry's shouldRetry uses isRetryableError,
which only matches SystemError or HttpError. A timeout would surface as a
non-retryable BundleError and fail on the first attempt instead of using
the configured retry budget (3 attempts, ~1.5s of linear backoff).

Switched to throwing a SystemError with code 'ETIMEDOUT' on AbortError
inside the retry loop. SystemError is matched by isRetryableError, so the
timeout now consumes the full retry budget like every other transient
network failure.

Adds a regression test that stubs cross-fetch to throw AbortError and
asserts (a) fetch is called 3 times (full budget), (b) every attempt's
error is a retryable SystemError with code 'ETIMEDOUT'.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(server): tag network/extract bundle errors with BundleError stage

streamDownloadVerifyExtract was rethrowing raw HttpError/SystemError values
on the network and extraction paths, so the only error surface that
actually carried the BundleError(kind, stage) tags promised by the rework
was the signature path. Sentry filtering on err.stage would have missed
every fetch failure, every mid-stream connection drop, every HTTP 4xx/5xx,
and every tar parse error.

Now both phases route through wrapAsBundleError, which:
  - keeps the underlying HttpError/SystemError as `cause` so asyncRetry's
    cause-aware shouldRetry preserves the existing isRetryableError-based
    classification (HTTP 408/429/5xx + SystemError keep retrying;
    HTTP 4xx + tar parse errors fail fast as before),
  - emits stage='network' for fetch-phase failures, AbortError timeouts,
    and POSIX-style syscall codes (ECONNRESET, ETIMEDOUT, EAI_AGAIN, ...)
    during the pipeline,
  - emits stage='extract' for non-syscall pipeline errors (overwhelmingly
    tar.Parse complaining about malformed bytes).

Tightened the syscall-shape heuristic to /^E[A-Z]/ and explicitly excluded
Node ERR_* codes, since a TypeError thrown inside HttpError.fromResponse
(via scrubUrl) carries code 'ERR_INVALID_URL' and would otherwise be
miswrapped as a retryable SystemError.

Adds 4 regression tests covering: timeout retry, HTTP 4xx no-retry, HTTP
503 retry, tar parse no-retry. Each asserts both the BundleError shape
(stage + kind + cause type) and the retry call count, so a future shape
change can't silently regress either property.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore(server): drop inline type import in ensure_signed_bundle

V8 snapshot processing can't handle mixed value/type imports on a
single line. Inline `type BundleKind` -> bare `BundleKind`. Same
treatment is already applied in stream_download_verify_extract.ts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* test(server): drop test/integration/cloud/extract_atomic_spec.ts

This integration spec imported the now-removed `extractAtomic` export and
crashed at runtime with "extractAtomic is not a function" once that
function was deleted in the dead-code prune. Missed it because I only
inspected test/unit/.

Coverage of the underlying fs primitive lives on:
- renameAtomicWithRetry has full retry/EPERM-EACCES coverage in the unit
  spec at test/unit/cloud/extract_atomic_spec.ts.
- The cross-process publish flow (which is the load-bearing tar-extract +
  per-file rename path the deleted integration spec was sanity-checking)
  has a real-FS regression test in
  test/unit/cloud/bundles/publish_staging_to_final_spec.ts that spawns
  two child processes against a shared finalDir.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* test(server): scope proxyquire.noPreserveCache() to TelemetryReporter spec

The TelemetryReporter spec called proxyquire.noPreserveCache() at module
load time, which mutates the global proxyquire instance for the rest of
the mocha process. Every subsequent proxyquire(...) call across the
entire suite then reloads transitive deps, so a downstream spec's
top-level imports and the proxyquired-module's transitive imports
end up resolving to *different instances* of the same module.

Concrete impact: in the full unit-suite run, CyPromptLifecycleManager
and StudioLifecycleManager would fail their setup `calledWith({...,
cloudApi: { CloudRequest, ... } })` assertions because the test's
imported CloudRequest and the lifecycle manager's imported CloudRequest
ended up as different `axios.create()` instances. The matcher
`sinon.match.func` also lost its identity (samsam's matcher detection
compared the matcher created with the pre-pollution sinon against a
post-pollution sinon, so it deep-equal'd as `{ test, message }`
instead of being recognized as a matcher).

Wrap the noPreserveCache() in before/after hooks scoped to this
describe block so the toggle is reverted by `proxyquire.preserveCache()`
on the way out -- no other spec is affected. The TelemetryReporter
spec's own 6 tests still pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore(server): drop dead exports left behind by the bundle cache rework

Cleanup of unused symbols that became dead once the streaming verify+extract
helper replaced the legacy fetcher path:

- write-file-atomic (package.json): only consumer was the deleted
  writeFileAtomicWithRetry buffer-based path in extract_atomic.ts.
- verifySignatureFromFile (encryption.ts) + its 2 spec tests: only callers
  were the deleted get_cy_prompt_bundle.ts / get_studio_bundle.ts. The
  streaming-verify path uses createStreamingSignatureVerifier instead.
- getBundleCacheRoot (cache_root.ts): only used internally by
  getBundleCacheDir; downgrade from `export const` to `const`.
- EnsureSignedBundleOptions / EnsureSignedBundleResult interfaces: only
  consumed inside ensure_signed_bundle.ts; drop `export` on both.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(server): surface cause's syscall code on BundleError so cert detection works

StudioLifecycleManager#updateStatus reads `error?.code` and feeds it into
isNonRetriableCertErrorCode() to decide whether to flip isCertError and
show the proxy-specific recovery message. Wrapping a SystemError inside
BundleError({cause: sysError}) made the top-level `code` undefined, so
certificate failures during studio bundle download silently lost their
cert classification and users got the generic error path instead.

BundleError now mirrors a string-typed `cause.code` onto its own `code`
property so any consumer that reads error.code keeps working.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(server): drain in-flight publish renames before throwing

publishStagingToFinal used Promise.all on the non-manifest renames, which
rejects on the first failure but leaves siblings running. The caller
(ensureSignedBundle) then runs `finally { remove(staging) }`, which races
the still-in-flight renames -- those renames find their src path missing
and reject with ENOENT, surfacing as unhandled rejections.

Mirrored the pattern stream_download_verify_extract.ts already uses for
its entryPromises: capture the promises in a let, await Promise.all in a
try, and Promise.allSettled them in the catch before re-throwing. The
function still rejects with the original error; in-flight siblings are
just guaranteed to settle first.

Adds a regression test that stubs renameAtomicWithRetry so one rel rejects
fast and the others resolve slowly, asserts no `unhandledRejection` event
fires during the test window, and confirms the slow renames settled
before the function returned.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(server): match CLI semantics for CYPRESS_CACHE_FOLDER override

cache_root.ts read process.env.CYPRESS_CACHE_FOLDER directly, but the
CLI's state.getCacheDir() routes the same variable through util.getEnv(),
which:
  - trims leading/trailing whitespace,
  - strips surrounding double quotes (so Windows CMD's
    `set CYPRESS_CACHE_FOLDER="C:\cache"` -- which sets the value with
    embedded quotes -- works the same on both sides), and
  - falls back to npm_config_* / npm_package_config_* env-var spellings
    so values from .npmrc or `--cypress-cache-folder=...` flags reach the
    server too.

Without this, the server resolved `<cache>/bundles` to a different path
than the one `cypress cache clear` removes, and npm-config-based
overrides were silently ignored server-side.

Adds a small dequote()/readEnvVar() pair (intentionally local rather than
importing from cli/, since the two packages don't share util code) and
seven tests covering: clean path, surrounding quotes, whitespace trim,
npm_config_* fallback, lowercase variant, bare-var precedence, and
empty/whitespace-only-override falling through to cachedir().

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(changelog): trim the bundle-cache rework entry

Cut the wording to two sentences to match the surrounding entries' style.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore(server): trim verbose comments in bundle cache files

Tightened comments to match the surrounding code style and dropped
rationale that wasn't load-bearing for a future reader.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(server): retry bundle fetches on HTTP 500 by passing GET to isRetryableError

isRetryableError(err, method) treats HTTP 500 as retryable only when the
method is one of the idempotent set (GET/HEAD/PUT/DELETE/OPTIONS). The
deleted get_cy_prompt_bundle.ts / get_studio_bundle.ts passed 'GET'
explicitly; shouldRetryBundleError was passing only the error, so a
transient 500 from the bundle CDN failed on the first attempt instead of
consuming the retry budget.

Pass 'GET' through both code paths (cause-unwrapped and bare). Adds a
regression test asserting fetchStub is called 3 times when the CDN
responds with 500.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Apply suggestion from @ryanthemanuel

* Update CHANGELOG.md

* fix(server): retry renameAtomicWithRetry on EBUSY for Windows

Adds EBUSY to the retry classifier alongside EPERM/EACCES. On Windows,
`fs.rename` can fail with EBUSY when the destination is held open by
another process (Defender / AV, an indexer, a file watcher) -- the same
failure class PR #33330 already retried for EPERM/EACCES, just under a
different code on a different transient holder.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(server): don't retry filesystem syscall errors during bundle extract

Filesystem errors (ENOSPC, EACCES, EROFS, EIO, ...) raised by tar-entry
writes during the pipeline phase were wrapped as SystemError and tagged
stage='network', causing isRetryableError to burn the full retry budget
on non-transient failures and mis-classifying them in Sentry. Now only
network-class POSIX codes (ECONNRESET, ETIMEDOUT, ...) retain
network/retryable wrapping mid-pipeline; everything else honors
defaultStage='extract' and keeps the raw error as cause.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore(server): drop shared counter in sweepOrphanStaging

Return per-task booleans and tally after Promise.all instead of mutating
a shared counter from concurrent async callbacks.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Apply suggestion from @ryanthemanuel

* Apply suggestion from @ryanthemanuel

* Apply suggestion from @ryanthemanuel

* Update cli/CHANGELOG.md

Co-authored-by: Matt Schile <mschile@cypress.io>

* Update CHANGELOG.md

* Apply suggestion from @ryanthemanuel

* chore: PR review follow-ups for bundle cache rework

- Rename NON_BINARY_CACHE_ENTRIES to EXTERNAL_CACHE_ENTRIES in cli cache task.
- Refactor walkFiles in publish_staging_to_final to collect arrays via
  Promise.all and flatten, instead of mutating a shared results array
  from concurrent async callbacks. Matches sweepOrphanStaging's pattern.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(cli): align CYPRESS_CACHE_FOLDER trim/dequote with server bundle cache

state.getCacheDir() now passes trim=true to util.getEnv, applying
dequote() + trim per util.ts:538. Without this, Windows CMD's
`set CYPRESS_CACHE_FOLDER="C:\path"` left literal quotes in the resolved
path (cypress-io/cypress#4506), so `cypress cache clear` targeted a
different directory than the server wrote bundles to. Updates the
server-side comment in cache_root.ts to reference the trim flag and
adds CLI tests covering quoted, whitespace-padded, and whitespace-only
overrides so parity stays enforced.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Matt Schile <mschile@cypress.io>
2026-05-15 09:43:55 -05:00
Matt Schile d0f90867a9 fix: guard ProjectLifecycleManager setup against project-cleared race (#33810) 2026-05-15 07:49:11 -06:00
Matt Schile 6877ece3fb fix: accept ECDSA client certificates in clientCertificates config (#33799) 2026-05-14 13:38:46 -06:00
Matt Schile 302747ded2 fix(server): retry BiDi sessionSubscribe when Firefox connection not ready (#33770) 2026-05-11 13:24:09 -06:00
Matt Schile 73a3e0e749 fix: serialize ProjectLifecycleManager.refreshLifecycle to avoid IPC race (#33775) 2026-05-11 12:30:21 -06:00
Edouard Bozon 99bec89905 fix: namespace Angular CT temp tsconfig by projectRoot hash (#33635)
* fix: namespace Angular CT temp tsconfig by projectRoot hash

In monorepos, multiple Angular component-testing projects can share
the same path.basename(projectRoot) (e.g. libs/feature-a/feat-shell
and libs/feature-b/feat-shell). The temp tsconfig was keyed only on
that basename, so parallel component-test runs raced on the same
/tmp/cypress-angular-ct/<basename>/tsconfig.json file and produced
flaky spec-resolution failures.

Suffix the temp directory with a short sha1 hash of projectRoot so
every project gets its own isolated config. projectRoot is optional
on getTempDir to preserve the public signature.

Fixes #33634

* docs: add CHANGELOG entry for Angular CT temp dir hash fix (#33635)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-08 18:36:20 -04:00
Matt Schile fb436e3ba0 dependency: upgrade uuid to 11.1.1 to address SNYK-JS-UUID-16133035 (#33765) 2026-05-08 14:10:23 -06:00
Chris Breiding 2d6f97f87a chore: Capture more env vars (#33766) 2026-05-08 15:56:05 -04:00
Matt Schile 599daafe50 dependency: upgrade socket.io stack to fix GHSA-677m-j7p3-52f9 (#33719) 2026-05-07 21:28:58 -06:00
Matt Schile d3b4d3780c fix(proxy): preserve Content-Length: 0 on empty proxied responses (#33754) 2026-05-07 13:15:26 -06:00
Bill Glesias d9b2be464b fix(vite-dev-server): exclude CT specs from Vite 8 JSX refresh (#33751)
* fix(vite-dev-server): exclude CT specs from Vite 8 JSX refresh

Prevents duplicate describe/it registration in headed mode when specs define
local React components (HMR self-accept). Sets oxc.jsxRefreshExclude for Vite 8.

Fixes #33750

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(vite-dev-server): scope jsxRefreshInclude so CSS is not transformed by Oxc

Pair jsxRefreshExclude with a script-only jsxRefreshInclude pattern; Vite's
createFilter(undefined, exclude) otherwise matches all non-spec assets.

Add unit tests including CSS path regression coverage.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-06 12:55:45 -04:00
Matt Schile c06792f643 fix(server): retry cloud requests on HTTP 500 (#33718) 2026-05-06 08:48:14 -06:00
Matt Schile c412a65297 fix(driver): escape CSS attribute selectors to prevent runner crash (#33739) 2026-05-05 10:33:49 -06:00
Jennifer Shehane 1c1734b2e0 fix: remove cross origin window ref in spec-bridge communicator lifecycle (#33704)
* perf: updates for cross-origin window ref

* update test

* add restores to tests

* restore in tests

* reset stubs/spies

* restore in screenshot test

* remove unused API

* changelog entry

* Address cursor feedback

* fix ts-check
2026-05-05 10:36:28 -04:00
Chris Breiding dfc00fb5d3 misc: Capture additional env vars for retrying failed tests (#33714) 2026-05-01 09:49:50 -04:00
Matt Schile 810cec32dd deprecation: cy.end() deprecation (#33707) 2026-04-30 17:44:07 -06:00
Cacie Prins 922988081b chore(deps): upgrade @cypress/request to ^4.0.0 (#33712)
* chore(deps): upgrade `@cypress/request` to `^4.0.0`

* changelog
2026-04-30 14:05:34 -04:00
Cacie Prins 545556ee30 dependency(listr): upgrade listr 3.x to 9.x (#33640)
* chore(deps): upgrade listr 3.x to 9.x

* use important strings to assert install output instead of fragile snapshots

* rm unused `enquirer` dep; write cli test results

* revert persisting junit xml for now

* ensure VerboseRenderer implements LstrRenderer contract

* remove unnecessary vendorized VerboseRenderer; use behavioral assertions in verify instead of brittle snapshots

* make most `any` types in cli installer explicit; remove unused deps

* changelog

* rm trailing slash from pr link

* Update cli/lib/tasks/install.ts

Co-authored-by: Bill Glesias <bglesias@gmail.com>

* simplify task list definitions; improve type annotations

* fix version output; improve readability of task generation

* rm unused types

* revert error handling regression

* fix implicit any

* more readability improvements; better listr mocking

* apply similar readability improvements to verify

* fix order of verify -> welcome message

* chore: skip adding the install comment on the commit (#33685)

* update axios (#33687)

---------

Co-authored-by: Bill Glesias <bglesias@gmail.com>
Co-authored-by: Matt Schile <mschile@cypress.io>
2026-04-28 15:06:42 -04:00
Cacie Prins f1592001cf chore: upgrades ts-loader to 9.5.7 (#33691)
* dependency: update ts-loader in webpack-preprocessor-batteries-included

* adds debug for ts loader rules to investigate publish binary failure

* rm extra debug

* update ts-loader in extension and server
2026-04-28 10:31:50 -06:00
Matt Schile 3442523620 fix: guard cy.wait against undefined retry responses (#33651) 2026-04-28 09:25:57 -06:00
Matt Schile 3592361a79 fix: set primary remote state before HTTP server accepts requests (#33686) 2026-04-28 09:25:22 -06:00
Matt Schile 6d29bb0f41 dependency: upgrade simple-git to 3.36.0 to fix RCE vulnerability (#33680)
* chore: upgrade simple-git to 3.36.0 to fix RCE vulnerability

Resolves Snyk SNYK-JS-SIMPLEGIT-15456078 (Critical Remote Code
Execution) in simple-git@3.33.0. The existing ^3.32.3 ranges in
packages/data-context and packages/app already cover 3.36.0, so
only a relock was needed. Carried the TasksPendingQueue static-field
patch forward to 3.36.0.
2026-04-24 16:24:58 -06:00
Ryan Manuel 002b3b7c88 fix: strip unsafe target from <base> tags to keep AUT inside Cypress frame (#33667)
* fix: strip unsafe target from <base> tags to keep AUT inside Cypress frame

A `<base target="_top">` (or `_parent`) is inherited by every untargeted
<a> and <form> on the page, so navigations escape the AUT iframe even
though the existing runtime guards only inspect each element's own
`target` attribute. Strip the attribute in both the AST and regex HTML
rewriters at proxy time, and neutralize dynamically inserted `<base>`
tags from the driver's click/submit capture-phase handlers as a backup.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix: match target keywords case-insensitively and always neutralize base on click

Two gaps in the driver runtime backstop for `<base target>`:

- `HTMLBaseElement.target` reflects the raw content attribute without case
  normalization, but the browser matches `_top` / `_parent` case-insensitively
  at navigation time. Lowercase the comparison so `<base target="_TOP">` is
  also neutralized.
- The click handler previously gated `neutralizeUnsafeBaseTarget` inside the
  `tagName === 'A'` check, which fails for clicks on descendants of an anchor
  (e.g. `<a><img>`). The base-target neutralization is document-scoped, so it
  must run regardless of which descendant the click lands on.

Add E2E regressions for both scenarios.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs: add changelog entry for <base> target rewrite fix

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs: clarify gating in <base> target changelog entry

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix: make <base> target regex work under replaceStream's match re-apply

replace_stream.ts re-applies each pattern to the already-matched substring
(`match.replace(pattern, replacement)`), so any boundary char enforced via
a positive lookahead falls off the end of the match and the lookahead fails
at end-of-string — silently skipping the replacement in the stream path
while the in-memory `strip()` path still worked.

Capture the boundary char in group 2 and re-emit it via `$2` so both paths
produce identical output. Add stream-path regressions covering quoted,
single-quoted, unquoted, and self-closing <base> variants.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs: reword test comments to describe behavior directly

Drop "Regression:" / test-justification framing in favor of comments that
describe the HTML or stream behavior the test exercises.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix: route <base> target neutralization through handleInvalidTarget

Move neutralizeUnsafeBaseTarget into handleInvalidTarget so every caller —
same-origin submit events, anchor clicks, and the cross-origin
form.submit() patch — gets document-scoped neutralization consistently.
The anchor click handler keeps an explicit neutralize call on the
descendant-click path (where handleInvalidTarget does not run because
e.target is not the anchor itself).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* test: rename driver spec from .cy.js to .cy.ts

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 15:38:50 +00:00
Jennifer Shehane 40eccdb777 perf(reporter): tear down Scroller scroll listener on reset and reassignment (#33607)
* perf(reporter): tear down Scroller scroll listener on reset and reassignment

setContainer and __reset could leave multiple scroll handlers on the same
container because each setContainer added a new anonymous listener and
never called removeEventListener.
Use a stable handler plus _listenToScrolls / _stopListeningToScrolls so the
previous node is always unsubscribed before attaching to a new container,
and __reset unsubscribes before clearing state.
Add a unit test for repeated setContainer and extend the test container type
so removeEventListener is typed as a Sinon spy.

* changelog entry

* fix broken tests

* fix unit test

* move changelog
2026-04-22 11:57:25 -04:00
renovate[bot] d9147a020d dependency: update dependency cachedir to ^2.4.0 (#33608)
* fix(deps): update dependency cachedir to ^2.4.0

* empty commit

* changelog entry

* empty commit

* update changelog

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jennifer Shehane <shehane.jennifer@gmail.com>
Co-authored-by: Jennifer Shehane <jennifer@cypress.io>
2026-04-22 10:37:29 -04:00
Matt Schile f652605d31 fix: (vite-dev-server) wait for support file (#33487) 2026-04-20 11:48:09 -06:00
Matt Schile 593c22eab0 perf: dispose mocha runner on run completion to prevent memory leak on rerun (#33631) 2026-04-20 11:34:00 -06:00
Matt Schile e5f0b32dee chore: remove release dates from cli/CHANGELOG.md (#33603) 2026-04-20 08:45:00 -06:00
Ryan Manuel b3202f0ae9 fix: pass inflateRaw override to decryptResponse for large cy.prompt payloads (#33619)
* fix: pass inflateRaw override to decryptResponse for large cy.prompt payloads

The jose library caps decompressed JWE payloads at ~250KB by default. Larger
cy.prompt /plan responses (which carry cached selectors and chains) inflated
past that limit and produced DecryptionError: decryption operation failed.
Match the 5MB ceiling the services-side @packages/encryption decrypt already
configures.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Update CHANGELOG for version 15.14.1

Added changelog entry for version 15.14.1 with bugfix details.

* Update CHANGELOG.md

* Apply suggestion from @ryanthemanuel

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 16:18:13 -05:00
Emily Wisniewski (Rohrbough) 515a3ca709 chore: Update dates to reduce misinterpretation to non US users (#33551) 2026-04-17 10:51:34 -05:00
Bill Glesias b8a9db27d4 chore: prep Cypress for 15.14.0 release (#33616) 2026-04-16 09:53:14 -04:00
Bill Glesias acf1eef15c feat: support vite 8 for component testing (#33580) 2026-04-15 23:27:48 -04:00
Matt Schile 9f1f15e8be revert: "fix: truncate long strings in chai inspect (#33512)" (#33611) 2026-04-15 17:16:17 -06:00
Matt Schile bf2f052371 revert: "fix: stream privileged file reads over HTTP (#33538)" (#33612) 2026-04-15 16:27:00 -06:00
Adam Alston 585b0df893 fix: stream privileged file reads over HTTP (#33538) 2026-04-14 21:57:26 -06:00
Jennifer Shehane cebbc85a87 perf(driver): allowlist keys kept after log memory cleanup when tests drop out of numTestsKeptInMemory (#33601)
* perf(driver): allowlist keys kept after log memory cleanup when tests drop out of numTestsKeptInMemory

Replace blocklisted string keys with REDUCE_MEMORY_PRESERVED_KEYS so
reduceMemory nulls every other field (including custom Cypress.log data)
when tests drop out of numTestsKeptInMemory. Add vitest coverage.

* changelog entry

* remove viewportHeight and Width in cleanup

* add back some necessary keys
2026-04-14 16:48:08 -04:00
Matt Schile 8eaedab0d8 feat: TypeScript 6 compatibility for webpack preprocessor and CT wizard (#33575) 2026-04-13 15:47:30 -06:00
Matt Schile 31622bea5e dependency: update axios to 1.15.0 (#33594) 2026-04-13 15:44:27 -06:00
Adam Alston 89a16f3538 fix: truncate long strings in chai inspect (#33512) 2026-04-08 15:28:32 -06:00
Cacie Prins 68054bc591 fix: restart when .env is changed in the config file (#33567)
* fix: restart when .env is changed in the config file

* changelog
2026-04-08 15:05:20 -04:00
Bill Glesias ab6082263b chore: release cypress app 15.3.1 (#33571) 2026-04-07 10:46:58 -04:00
Craigory Coppola 44fd8b95dd perf: skip git status and git log calls in run mode (#33552) 2026-04-06 09:54:31 -06:00
Cacie Prins 28441711af dependency: Upgrade node-forge to ^1.4.0 (#33546)
* deps: node-forge

* rm from root

* changelog

* Apply suggestion from @jennifer-shehane

---------

Co-authored-by: Jennifer Shehane <jennifer@cypress.io>
2026-04-01 13:08:28 -04:00
Varun Chawla 60325bb11f fix: reject cy.intercept delay values >= 2^31 (#33377)
* fix: login attempts subsequent to a cancelled one now launch the browser and can complete (#33366)

* fix: login attempts subsequent to a cancelled one now launch the browser and can complete

* resolve cached responses with a bluebird promise instead of a native promise

* adds typedefs for @cypress/request and extends interfaces so that api/index.ts can be better typed

* correct type declaration in packages/server/lib/cloud/user.ts

* clean up ts/package.json

* changelog

* rm expect-error directive and repalce with ignore - why is driver typechecking this?

* changelog

* fix: reject cy.intercept delay values >= 2^31

setTimeout uses a signed 32-bit integer internally, so delay values
>= 2^31 (~24.8 days) are silently coerced to 1ms. This causes the
delay to be effectively ignored with no indication to the user.

Add validation in validateStaticResponse to throw a clear error
when delay >= 2^31, consistent with how throttleKbps is validated.

Fixes #33183

* fix: add changelog entry and fix lint padding

- Add changelog entry for delay validation to cli/CHANGELOG.md
- Fix padding-line-between-statements lint error (blank line after const)

* fix: use 2**31 notation instead of 2^31 and add PR link to changelog

Address review feedback: use JavaScript exponentiation notation (2**31)
instead of mathematical caret notation (2^31) in comments and changelog
to avoid confusion with JavaScript's XOR operator. Also add the missing
PR reference link in the changelog entry.

* Update CHANGELOG.md

Moves the changelog entry to 15.11.1

* Fix off-by-one error

* Update error message with regard to off-by-one

* Fix test with regard to off-by-one

* lint

* Update CHANGELOG.md

---------

Co-authored-by: Cacie Prins <cacieprins@users.noreply.github.com>
Co-authored-by: Matt Schile <mschile@cypress.io>
2026-04-01 09:38:44 -04:00