Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions src/main/presenter/agentRuntimePresenter/dispatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -802,7 +802,9 @@ export async function executeTools(

let preCheckedPermission: PendingToolInteraction['permission'] | null = null
if (toolPresenter.preCheckToolPermission) {
const preChecked = await toolPresenter.preCheckToolPermission(toolCall)
const preChecked = await toolPresenter.preCheckToolPermission(toolCall, {
permissionMode
})
if (preChecked?.needsPermission) {
preCheckedPermission = normalizePermissionRequest(preChecked as PermissionRequestLike, {
toolName: toolContext.name,
Expand Down Expand Up @@ -857,7 +859,8 @@ export async function executeTools(

const toolCallResult = await toolPresenter.callTool(toolCall, {
onProgress: applyProgressUpdate,
signal: io.abortSignal
signal: io.abortSignal,
permissionMode
})
let toolRawData = toolCallResult.rawData

Expand All @@ -876,7 +879,8 @@ export async function executeTools(
await autoGrantPermission(hooks, io.sessionId, pendingPermission)
const retryCallResult = await toolPresenter.callTool(toolCall, {
onProgress: applyProgressUpdate,
signal: io.abortSignal
signal: io.abortSignal,
permissionMode
})
toolRawData = retryCallResult.rawData
} else {
Expand Down
5 changes: 4 additions & 1 deletion src/main/presenter/agentRuntimePresenter/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3819,7 +3819,10 @@ export class AgentRuntimePresenter implements IAgentImplementation {

if (serverName === 'agent-filesystem' && Array.isArray(payload.paths) && payload.paths.length) {
await sessionPermissionPort.approvePermission(sessionId, {
permissionType: 'write',
permissionType:
permissionType === 'read' || permissionType === 'write' || permissionType === 'all'
? permissionType
: 'write',
serverName,
toolName,
paths: payload.paths
Expand Down
17 changes: 17 additions & 0 deletions src/main/presenter/configPresenter/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1772,6 +1772,23 @@ export class ConfigPresenter implements IConfigPresenter {
}, 1000)
}

getLaunchAtLoginEnabled(): boolean {
return app.getLoginItemSettings().openAtLogin
}

setLaunchAtLoginEnabled(enabled: boolean): void {
app.setLoginItemSettings({
openAtLogin: Boolean(enabled)
})
publishDeepchatEvent('settings.changed', {
changedKeys: ['launchAtLoginEnabled'],
version: Date.now(),
values: {
launchAtLoginEnabled: this.getLaunchAtLoginEnabled()
}
})
}

getCopyWithCotEnabled(): boolean {
return this.uiSettingsHelper.getCopyWithCotEnabled()
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/presenter/permission/filePermissionService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import path from 'path'
export interface FilePermissionRequest {
toolName: string
serverName: string
permissionType: 'write'
permissionType: 'read' | 'write' | 'all'
description: string
paths?: string[]
conversationId?: string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export interface ExecuteCommandOptions {
env?: Record<string, string>
stdin?: string
outputPrefix?: string
allowExternalCwd?: boolean
}

interface PreparedCommand {
Expand Down Expand Up @@ -104,7 +105,7 @@ export class AgentBashHandler {
}

const { command, timeout, background, cwd: requestedCwd, yieldMs } = parsed.data
const cwd = this.resolveWorkingDirectory(requestedCwd)
const cwd = this.resolveWorkingDirectory(requestedCwd, options.allowExternalCwd)

// Handle background execution
if (background) {
Expand Down Expand Up @@ -226,7 +227,7 @@ export class AgentBashHandler {
})
}

private resolveWorkingDirectory(requestedCwd?: string): string {
private resolveWorkingDirectory(requestedCwd?: string, allowExternalCwd = false): string {
const defaultCwd = this.allowedDirectories[0]
const normalizedInput = requestedCwd?.trim()
if (!normalizedInput) {
Expand All @@ -238,7 +239,7 @@ export class AgentBashHandler {
? this.normalizePath(path.resolve(expanded))
: this.normalizePath(path.resolve(defaultCwd, expanded))

if (!this.isPathAllowed(resolved)) {
if (!allowExternalCwd && !this.isPathAllowed(resolved)) {
throw new Error(`Working directory is not allowed: ${requestedCwd}`)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,8 +188,12 @@ export class AgentFileSystemHandler {
private readonly allowedDirectoryRoots: string[]
private conversationId?: string
private readonly sessionsRoot: string
private readonly allowExternalAccess: boolean

constructor(allowedDirectories: string[], options: { conversationId?: string } = {}) {
constructor(
allowedDirectories: string[],
options: { conversationId?: string; allowExternalAccess?: boolean } = {}
) {
if (allowedDirectories.length === 0) {
throw new Error('At least one allowed directory must be provided')
}
Expand All @@ -211,6 +215,7 @@ export class AgentFileSystemHandler {
)
this.conversationId = options.conversationId
this.sessionsRoot = this.normalizePath(getSessionsRoot())
this.allowExternalAccess = options.allowExternalAccess === true
}

private normalizePath(p: string): string {
Expand Down Expand Up @@ -272,14 +277,14 @@ export class AgentFileSystemHandler {
baseDirectory?: string,
options: PathValidationOptions = {}
): Promise<string> {
const enforceAllowed = options.enforceAllowed ?? true
const enforceAllowed = options.enforceAllowed ?? !this.allowExternalAccess
const normalizedRequested = this.resolvePath(requestedPath, baseDirectory)
const requestedPathAllowed = !enforceAllowed || this.isPathAllowed(normalizedRequested)
if (options.accessType === 'read') {
this.assertSessionReadAllowed(normalizedRequested)
}
if (enforceAllowed) {
const isAllowed = this.isPathAllowed(normalizedRequested)
if (!isAllowed) {
if (!requestedPathAllowed) {
throw new Error(
`Access denied - path outside allowed directories: ${normalizedRequested} not in ${this.allowedDirectoryRoots.join(', ')}`
)
Expand Down Expand Up @@ -308,6 +313,9 @@ export class AgentFileSystemHandler {
if (enforceAllowed) {
const isParentAllowed = this.isPathAllowed(normalizedParent)
if (!isParentAllowed) {
if (options.accessType === 'write' && requestedPathAllowed) {
return normalizedRequested
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
throw new Error('Access denied - parent directory outside allowed directories')
}
}
Expand Down Expand Up @@ -350,6 +358,10 @@ export class AgentFileSystemHandler {
}
}

assertReadAllowedAbsolute(candidatePath: string): void {
this.assertSessionReadAllowed(this.normalizePath(path.resolve(candidatePath)))
}

private countLines(value: string): number {
if (value.length === 0) return 0
const lineCount = value.split('\n').length
Expand Down Expand Up @@ -953,7 +965,9 @@ export class AgentFileSystemHandler {
if (!parsed.success) {
throw new Error(`Invalid arguments: ${parsed.error}`)
}
const validPath = await this.validatePath(parsed.data.path, baseDirectory)
const validPath = await this.validatePath(parsed.data.path, baseDirectory, {
accessType: 'write'
})
await fs.writeFile(validPath, parsed.data.content, 'utf-8')
return `Successfully wrote to ${parsed.data.path}`
}
Expand Down Expand Up @@ -982,7 +996,9 @@ export class AgentFileSystemHandler {
if (!parsed.success) {
throw new Error(`Invalid arguments: ${parsed.error}`)
}
const validPath = await this.validatePath(parsed.data.path, baseDirectory)
const validPath = await this.validatePath(parsed.data.path, baseDirectory, {
accessType: 'write'
})
await fs.mkdir(validPath, { recursive: true })
return `Successfully created directory ${parsed.data.path}`
}
Expand All @@ -994,10 +1010,15 @@ export class AgentFileSystemHandler {
}
const results = await Promise.all(
parsed.data.sources.map(async (source) => {
const validSourcePath = await this.validatePath(source, baseDirectory)
const validSourcePath = await this.validatePath(source, baseDirectory, {
accessType: 'write'
})
const validDestPath = await this.validatePath(
path.join(parsed.data.destination, path.basename(source)),
baseDirectory
baseDirectory,
{
accessType: 'write'
}
)
try {
await fs.rename(validSourcePath, validDestPath)
Expand All @@ -1015,7 +1036,9 @@ export class AgentFileSystemHandler {
if (!parsed.success) {
throw new Error(`Invalid arguments: ${parsed.error}`)
}
const validPath = await this.validatePath(parsed.data.path, baseDirectory)
const validPath = await this.validatePath(parsed.data.path, baseDirectory, {
accessType: 'write'
})
const content = await fs.readFile(validPath, 'utf-8')
let modifiedContent = content

Expand Down Expand Up @@ -1108,7 +1131,9 @@ export class AgentFileSystemHandler {
throw new Error(`Invalid arguments: ${parsed.error}`)
}

const validPath = await this.validatePath(parsed.data.path, baseDirectory)
const validPath = await this.validatePath(parsed.data.path, baseDirectory, {
accessType: 'write'
})
const result = await this.replaceTextInFile(
validPath,
parsed.data.pattern,
Expand Down Expand Up @@ -1147,7 +1172,9 @@ export class AgentFileSystemHandler {
}

const { path: filePath, oldText, newText } = parsed.data
const validPath = await this.validatePath(filePath, baseDirectory)
const validPath = await this.validatePath(filePath, baseDirectory, {
accessType: 'write'
})

const content = await fs.readFile(validPath, 'utf-8')
const normalizedOldText = this.normalizeLineEndings(oldText)
Expand Down
Loading