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:
committed by
GitHub
parent
7c8bdcc3e2
commit
31be66d764
@@ -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
|
||||
|
||||
@@ -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'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
27
src/utils/settings/allowBypassPermissionsMode.test.ts
Normal file
27
src/utils/settings/allowBypassPermissionsMode.test.ts
Normal 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)
|
||||
})
|
||||
})
|
||||
@@ -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 —
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user