ccperm 1.3.3 → 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.
Files changed (2) hide show
  1. package/dist/scanner.js +59 -65
  2. package/package.json +1 -1
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,73 +27,67 @@ function countDeprecated(results) {
27
27
  }
28
28
  return out;
29
29
  }
30
- function isWritable(f) {
31
- try {
32
- node_fs_1.default.accessSync(f, node_fs_1.default.constants.W_OK);
33
- return true;
34
- }
35
- catch {
36
- return false;
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
+ }
37
84
  }
38
- }
39
- function findSettingsFiles(searchDir, onProgress, debug = false) {
40
- const prune = [
41
- // common
42
- 'node_modules', '.git', '.cache', '.local', '.npm', '.nvm', '.bun',
43
- '.vscode', '.docker', '.cargo', '.rustup', 'go', '.gradle', '.m2',
44
- '.Trash', 'Pictures', 'Music', 'Videos', 'Downloads',
45
- // linux
46
- 'snap', '.snap',
47
- // macOS
48
- 'Library', 'Applications', '.Spotlight-V100', '.fseventsd',
49
- 'Movies', 'Photos', '.iCloud',
50
- ];
51
- const pruneArgs = prune.flatMap((d) => ['-name', d, '-o']).slice(0, -1);
52
- const findArgs = [
53
- searchDir,
54
- '(', ...pruneArgs, ')', '-prune',
55
- '-o', '-path', '*/.claude/settings*.json', '-type', 'f', '-print',
56
- ];
85
+ await walk(searchDir);
57
86
  if (debug) {
58
- console.error(`\n [debug] find ${findArgs.join(' ')}`);
59
- console.error(` [debug] prune: ${prune.join(', ')}`);
87
+ console.error(` [debug] scan completed in ${Date.now() - t0}ms`);
88
+ console.error(` [debug] found ${results.length} files`);
60
89
  }
61
- const t0 = Date.now();
62
- return new Promise((resolve) => {
63
- const results = [];
64
- let buf = '';
65
- const child = (0, node_child_process_1.execFile)('find', findArgs, { encoding: 'utf8', timeout: 30000 });
66
- child.stderr?.on('data', (chunk) => {
67
- if (debug)
68
- process.stderr.write(` [debug] stderr: ${chunk}`);
69
- });
70
- child.stdout?.on('data', (chunk) => {
71
- buf += chunk;
72
- const lines = buf.split('\n');
73
- buf = lines.pop() || '';
74
- for (const line of lines) {
75
- if (line && isWritable(line))
76
- results.push(line);
77
- }
78
- onProgress?.(results.length);
79
- });
80
- child.on('close', (code) => {
81
- if (buf && isWritable(buf))
82
- results.push(buf);
83
- onProgress?.(results.length);
84
- if (debug) {
85
- console.error(` [debug] find exited with code ${code} in ${Date.now() - t0}ms`);
86
- console.error(` [debug] found ${results.length} files`);
87
- results.forEach((f) => console.error(` [debug] ${f}`));
88
- }
89
- resolve(results);
90
- });
91
- child.on('error', (err) => {
92
- if (debug)
93
- console.error(` [debug] find error: ${err.message}`);
94
- resolve(results);
95
- });
96
- });
90
+ return results;
97
91
  }
98
92
  function scanFile(filePath) {
99
93
  const home = node_os_1.default.homedir();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccperm",
3
- "version": "1.3.3",
3
+ "version": "1.4.0",
4
4
  "description": "Audit Claude Code permissions across all your projects",
5
5
  "bin": {
6
6
  "ccperm": "bin/ccperm.js"