feat: harden local shell, SFTP, and desktop session reliability

Finder-launched terminals now inherit a login PATH so tools like ollama resolve, destructive SFTP/editor actions require confirmation, and terminal search plus window dragging work again. SSH forwards and RDP sessions keep listeners and survive reconnect without FastPath crashes.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Logan.LiandCursor committed 2026-08-16 16:08:11 +08:00
1 parent 188f3556ed
commit 08696896f9
223 files changed
+13493 -3423

No files matched your search

+40 -1
View File
@@ -1,5 +1,5 @@
import { describe, expect, it } from 'vitest'
import { guessLanguageFromPath } from '../../src/utils/fileLanguage'
import { guessLanguageFromPath, isEditableRemoteTextFile } from '../../src/utils/fileLanguage'
describe('guessLanguageFromPath', () => {
it('detects common file extensions for the SFTP editor', () => {
@@ -7,5 +7,44 @@ describe('guessLanguageFromPath', () => {
expect(guessLanguageFromPath('Dockerfile')).toBe('dockerfile')
expect(guessLanguageFromPath('app.py')).toBe('python')
expect(guessLanguageFromPath('readme.md')).toBe('markdown')
expect(guessLanguageFromPath('index.html.bak')).toBe('html')
})
})
describe('isEditableRemoteTextFile (denylist)', () => {
it('allows text, code, config, backups, and unknown extensions', () => {
expect(isEditableRemoteTextFile('notes.txt')).toBe(true)
expect(isEditableRemoteTextFile('app.yaml')).toBe(true)
expect(isEditableRemoteTextFile('index.html')).toBe(true)
expect(isEditableRemoteTextFile('app.css')).toBe(true)
expect(isEditableRemoteTextFile('main.go')).toBe(true)
expect(isEditableRemoteTextFile('weird.xyz')).toBe(true)
expect(isEditableRemoteTextFile('Makefile')).toBe(true)
expect(isEditableRemoteTextFile('.env')).toBe(true)
expect(isEditableRemoteTextFile('nginx.conf.bak')).toBe(true)
expect(isEditableRemoteTextFile('app.py.old')).toBe(true)
expect(isEditableRemoteTextFile('mystery.bak')).toBe(true)
expect(isEditableRemoteTextFile('noext')).toBe(true)
})
it('rejects archives, binaries, media, and office files', () => {
expect(isEditableRemoteTextFile('pkg.tar')).toBe(false)
expect(isEditableRemoteTextFile('pkg.tar.gz')).toBe(false)
expect(isEditableRemoteTextFile('data.gz')).toBe(false)
expect(isEditableRemoteTextFile('archive.zip')).toBe(false)
expect(isEditableRemoteTextFile('setup.exe')).toBe(false)
expect(isEditableRemoteTextFile('lib.so')).toBe(false)
expect(isEditableRemoteTextFile('image.png')).toBe(false)
expect(isEditableRemoteTextFile('photo.jpg')).toBe(false)
expect(isEditableRemoteTextFile('video.mp4')).toBe(false)
expect(isEditableRemoteTextFile('font.woff2')).toBe(false)
expect(isEditableRemoteTextFile('blob.bin')).toBe(false)
expect(isEditableRemoteTextFile('doc.pdf')).toBe(false)
expect(isEditableRemoteTextFile('sheet.xlsx')).toBe(false)
expect(isEditableRemoteTextFile('archive.zip.bak')).toBe(false)
})
it('rejects directories', () => {
expect(isEditableRemoteTextFile('/home/u/src', 'directory')).toBe(false)
})
})
+21
View File
@@ -0,0 +1,21 @@
import { describe, expect, it } from 'vitest'
import {
isWithinRemoteTextEditLimit,
MAX_REMOTE_TEXT_EDIT_BYTES,
} from '../../src/utils/remoteTextLimits'
describe('remoteTextLimits', () => {
it('caps at 2 MiB', () => {
expect(MAX_REMOTE_TEXT_EDIT_BYTES).toBe(2 * 1024 * 1024)
})
it('allows missing or zero size', () => {
expect(isWithinRemoteTextEditLimit(undefined)).toBe(true)
expect(isWithinRemoteTextEditLimit(0)).toBe(true)
})
it('rejects over the soft cap', () => {
expect(isWithinRemoteTextEditLimit(MAX_REMOTE_TEXT_EDIT_BYTES)).toBe(true)
expect(isWithinRemoteTextEditLimit(MAX_REMOTE_TEXT_EDIT_BYTES + 1)).toBe(false)
})
})
+89
View File
@@ -0,0 +1,89 @@
import { describe, expect, it, vi } from 'vitest'
import type { FitAddon } from '@xterm/addon-fit'
import type { Terminal } from '@xterm/xterm'
import {
isTerminalDisposed,
markTerminalDisposed,
safeTerminalDispose,
safeTerminalFit,
safeTerminalWrite,
} from '../../src/utils/safeTerminalFit'
describe('safeTerminalFit', () => {
it('returns false without an addon', () => {
expect(safeTerminalFit(null)).toBe(false)
})
it('skips fit when terminal element is detached', () => {
const fit = vi.fn()
const term = {
element: { isConnected: false, parentElement: { clientWidth: 100, clientHeight: 40 } },
} as unknown as Terminal
expect(safeTerminalFit({ fit } as unknown as FitAddon, term)).toBe(false)
expect(fit).not.toHaveBeenCalled()
})
it('skips fit when host has zero size', () => {
const fit = vi.fn()
const term = {
element: { isConnected: true, parentElement: { clientWidth: 0, clientHeight: 0 } },
} as unknown as Terminal
expect(safeTerminalFit({ fit } as unknown as FitAddon, term)).toBe(false)
expect(fit).not.toHaveBeenCalled()
})
it('swallows renderer dimension races', () => {
const fit = vi.fn(() => {
throw new TypeError("undefined is not an object (evaluating 'this._renderer.value.dimensions')")
})
const term = {
element: { isConnected: true, parentElement: { clientWidth: 80, clientHeight: 40 } },
} as unknown as Terminal
expect(safeTerminalFit({ fit } as unknown as FitAddon, term)).toBe(false)
expect(fit).toHaveBeenCalledOnce()
})
it('returns true on successful fit', () => {
const fit = vi.fn()
const term = {
element: { isConnected: true, parentElement: { clientWidth: 80, clientHeight: 40 } },
} as unknown as Terminal
expect(safeTerminalFit({ fit } as unknown as FitAddon, term)).toBe(true)
})
it('blocks write after mark disposed', () => {
const write = vi.fn()
const term = { write } as unknown as Terminal
markTerminalDisposed(term)
expect(isTerminalDisposed(term)).toBe(true)
safeTerminalWrite(term, 'x')
expect(write).not.toHaveBeenCalled()
})
it('marks disposed and removes element before dispose', () => {
const raf: FrameRequestCallback[] = []
const original = globalThis.requestAnimationFrame
globalThis.requestAnimationFrame = ((cb: FrameRequestCallback) => {
raf.push(cb)
return raf.length
}) as typeof requestAnimationFrame
const dispose = vi.fn()
const remove = vi.fn()
const term = {
dispose,
element: { remove },
} as unknown as Terminal
safeTerminalDispose(term)
expect(isTerminalDisposed(term)).toBe(true)
expect(remove).toHaveBeenCalledOnce()
expect(dispose).not.toHaveBeenCalled()
// Drain double-rAF queue used by safeTerminalDispose.
while (raf.length) {
const next = raf.shift()!
next(0)
}
expect(dispose).toHaveBeenCalledOnce()
globalThis.requestAnimationFrame = original
})
})
+38
View File
@@ -0,0 +1,38 @@
import { beforeAll, describe, expect, it } from 'vitest'
import i18n from '../../src/i18n'
import {
filterSettingsTabsByQuery,
settingsTabMatchesQuery,
} from '../../src/utils/settingsSearch'
describe('settingsSearch multilingual', () => {
beforeAll(async () => {
await i18n.changeLanguage('zh-CN')
})
it('finds general via Language / lang while UI is Chinese', () => {
expect(settingsTabMatchesQuery('general', 'Language')).toBe(true)
expect(settingsTabMatchesQuery('general', 'lang')).toBe(true)
expect(settingsTabMatchesQuery('general', '语言')).toBe(true)
expect(settingsTabMatchesQuery('general', '言語')).toBe(true)
})
it('finds workspace via English aliases while UI is Chinese', () => {
expect(settingsTabMatchesQuery('general', 'workspace')).toBe(true)
expect(settingsTabMatchesQuery('general', '通用')).toBe(true)
})
it('finds account via license / 账户 while UI is Chinese', () => {
expect(settingsTabMatchesQuery('account', 'license')).toBe(true)
expect(settingsTabMatchesQuery('account', '账户')).toBe(true)
expect(settingsTabMatchesQuery('account', 'email')).toBe(true)
})
it('filters tab list for language query', () => {
const matched = filterSettingsTabsByQuery(
['general', 'account', 'terminal', 'ai', 'shortcuts', 'ssh', 'security', 'about'],
'lang'
)
expect(matched).toContain('general')
})
})
+2 -2
View File
@@ -55,7 +55,7 @@ describe('applyKeywordHighlights', () => {
const osc = '\x1b]0;ssh root@165.154.155.48\x07'
const out = applyKeywordHighlights(`${osc}Welcome to Ubuntu`, rules)
expect(out).toContain(osc)
expect(out).not.toMatch(/\x1b\]0;.*\x1b\[38/)
expect(out).not.toMatch(/\x1b\]0;.*\x1b\[38/) // eslint-disable-line no-control-regex -- OSC must stay intact
expect(out).toContain('Welcome to Ubuntu')
})
@@ -102,7 +102,7 @@ describe('StreamingKeywordHighlight', () => {
const done = stream.push('\x07Welcome', ipv4Rules)
expect(done).toContain('\x1b]0;ssh root@1.2.3.4\x07')
expect(done).toContain('Welcome')
expect(done).not.toMatch(/\x1b\]0;.*\x1b\[38/)
expect(done).not.toMatch(/\x1b\]0;.*\x1b\[38/) // eslint-disable-line no-control-regex -- OSC must stay intact
})
it('holds incomplete CSI across chunks', () => {