Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
5 changes: 0 additions & 5 deletions src/permissions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,6 @@ export class PermissionManager {
private readonly sessionDeniedCommands = new Set<string>()
private readonly allowedEditPatterns = new Set<string>()
private readonly deniedEditPatterns = new Set<string>()
private readonly sessionAllowedEdits = new Set<string>()
private readonly sessionDeniedEdits = new Set<string>()
private readonly turnAllowedEdits = new Set<string>()
private turnAllowAllEdits = false
Expand Down Expand Up @@ -419,7 +418,6 @@ export class PermissionManager {
}

if (
this.sessionAllowedEdits.has(normalizedTarget) ||
this.turnAllowedEdits.has(normalizedTarget) ||
this.turnAllowAllEdits ||
this.allowedEditPatterns.has(normalizedTarget)
Expand Down Expand Up @@ -454,7 +452,6 @@ export class PermissionManager {
})

if (promptResult.decision === 'allow_once') {
this.sessionAllowedEdits.add(normalizedTarget)
return
}

Expand Down Expand Up @@ -488,8 +485,6 @@ export class PermissionManager {
if (promptResult.decision === 'deny_always') {
this.deniedEditPatterns.add(normalizedTarget)
await this.persist()
} else {
this.sessionDeniedEdits.add(normalizedTarget)
}

throw new Error(`Edit denied: ${normalizedTarget}`)
Expand Down
97 changes: 97 additions & 0 deletions test/permissions.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { describe, it } from 'node:test'
import assert from 'node:assert/strict'
import { mkdtemp, rm } from 'node:fs/promises'
import os from 'node:os'
import path from 'node:path'
import {
PermissionManager,
type PermissionDecision,
} from '../src/permissions.js'

async function withWorkspace(
prefix: string,
run: (workspace: string) => Promise<void>,
): Promise<void> {
const workspace = await mkdtemp(path.join(os.tmpdir(), prefix))
try {
await run(workspace)
} finally {
await rm(workspace, { recursive: true, force: true })
}
}

describe('edit permission decisions', () => {
it('prompts again for a revised diff after allow_once', async () => {
await withWorkspace('minicode-allow-once-', async workspace => {
const target = path.join(workspace, 'example.ts')
const firstDiff = 'diff: first revision'
const secondDiff = 'diff: second revision'
const promptedDiffs: string[] = []
const permissions = new PermissionManager(workspace, async request => {
promptedDiffs.push(request.details.at(-1) ?? '')
return { decision: 'allow_once' }
})

permissions.beginTurn()
await permissions.ensureEdit(target, firstDiff)
await permissions.ensureEdit(target, secondDiff)

assert.deepEqual(promptedDiffs, [firstDiff, secondDiff])
})
})

it('prompts again for a revised diff after deny_once', async () => {
await withWorkspace('minicode-deny-once-', async workspace => {
const target = path.join(workspace, 'example.ts')
const firstDiff = 'diff: first rejection'
const secondDiff = 'diff: second revision'
const promptedDiffs: string[] = []
const decisions: PermissionDecision[] = ['deny_once', 'allow_once']
const permissions = new PermissionManager(workspace, async request => {
const decision = decisions[promptedDiffs.length]
assert.ok(decision)
promptedDiffs.push(request.details.at(-1) ?? '')
return { decision }
})

permissions.beginTurn()
await assert.rejects(permissions.ensureEdit(target, firstDiff), /Edit denied/)
await permissions.ensureEdit(target, secondDiff)

assert.deepEqual(promptedDiffs, [firstDiff, secondDiff])
})
})

it('limits allow_turn to its file and current turn', async () => {
await withWorkspace('minicode-allow-turn-', async workspace => {
const firstTarget = path.join(workspace, 'first.ts')
const secondTarget = path.join(workspace, 'second.ts')
const firstDiff = 'diff: first file initial'
const revisedDiff = 'diff: first file revised'
const otherFileDiff = 'diff: second file'
const nextTurnDiff = 'diff: first file next turn'
const promptedDiffs: string[] = []
const decisions: PermissionDecision[] = [
'allow_turn',
'allow_once',
'allow_once',
]
const permissions = new PermissionManager(workspace, async request => {
const decision = decisions[promptedDiffs.length]
assert.ok(decision)
promptedDiffs.push(request.details.at(-1) ?? '')
return { decision }
})

permissions.beginTurn()
await permissions.ensureEdit(firstTarget, firstDiff)
await permissions.ensureEdit(firstTarget, revisedDiff)
await permissions.ensureEdit(secondTarget, otherFileDiff)
permissions.endTurn()
permissions.beginTurn()
await permissions.ensureEdit(firstTarget, nextTurnDiff)

assert.deepEqual(promptedDiffs, [firstDiff, otherFileDiff, nextTurnDiff])
})
})
})