ctx-sync 1.0.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 (145) hide show
  1. package/dist/commands/audit.d.ts +76 -0
  2. package/dist/commands/audit.d.ts.map +1 -0
  3. package/dist/commands/audit.js +367 -0
  4. package/dist/commands/audit.js.map +1 -0
  5. package/dist/commands/config.d.ts +58 -0
  6. package/dist/commands/config.d.ts.map +1 -0
  7. package/dist/commands/config.js +114 -0
  8. package/dist/commands/config.js.map +1 -0
  9. package/dist/commands/dir.d.ts +56 -0
  10. package/dist/commands/dir.d.ts.map +1 -0
  11. package/dist/commands/dir.js +172 -0
  12. package/dist/commands/dir.js.map +1 -0
  13. package/dist/commands/docker.d.ts +140 -0
  14. package/dist/commands/docker.d.ts.map +1 -0
  15. package/dist/commands/docker.js +380 -0
  16. package/dist/commands/docker.js.map +1 -0
  17. package/dist/commands/env.d.ts +96 -0
  18. package/dist/commands/env.d.ts.map +1 -0
  19. package/dist/commands/env.js +352 -0
  20. package/dist/commands/env.js.map +1 -0
  21. package/dist/commands/init.d.ts +89 -0
  22. package/dist/commands/init.d.ts.map +1 -0
  23. package/dist/commands/init.js +272 -0
  24. package/dist/commands/init.js.map +1 -0
  25. package/dist/commands/key.d.ts +92 -0
  26. package/dist/commands/key.d.ts.map +1 -0
  27. package/dist/commands/key.js +274 -0
  28. package/dist/commands/key.js.map +1 -0
  29. package/dist/commands/list.d.ts +38 -0
  30. package/dist/commands/list.d.ts.map +1 -0
  31. package/dist/commands/list.js +84 -0
  32. package/dist/commands/list.js.map +1 -0
  33. package/dist/commands/note.d.ts +151 -0
  34. package/dist/commands/note.d.ts.map +1 -0
  35. package/dist/commands/note.js +411 -0
  36. package/dist/commands/note.js.map +1 -0
  37. package/dist/commands/pull.d.ts +47 -0
  38. package/dist/commands/pull.d.ts.map +1 -0
  39. package/dist/commands/pull.js +94 -0
  40. package/dist/commands/pull.js.map +1 -0
  41. package/dist/commands/push.d.ts +40 -0
  42. package/dist/commands/push.d.ts.map +1 -0
  43. package/dist/commands/push.js +94 -0
  44. package/dist/commands/push.js.map +1 -0
  45. package/dist/commands/restore.d.ts +116 -0
  46. package/dist/commands/restore.d.ts.map +1 -0
  47. package/dist/commands/restore.js +336 -0
  48. package/dist/commands/restore.js.map +1 -0
  49. package/dist/commands/service.d.ts +83 -0
  50. package/dist/commands/service.d.ts.map +1 -0
  51. package/dist/commands/service.js +259 -0
  52. package/dist/commands/service.js.map +1 -0
  53. package/dist/commands/show.d.ts +63 -0
  54. package/dist/commands/show.d.ts.map +1 -0
  55. package/dist/commands/show.js +243 -0
  56. package/dist/commands/show.js.map +1 -0
  57. package/dist/commands/status.d.ts +53 -0
  58. package/dist/commands/status.d.ts.map +1 -0
  59. package/dist/commands/status.js +150 -0
  60. package/dist/commands/status.js.map +1 -0
  61. package/dist/commands/sync.d.ts +105 -0
  62. package/dist/commands/sync.d.ts.map +1 -0
  63. package/dist/commands/sync.js +243 -0
  64. package/dist/commands/sync.js.map +1 -0
  65. package/dist/commands/team.d.ts +79 -0
  66. package/dist/commands/team.d.ts.map +1 -0
  67. package/dist/commands/team.js +233 -0
  68. package/dist/commands/team.js.map +1 -0
  69. package/dist/commands/track.d.ts +109 -0
  70. package/dist/commands/track.d.ts.map +1 -0
  71. package/dist/commands/track.js +406 -0
  72. package/dist/commands/track.js.map +1 -0
  73. package/dist/core/command-validator.d.ts +100 -0
  74. package/dist/core/command-validator.d.ts.map +1 -0
  75. package/dist/core/command-validator.js +299 -0
  76. package/dist/core/command-validator.js.map +1 -0
  77. package/dist/core/config-store.d.ts +76 -0
  78. package/dist/core/config-store.d.ts.map +1 -0
  79. package/dist/core/config-store.js +148 -0
  80. package/dist/core/config-store.js.map +1 -0
  81. package/dist/core/directories-handler.d.ts +116 -0
  82. package/dist/core/directories-handler.d.ts.map +1 -0
  83. package/dist/core/directories-handler.js +199 -0
  84. package/dist/core/directories-handler.js.map +1 -0
  85. package/dist/core/docker-handler.d.ts +183 -0
  86. package/dist/core/docker-handler.d.ts.map +1 -0
  87. package/dist/core/docker-handler.js +515 -0
  88. package/dist/core/docker-handler.js.map +1 -0
  89. package/dist/core/encryption.d.ts +79 -0
  90. package/dist/core/encryption.d.ts.map +1 -0
  91. package/dist/core/encryption.js +111 -0
  92. package/dist/core/encryption.js.map +1 -0
  93. package/dist/core/env-handler.d.ts +128 -0
  94. package/dist/core/env-handler.d.ts.map +1 -0
  95. package/dist/core/env-handler.js +272 -0
  96. package/dist/core/env-handler.js.map +1 -0
  97. package/dist/core/git-sync.d.ts +88 -0
  98. package/dist/core/git-sync.d.ts.map +1 -0
  99. package/dist/core/git-sync.js +143 -0
  100. package/dist/core/git-sync.js.map +1 -0
  101. package/dist/core/key-store.d.ts +51 -0
  102. package/dist/core/key-store.d.ts.map +1 -0
  103. package/dist/core/key-store.js +108 -0
  104. package/dist/core/key-store.js.map +1 -0
  105. package/dist/core/log-sanitizer.d.ts +72 -0
  106. package/dist/core/log-sanitizer.d.ts.map +1 -0
  107. package/dist/core/log-sanitizer.js +202 -0
  108. package/dist/core/log-sanitizer.js.map +1 -0
  109. package/dist/core/path-validator.d.ts +37 -0
  110. package/dist/core/path-validator.d.ts.map +1 -0
  111. package/dist/core/path-validator.js +127 -0
  112. package/dist/core/path-validator.js.map +1 -0
  113. package/dist/core/recipients.d.ts +99 -0
  114. package/dist/core/recipients.d.ts.map +1 -0
  115. package/dist/core/recipients.js +206 -0
  116. package/dist/core/recipients.js.map +1 -0
  117. package/dist/core/services-handler.d.ts +113 -0
  118. package/dist/core/services-handler.d.ts.map +1 -0
  119. package/dist/core/services-handler.js +176 -0
  120. package/dist/core/services-handler.js.map +1 -0
  121. package/dist/core/state-manager.d.ts +96 -0
  122. package/dist/core/state-manager.d.ts.map +1 -0
  123. package/dist/core/state-manager.js +165 -0
  124. package/dist/core/state-manager.js.map +1 -0
  125. package/dist/core/transport.d.ts +28 -0
  126. package/dist/core/transport.d.ts.map +1 -0
  127. package/dist/core/transport.js +79 -0
  128. package/dist/core/transport.js.map +1 -0
  129. package/dist/index.d.ts +20 -0
  130. package/dist/index.d.ts.map +1 -0
  131. package/dist/index.js +80 -0
  132. package/dist/index.js.map +1 -0
  133. package/dist/types/index.d.ts +5 -0
  134. package/dist/types/index.d.ts.map +1 -0
  135. package/dist/types/index.js +2 -0
  136. package/dist/types/index.js.map +1 -0
  137. package/dist/utils/errors.d.ts +81 -0
  138. package/dist/utils/errors.d.ts.map +1 -0
  139. package/dist/utils/errors.js +191 -0
  140. package/dist/utils/errors.js.map +1 -0
  141. package/dist/utils/secure-memory.d.ts +65 -0
  142. package/dist/utils/secure-memory.d.ts.map +1 -0
  143. package/dist/utils/secure-memory.js +86 -0
  144. package/dist/utils/secure-memory.js.map +1 -0
  145. package/package.json +58 -0
