feat: add allowBypassPermissionsMode setting (#658)

* feat: add allowBypassPermissionsMode setting

Allow bypass permissions mode to appear in the mode list via
settings.json without requiring the --allow-dangerously-skip-permissions
CLI flag. The disableBypassPermissionsMode setting retains priority.

* fix: address Copilot review feedback on allowBypassPermissionsMode

- Security: read allowBypassPermissionsMode only from trusted settings
  sources (user/local/flag/policy), excluding projectSettings to prevent
  a malicious repo from enabling bypass mode
- UX: update error messages to reference the correct CLI flag
  (--allow-dangerously-skip-permissions) and the new settings option
- Tests: add schema validation tests for the new field
This commit is contained in:
Nourrisse Florian
2026-04-13 14:05:21 +02:00
committed by GitHub
parent 7c8bdcc3e2
commit 31be66d764
6 changed files with 58 additions and 3 deletions

View File

@@ -4582,7 +4582,7 @@ function handleSetPermissionMode(
subtype: 'error',
request_id: requestId,
error:
'Cannot set permission mode to bypassPermissions because the session was not launched with --dangerously-skip-permissions',
'Cannot set permission mode to bypassPermissions. Enable it with --allow-dangerously-skip-permissions or set permissions.allowBypassPermissionsMode in settings.json',
},
})
return toolPermissionContext

View File

@@ -434,7 +434,7 @@ export function useReplBridge(messages: Message[], setMessages: (action: React.S
if (!store.getState().toolPermissionContext.isBypassPermissionsModeAvailable) {
return {
ok: false,
error: 'Cannot set permission mode to bypassPermissions because the session was not launched with --dangerously-skip-permissions'
error: 'Cannot set permission mode to bypassPermissions. Enable it with --allow-dangerously-skip-permissions or set permissions.allowBypassPermissionsMode in settings.json'
};
}
}

View File

@@ -19,6 +19,7 @@ import {
getSettings_DEPRECATED,
getSettingsFilePathForSource,
getUseAutoModeDuringPlan,
hasAllowBypassPermissionsMode,
hasAutoModeOptIn,
} from '../settings/settings.js'
import {
@@ -936,9 +937,11 @@ export async function initializeToolPermissionContext({
const settings = getSettings_DEPRECATED() || {}
const settingsDisableBypassPermissionsMode =
settings.permissions?.disableBypassPermissionsMode === 'disable'
const settingsAllowBypassPermissionsMode = hasAllowBypassPermissionsMode()
const isBypassPermissionsModeAvailable =
(permissionMode === 'bypassPermissions' ||
allowDangerouslySkipPermissions) &&
allowDangerouslySkipPermissions ||
settingsAllowBypassPermissionsMode) &&
!growthBookDisableBypassPermissionsMode &&
!settingsDisableBypassPermissionsMode

View File

@@ -0,0 +1,27 @@
import { describe, test, expect } from 'bun:test'
describe('SettingsSchema allowBypassPermissionsMode', () => {
test('accepts allowBypassPermissionsMode: true', async () => {
const { SettingsSchema } = await import('./types.js')
const result = SettingsSchema().safeParse({
permissions: { allowBypassPermissionsMode: true },
})
expect(result.success).toBe(true)
})
test('accepts allowBypassPermissionsMode: false', async () => {
const { SettingsSchema } = await import('./types.js')
const result = SettingsSchema().safeParse({
permissions: { allowBypassPermissionsMode: false },
})
expect(result.success).toBe(true)
})
test('rejects non-boolean allowBypassPermissionsMode', async () => {
const { SettingsSchema } = await import('./types.js')
const result = SettingsSchema().safeParse({
permissions: { allowBypassPermissionsMode: 'yes' },
})
expect(result.success).toBe(false)
})
})

View File

@@ -574,6 +574,7 @@ export function getManagedSettingsKeysForLogging(
'ask',
'defaultMode',
'disableBypassPermissionsMode',
'allowBypassPermissionsMode',
...(feature('TRANSCRIPT_CLASSIFIER') ? ['disableAutoMode'] : []),
'additionalDirectories',
]),
@@ -888,6 +889,24 @@ export function hasSkipDangerousModePermissionPrompt(): boolean {
)
}
/**
* Returns true if any trusted settings source has enabled bypass permissions
* mode availability. projectSettings is intentionally excluded — a malicious
* project could otherwise enable bypass mode (security risk).
*/
export function hasAllowBypassPermissionsMode(): boolean {
return !!(
getSettingsForSource('userSettings')?.permissions
?.allowBypassPermissionsMode ||
getSettingsForSource('localSettings')?.permissions
?.allowBypassPermissionsMode ||
getSettingsForSource('flagSettings')?.permissions
?.allowBypassPermissionsMode ||
getSettingsForSource('policySettings')?.permissions
?.allowBypassPermissionsMode
)
}
/**
* Returns true if any trusted settings source has accepted the auto
* mode opt-in dialog. projectSettings is intentionally excluded —

View File

@@ -69,6 +69,12 @@ export const PermissionsSchema = lazySchema(() =>
.enum(['disable'])
.optional()
.describe('Disable the ability to bypass permission prompts'),
allowBypassPermissionsMode: z
.boolean()
.optional()
.describe(
'Allow bypass permissions mode to appear in the mode list without requiring the CLI flag',
),
...(feature('TRANSCRIPT_CLASSIFIER')
? {
disableAutoMode: z