Skip to content

Commit 31be66d

Browse files
authored
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
1 parent 7c8bdcc commit 31be66d

6 files changed

Lines changed: 58 additions & 3 deletions

File tree

src/cli/print.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4582,7 +4582,7 @@ function handleSetPermissionMode(
45824582
subtype: 'error',
45834583
request_id: requestId,
45844584
error:
4585-
'Cannot set permission mode to bypassPermissions because the session was not launched with --dangerously-skip-permissions',
4585+
'Cannot set permission mode to bypassPermissions. Enable it with --allow-dangerously-skip-permissions or set permissions.allowBypassPermissionsMode in settings.json',
45864586
},
45874587
})
45884588
return toolPermissionContext

src/hooks/useReplBridge.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -434,7 +434,7 @@ export function useReplBridge(messages: Message[], setMessages: (action: React.S
434434
if (!store.getState().toolPermissionContext.isBypassPermissionsModeAvailable) {
435435
return {
436436
ok: false,
437-
error: 'Cannot set permission mode to bypassPermissions because the session was not launched with --dangerously-skip-permissions'
437+
error: 'Cannot set permission mode to bypassPermissions. Enable it with --allow-dangerously-skip-permissions or set permissions.allowBypassPermissionsMode in settings.json'
438438
};
439439
}
440440
}

src/utils/permissions/permissionSetup.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
getSettings_DEPRECATED,
2020
getSettingsFilePathForSource,
2121
getUseAutoModeDuringPlan,
22+
hasAllowBypassPermissionsMode,
2223
hasAutoModeOptIn,
2324
} from '../settings/settings.js'
2425
import {
@@ -936,9 +937,11 @@ export async function initializeToolPermissionContext({
936937
const settings = getSettings_DEPRECATED() || {}
937938
const settingsDisableBypassPermissionsMode =
938939
settings.permissions?.disableBypassPermissionsMode === 'disable'
940+
const settingsAllowBypassPermissionsMode = hasAllowBypassPermissionsMode()
939941
const isBypassPermissionsModeAvailable =
940942
(permissionMode === 'bypassPermissions' ||
941-
allowDangerouslySkipPermissions) &&
943+
allowDangerouslySkipPermissions ||
944+
settingsAllowBypassPermissionsMode) &&
942945
!growthBookDisableBypassPermissionsMode &&
943946
!settingsDisableBypassPermissionsMode
944947

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { describe, test, expect } from 'bun:test'
2+
3+
describe('SettingsSchema allowBypassPermissionsMode', () => {
4+
test('accepts allowBypassPermissionsMode: true', async () => {
5+
const { SettingsSchema } = await import('./types.js')
6+
const result = SettingsSchema().safeParse({
7+
permissions: { allowBypassPermissionsMode: true },
8+
})
9+
expect(result.success).toBe(true)
10+
})
11+
12+
test('accepts allowBypassPermissionsMode: false', async () => {
13+
const { SettingsSchema } = await import('./types.js')
14+
const result = SettingsSchema().safeParse({
15+
permissions: { allowBypassPermissionsMode: false },
16+
})
17+
expect(result.success).toBe(true)
18+
})
19+
20+
test('rejects non-boolean allowBypassPermissionsMode', async () => {
21+
const { SettingsSchema } = await import('./types.js')
22+
const result = SettingsSchema().safeParse({
23+
permissions: { allowBypassPermissionsMode: 'yes' },
24+
})
25+
expect(result.success).toBe(false)
26+
})
27+
})

src/utils/settings/settings.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,7 @@ export function getManagedSettingsKeysForLogging(
574574
'ask',
575575
'defaultMode',
576576
'disableBypassPermissionsMode',
577+
'allowBypassPermissionsMode',
577578
...(feature('TRANSCRIPT_CLASSIFIER') ? ['disableAutoMode'] : []),
578579
'additionalDirectories',
579580
]),
@@ -888,6 +889,24 @@ export function hasSkipDangerousModePermissionPrompt(): boolean {
888889
)
889890
}
890891

892+
/**
893+
* Returns true if any trusted settings source has enabled bypass permissions
894+
* mode availability. projectSettings is intentionally excluded — a malicious
895+
* project could otherwise enable bypass mode (security risk).
896+
*/
897+
export function hasAllowBypassPermissionsMode(): boolean {
898+
return !!(
899+
getSettingsForSource('userSettings')?.permissions
900+
?.allowBypassPermissionsMode ||
901+
getSettingsForSource('localSettings')?.permissions
902+
?.allowBypassPermissionsMode ||
903+
getSettingsForSource('flagSettings')?.permissions
904+
?.allowBypassPermissionsMode ||
905+
getSettingsForSource('policySettings')?.permissions
906+
?.allowBypassPermissionsMode
907+
)
908+
}
909+
891910
/**
892911
* Returns true if any trusted settings source has accepted the auto
893912
* mode opt-in dialog. projectSettings is intentionally excluded —

src/utils/settings/types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,12 @@ export const PermissionsSchema = lazySchema(() =>
6969
.enum(['disable'])
7070
.optional()
7171
.describe('Disable the ability to bypass permission prompts'),
72+
allowBypassPermissionsMode: z
73+
.boolean()
74+
.optional()
75+
.describe(
76+
'Allow bypass permissions mode to appear in the mode list without requiring the CLI flag',
77+
),
7278
...(feature('TRANSCRIPT_CLASSIFIER')
7379
? {
7480
disableAutoMode: z

0 commit comments

Comments
 (0)