microsoft / microsoft/vscode

Agent terminal file-write detection misses cmd.exe root deletion caused by PowerShell quoting

Open
#334,178 0 comments 0 reactions 1 assignee Claimed by @anthonykim1 View on GitHub
Dominant language
TypeScript
Stars
193k
Forks
42.4k
PR merge metrics
PR metrics pending

Description

## Type

Bug / data-loss safety issue

## Summary

A Copilot agent running in Windows PowerShell generated a nested `cmd.exe /c` cleanup command using C-style `\"` quote escaping. PowerShell does not interpret `\"` as an escaped double quote. The intended target path was therefore split from the command string, and `cmd.exe` received a standalone `\` as the target of `rd /s /q`.

The terminal log immediately before execution reported:

```text
RunInTerminalTool#CommandLineFileWriteAnalyzer: File writes detected [[]]
```

The command then recursively deleted writable content from the current drive root for over two minutes. Protected and open files survived; substantial user and application data did not.

The session used the **Bypass Approvals** (`autoApprove`) permission level. I understand that this level intentionally skips normal approval prompts. The issue is that the file-write/risk analysis reported no writes for a nested destructive command, no drive-root guard stopped it, and the agent continued after the output monitor timed out.

## Versions

- VS Code: 1.135.0
- Commit: `08d4889f9ec4a1685d257b9b95de036c8e1ce1e5`
- Date: `2026-08-25T14:26:52Z`
- GitHub Copilot Chat extension: 0.63.0
- OS: Windows x64, build 10.0.26200.9168
- Outer shell: Windows PowerShell 5.1
- Terminal shell integration quality in the log: `none`
- Session permission: `autoApprove` / Bypass Approvals

## Actual command

The private path is replaced with `C:\workspace\temp\dependency` below; quoting is unchanged.

```powershell
cmd.exe /d /c "rd /s /q \"C:\workspace\temp\dependency\""; if (Test-Path -LiteralPath 'C:\workspace\temp\dependency') { 'REMOVED=False' } else { 'REMOVED=True' }
```

## Safe reproduction of the argument parsing

Do **not** execute the real `cmd.exe` command. Shadow `cmd.exe` with a PowerShell function so only the received arguments are printed:

```powershell
function global:cmd.exe { $args | ConvertTo-Json -Compress }
cmd.exe /d /c "rd /s /q \"C:\workspace\temp\dependency\""
```

Observed arguments:

```json
["/d","/c","rd /s /q \","C:\\workspace\\temp\\dependency\\"]
```

The third argument is the command string `rd /s /q \`. Under `cmd.exe`, `\` resolves to the root of the current drive. The intended path is a separate argument and does not constrain `rd`.

## Relevant terminal log excerpt

```text
2026-09-02 22:09:51.298 [info] RunInTerminalTool#CommandLineFileWriteAnalyzer: File writes detected [[]]
2026-09-02 22:09:51.397 [info] RunInTerminalTool: Using `none` execute strategy for command `cmd.exe /d /c "rd /s /q \"C:\...\dependency\""; ...` []
2026-09-02 22:12:12.853 [info] OutputMonitor: Extended polling timeout reached after 2 minutes, signaling potential input needed []
2026-09-02 22:14:10.797 [info] RunInTerminalTool: Using `none` execute strategy for command `` []
```

The command output contained access-denied and file-in-use errors from the drive root, including system application directories and the user profile. Successful deletions were silent because `rd /s /q` was used.

## Expected behavior

At least one of the following should happen:

1. The PowerShell command parser should recognize the effective nested `cmd.exe /c` payload and classify it as a recursive file deletion.
2. Ambiguous or malformed quoting around a shell interpreter should fail closed rather than produce `File writes detected [[]]`.
3. Recursive deletion of a filesystem root, drive root, user profile root, or workspace ancestor should have a non-bypassable hard guard or an additional explicit confirmation.
4. The terminal tool should parse or validate the effective argv passed to common shell wrappers (`cmd.exe /c`, `powershell -Command`, `pwsh -Command`, `sh -c`, etc.).
5. After a long-running cleanup command starts returning errors from outside the requested target, the agent should stop rather than continue cleanup escalation.

## Actual impact

- Local Desktop and Documents contents were almost entirely deleted.
- Downloads and Music directories were deleted.
- Parts of installed applications and development tooling were deleted.
- Protected Windows files mostly survived.
- Cloud-backed files appear recoverable, but non-synced local files may not be recoverable because the system drive is an NVMe SSD with TRIM enabled.
- The machine will be reinstalled.

## Related issues checked

- #261794 replaced naive terminal auto-approval parsing with tree-sitter and shipped in VS Code 1.106.0.
- #271351 added prompts to discourage unnecessary sub-shell commands and also shipped in VS Code 1.106.0.
- #267975 and #270612 were closed as duplicates of those two issues.
- #280017 concerns destructive commands being auto-approved despite deny rules, but does not cover this quoting failure or drive-root deletion.

This incident occurred in VS Code 1.135.0, after the fixes above. It adds a current, safely reproducible nested-quote case where the file-write analyzer explicitly reported no writes and a malformed `cmd.exe /c` payload became a drive-root deletion.

## Diagnostics

A private evidence bundle is being retained with the full terminal log, relevant chat session state, version metadata, checksums, and an exact event timeline. It contains local paths and potentially sensitive workspace names, so it should not be posted publicly without redaction. I can provide a minimized redacted excerpt or additional diagnostics to maintainers on request.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.