@@ -0,0 +1,76 @@
1
+ /**
2
+ * `ctx-sync audit` command.
3
+ *
4
+ * Runs a comprehensive security audit:
5
+ * - Key file permissions (0o600).
6
+ * - Config directory permissions (0o700).
7
+ * - Remote transport security (SSH/HTTPS only).
8
+ * - All state files are .age (no plaintext .json state).
9
+ * - Git history scan for plaintext secret patterns.
10
+ * - Repo size report.
11
+ *
12
+ * Reports issues with severity levels: critical, warning, info.
13
+ *
14
+ * @module commands/audit
15
+ */
16
+ import type { Command } from 'commander';
17
+ /** Severity level for audit findings */
18
+ export type AuditSeverity = 'critical' | 'warning' | 'info';
19
+ /** A single audit finding */
20
+ export interface AuditFinding {
21
+ severity: AuditSeverity;
22
+ check: string;
23
+ message: string;
24
+ }
25
+ /** Result of the full audit */
26
+ export interface AuditResult {
27
+ findings: AuditFinding[];
28
+ passed: boolean;
29
+ repoSizeBytes: number | null;
30
+ repoSizeHuman: string | null;
31
+ stateFileCount: number;
32
+ hasRemote: boolean;
33
+ }
34
+ /**
35
+ * Check key file and config directory permissions.
36
+ */
37
+ export declare function checkPermissions(configDir: string): AuditFinding[];
38
+ /**
39
+ * Validate remote transport security.
40
+ */
41
+ export declare function checkRemoteTransport(syncDir: string): {
42
+ findings: AuditFinding[];
43
+ hasRemote: boolean;
44
+ };
45
+ /**
46
+ * Verify all state files are .age (not .json).
47
+ */
48
+ export declare function checkStateFiles(syncDir: string): {
49
+ findings: AuditFinding[];
50
+ stateFileCount: number;
51
+ };
52
+ /**
53
+ * Scan Git history for plaintext secret patterns.
54
+ */
55
+ export declare function checkGitHistory(syncDir: string): AuditFinding[];
56
+ /**
57
+ * Report repository size.
58
+ */
59
+ export declare function checkRepoSize(syncDir: string): {
60
+ findings: AuditFinding[];
61
+ sizeBytes: number | null;
62
+ sizeHuman: string | null;
63
+ };
64
+ /**
65
+ * Format bytes into human-readable string.
66
+ */
67
+ export declare function formatBytes(bytes: number): string;
68
+ /**
69
+ * Execute the full audit.
70
+ */
71
+ export declare function executeAudit(): AuditResult;
72
+ /**
73
+ * Register the `ctx-sync audit` command on the given program.
74
+ */
75
+ export declare function registerAuditCommand(program: Command): void;
76
+ //# sourceMappingURL=audit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/commands/audit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAKH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AASzC,wCAAwC;AACxC,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,SAAS,GAAG,MAAM,CAAC;AAE5D,6BAA6B;AAC7B,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,aAAa,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,+BAA+B;AAC/B,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,MAAM,EAAE,OAAO,CAAC;IAChB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,OAAO,CAAC;CACpB;AAiBD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,YAAY,EAAE,CAqBlE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG;IACrD,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,SAAS,EAAE,OAAO,CAAC;CACpB,CAmEA;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG;IAChD,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;CACxB,CAuEA;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,EAAE,CA6C/D;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG;IAC9C,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B,CAsBA;AAqBD;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CASjD;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,WAAW,CAkC1C;AAID;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA0D3D"}
@@ -0,0 +1,367 @@
1
+ /**
2
+ * `ctx-sync audit` command.
3
+ *
4
+ * Runs a comprehensive security audit:
5
+ * - Key file permissions (0o600).
6
+ * - Config directory permissions (0o700).
7
+ * - Remote transport security (SSH/HTTPS only).
8
+ * - All state files are .age (no plaintext .json state).
9
+ * - Git history scan for plaintext secret patterns.
10
+ * - Repo size report.
11
+ *
12
+ * Reports issues with severity levels: critical, warning, info.
13
+ *
14
+ * @module commands/audit
15
+ */
16
+ import * as fs from 'node:fs';
17
+ import * as path from 'node:path';
18
+ import { execSync } from 'node:child_process';
19
+ import { withErrorHandler } from '../utils/errors.js';
20
+ import { STATE_FILES } from '@ctx-sync/shared';
21
+ import { verifyPermissions } from '../core/key-store.js';
22
+ import { validateRemoteUrl } from '../core/transport.js';
23
+ import { getConfigDir, getSyncDir } from './init.js';
24
+ // ─── Audit Checks ─────────────────────────────────────────────────────────
25
+ /** Secret patterns to scan for in Git history */
26
+ const SECRET_PATTERNS = [
27
+ { pattern: /sk_live_[a-zA-Z0-9]+/, label: 'Stripe live key' },
28
+ { pattern: /sk_test_[a-zA-Z0-9]+/, label: 'Stripe test key' },
29
+ { pattern: /ghp_[a-zA-Z0-9]+/, label: 'GitHub PAT' },
30
+ { pattern: /gho_[a-zA-Z0-9]+/, label: 'GitHub OAuth token' },
31
+ { pattern: /github_pat_[a-zA-Z0-9]+/, label: 'GitHub fine-grained PAT' },
32
+ { pattern: /xoxb-[0-9]+-[0-9]+-[a-zA-Z0-9]+/, label: 'Slack bot token' },
33
+ { pattern: /xoxp-[0-9]+-[0-9]+-[a-zA-Z0-9]+/, label: 'Slack user token' },
34
+ { pattern: /AKIA[A-Z0-9]{16}/, label: 'AWS access key' },
35
+ { pattern: /AGE-SECRET-KEY-[A-Z0-9]+/, label: 'Age private key' },
36
+ ];
37
+ /**
38
+ * Check key file and config directory permissions.
39
+ */
40
+ export function checkPermissions(configDir) {
41
+ const findings = [];
42
+ const result = verifyPermissions(configDir);
43
+ if (result.valid) {
44
+ findings.push({
45
+ severity: 'info',
46
+ check: 'permissions',
47
+ message: 'Key file (600) and config directory (700) permissions are correct.',
48
+ });
49
+ }
50
+ else {
51
+ for (const issue of result.issues) {
52
+ findings.push({
53
+ severity: 'critical',
54
+ check: 'permissions',
55
+ message: issue,
56
+ });
57
+ }
58
+ }
59
+ return findings;
60
+ }
61
+ /**
62
+ * Validate remote transport security.
63
+ */
64
+ export function checkRemoteTransport(syncDir) {
65
+ const findings = [];
66
+ let hasRemote = false;
67
+ const gitDir = path.join(syncDir, '.git');
68
+ if (!fs.existsSync(gitDir)) {
69
+ findings.push({
70
+ severity: 'warning',
71
+ check: 'transport',
72
+ message: 'No Git repository found in sync directory.',
73
+ });
74
+ return { findings, hasRemote };
75
+ }
76
+ try {
77
+ const remoteOutput = execSync('git remote -v', {
78
+ cwd: syncDir,
79
+ encoding: 'utf-8',
80
+ stdio: ['pipe', 'pipe', 'pipe'],
81
+ }).trim();
82
+ if (!remoteOutput) {
83
+ findings.push({
84
+ severity: 'info',
85
+ check: 'transport',
86
+ message: 'No remote configured (local-only mode).',
87
+ });
88
+ return { findings, hasRemote };
89
+ }
90
+ hasRemote = true;
91
+ // Parse remote URLs
92
+ const lines = remoteOutput.split('\n');
93
+ const urls = new Set();
94
+ for (const line of lines) {
95
+ const parts = line.split(/\s+/);
96
+ if (parts[1]) {
97
+ urls.add(parts[1]);
98
+ }
99
+ }
100
+ for (const url of urls) {
101
+ try {
102
+ validateRemoteUrl(url);
103
+ findings.push({
104
+ severity: 'info',
105
+ check: 'transport',
106
+ message: `Remote URL is secure: ${url}`,
107
+ });
108
+ }
109
+ catch (err) {
110
+ findings.push({
111
+ severity: 'critical',
112
+ check: 'transport',
113
+ message: `Insecure remote: ${err instanceof Error ? err.message : String(err)}`,
114
+ });
115
+ }
116
+ }
117
+ }
118
+ catch {
119
+ findings.push({
120
+ severity: 'warning',
121
+ check: 'transport',
122
+ message: 'Could not check remote URLs (git command failed).',
123
+ });
124
+ }
125
+ return { findings, hasRemote };
126
+ }
127
+ /**
128
+ * Verify all state files are .age (not .json).
129
+ */
130
+ export function checkStateFiles(syncDir) {
131
+ const findings = [];
132
+ if (!fs.existsSync(syncDir)) {
133
+ findings.push({
134
+ severity: 'warning',
135
+ check: 'state-files',
136
+ message: 'Sync directory does not exist.',
137
+ });
138
+ return { findings, stateFileCount: 0 };
139
+ }
140
+ const entries = fs.readdirSync(syncDir).filter((e) => !e.startsWith('.') && e !== 'sessions');
141
+ const ageFiles = entries.filter((e) => e.endsWith('.age'));
142
+ const jsonFiles = entries.filter((e) => e.endsWith('.json') && e !== STATE_FILES.MANIFEST);
143
+ if (jsonFiles.length > 0) {
144
+ for (const file of jsonFiles) {
145
+ findings.push({
146
+ severity: 'critical',
147
+ check: 'state-files',
148
+ message: `Plaintext state file found: ${file}. State files must be encrypted as .age files.`,
149
+ });
150
+ }
151
+ }
152
+ if (ageFiles.length > 0) {
153
+ findings.push({
154
+ severity: 'info',
155
+ check: 'state-files',
156
+ message: `${String(ageFiles.length)} encrypted state file(s) found.`,
157
+ });
158
+ }
159
+ // Check manifest is present and minimal
160
+ const manifestPath = path.join(syncDir, STATE_FILES.MANIFEST);
161
+ if (fs.existsSync(manifestPath)) {
162
+ try {
163
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
164
+ const allowedKeys = ['version', 'lastSync', 'files'];
165
+ const extraKeys = Object.keys(manifest).filter((k) => !allowedKeys.includes(k));
166
+ if (extraKeys.length > 0) {
167
+ findings.push({
168
+ severity: 'warning',
169
+ check: 'state-files',
170
+ message: `Manifest contains unexpected keys: ${extraKeys.join(', ')}`,
171
+ });
172
+ }
173
+ else {
174
+ findings.push({
175
+ severity: 'info',
176
+ check: 'state-files',
177
+ message: 'Manifest contains only version and timestamps (correct).',
178
+ });
179
+ }
180
+ }
181
+ catch {
182
+ findings.push({
183
+ severity: 'warning',
184
+ check: 'state-files',
185
+ message: 'Could not parse manifest.json.',
186
+ });
187
+ }
188
+ }
189
+ return { findings, stateFileCount: ageFiles.length };
190
+ }
191
+ /**
192
+ * Scan Git history for plaintext secret patterns.
193
+ */
194
+ export function checkGitHistory(syncDir) {
195
+ const findings = [];
196
+ const gitDir = path.join(syncDir, '.git');
197
+ if (!fs.existsSync(gitDir)) {
198
+ return findings; // No git repo, nothing to scan
199
+ }
200
+ try {
201
+ const history = execSync('git log -p --all --full-history', {
202
+ cwd: syncDir,
203
+ encoding: 'utf-8',
204
+ stdio: ['pipe', 'pipe', 'pipe'],
205
+ maxBuffer: 50 * 1024 * 1024, // 50MB
206
+ });
207
+ let foundSecrets = false;
208
+ for (const { pattern, label } of SECRET_PATTERNS) {
209
+ if (pattern.test(history)) {
210
+ foundSecrets = true;
211
+ findings.push({
212
+ severity: 'critical',
213
+ check: 'git-history',
214
+ message: `Potential ${label} found in Git history. Run \`key rotate\` to re-encrypt and rewrite history.`,
215
+ });
216
+ }
217
+ }
218
+ if (!foundSecrets) {
219
+ findings.push({
220
+ severity: 'info',
221
+ check: 'git-history',
222
+ message: 'No plaintext secret patterns found in Git history.',
223
+ });
224
+ }
225
+ }
226
+ catch {
227
+ // May fail if there's no commit history yet
228
+ findings.push({
229
+ severity: 'info',
230
+ check: 'git-history',
231
+ message: 'No Git history to scan (no commits yet).',
232
+ });
233
+ }
234
+ return findings;
235
+ }
236
+ /**
237
+ * Report repository size.
238
+ */
239
+ export function checkRepoSize(syncDir) {
240
+ const findings = [];
241
+ if (!fs.existsSync(syncDir)) {
242
+ return { findings, sizeBytes: null, sizeHuman: null };
243
+ }
244
+ try {
245
+ // Calculate total size recursively
246
+ const sizeBytes = getDirectorySize(syncDir);
247
+ const sizeHuman = formatBytes(sizeBytes);
248
+ findings.push({
249
+ severity: sizeBytes > 100 * 1024 * 1024 ? 'warning' : 'info',
250
+ check: 'repo-size',
251
+ message: `Repository size: ${sizeHuman}${sizeBytes > 100 * 1024 * 1024 ? ' (consider cleanup)' : ''}`,
252
+ });
253
+ return { findings, sizeBytes, sizeHuman };
254
+ }
255
+ catch {
256
+ return { findings, sizeBytes: null, sizeHuman: null };
257
+ }
258
+ }
259
+ /**
260
+ * Calculate directory size recursively.
261
+ */
262
+ function getDirectorySize(dirPath) {
263
+ let totalSize = 0;
264
+ const entries = fs.readdirSync(dirPath, { withFileTypes: true });
265
+ for (const entry of entries) {
266
+ const fullPath = path.join(dirPath, entry.name);
267
+ if (entry.isDirectory()) {
268
+ totalSize += getDirectorySize(fullPath);
269
+ }
270
+ else if (entry.isFile()) {
271
+ totalSize += fs.statSync(fullPath).size;
272
+ }
273
+ }
274
+ return totalSize;
275
+ }
276
+ /**
277
+ * Format bytes into human-readable string.
278
+ */
279
+ export function formatBytes(bytes) {
280
+ if (bytes === 0)
281
+ return '0 B';
282
+ const units = ['B', 'KB', 'MB', 'GB'];
283
+ const k = 1024;
284
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
285
+ const value = bytes / Math.pow(k, i);
286
+ return `${value.toFixed(i === 0 ? 0 : 1)} ${units[i] ?? 'TB'}`;
287
+ }
288
+ /**
289
+ * Execute the full audit.
290
+ */
291
+ export function executeAudit() {
292
+ const configDir = getConfigDir();
293
+ const syncDir = getSyncDir();
294
+ const allFindings = [];
295
+ // 1. Permissions check
296
+ allFindings.push(...checkPermissions(configDir));
297
+ // 2. Remote transport check
298
+ const transport = checkRemoteTransport(syncDir);
299
+ allFindings.push(...transport.findings);
300
+ // 3. State files check
301
+ const stateFiles = checkStateFiles(syncDir);
302
+ allFindings.push(...stateFiles.findings);
303
+ // 4. Git history scan
304
+ allFindings.push(...checkGitHistory(syncDir));
305
+ // 5. Repo size
306
+ const repoSize = checkRepoSize(syncDir);
307
+ allFindings.push(...repoSize.findings);
308
+ const hasCritical = allFindings.some((f) => f.severity === 'critical');
309
+ return {
310
+ findings: allFindings,
311
+ passed: !hasCritical,
312
+ repoSizeBytes: repoSize.sizeBytes,
313
+ repoSizeHuman: repoSize.sizeHuman,
314
+ stateFileCount: stateFiles.stateFileCount,
315
+ hasRemote: transport.hasRemote,
316
+ };
317
+ }
318
+ // ─── Commander Registration ───────────────────────────────────────────────
319
+ /**
320
+ * Register the `ctx-sync audit` command on the given program.
321
+ */
322
+ export function registerAuditCommand(program) {
323
+ program
324
+ .command('audit')
325
+ .description('Run a comprehensive security audit')
326
+ .action(withErrorHandler(async () => {
327
+ const result = executeAudit();
328
+ const critical = result.findings.filter((f) => f.severity === 'critical');
329
+ const warnings = result.findings.filter((f) => f.severity === 'warning');
330
+ const info = result.findings.filter((f) => f.severity === 'info');
331
+ console.log('\n🔒 ctx-sync Security Audit\n');
332
+ if (critical.length > 0) {
333
+ console.log('❌ Critical Issues:');
334
+ for (const f of critical) {
335
+ console.log(` [${f.check}] ${f.message}`);
336
+ }
337
+ console.log('');
338
+ }
339
+ if (warnings.length > 0) {
340
+ console.log('⚠ Warnings:');
341
+ for (const f of warnings) {
342
+ console.log(` [${f.check}] ${f.message}`);
343
+ }
344
+ console.log('');
345
+ }
346
+ if (info.length > 0) {
347
+ console.log('ℹ Info:');
348
+ for (const f of info) {
349
+ console.log(` [${f.check}] ${f.message}`);
350
+ }
351
+ console.log('');
352
+ }
353
+ if (result.repoSizeHuman) {
354
+ console.log(`📦 Repository size: ${result.repoSizeHuman}`);
355
+ }
356
+ console.log(`📄 Encrypted state files: ${String(result.stateFileCount)}`);
357
+ console.log('');
358
+ if (result.passed) {
359
+ console.log('✅ Audit passed — no critical issues found.');
360
+ }
361
+ else {
362
+ console.log('❌ Audit failed — critical issues require attention.');
363
+ process.exit(1);
364
+ }
365
+ }));
366
+ }
367
+ //# sourceMappingURL=audit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/commands/audit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * `ctx-sync config` command group.
3
+ *
4
+ * Subcommands:
5
+ * - `config safe-list` — View the current safe-list (default + custom).
6
+ * - `config safe-list add <key>` — Add a key to the user's custom safe-list.
7
+ * - `config safe-list remove <key>` — Remove a key from the custom safe-list.
8
+ *
9
+ * The safe-list determines which env var keys MAY be stored as plaintext
10
+ * when `--allow-plain` is used during `env import`. Everything else is
11
+ * encrypted by default.
12
+ *
13
+ * Config is stored in `~/.config/ctx-sync/config.json` (local, never synced).
14
+ *
15
+ * @module commands/config
16
+ */
17
+ import type { Command } from 'commander';
18
+ /**
19
+ * Result of listing the safe-list.
20
+ */
21
+ export interface SafeListViewResult {
22
+ defaults: readonly string[];
23
+ custom: string[];
24
+ effective: string[];
25
+ }
26
+ /**
27
+ * Execute safe-list view.
28
+ *
29
+ * @returns The safe-list broken down by defaults, custom, and effective.
30
+ */
31
+ export declare function executeSafeListView(): SafeListViewResult;
32
+ /**
33
+ * Execute safe-list add.
34
+ *
35
+ * @param key - The env var key to add to the safe-list.
36
+ * @returns Object with `added` flag and descriptive message.
37
+ */
38
+ export declare function executeSafeListAdd(key: string): {
39
+ added: boolean;
40
+ message: string;
41
+ };
42
+ /**
43
+ * Execute safe-list remove.
44
+ *
45
+ * @param key - The env var key to remove from the safe-list.
46
+ * @returns Object with `removed` flag and descriptive message.
47
+ */
48
+ export declare function executeSafeListRemove(key: string): {
49
+ removed: boolean;
50
+ message: string;
51
+ };
52
+ /**
53
+ * Register the `config` command group on the given Commander program.
54
+ *
55
+ * @param program - The Commander program instance.
56
+ */
57
+ export declare function registerConfigCommand(program: Command): void;
58
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AASzC;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,IAAI,kBAAkB,CAGxD;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG;IAC/C,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB,CAGA;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG;IAClD,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB,CAGA;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAiF5D"}
@@ -0,0 +1,114 @@
1
+ /**
2
+ * `ctx-sync config` command group.
3
+ *
4
+ * Subcommands:
5
+ * - `config safe-list` — View the current safe-list (default + custom).
6
+ * - `config safe-list add <key>` — Add a key to the user's custom safe-list.
7
+ * - `config safe-list remove <key>` — Remove a key from the custom safe-list.
8
+ *
9
+ * The safe-list determines which env var keys MAY be stored as plaintext
10
+ * when `--allow-plain` is used during `env import`. Everything else is
11
+ * encrypted by default.
12
+ *
13
+ * Config is stored in `~/.config/ctx-sync/config.json` (local, never synced).
14
+ *
15
+ * @module commands/config
16
+ */
17
+ import { withErrorHandler } from '../utils/errors.js';
18
+ import { getConfigDir } from './init.js';
19
+ import { listSafeList, addToSafeList, removeFromSafeList, } from '../core/config-store.js';
20
+ /**
21
+ * Execute safe-list view.
22
+ *
23
+ * @returns The safe-list broken down by defaults, custom, and effective.
24
+ */
25
+ export function executeSafeListView() {
26
+ const configDir = getConfigDir();
27
+ return listSafeList(configDir);
28
+ }
29
+ /**
30
+ * Execute safe-list add.
31
+ *
32
+ * @param key - The env var key to add to the safe-list.
33
+ * @returns Object with `added` flag and descriptive message.
34
+ */
35
+ export function executeSafeListAdd(key) {
36
+ const configDir = getConfigDir();
37
+ return addToSafeList(configDir, key);
38
+ }
39
+ /**
40
+ * Execute safe-list remove.
41
+ *
42
+ * @param key - The env var key to remove from the safe-list.
43
+ * @returns Object with `removed` flag and descriptive message.
44
+ */
45
+ export function executeSafeListRemove(key) {
46
+ const configDir = getConfigDir();
47
+ return removeFromSafeList(configDir, key);
48
+ }
49
+ /**
50
+ * Register the `config` command group on the given Commander program.
51
+ *
52
+ * @param program - The Commander program instance.
53
+ */
54
+ export function registerConfigCommand(program) {
55
+ const configCmd = program
56
+ .command('config')
57
+ .description('Manage local configuration and preferences');
58
+ // ── config safe-list ──────────────────────────────────────────────
59
+ const safeListCmd = configCmd
60
+ .command('safe-list')
61
+ .description('View or manage the env var safe-list');
62
+ // Default action: view the safe-list
63
+ safeListCmd.action(withErrorHandler(async () => {
64
+ const result = executeSafeListView();
65
+ const chalk = (await import('chalk')).default;
66
+ console.log(chalk.bold('\nEnvironment Variable Safe-List\n'));
67
+ console.log(chalk.dim('Keys on the safe-list MAY be stored as plaintext when --allow-plain is used.\n' +
68
+ 'Everything else is always encrypted (encrypt-by-default).\n'));
69
+ console.log(chalk.cyan('Built-in defaults:'));
70
+ for (const key of result.defaults) {
71
+ console.log(` ${key}`);
72
+ }
73
+ if (result.custom.length > 0) {
74
+ console.log(chalk.cyan('\nCustom additions:'));
75
+ for (const key of result.custom) {
76
+ console.log(` ${key}`);
77
+ }
78
+ }
79
+ else {
80
+ console.log(chalk.dim('\nNo custom additions.'));
81
+ }
82
+ console.log(chalk.dim(`\nTotal effective safe-list: ${result.effective.length} keys`));
83
+ }));
84
+ // ── config safe-list add <key> ────────────────────────────────────
85
+ safeListCmd
86
+ .command('add <key>')
87
+ .description('Add a key to your custom safe-list')
88
+ .action(withErrorHandler(async (key) => {
89
+ const result = executeSafeListAdd(key);
90
+ const chalk = (await import('chalk')).default;
91
+ if (result.added) {
92
+ console.log(chalk.green(`✅ ${result.message}`));
93
+ console.log(chalk.dim(' This key will be treated as plaintext-safe when --allow-plain is used.'));
94
+ }
95
+ else {
96
+ console.log(chalk.yellow(`⚠️ ${result.message}`));
97
+ }
98
+ }));
99
+ // ── config safe-list remove <key> ─────────────────────────────────
100
+ safeListCmd
101
+ .command('remove <key>')
102
+ .description('Remove a key from your custom safe-list')
103
+ .action(withErrorHandler(async (key) => {
104
+ const result = executeSafeListRemove(key);
105
+ const chalk = (await import('chalk')).default;
106
+ if (result.removed) {
107
+ console.log(chalk.green(`✅ ${result.message}`));
108
+ }
109
+ else {
110
+ console.log(chalk.yellow(`⚠️ ${result.message}`));
111
+ }
112
+ }));
113
+ }
114
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"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"}