Skip to content

Commit 1e1d53d

Browse files
committed
feat(config): add permission cleanup and merging functionality
1 parent cdf9dcd commit 1e1d53d

5 files changed

Lines changed: 130 additions & 8 deletions

File tree

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Permission Cleanup Task Plan
2+
3+
## Context
4+
ZCF v2.0 之前的版本中,用户配置可能包含无效的权限项 `"mcp__.*"`(已被官方确认无法生效),以及冗余的权限项(如同时存在 `Bash``Bash(*)`)。需要在配置合并时自动清理这些无效和冗余的权限。
5+
6+
## Implementation Plan
7+
8+
### 1. Create Permission Cleanup Utility
9+
- **File**: `src/utils/permission-cleaner.ts`
10+
- **Functions**:
11+
- `cleanupPermissions()`: Core cleanup logic
12+
- `mergeAndCleanPermissions()`: Wrapper for merge operations
13+
- **Cleanup Rules**:
14+
- Remove literal `"mcp__.*"` (invalid from v2.0 and earlier)
15+
- Remove permissions that start with template permissions (e.g., remove `Bash(*)` if template has `Bash`)
16+
17+
### 2. Update Config Merge Logic
18+
- **File**: `src/utils/config.ts`
19+
- **Function**: `mergeSettingsFile()`
20+
- **Change**: Replace `mergeArraysUnique()` with `mergeAndCleanPermissions()`
21+
22+
### 3. Update Simple Config Import
23+
- **File**: `src/utils/simple-config.ts`
24+
- **Function**: `importRecommendedPermissions()`
25+
- **Change**: Apply cleanup when importing permissions
26+
27+
### 4. Test Coverage
28+
- Verify cleanup of `"mcp__.*"` literal
29+
- Verify removal of redundant permissions
30+
- Ensure valid permissions are preserved
31+
32+
## Expected Results
33+
- Automatic cleanup of invalid `"mcp__.*"` permissions during config updates
34+
- Prevention of permission redundancy (e.g., both `Bash` and `Bash(*)`)
35+
- Clean and effective permission lists
36+
37+
## Files Modified
38+
1. `/Users/miaoda/Documents/code/zcf/src/utils/permission-cleaner.ts` (created)
39+
2. `/Users/miaoda/Documents/code/zcf/src/utils/config.ts` (modified)
40+
3. `/Users/miaoda/Documents/code/zcf/src/utils/simple-config.ts` (modified)

src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ export { getPlatform, commandExists } from './utils/platform'
55
export * from './utils/installer'
66
export * from './utils/config'
77
export * from './utils/mcp'
8-
export { importRecommendedEnv, importRecommendedPermissions, openSettingsJson } from './utils/simple-config'
8+
export { importRecommendedEnv, importRecommendedPermissions, openSettingsJson } from './utils/simple-config'
9+
export { cleanupPermissions, mergeAndCleanPermissions } from './utils/permission-cleaner'

src/utils/config.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@ import {
1212
type CopyDirOptions
1313
} from './fs-operations';
1414
import { readJsonConfig, writeJsonConfig } from './json-config';
15-
import { deepMerge, mergeArraysUnique } from './object-utils';
15+
import { deepMerge } from './object-utils';
1616
import type { ClaudeSettings, ApiConfig } from '../types/config';
1717
import { readZcfConfig } from './zcf-config';
18+
import { mergeAndCleanPermissions } from './permission-cleaner';
1819
export type { ApiConfig } from '../types/config';
1920

