tlc-claude-code 1.3.0 → 1.4.1
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/dashboard/dist/components/AuditPane.d.ts +30 -0
- package/dashboard/dist/components/AuditPane.js +127 -0
- package/dashboard/dist/components/AuditPane.test.d.ts +1 -0
- package/dashboard/dist/components/AuditPane.test.js +339 -0
- package/dashboard/dist/components/CompliancePane.d.ts +39 -0
- package/dashboard/dist/components/CompliancePane.js +96 -0
- package/dashboard/dist/components/CompliancePane.test.d.ts +1 -0
- package/dashboard/dist/components/CompliancePane.test.js +183 -0
- package/dashboard/dist/components/SSOPane.d.ts +36 -0
- package/dashboard/dist/components/SSOPane.js +71 -0
- package/dashboard/dist/components/SSOPane.test.d.ts +1 -0
- package/dashboard/dist/components/SSOPane.test.js +155 -0
- package/dashboard/dist/components/WorkspaceDocsPane.js +0 -16
- package/dashboard/dist/components/WorkspacePane.d.ts +1 -1
- package/dashboard/dist/components/ZeroRetentionPane.d.ts +44 -0
- package/dashboard/dist/components/ZeroRetentionPane.js +83 -0
- package/dashboard/dist/components/ZeroRetentionPane.test.d.ts +1 -0
- package/dashboard/dist/components/ZeroRetentionPane.test.js +160 -0
- package/package.json +1 -1
- package/server/lib/access-control-doc.js +541 -0
- package/server/lib/access-control-doc.test.js +672 -0
- package/server/lib/adr-generator.js +423 -0
- package/server/lib/adr-generator.test.js +586 -0
- package/server/lib/agent-progress-monitor.js +223 -0
- package/server/lib/agent-progress-monitor.test.js +202 -0
- package/server/lib/audit-attribution.js +191 -0
- package/server/lib/audit-attribution.test.js +359 -0
- package/server/lib/audit-classifier.js +202 -0
- package/server/lib/audit-classifier.test.js +209 -0
- package/server/lib/audit-command.js +275 -0
- package/server/lib/audit-command.test.js +325 -0
- package/server/lib/audit-exporter.js +380 -0
- package/server/lib/audit-exporter.test.js +464 -0
- package/server/lib/audit-logger.js +236 -0
- package/server/lib/audit-logger.test.js +364 -0
- package/server/lib/audit-query.js +257 -0
- package/server/lib/audit-query.test.js +352 -0
- package/server/lib/audit-storage.js +269 -0
- package/server/lib/audit-storage.test.js +272 -0
- package/server/lib/bulk-repo-init.js +342 -0
- package/server/lib/bulk-repo-init.test.js +388 -0
- package/server/lib/compliance-checklist.js +866 -0
- package/server/lib/compliance-checklist.test.js +476 -0
- package/server/lib/compliance-command.js +616 -0
- package/server/lib/compliance-command.test.js +551 -0
- package/server/lib/compliance-reporter.js +692 -0
- package/server/lib/compliance-reporter.test.js +707 -0
- package/server/lib/data-flow-doc.js +665 -0
- package/server/lib/data-flow-doc.test.js +659 -0
- package/server/lib/ephemeral-storage.js +249 -0
- package/server/lib/ephemeral-storage.test.js +254 -0
- package/server/lib/evidence-collector.js +627 -0
- package/server/lib/evidence-collector.test.js +901 -0
- package/server/lib/flow-diagram-generator.js +474 -0
- package/server/lib/flow-diagram-generator.test.js +446 -0
- package/server/lib/idp-manager.js +626 -0
- package/server/lib/idp-manager.test.js +587 -0
- package/server/lib/memory-exclusion.js +326 -0
- package/server/lib/memory-exclusion.test.js +241 -0
- package/server/lib/mfa-handler.js +452 -0
- package/server/lib/mfa-handler.test.js +490 -0
- package/server/lib/oauth-flow.js +375 -0
- package/server/lib/oauth-flow.test.js +487 -0
- package/server/lib/oauth-registry.js +190 -0
- package/server/lib/oauth-registry.test.js +306 -0
- package/server/lib/readme-generator.js +490 -0
- package/server/lib/readme-generator.test.js +493 -0
- package/server/lib/repo-dependency-tracker.js +261 -0
- package/server/lib/repo-dependency-tracker.test.js +350 -0
- package/server/lib/retention-policy.js +281 -0
- package/server/lib/retention-policy.test.js +486 -0
- package/server/lib/role-mapper.js +236 -0
- package/server/lib/role-mapper.test.js +395 -0
- package/server/lib/saml-provider.js +765 -0
- package/server/lib/saml-provider.test.js +643 -0
- package/server/lib/security-policy-generator.js +682 -0
- package/server/lib/security-policy-generator.test.js +544 -0
- package/server/lib/sensitive-detector.js +112 -0
- package/server/lib/sensitive-detector.test.js +209 -0
- package/server/lib/service-interaction-diagram.js +700 -0
- package/server/lib/service-interaction-diagram.test.js +638 -0
- package/server/lib/service-summary.js +553 -0
- package/server/lib/service-summary.test.js +619 -0
- package/server/lib/session-purge.js +460 -0
- package/server/lib/session-purge.test.js +312 -0
- package/server/lib/sso-command.js +544 -0
- package/server/lib/sso-command.test.js +552 -0
- package/server/lib/sso-session.js +492 -0
- package/server/lib/sso-session.test.js +670 -0
- package/server/lib/workspace-command.js +249 -0
- package/server/lib/workspace-command.test.js +264 -0
- package/server/lib/workspace-config.js +270 -0
- package/server/lib/workspace-config.test.js +312 -0
- package/server/lib/workspace-docs-command.js +547 -0
- package/server/lib/workspace-docs-command.test.js +692 -0
- package/server/lib/workspace-memory.js +451 -0
- package/server/lib/workspace-memory.test.js +403 -0
- package/server/lib/workspace-scanner.js +452 -0
- package/server/lib/workspace-scanner.test.js +677 -0
- package/server/lib/workspace-test-runner.js +315 -0
- package/server/lib/workspace-test-runner.test.js +294 -0
- package/server/lib/zero-retention-command.js +439 -0
- package/server/lib/zero-retention-command.test.js +448 -0
- package/server/lib/zero-retention.js +322 -0
- package/server/lib/zero-retention.test.js +258 -0
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zero-Retention Mode
|
|
3
|
+
*
|
|
4
|
+
* Master switch that configures all subsystems for zero-data-retention.
|
|
5
|
+
* When enabled, ensures no sensitive data persists beyond the current session.
|
|
6
|
+
*
|
|
7
|
+
* Integrates with:
|
|
8
|
+
* - sensitive-detector.js - Detects sensitive data types
|
|
9
|
+
* - retention-policy.js - Configures immediate purge policies
|
|
10
|
+
* - ephemeral-storage.js - In-memory only storage
|
|
11
|
+
* - session-purge.js - Automatic session end purging
|
|
12
|
+
* - memory-exclusion.js - Excludes all files from memory
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { detectSensitive, getSensitivityLevel } from './sensitive-detector.js';
|
|
16
|
+
import {
|
|
17
|
+
getPolicy,
|
|
18
|
+
evaluateRetention,
|
|
19
|
+
loadPolicies,
|
|
20
|
+
} from './retention-policy.js';
|
|
21
|
+
import { EphemeralStorage } from './ephemeral-storage.js';
|
|
22
|
+
import { SessionPurgeManager, createPurgeManager } from './session-purge.js';
|
|
23
|
+
import { shouldExclude, loadPatterns } from './memory-exclusion.js';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Zero-Retention Mode Class
|
|
27
|
+
*
|
|
28
|
+
* Provides a master switch to configure all subsystems for zero data retention.
|
|
29
|
+
*/
|
|
30
|
+
export class ZeroRetentionMode {
|
|
31
|
+
constructor(options = {}) {
|
|
32
|
+
this._enabled = false;
|
|
33
|
+
this._config = null;
|
|
34
|
+
this._options = options;
|
|
35
|
+
this._subsystems = {
|
|
36
|
+
ephemeralStorage: null,
|
|
37
|
+
sessionPurge: null,
|
|
38
|
+
memoryExclusion: null,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Enable zero-retention mode
|
|
44
|
+
* Configures all subsystems for immediate data purging and no persistence.
|
|
45
|
+
*
|
|
46
|
+
* @param {Object} options - Configuration options
|
|
47
|
+
* @param {boolean} options.auditLogging - Enable audit logging (conflicts with zero-retention)
|
|
48
|
+
* @returns {Object} Result with success status and configuration
|
|
49
|
+
*/
|
|
50
|
+
enable(options = {}) {
|
|
51
|
+
const enableOptions = { ...this._options, ...options };
|
|
52
|
+
|
|
53
|
+
// Configure ephemeral storage - memory only, encrypted
|
|
54
|
+
const ephemeralStorageConfig = {
|
|
55
|
+
encrypt: true,
|
|
56
|
+
registerExitHandler: true,
|
|
57
|
+
basePath: null, // No disk persistence
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// Configure session purge - aggressive purging
|
|
61
|
+
const sessionPurgeConfig = {
|
|
62
|
+
aggressive: true,
|
|
63
|
+
purgeOnSessionEnd: true,
|
|
64
|
+
purgeOnTimeout: true,
|
|
65
|
+
idleTimeout: 5 * 60 * 1000, // 5 minutes
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
// Configure memory exclusion - exclude everything
|
|
69
|
+
const memoryExclusionConfig = {
|
|
70
|
+
excludeAll: true,
|
|
71
|
+
mode: 'blacklist',
|
|
72
|
+
filePatterns: ['*'],
|
|
73
|
+
contentPatterns: ['*'],
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// Configure retention policy - immediate purge, no persistence
|
|
77
|
+
const retentionPolicyConfig = {
|
|
78
|
+
retention: 'immediate',
|
|
79
|
+
persist: false,
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// Store subsystem configurations
|
|
83
|
+
this._subsystems = {
|
|
84
|
+
ephemeralStorage: true,
|
|
85
|
+
sessionPurge: true,
|
|
86
|
+
memoryExclusion: true,
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// Build full configuration
|
|
90
|
+
this._config = {
|
|
91
|
+
enabled: true,
|
|
92
|
+
ephemeralStorage: ephemeralStorageConfig,
|
|
93
|
+
sessionPurge: sessionPurgeConfig,
|
|
94
|
+
memoryExclusion: memoryExclusionConfig,
|
|
95
|
+
retentionPolicy: retentionPolicyConfig,
|
|
96
|
+
auditLogging: enableOptions.auditLogging || false,
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
this._enabled = true;
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
success: true,
|
|
103
|
+
enabled: true,
|
|
104
|
+
subsystems: { ...this._subsystems },
|
|
105
|
+
config: { ...this._config },
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Disable zero-retention mode
|
|
111
|
+
* Returns all subsystems to normal operation.
|
|
112
|
+
*
|
|
113
|
+
* @returns {Object} Result with success status
|
|
114
|
+
*/
|
|
115
|
+
disable() {
|
|
116
|
+
this._enabled = false;
|
|
117
|
+
this._config = {
|
|
118
|
+
enabled: false,
|
|
119
|
+
ephemeralStorage: null,
|
|
120
|
+
sessionPurge: null,
|
|
121
|
+
memoryExclusion: null,
|
|
122
|
+
retentionPolicy: null,
|
|
123
|
+
};
|
|
124
|
+
this._subsystems = {
|
|
125
|
+
ephemeralStorage: null,
|
|
126
|
+
sessionPurge: null,
|
|
127
|
+
memoryExclusion: null,
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
return {
|
|
131
|
+
success: true,
|
|
132
|
+
enabled: false,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Check if zero-retention mode is enabled
|
|
138
|
+
*
|
|
139
|
+
* @returns {boolean} True if enabled
|
|
140
|
+
*/
|
|
141
|
+
isEnabled() {
|
|
142
|
+
return this._enabled;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Get the current configuration
|
|
147
|
+
*
|
|
148
|
+
* @returns {Object} Current configuration
|
|
149
|
+
*/
|
|
150
|
+
getConfig() {
|
|
151
|
+
if (!this._enabled) {
|
|
152
|
+
return {
|
|
153
|
+
enabled: false,
|
|
154
|
+
ephemeralStorage: null,
|
|
155
|
+
sessionPurge: null,
|
|
156
|
+
memoryExclusion: null,
|
|
157
|
+
retentionPolicy: null,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return { ...this._config };
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Validate the current configuration for conflicts
|
|
166
|
+
*
|
|
167
|
+
* @returns {Object} Validation result with conflicts and warnings
|
|
168
|
+
*/
|
|
169
|
+
validate() {
|
|
170
|
+
const conflicts = [];
|
|
171
|
+
const warnings = [];
|
|
172
|
+
|
|
173
|
+
// Check for audit logging conflict
|
|
174
|
+
if (this._config && this._config.auditLogging) {
|
|
175
|
+
warnings.push(
|
|
176
|
+
'Audit logging is enabled but conflicts with zero-retention mode. ' +
|
|
177
|
+
'Audit logs may retain sensitive data references.'
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Check for persistence conflicts
|
|
182
|
+
if (this._enabled && this._config) {
|
|
183
|
+
// Ephemeral storage should not persist
|
|
184
|
+
if (
|
|
185
|
+
this._config.ephemeralStorage &&
|
|
186
|
+
this._config.ephemeralStorage.basePath
|
|
187
|
+
) {
|
|
188
|
+
conflicts.push(
|
|
189
|
+
'Ephemeral storage has basePath set, which may enable disk persistence.'
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Retention policy should be immediate
|
|
194
|
+
if (
|
|
195
|
+
this._config.retentionPolicy &&
|
|
196
|
+
this._config.retentionPolicy.retention !== 'immediate'
|
|
197
|
+
) {
|
|
198
|
+
conflicts.push(
|
|
199
|
+
'Retention policy is not set to immediate in zero-retention mode.'
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Retention policy should not persist
|
|
204
|
+
if (
|
|
205
|
+
this._config.retentionPolicy &&
|
|
206
|
+
this._config.retentionPolicy.persist === true
|
|
207
|
+
) {
|
|
208
|
+
conflicts.push('Retention policy has persist enabled in zero-retention mode.');
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return {
|
|
213
|
+
valid: conflicts.length === 0,
|
|
214
|
+
conflicts,
|
|
215
|
+
warnings,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Create configured subsystem instances
|
|
221
|
+
* Returns ready-to-use instances of all subsystems configured for zero-retention.
|
|
222
|
+
*
|
|
223
|
+
* @returns {Object} Configured subsystem instances
|
|
224
|
+
*/
|
|
225
|
+
createSubsystems() {
|
|
226
|
+
if (!this._enabled) {
|
|
227
|
+
throw new Error('Zero-retention mode must be enabled before creating subsystems');
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const ephemeralStorage = new EphemeralStorage(this._config.ephemeralStorage);
|
|
231
|
+
|
|
232
|
+
const sessionPurge = createPurgeManager({
|
|
233
|
+
storage: ephemeralStorage,
|
|
234
|
+
auditEnabled: false, // Audit logging disabled in zero-retention
|
|
235
|
+
policies: {
|
|
236
|
+
sensitivityLevels: {
|
|
237
|
+
critical: { retention: 'immediate', persist: false },
|
|
238
|
+
high: { retention: 'immediate', persist: false },
|
|
239
|
+
medium: { retention: 'immediate', persist: false },
|
|
240
|
+
low: { retention: 'immediate', persist: false },
|
|
241
|
+
},
|
|
242
|
+
dataTypes: {
|
|
243
|
+
secrets: { retention: 'immediate', persist: false },
|
|
244
|
+
pii: { retention: 'immediate', persist: false },
|
|
245
|
+
general: { retention: 'immediate', persist: false },
|
|
246
|
+
},
|
|
247
|
+
default: { retention: 'immediate', persist: false },
|
|
248
|
+
},
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
return {
|
|
252
|
+
ephemeralStorage,
|
|
253
|
+
sessionPurge,
|
|
254
|
+
config: this._config,
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Global singleton instance
|
|
260
|
+
let globalInstance = new ZeroRetentionMode();
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Enable zero-retention mode globally
|
|
264
|
+
*
|
|
265
|
+
* @param {Object} options - Configuration options
|
|
266
|
+
* @returns {Object} Result with success status
|
|
267
|
+
*/
|
|
268
|
+
export function enable(options = {}) {
|
|
269
|
+
return globalInstance.enable(options);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Disable zero-retention mode globally
|
|
274
|
+
*
|
|
275
|
+
* @returns {Object} Result with success status
|
|
276
|
+
*/
|
|
277
|
+
export function disable() {
|
|
278
|
+
return globalInstance.disable();
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Check if zero-retention mode is enabled globally
|
|
283
|
+
*
|
|
284
|
+
* @returns {boolean} True if enabled
|
|
285
|
+
*/
|
|
286
|
+
export function isEnabled() {
|
|
287
|
+
return globalInstance.isEnabled();
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Get the current global configuration
|
|
292
|
+
*
|
|
293
|
+
* @returns {Object} Current configuration
|
|
294
|
+
*/
|
|
295
|
+
export function getConfig() {
|
|
296
|
+
return globalInstance.getConfig();
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Validate the current global configuration
|
|
301
|
+
*
|
|
302
|
+
* @returns {Object} Validation result
|
|
303
|
+
*/
|
|
304
|
+
export function validate() {
|
|
305
|
+
return globalInstance.validate();
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Reset the global instance (for testing)
|
|
310
|
+
*/
|
|
311
|
+
export function _resetGlobalInstance() {
|
|
312
|
+
globalInstance = new ZeroRetentionMode();
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
export default {
|
|
316
|
+
ZeroRetentionMode,
|
|
317
|
+
enable,
|
|
318
|
+
disable,
|
|
319
|
+
isEnabled,
|
|
320
|
+
getConfig,
|
|
321
|
+
validate,
|
|
322
|
+
};
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zero-Retention Mode Tests
|
|
3
|
+
*
|
|
4
|
+
* Tests for the master switch that configures all subsystems for zero-data-retention.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
8
|
+
import {
|
|
9
|
+
enable,
|
|
10
|
+
disable,
|
|
11
|
+
isEnabled,
|
|
12
|
+
getConfig,
|
|
13
|
+
validate,
|
|
14
|
+
ZeroRetentionMode,
|
|
15
|
+
_resetGlobalInstance,
|
|
16
|
+
} from './zero-retention.js';
|
|
17
|
+
|
|
18
|
+
// Mock dependencies
|
|
19
|
+
vi.mock('./sensitive-detector.js', () => ({
|
|
20
|
+
detectSensitive: vi.fn(() => []),
|
|
21
|
+
getSensitivityLevel: vi.fn(() => 'low'),
|
|
22
|
+
}));
|
|
23
|
+
|
|
24
|
+
vi.mock('./retention-policy.js', () => ({
|
|
25
|
+
getPolicy: vi.fn(() => ({ retention: 'immediate', persist: false })),
|
|
26
|
+
evaluateRetention: vi.fn(() => 'purge'),
|
|
27
|
+
loadPolicies: vi.fn(() => ({})),
|
|
28
|
+
}));
|
|
29
|
+
|
|
30
|
+
vi.mock('./ephemeral-storage.js', () => ({
|
|
31
|
+
EphemeralStorage: vi.fn().mockImplementation(() => ({
|
|
32
|
+
set: vi.fn(),
|
|
33
|
+
get: vi.fn(),
|
|
34
|
+
delete: vi.fn(),
|
|
35
|
+
clear: vi.fn(),
|
|
36
|
+
keys: vi.fn(() => []),
|
|
37
|
+
})),
|
|
38
|
+
}));
|
|
39
|
+
|
|
40
|
+
vi.mock('./session-purge.js', () => ({
|
|
41
|
+
SessionPurgeManager: vi.fn().mockImplementation(() => ({
|
|
42
|
+
onSessionEnd: vi.fn(),
|
|
43
|
+
onProcessExit: vi.fn(),
|
|
44
|
+
startIdleTimer: vi.fn(),
|
|
45
|
+
stopIdleTimer: vi.fn(),
|
|
46
|
+
forcePurge: vi.fn(),
|
|
47
|
+
})),
|
|
48
|
+
createPurgeManager: vi.fn((options) => ({
|
|
49
|
+
onSessionEnd: vi.fn(),
|
|
50
|
+
onProcessExit: vi.fn(),
|
|
51
|
+
startIdleTimer: vi.fn(),
|
|
52
|
+
stopIdleTimer: vi.fn(),
|
|
53
|
+
forcePurge: vi.fn(),
|
|
54
|
+
...options,
|
|
55
|
+
})),
|
|
56
|
+
}));
|
|
57
|
+
|
|
58
|
+
vi.mock('./memory-exclusion.js', () => ({
|
|
59
|
+
shouldExclude: vi.fn(() => true),
|
|
60
|
+
loadPatterns: vi.fn(() => ({
|
|
61
|
+
mode: 'blacklist',
|
|
62
|
+
filePatterns: ['*'],
|
|
63
|
+
contentPatterns: ['*'],
|
|
64
|
+
})),
|
|
65
|
+
}));
|
|
66
|
+
|
|
67
|
+
describe('Zero-Retention Mode', () => {
|
|
68
|
+
let zeroRetention;
|
|
69
|
+
|
|
70
|
+
beforeEach(() => {
|
|
71
|
+
vi.clearAllMocks();
|
|
72
|
+
// Reset global instance to prevent state leakage
|
|
73
|
+
_resetGlobalInstance();
|
|
74
|
+
// Create a fresh instance for each test
|
|
75
|
+
zeroRetention = new ZeroRetentionMode();
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
afterEach(() => {
|
|
79
|
+
// Ensure mode is disabled after each test
|
|
80
|
+
if (zeroRetention && zeroRetention.isEnabled()) {
|
|
81
|
+
zeroRetention.disable();
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
describe('enable', () => {
|
|
86
|
+
it('activates zero-retention mode', () => {
|
|
87
|
+
const result = zeroRetention.enable();
|
|
88
|
+
|
|
89
|
+
expect(result.success).toBe(true);
|
|
90
|
+
expect(result.enabled).toBe(true);
|
|
91
|
+
expect(zeroRetention.isEnabled()).toBe(true);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('configures ephemeral storage', () => {
|
|
95
|
+
const result = zeroRetention.enable();
|
|
96
|
+
|
|
97
|
+
expect(result.success).toBe(true);
|
|
98
|
+
expect(result.subsystems.ephemeralStorage).toBe(true);
|
|
99
|
+
expect(result.config.ephemeralStorage).toBeDefined();
|
|
100
|
+
expect(result.config.ephemeralStorage.encrypt).toBe(true);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('configures session purge', () => {
|
|
104
|
+
const result = zeroRetention.enable();
|
|
105
|
+
|
|
106
|
+
expect(result.success).toBe(true);
|
|
107
|
+
expect(result.subsystems.sessionPurge).toBe(true);
|
|
108
|
+
expect(result.config.sessionPurge).toBeDefined();
|
|
109
|
+
expect(result.config.sessionPurge.aggressive).toBe(true);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('configures memory exclusions', () => {
|
|
113
|
+
const result = zeroRetention.enable();
|
|
114
|
+
|
|
115
|
+
expect(result.success).toBe(true);
|
|
116
|
+
expect(result.subsystems.memoryExclusion).toBe(true);
|
|
117
|
+
expect(result.config.memoryExclusion).toBeDefined();
|
|
118
|
+
expect(result.config.memoryExclusion.excludeAll).toBe(true);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
describe('disable', () => {
|
|
123
|
+
it('returns to normal mode', () => {
|
|
124
|
+
// First enable
|
|
125
|
+
zeroRetention.enable();
|
|
126
|
+
expect(zeroRetention.isEnabled()).toBe(true);
|
|
127
|
+
|
|
128
|
+
// Then disable
|
|
129
|
+
const result = zeroRetention.disable();
|
|
130
|
+
|
|
131
|
+
expect(result.success).toBe(true);
|
|
132
|
+
expect(result.enabled).toBe(false);
|
|
133
|
+
expect(zeroRetention.isEnabled()).toBe(false);
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
describe('isEnabled', () => {
|
|
138
|
+
it('returns current state', () => {
|
|
139
|
+
expect(zeroRetention.isEnabled()).toBe(false);
|
|
140
|
+
|
|
141
|
+
zeroRetention.enable();
|
|
142
|
+
expect(zeroRetention.isEnabled()).toBe(true);
|
|
143
|
+
|
|
144
|
+
zeroRetention.disable();
|
|
145
|
+
expect(zeroRetention.isEnabled()).toBe(false);
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
describe('getConfig', () => {
|
|
150
|
+
it('returns active configuration', () => {
|
|
151
|
+
zeroRetention.enable();
|
|
152
|
+
|
|
153
|
+
const config = zeroRetention.getConfig();
|
|
154
|
+
|
|
155
|
+
expect(config).toBeDefined();
|
|
156
|
+
expect(config.enabled).toBe(true);
|
|
157
|
+
expect(config.ephemeralStorage).toBeDefined();
|
|
158
|
+
expect(config.sessionPurge).toBeDefined();
|
|
159
|
+
expect(config.memoryExclusion).toBeDefined();
|
|
160
|
+
expect(config.retentionPolicy).toBeDefined();
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('returns default configuration when disabled', () => {
|
|
164
|
+
const config = zeroRetention.getConfig();
|
|
165
|
+
|
|
166
|
+
expect(config).toBeDefined();
|
|
167
|
+
expect(config.enabled).toBe(false);
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
describe('validate', () => {
|
|
172
|
+
it('checks for conflicts', () => {
|
|
173
|
+
const result = zeroRetention.validate();
|
|
174
|
+
|
|
175
|
+
expect(result).toBeDefined();
|
|
176
|
+
expect(result.valid).toBeDefined();
|
|
177
|
+
expect(result.conflicts).toBeDefined();
|
|
178
|
+
expect(Array.isArray(result.conflicts)).toBe(true);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it('warns about audit logging conflict', () => {
|
|
182
|
+
// Enable with audit logging (which conflicts with zero-retention)
|
|
183
|
+
zeroRetention.enable({ auditLogging: true });
|
|
184
|
+
|
|
185
|
+
const result = zeroRetention.validate();
|
|
186
|
+
|
|
187
|
+
expect(result.warnings).toBeDefined();
|
|
188
|
+
expect(Array.isArray(result.warnings)).toBe(true);
|
|
189
|
+
expect(
|
|
190
|
+
result.warnings.some((w) => w.toLowerCase().includes('audit'))
|
|
191
|
+
).toBe(true);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it('returns valid when no conflicts', () => {
|
|
195
|
+
zeroRetention.enable();
|
|
196
|
+
|
|
197
|
+
const result = zeroRetention.validate();
|
|
198
|
+
|
|
199
|
+
expect(result.valid).toBe(true);
|
|
200
|
+
expect(result.conflicts.length).toBe(0);
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
describe('module-level functions', () => {
|
|
205
|
+
it('enable function works', () => {
|
|
206
|
+
const result = enable();
|
|
207
|
+
expect(result.success).toBe(true);
|
|
208
|
+
expect(isEnabled()).toBe(true);
|
|
209
|
+
disable(); // cleanup
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it('disable function works', () => {
|
|
213
|
+
enable();
|
|
214
|
+
const result = disable();
|
|
215
|
+
expect(result.success).toBe(true);
|
|
216
|
+
expect(isEnabled()).toBe(false);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it('isEnabled function works', () => {
|
|
220
|
+
expect(isEnabled()).toBe(false);
|
|
221
|
+
enable();
|
|
222
|
+
expect(isEnabled()).toBe(true);
|
|
223
|
+
disable();
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it('getConfig function works', () => {
|
|
227
|
+
enable();
|
|
228
|
+
const config = getConfig();
|
|
229
|
+
expect(config).toBeDefined();
|
|
230
|
+
expect(config.enabled).toBe(true);
|
|
231
|
+
disable();
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it('validate function works', () => {
|
|
235
|
+
const result = validate();
|
|
236
|
+
expect(result).toBeDefined();
|
|
237
|
+
expect(result.valid).toBeDefined();
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
describe('integration with subsystems', () => {
|
|
242
|
+
it('sets immediate retention policy on enable', () => {
|
|
243
|
+
zeroRetention.enable();
|
|
244
|
+
|
|
245
|
+
const config = zeroRetention.getConfig();
|
|
246
|
+
expect(config.retentionPolicy.retention).toBe('immediate');
|
|
247
|
+
expect(config.retentionPolicy.persist).toBe(false);
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
it('restores normal retention policy on disable', () => {
|
|
251
|
+
zeroRetention.enable();
|
|
252
|
+
zeroRetention.disable();
|
|
253
|
+
|
|
254
|
+
const config = zeroRetention.getConfig();
|
|
255
|
+
expect(config.retentionPolicy).toBeNull();
|
|
256
|
+
});
|
|
257
|
+
});
|
|
258
|
+
});
|