ccperm 1.3.2 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +3 -2
- package/dist/scanner.js +60 -39
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -12,7 +12,7 @@ const updater_js_1 = require("./updater.js");
|
|
|
12
12
|
const aggregator_js_1 = require("./aggregator.js");
|
|
13
13
|
const renderer_js_1 = require("./renderer.js");
|
|
14
14
|
const interactive_js_1 = require("./interactive.js");
|
|
15
|
-
const KNOWN_FLAGS = new Set(['--cwd', '--verbose', '--static', '--update', '--fix', '--help', '-h', '--version', '-v']);
|
|
15
|
+
const KNOWN_FLAGS = new Set(['--cwd', '--verbose', '--static', '--update', '--fix', '--debug', '--help', '-h', '--version', '-v']);
|
|
16
16
|
const HELP = `${colors_js_1.CYAN}ccperm${colors_js_1.NC} — Audit Claude Code permissions across projects
|
|
17
17
|
|
|
18
18
|
${colors_js_1.YELLOW}Usage:${colors_js_1.NC}
|
|
@@ -75,8 +75,9 @@ async function main() {
|
|
|
75
75
|
const countText = fileCount > 0 ? ` ${colors_js_1.BOLD}${fileCount}${colors_js_1.NC} found` : '';
|
|
76
76
|
process.stdout.write(`\r ${colors_js_1.CYAN}${frames[frame++ % frames.length]}${colors_js_1.NC} Scanning...${countText}`);
|
|
77
77
|
}, 80) : null;
|
|
78
|
+
const isDebug = args.includes('--debug');
|
|
78
79
|
const onProgress = (count) => { fileCount = count; };
|
|
79
|
-
const files = await (0, scanner_js_1.findSettingsFiles)(searchDir, onProgress);
|
|
80
|
+
const files = await (0, scanner_js_1.findSettingsFiles)(searchDir, onProgress, isDebug);
|
|
80
81
|
if (spinner) {
|
|
81
82
|
clearInterval(spinner);
|
|
82
83
|
process.stdout.write('\r\x1b[K');
|
package/dist/scanner.js
CHANGED
|
@@ -7,8 +7,8 @@ exports.countDeprecated = countDeprecated;
|
|
|
7
7
|
exports.findSettingsFiles = findSettingsFiles;
|
|
8
8
|
exports.scanFile = scanFile;
|
|
9
9
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
10
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
10
11
|
const node_os_1 = __importDefault(require("node:os"));
|
|
11
|
-
const node_child_process_1 = require("node:child_process");
|
|
12
12
|
const PERM_RE = /"(Bash|Write|Edit|Read|Glob|Grep|WebSearch|WebFetch|mcp_)[^"]*"/g;
|
|
13
13
|
const DEPRECATED_RE = /:\*\)|:\*"/g;
|
|
14
14
|
function countDeprecated(results) {
|
|
@@ -27,46 +27,67 @@ function countDeprecated(results) {
|
|
|
27
27
|
}
|
|
28
28
|
return out;
|
|
29
29
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
const SKIP = new Set([
|
|
31
|
+
'node_modules', '.git', '.cache', '.local', '.npm', '.nvm', '.bun',
|
|
32
|
+
'.vscode', '.docker', '.cargo', '.rustup', 'go', '.gradle', '.m2',
|
|
33
|
+
'.Trash', 'Pictures', 'Music', 'Videos', 'Downloads',
|
|
34
|
+
'snap', '.snap',
|
|
35
|
+
'Library', 'Applications', '.Spotlight-V100', '.fseventsd',
|
|
36
|
+
'Movies', 'Photos', '.iCloud',
|
|
37
|
+
]);
|
|
38
|
+
async function findSettingsFiles(searchDir, onProgress, debug = false) {
|
|
39
|
+
const results = [];
|
|
40
|
+
const t0 = Date.now();
|
|
41
|
+
async function walk(dir) {
|
|
42
|
+
let entries;
|
|
43
|
+
try {
|
|
44
|
+
entries = await node_fs_1.default.promises.readdir(dir, { withFileTypes: true });
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return; // permission denied, etc.
|
|
48
|
+
}
|
|
49
|
+
for (const entry of entries) {
|
|
50
|
+
const name = entry.name;
|
|
51
|
+
if (name === '.claude' && entry.isDirectory()) {
|
|
52
|
+
// found a .claude dir — check for settings files inside
|
|
53
|
+
const claudeDir = node_path_1.default.join(dir, '.claude');
|
|
54
|
+
let inner;
|
|
55
|
+
try {
|
|
56
|
+
inner = await node_fs_1.default.promises.readdir(claudeDir);
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
for (const f of inner) {
|
|
62
|
+
if (f.startsWith('settings') && f.endsWith('.json')) {
|
|
63
|
+
const full = node_path_1.default.join(claudeDir, f);
|
|
64
|
+
try {
|
|
65
|
+
await node_fs_1.default.promises.access(full, node_fs_1.default.constants.W_OK);
|
|
66
|
+
results.push(full);
|
|
67
|
+
onProgress?.(results.length);
|
|
68
|
+
if (debug)
|
|
69
|
+
console.error(` [debug] found: ${full}`);
|
|
70
|
+
}
|
|
71
|
+
catch { /* not writable */ }
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
continue; // don't recurse into .claude
|
|
75
|
+
}
|
|
76
|
+
if (!entry.isDirectory())
|
|
77
|
+
continue;
|
|
78
|
+
if (SKIP.has(name))
|
|
79
|
+
continue;
|
|
80
|
+
if (name.startsWith('.') && name !== '.claude')
|
|
81
|
+
continue; // skip other hidden dirs
|
|
82
|
+
await walk(node_path_1.default.join(dir, name));
|
|
83
|
+
}
|
|
34
84
|
}
|
|
35
|
-
|
|
36
|
-
|
|
85
|
+
await walk(searchDir);
|
|
86
|
+
if (debug) {
|
|
87
|
+
console.error(` [debug] scan completed in ${Date.now() - t0}ms`);
|
|
88
|
+
console.error(` [debug] found ${results.length} files`);
|
|
37
89
|
}
|
|
38
|
-
|
|
39
|
-
function findSettingsFiles(searchDir, onProgress) {
|
|
40
|
-
const prune = ['node_modules', '.git', '.cache', '.local', '.npm', '.nvm', '.bun',
|
|
41
|
-
'snap', '.vscode', '.docker', '.cargo', '.rustup', 'go', '.gradle', '.m2',
|
|
42
|
-
'Library', '.Trash', 'Pictures', 'Music', 'Videos', 'Downloads'];
|
|
43
|
-
const pruneArgs = prune.flatMap((d) => ['-name', d, '-o']).slice(0, -1);
|
|
44
|
-
return new Promise((resolve) => {
|
|
45
|
-
const results = [];
|
|
46
|
-
let buf = '';
|
|
47
|
-
const child = (0, node_child_process_1.execFile)('find', [
|
|
48
|
-
searchDir,
|
|
49
|
-
'(', ...pruneArgs, ')', '-prune',
|
|
50
|
-
'-o', '-path', '*/.claude/settings*.json', '-type', 'f', '-print',
|
|
51
|
-
], { encoding: 'utf8', timeout: 15000 });
|
|
52
|
-
child.stdout?.on('data', (chunk) => {
|
|
53
|
-
buf += chunk;
|
|
54
|
-
const lines = buf.split('\n');
|
|
55
|
-
buf = lines.pop() || '';
|
|
56
|
-
for (const line of lines) {
|
|
57
|
-
if (line && isWritable(line))
|
|
58
|
-
results.push(line);
|
|
59
|
-
}
|
|
60
|
-
onProgress?.(results.length);
|
|
61
|
-
});
|
|
62
|
-
child.on('close', () => {
|
|
63
|
-
if (buf && isWritable(buf))
|
|
64
|
-
results.push(buf);
|
|
65
|
-
onProgress?.(results.length);
|
|
66
|
-
resolve(results);
|
|
67
|
-
});
|
|
68
|
-
child.on('error', () => resolve(results));
|
|
69
|
-
});
|
|
90
|
+
return results;
|
|
70
91
|
}
|
|
71
92
|
function scanFile(filePath) {
|
|
72
93
|
const home = node_os_1.default.homedir();
|