2021
export function ensureClaudeDir() {
@@ -211,11 +212,11 @@ export function mergeSettingsFile(templatePath: string, targetPath: string): voi
211212
// Ensure user's env vars are preserved
212213
mergedSettings.env = mergedEnv;
213214

214-
// Handle permissions.allow array specially to avoid duplicates
215+
// Handle permissions.allow array specially to avoid duplicates and clean invalid entries
215216
if (mergedSettings.permissions && mergedSettings.permissions.allow) {
216-
mergedSettings.permissions.allow = mergeArraysUnique(
217-
templateSettings.permissions?.allow || [],
218-
existingSettings.permissions?.allow || []
217+
mergedSettings.permissions.allow = mergeAndCleanPermissions(
218+
templateSettings.permissions?.allow,
219+
existingSettings.permissions?.allow
219220
);
220221
}
221222

src/utils/permission-cleaner.ts

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/**
2+
* Clean up and deduplicate permissions array
3+
* Removes invalid and redundant permissions based on template
4+
*/
5+
6+
/**
7+
* Clean up permissions array by removing invalid and redundant entries
8+
* @param templatePermissions - Permissions from template (source of truth)
9+
* @param userPermissions - User's existing permissions
10+
* @returns Cleaned permissions array
11+
*/
12+
export function cleanupPermissions(templatePermissions: string[], userPermissions: string[]): string[] {
13+
// Create a set for template permissions for O(1) lookup
14+
const templateSet = new Set(templatePermissions);
15+
16+
// Filter user permissions
17+
const cleanedPermissions = userPermissions.filter((permission) => {
18+
// Remove literal "mcp__.*" (invalid wildcard from v2.0 and earlier)
19+
if (['mcp__.*', 'mcp__*', 'mcp__(*)'].includes(permission)) {
20+
return false;
21+
}
22+
23+
// Check if this permission is redundant (covered by a template permission)
24+
// For example, if template has "Bash", remove "Bash(*)", "Bash(mkdir:*)", etc.
25+
for (const templatePerm of templatePermissions) {
26+
// Skip if it's the exact same permission (will be handled by mergeArraysUnique)
27+
if (permission === templatePerm) {
28+
continue;
29+
}
30+
31+
// Check if user permission starts with template permission followed by "("
32+
// This catches patterns like "Bash(*)", "Bash(mkdir:*)" when template has "Bash"
33+
if (permission.startsWith(templatePerm)) {
34+
return false; // Remove this redundant permission
35+
}
36+
}
37+
38+
// Keep all other permissions
39+
return true;
40+
});
41+
42+
// Merge template and cleaned user permissions, removing duplicates
43+
const merged = [...templateSet];
44+
45+
for (const permission of cleanedPermissions) {
46+
if (!templateSet.has(permission)) {
47+
merged.push(permission);
48+
}
49+
}
50+
51+
return merged;
52+
}
53+
54+
/**
55+
* Merge and clean permissions arrays
56+
* Combines template and user permissions while removing invalid/redundant entries
57+
* @param templatePermissions - Permissions from template
58+
* @param userPermissions - User's existing permissions
59+
* @returns Merged and cleaned permissions array
60+
*/
61+
export function mergeAndCleanPermissions(
62+
templatePermissions: string[] | undefined,
63+
userPermissions: string[] | undefined
64+
): string[] {
65+
const template = templatePermissions || [];
66+
const user = userPermissions || [];
67+
68+
return cleanupPermissions(template, user);
69+
}

src/utils/simple-config.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { SETTINGS_FILE, CLAUDE_DIR } from '../constants.js';
55
import { ensureDir } from './fs-operations.js';
66
import { exec } from 'tinyexec';
77
import { getPlatform } from './platform.js';
8+
import { mergeAndCleanPermissions } from './permission-cleaner.js';
89

910
const __dirname = dirname(fileURLToPath(import.meta.url));
1011

@@ -54,8 +55,18 @@ export async function importRecommendedPermissions(): Promise<void> {
5455
const templateSettings = getTemplateSettings();
5556
const currentSettings = loadCurrentSettings();
5657

57-
// Merge permissions
58-
currentSettings.permissions = templateSettings.permissions;
58+
// Merge permissions with cleanup
59+
if (templateSettings.permissions && templateSettings.permissions.allow) {
60+
currentSettings.permissions = {
61+
...templateSettings.permissions,
62+
allow: mergeAndCleanPermissions(
63+
templateSettings.permissions.allow,
64+
currentSettings.permissions?.allow
65+
)
66+
};
67+
} else {
68+
currentSettings.permissions = templateSettings.permissions;
69+
}
5970

6071
saveSettings(currentSettings);
6172
}

0 commit comments

Comments
 (0)