@pacamelo/core 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.
- package/LICENSE +21 -0
- package/README.md +77 -0
- package/dist/index.d.mts +1669 -0
- package/dist/index.d.ts +1669 -0
- package/dist/index.js +4999 -0
- package/dist/index.mjs +4966 -0
- package/package.json +58 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,1669 @@
|
|
|
1
|
+
import * as zustand from 'zustand';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* PURGE Module Types
|
|
5
|
+
* Pacamelo's Universal Redaction & Governance Engine
|
|
6
|
+
*/
|
|
7
|
+
type PurgeState = 'idle' | 'loaded' | 'column_select' | 'configuring' | 'scanning' | 'preview' | 'purging' | 'complete';
|
|
8
|
+
type SupportedFileType = 'docx' | 'xlsx' | 'pptx' | 'pdf';
|
|
9
|
+
interface QueuedFile {
|
|
10
|
+
id: string;
|
|
11
|
+
file: File;
|
|
12
|
+
name: string;
|
|
13
|
+
size: number;
|
|
14
|
+
type: SupportedFileType;
|
|
15
|
+
status: 'queued' | 'scanning' | 'detected' | 'purging' | 'complete' | 'error';
|
|
16
|
+
error?: string;
|
|
17
|
+
}
|
|
18
|
+
interface ProcessedFile {
|
|
19
|
+
id: string;
|
|
20
|
+
originalName: string;
|
|
21
|
+
purgedName: string;
|
|
22
|
+
originalSize: number;
|
|
23
|
+
purgedSize: number;
|
|
24
|
+
type: SupportedFileType;
|
|
25
|
+
blob: Blob;
|
|
26
|
+
detectionsRemoved: number;
|
|
27
|
+
timestamp: number;
|
|
28
|
+
}
|
|
29
|
+
type PIICategory = 'person_name' | 'email' | 'phone' | 'address' | 'ssn' | 'credit_card' | 'ip_address' | 'date_of_birth' | 'custom';
|
|
30
|
+
interface Detection {
|
|
31
|
+
id: string;
|
|
32
|
+
fileId: string;
|
|
33
|
+
sectionId: string;
|
|
34
|
+
category: PIICategory;
|
|
35
|
+
value: string;
|
|
36
|
+
startOffset: number;
|
|
37
|
+
endOffset: number;
|
|
38
|
+
confidence: number;
|
|
39
|
+
context: string;
|
|
40
|
+
source?: 'regex' | 'ai';
|
|
41
|
+
location?: {
|
|
42
|
+
page?: number;
|
|
43
|
+
sheet?: string;
|
|
44
|
+
cell?: string;
|
|
45
|
+
slide?: number;
|
|
46
|
+
paragraph?: number;
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
interface DetectionResult {
|
|
50
|
+
detections: Detection[];
|
|
51
|
+
processingTimeMs: number;
|
|
52
|
+
engineVersion: string;
|
|
53
|
+
}
|
|
54
|
+
interface ContentSection {
|
|
55
|
+
id: string;
|
|
56
|
+
text: string;
|
|
57
|
+
type: 'paragraph' | 'cell' | 'slide' | 'heading' | 'footer' | 'header';
|
|
58
|
+
location: {
|
|
59
|
+
page?: number;
|
|
60
|
+
sheet?: string;
|
|
61
|
+
cell?: string;
|
|
62
|
+
slide?: number;
|
|
63
|
+
paragraph?: number;
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
interface DocumentContent {
|
|
67
|
+
fileId: string;
|
|
68
|
+
fileName: string;
|
|
69
|
+
fileType: SupportedFileType;
|
|
70
|
+
sections: ContentSection[];
|
|
71
|
+
}
|
|
72
|
+
interface ParsedDocument {
|
|
73
|
+
content: DocumentContent;
|
|
74
|
+
metadata: Record<string, unknown>;
|
|
75
|
+
rawData: unknown;
|
|
76
|
+
}
|
|
77
|
+
type RedactionStyle = 'blackout' | 'replacement' | 'pseudonym' | 'partial';
|
|
78
|
+
interface CustomPattern {
|
|
79
|
+
id: string;
|
|
80
|
+
name: string;
|
|
81
|
+
regex: string;
|
|
82
|
+
category: 'custom';
|
|
83
|
+
enabled: boolean;
|
|
84
|
+
}
|
|
85
|
+
interface ScrubConfig {
|
|
86
|
+
categories: Record<PIICategory, boolean>;
|
|
87
|
+
redactionStyle: RedactionStyle;
|
|
88
|
+
replacementText: string;
|
|
89
|
+
customPatterns: CustomPattern[];
|
|
90
|
+
sensitivity: 'low' | 'medium' | 'high';
|
|
91
|
+
}
|
|
92
|
+
interface ProcessingProgress {
|
|
93
|
+
currentFileIndex: number;
|
|
94
|
+
totalFiles: number;
|
|
95
|
+
currentFileName: string;
|
|
96
|
+
phase: 'detecting' | 'redacting' | 'finalizing';
|
|
97
|
+
percent: number;
|
|
98
|
+
}
|
|
99
|
+
interface NetworkRequest {
|
|
100
|
+
id: string;
|
|
101
|
+
method: string;
|
|
102
|
+
url: string;
|
|
103
|
+
size: number;
|
|
104
|
+
status: number;
|
|
105
|
+
timestamp: number;
|
|
106
|
+
}
|
|
107
|
+
interface StorageSnapshot {
|
|
108
|
+
localStorage: number;
|
|
109
|
+
sessionStorage: number;
|
|
110
|
+
indexedDB: number;
|
|
111
|
+
cookies: number;
|
|
112
|
+
cacheAPI: number;
|
|
113
|
+
watermarkPlanted: boolean;
|
|
114
|
+
watermarkVerified: boolean;
|
|
115
|
+
}
|
|
116
|
+
interface MemoryStats {
|
|
117
|
+
allocated: number;
|
|
118
|
+
wiped: number;
|
|
119
|
+
buffersCleared: number;
|
|
120
|
+
totalBuffers: number;
|
|
121
|
+
}
|
|
122
|
+
interface DetectionCapabilities {
|
|
123
|
+
supportedCategories: PIICategory[];
|
|
124
|
+
supportsCustomPatterns: boolean;
|
|
125
|
+
supportsContextualDetection: boolean;
|
|
126
|
+
maxFileSizeMB: number;
|
|
127
|
+
}
|
|
128
|
+
interface DetectionEngine {
|
|
129
|
+
detect(content: DocumentContent, config: ScrubConfig): Promise<DetectionResult>;
|
|
130
|
+
isAvailable(): Promise<boolean>;
|
|
131
|
+
getCapabilities(): DetectionCapabilities;
|
|
132
|
+
}
|
|
133
|
+
interface Redaction {
|
|
134
|
+
detectionId: string;
|
|
135
|
+
sectionId: string;
|
|
136
|
+
startOffset: number;
|
|
137
|
+
endOffset: number;
|
|
138
|
+
replacement: string;
|
|
139
|
+
}
|
|
140
|
+
interface DocumentProcessor {
|
|
141
|
+
canProcess(file: File): boolean;
|
|
142
|
+
parse(file: File): Promise<ParsedDocument>;
|
|
143
|
+
applyRedactions(document: ParsedDocument, redactions: Redaction[]): Promise<Blob>;
|
|
144
|
+
getSupportedMimeTypes(): string[];
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* A single block of file data with its entropy measurement.
|
|
148
|
+
* Used for heat map visualization.
|
|
149
|
+
*/
|
|
150
|
+
interface EntropyBlock {
|
|
151
|
+
/** Block index in the file */
|
|
152
|
+
index: number;
|
|
153
|
+
/** Shannon entropy value (0-8 bits) */
|
|
154
|
+
entropy: number;
|
|
155
|
+
/** Normalized entropy for color mapping (0-1) */
|
|
156
|
+
normalizedEntropy: number;
|
|
157
|
+
/** Byte offset in the original file */
|
|
158
|
+
offset: number;
|
|
159
|
+
/** Size of this block in bytes */
|
|
160
|
+
size: number;
|
|
161
|
+
/** Whether this block overlaps with a PII detection */
|
|
162
|
+
containsPII: boolean;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Complete entropy analysis of a file.
|
|
166
|
+
*/
|
|
167
|
+
interface EntropyData {
|
|
168
|
+
/** Array of entropy blocks */
|
|
169
|
+
blocks: EntropyBlock[];
|
|
170
|
+
/** Overall file entropy */
|
|
171
|
+
globalEntropy: number;
|
|
172
|
+
/** Highest block entropy */
|
|
173
|
+
maxEntropy: number;
|
|
174
|
+
/** Lowest block entropy */
|
|
175
|
+
minEntropy: number;
|
|
176
|
+
/** Total bytes analyzed */
|
|
177
|
+
totalBytes: number;
|
|
178
|
+
/** Block size used for analysis */
|
|
179
|
+
blockSize: number;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Comparison data for before/after visualization.
|
|
183
|
+
*/
|
|
184
|
+
interface EntropyComparison {
|
|
185
|
+
/** Entropy data before redaction */
|
|
186
|
+
before: EntropyData | null;
|
|
187
|
+
/** Entropy data after redaction */
|
|
188
|
+
after: EntropyData | null;
|
|
189
|
+
/** Regions where entropy changed significantly */
|
|
190
|
+
changedRegions: Array<{
|
|
191
|
+
startBlock: number;
|
|
192
|
+
endBlock: number;
|
|
193
|
+
entropyDrop: number;
|
|
194
|
+
}>;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Configuration for which columns to process in XLSX files.
|
|
198
|
+
* Enables "column isolation" where non-selected columns are never read.
|
|
199
|
+
*/
|
|
200
|
+
interface ColumnSelectionConfig {
|
|
201
|
+
/** Processing mode: 'all' scans everything, 'selected' only scans specified columns */
|
|
202
|
+
mode: 'all' | 'selected';
|
|
203
|
+
/** Set of column letters to process (e.g., 'A', 'B', 'C') */
|
|
204
|
+
selectedColumns: Set<string>;
|
|
205
|
+
/** Set of sheet names to process */
|
|
206
|
+
selectedSheets: Set<string>;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Cryptographic attestation proving a column was never accessed.
|
|
210
|
+
* The hash is computed from column metadata WITHOUT reading cell values.
|
|
211
|
+
*/
|
|
212
|
+
interface ColumnAttestation {
|
|
213
|
+
/** Column letter (A, B, C, etc.) */
|
|
214
|
+
column: string;
|
|
215
|
+
/** Sheet name */
|
|
216
|
+
sheet: string;
|
|
217
|
+
/** Number of cells in the column */
|
|
218
|
+
cellCount: number;
|
|
219
|
+
/** SHA-256 hash of column metadata (proves we didn't modify it) */
|
|
220
|
+
rawBytesHash: string;
|
|
221
|
+
/** Always false - indicates this column was skipped */
|
|
222
|
+
wasAccessed: false;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Metadata about a single sheet in the spreadsheet.
|
|
226
|
+
* Extracted without reading sensitive cell values.
|
|
227
|
+
*/
|
|
228
|
+
interface SheetMetadata {
|
|
229
|
+
/** Sheet name */
|
|
230
|
+
name: string;
|
|
231
|
+
/** Array of column letters present (e.g., ['A', 'B', 'C']) */
|
|
232
|
+
columns: string[];
|
|
233
|
+
/** Total row count */
|
|
234
|
+
rowCount: number;
|
|
235
|
+
/** Total column count */
|
|
236
|
+
columnCount: number;
|
|
237
|
+
/** Optional first-row values as column headers (can be disabled for privacy) */
|
|
238
|
+
sampleHeaders?: string[];
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Complete spreadsheet structure metadata.
|
|
242
|
+
* Allows column selection UI without exposing sensitive data.
|
|
243
|
+
*/
|
|
244
|
+
interface SpreadsheetMetadata {
|
|
245
|
+
/** Array of sheet metadata */
|
|
246
|
+
sheets: SheetMetadata[];
|
|
247
|
+
/** Total cell count across all sheets */
|
|
248
|
+
totalCells: number;
|
|
249
|
+
/** Column range (first and last column letters) */
|
|
250
|
+
columnRange: {
|
|
251
|
+
min: string;
|
|
252
|
+
max: string;
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Record of which columns were skipped during processing.
|
|
257
|
+
* Displayed in TrustPanel for user visibility.
|
|
258
|
+
* Note: This is a self-reported indicator, not a cryptographic proof.
|
|
259
|
+
*/
|
|
260
|
+
interface ColumnAccessProof {
|
|
261
|
+
/** Attestations for each skipped column */
|
|
262
|
+
attestations: ColumnAttestation[];
|
|
263
|
+
/** When the record was generated */
|
|
264
|
+
timestamp: string;
|
|
265
|
+
/** Hash of all attestations combined */
|
|
266
|
+
verificationHash: string;
|
|
267
|
+
/** Whether post-processing verification passed */
|
|
268
|
+
verified: boolean;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Attributes leaked through context that could identify a person.
|
|
272
|
+
* Each attribute narrows the potential population of matches.
|
|
273
|
+
*/
|
|
274
|
+
interface LeakedAttribute {
|
|
275
|
+
/** Type of attribute detected */
|
|
276
|
+
type: 'profession' | 'affiliation' | 'temporal_marker' | 'geographic_signal' | 'relational_context' | 'unique_event' | 'demographic' | 'achievement' | 'public_role';
|
|
277
|
+
/** The phrase or context that reveals this attribute */
|
|
278
|
+
phrase: string;
|
|
279
|
+
/** Estimated population narrowing factor (smaller = more identifying) */
|
|
280
|
+
narrowingFactor: number;
|
|
281
|
+
/** Human-readable explanation */
|
|
282
|
+
explanation: string;
|
|
283
|
+
/** Suggested generalization to reduce risk */
|
|
284
|
+
suggestion?: string;
|
|
285
|
+
/** Location in the redacted text */
|
|
286
|
+
location: {
|
|
287
|
+
sectionId: string;
|
|
288
|
+
startOffset: number;
|
|
289
|
+
endOffset: number;
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* A phrase that could be searched to find the person.
|
|
294
|
+
*/
|
|
295
|
+
interface SearchableFragment {
|
|
296
|
+
/** The searchable text */
|
|
297
|
+
fragment: string;
|
|
298
|
+
/** How easy it would be to find the person via search */
|
|
299
|
+
searchability: 'trivial' | 'moderate' | 'difficult';
|
|
300
|
+
/** Why this is searchable */
|
|
301
|
+
reason: string;
|
|
302
|
+
/** What search would likely return */
|
|
303
|
+
predictedResults: string;
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Semantic fingerprint - how uniquely identifying is the redacted text?
|
|
307
|
+
*/
|
|
308
|
+
interface SemanticFingerprint {
|
|
309
|
+
/** Estimated population size that matches this description */
|
|
310
|
+
estimatedPopulationSize: number;
|
|
311
|
+
/** Human-readable population description */
|
|
312
|
+
populationDescription: string;
|
|
313
|
+
/** Primary factors driving uniqueness */
|
|
314
|
+
uniquenessDrivers: Array<{
|
|
315
|
+
phrase: string;
|
|
316
|
+
impact: 'critical' | 'high' | 'medium' | 'low';
|
|
317
|
+
narrowingFactor: number;
|
|
318
|
+
suggestion: string;
|
|
319
|
+
}>;
|
|
320
|
+
/** Combined re-identification risk score (0-100) */
|
|
321
|
+
riskScore: number;
|
|
322
|
+
/** Risk level classification */
|
|
323
|
+
riskLevel: 'critical' | 'high' | 'medium' | 'low' | 'minimal';
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Cross-reference vulnerability assessment.
|
|
327
|
+
*/
|
|
328
|
+
interface CrossReferenceRisk {
|
|
329
|
+
/** Fragments that could be used to search/identify */
|
|
330
|
+
searchableFragments: SearchableFragment[];
|
|
331
|
+
/** Databases/sources that could be cross-referenced */
|
|
332
|
+
vulnerableSources: Array<{
|
|
333
|
+
source: string;
|
|
334
|
+
matchLikelihood: 'certain' | 'likely' | 'possible' | 'unlikely';
|
|
335
|
+
dataPoints: string[];
|
|
336
|
+
}>;
|
|
337
|
+
/** Overall cross-reference risk score (0-100) */
|
|
338
|
+
riskScore: number;
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Result of an adversarial analysis pass.
|
|
342
|
+
*/
|
|
343
|
+
interface AdversarialAnalysis {
|
|
344
|
+
/** Unique ID for this analysis */
|
|
345
|
+
id: string;
|
|
346
|
+
/** When the analysis was performed */
|
|
347
|
+
timestamp: number;
|
|
348
|
+
/** Overall re-identification confidence (0-100) */
|
|
349
|
+
reidentificationConfidence: number;
|
|
350
|
+
/** Risk classification */
|
|
351
|
+
riskLevel: 'critical' | 'high' | 'medium' | 'low' | 'minimal';
|
|
352
|
+
/** Leaked attributes found in the text */
|
|
353
|
+
leakedAttributes: LeakedAttribute[];
|
|
354
|
+
/** Semantic fingerprint analysis */
|
|
355
|
+
semanticFingerprint: SemanticFingerprint;
|
|
356
|
+
/** Cross-reference vulnerability */
|
|
357
|
+
crossReferenceRisk: CrossReferenceRisk;
|
|
358
|
+
/** Sections analyzed */
|
|
359
|
+
sectionsAnalyzed: string[];
|
|
360
|
+
/** Processing time in ms */
|
|
361
|
+
processingTimeMs: number;
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Suggested action to reduce re-identification risk.
|
|
365
|
+
*/
|
|
366
|
+
interface AdversarialSuggestion {
|
|
367
|
+
/** Unique ID */
|
|
368
|
+
id: string;
|
|
369
|
+
/** Type of suggestion */
|
|
370
|
+
type: 'redact' | 'generalize' | 'rephrase' | 'remove';
|
|
371
|
+
/** Priority (1 = highest) */
|
|
372
|
+
priority: number;
|
|
373
|
+
/** The problematic phrase */
|
|
374
|
+
originalPhrase: string;
|
|
375
|
+
/** Suggested replacement */
|
|
376
|
+
suggestedReplacement: string;
|
|
377
|
+
/** Expected risk reduction (percentage points) */
|
|
378
|
+
expectedRiskReduction: number;
|
|
379
|
+
/** Location in document */
|
|
380
|
+
location: {
|
|
381
|
+
sectionId: string;
|
|
382
|
+
startOffset: number;
|
|
383
|
+
endOffset: number;
|
|
384
|
+
};
|
|
385
|
+
/** Why this change helps */
|
|
386
|
+
rationale: string;
|
|
387
|
+
/** Whether user has accepted this suggestion */
|
|
388
|
+
accepted: boolean;
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Complete adversarial verification result.
|
|
392
|
+
*/
|
|
393
|
+
interface AdversarialVerificationResult {
|
|
394
|
+
/** The analysis performed */
|
|
395
|
+
analysis: AdversarialAnalysis;
|
|
396
|
+
/** Suggested actions to reduce risk */
|
|
397
|
+
suggestions: AdversarialSuggestion[];
|
|
398
|
+
/** Whether the document passes the risk threshold */
|
|
399
|
+
passesThreshold: boolean;
|
|
400
|
+
/** Configured risk threshold */
|
|
401
|
+
riskThreshold: number;
|
|
402
|
+
/** Iteration number (for adversarial loop) */
|
|
403
|
+
iteration: number;
|
|
404
|
+
/** Previous iteration's confidence (to show improvement) */
|
|
405
|
+
previousConfidence?: number;
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Configuration for adversarial verification.
|
|
409
|
+
*/
|
|
410
|
+
interface AdversarialConfig {
|
|
411
|
+
/** Enable adversarial verification */
|
|
412
|
+
enabled: boolean;
|
|
413
|
+
/** Risk threshold (0-100) - fail if reidentification confidence exceeds this */
|
|
414
|
+
riskThreshold: number;
|
|
415
|
+
/** Maximum iterations of the adversarial loop */
|
|
416
|
+
maxIterations: number;
|
|
417
|
+
/** Whether to auto-apply low-risk suggestions */
|
|
418
|
+
autoApplyLowRisk: boolean;
|
|
419
|
+
/** Analysis depth */
|
|
420
|
+
analysisDepth: 'quick' | 'standard' | 'thorough';
|
|
421
|
+
/** Categories of analysis to perform */
|
|
422
|
+
enabledAnalyses: {
|
|
423
|
+
attributeLeakage: boolean;
|
|
424
|
+
semanticFingerprinting: boolean;
|
|
425
|
+
crossReferenceCheck: boolean;
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Get the confidence threshold for a given sensitivity level.
|
|
431
|
+
* Used for client-side filtering of detections.
|
|
432
|
+
*/
|
|
433
|
+
declare const getSensitivityThreshold: (sensitivity: ScrubConfig["sensitivity"]) => number;
|
|
434
|
+
interface PurgeStore {
|
|
435
|
+
state: PurgeState;
|
|
436
|
+
queuedFiles: QueuedFile[];
|
|
437
|
+
processedFiles: ProcessedFile[];
|
|
438
|
+
detections: Detection[];
|
|
439
|
+
selectedDetections: Set<string>;
|
|
440
|
+
config: ScrubConfig;
|
|
441
|
+
progress: ProcessingProgress | null;
|
|
442
|
+
columnConfig: ColumnSelectionConfig | null;
|
|
443
|
+
spreadsheetMetadata: SpreadsheetMetadata | null;
|
|
444
|
+
columnAccessProof: ColumnAccessProof | null;
|
|
445
|
+
adversarialResult: AdversarialVerificationResult | null;
|
|
446
|
+
adversarialConfig: AdversarialConfig;
|
|
447
|
+
isAnalyzingAdversarial: boolean;
|
|
448
|
+
paranoidMode: boolean;
|
|
449
|
+
setState: (state: PurgeState) => void;
|
|
450
|
+
feedDocuments: (files: QueuedFile[]) => void;
|
|
451
|
+
removeDocument: (id: string) => void;
|
|
452
|
+
updateFileStatus: (id: string, status: QueuedFile['status'], error?: string) => void;
|
|
453
|
+
addProcessedFile: (file: ProcessedFile) => void;
|
|
454
|
+
clearProcessedFiles: () => void;
|
|
455
|
+
setDetections: (detections: Detection[]) => void;
|
|
456
|
+
toggleDetection: (id: string) => void;
|
|
457
|
+
/**
|
|
458
|
+
* Select all detections. If visibleIds is provided, only select those
|
|
459
|
+
* (used for sensitivity-filtered selection).
|
|
460
|
+
*/
|
|
461
|
+
selectAllDetections: (visibleIds?: string[]) => void;
|
|
462
|
+
deselectAllDetections: () => void;
|
|
463
|
+
selectDetectionsByCategory: (category: PIICategory, selected: boolean) => void;
|
|
464
|
+
/**
|
|
465
|
+
* Clear PII values from detection objects after purging completes.
|
|
466
|
+
* This is a privacy-focused cleanup - the detection metadata (id, category,
|
|
467
|
+
* position) is preserved for UI display, but actual PII values are scrubbed.
|
|
468
|
+
*/
|
|
469
|
+
clearDetectionValues: () => void;
|
|
470
|
+
/**
|
|
471
|
+
* M-6 SECURITY FIX: Aggressively clear all sensitive data from store.
|
|
472
|
+
* Use this instead of clearDetectionValues when you want complete cleanup.
|
|
473
|
+
* Clears detections entirely rather than just replacing values.
|
|
474
|
+
*/
|
|
475
|
+
secureClear: () => void;
|
|
476
|
+
updateConfig: (config: Partial<ScrubConfig>) => void;
|
|
477
|
+
toggleCategory: (category: PIICategory) => void;
|
|
478
|
+
setRedactionStyle: (style: ScrubConfig['redactionStyle']) => void;
|
|
479
|
+
setProgress: (progress: ProcessingProgress | null) => void;
|
|
480
|
+
setColumnConfig: (config: ColumnSelectionConfig | null) => void;
|
|
481
|
+
setSpreadsheetMetadata: (metadata: SpreadsheetMetadata | null) => void;
|
|
482
|
+
setColumnAccessProof: (proof: ColumnAccessProof | null) => void;
|
|
483
|
+
setAdversarialResult: (result: AdversarialVerificationResult | null) => void;
|
|
484
|
+
setAdversarialConfig: (config: Partial<AdversarialConfig>) => void;
|
|
485
|
+
setIsAnalyzingAdversarial: (isAnalyzing: boolean) => void;
|
|
486
|
+
applySuggestion: (suggestion: AdversarialSuggestion) => void;
|
|
487
|
+
clearAdversarialState: () => void;
|
|
488
|
+
setParanoidMode: (enabled: boolean) => void;
|
|
489
|
+
reset: () => void;
|
|
490
|
+
/**
|
|
491
|
+
* Soft reset for multi-run support.
|
|
492
|
+
* Clears files/detections but preserves config settings.
|
|
493
|
+
* Returns to idle state ready for new files.
|
|
494
|
+
*/
|
|
495
|
+
softReset: () => void;
|
|
496
|
+
}
|
|
497
|
+
declare const usePurgeStore: zustand.UseBoundStore<zustand.StoreApi<PurgeStore>>;
|
|
498
|
+
|
|
499
|
+
/**
|
|
500
|
+
* Regex Detection Engine
|
|
501
|
+
* Client-side PII detection using regex patterns
|
|
502
|
+
*
|
|
503
|
+
* SECURITY NOTE: Uses Web Workers for regex execution to enable true timeout
|
|
504
|
+
* termination. When a regex takes too long, the worker is terminated, killing
|
|
505
|
+
* the regex execution immediately. This prevents ReDoS attacks from freezing
|
|
506
|
+
* the browser tab.
|
|
507
|
+
*/
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* Regex-based detection engine
|
|
511
|
+
* Provides client-side PII detection without network requests
|
|
512
|
+
*/
|
|
513
|
+
declare class RegexDetectionEngine implements DetectionEngine {
|
|
514
|
+
private version;
|
|
515
|
+
detect(content: DocumentContent, config: ScrubConfig): Promise<DetectionResult>;
|
|
516
|
+
isAvailable(): Promise<boolean>;
|
|
517
|
+
getCapabilities(): DetectionCapabilities;
|
|
518
|
+
/**
|
|
519
|
+
* Calculate confidence score based on pattern and context
|
|
520
|
+
*/
|
|
521
|
+
private calculateConfidence;
|
|
522
|
+
/**
|
|
523
|
+
* Get minimum confidence threshold for sensitivity level
|
|
524
|
+
*/
|
|
525
|
+
private getSensitivityThreshold;
|
|
526
|
+
/**
|
|
527
|
+
* Remove duplicate detections at the same position
|
|
528
|
+
*/
|
|
529
|
+
private deduplicateDetections;
|
|
530
|
+
}
|
|
531
|
+
declare const regexDetectionEngine: RegexDetectionEngine;
|
|
532
|
+
|
|
533
|
+
/**
|
|
534
|
+
* PII Detection Patterns
|
|
535
|
+
* Regex patterns for detecting personally identifiable information
|
|
536
|
+
*/
|
|
537
|
+
|
|
538
|
+
interface PatternDefinition {
|
|
539
|
+
category: PIICategory;
|
|
540
|
+
pattern: RegExp;
|
|
541
|
+
priority: number;
|
|
542
|
+
validator?: (match: string) => boolean;
|
|
543
|
+
}
|
|
544
|
+
/**
|
|
545
|
+
* Get patterns for specific categories (cached)
|
|
546
|
+
* Uses a cache to avoid repeated filtering for the same category combinations.
|
|
547
|
+
*/
|
|
548
|
+
declare function getPatternsForCategories(enabledCategories: PIICategory[]): PatternDefinition[];
|
|
549
|
+
|
|
550
|
+
/**
|
|
551
|
+
* BaseProcessor
|
|
552
|
+
* Abstract base class for document processors
|
|
553
|
+
*/
|
|
554
|
+
|
|
555
|
+
/**
|
|
556
|
+
* Generate unique section ID using cryptographically secure random
|
|
557
|
+
*/
|
|
558
|
+
declare function generateSectionId(): string;
|
|
559
|
+
/**
|
|
560
|
+
* Abstract base class for document processors
|
|
561
|
+
*/
|
|
562
|
+
declare abstract class BaseProcessor implements DocumentProcessor {
|
|
563
|
+
abstract readonly fileType: SupportedFileType;
|
|
564
|
+
abstract readonly mimeTypes: string[];
|
|
565
|
+
abstract readonly extensions: string[];
|
|
566
|
+
/**
|
|
567
|
+
* Check if this processor can handle the file
|
|
568
|
+
*/
|
|
569
|
+
canProcess(file: File): boolean;
|
|
570
|
+
/**
|
|
571
|
+
* Parse the document and extract text content
|
|
572
|
+
*/
|
|
573
|
+
abstract parse(file: File): Promise<ParsedDocument>;
|
|
574
|
+
/**
|
|
575
|
+
* Apply redactions and generate output
|
|
576
|
+
*/
|
|
577
|
+
abstract applyRedactions(document: ParsedDocument, redactions: Redaction[]): Promise<Blob>;
|
|
578
|
+
/**
|
|
579
|
+
* Get supported MIME types
|
|
580
|
+
*/
|
|
581
|
+
getSupportedMimeTypes(): string[];
|
|
582
|
+
/**
|
|
583
|
+
* Helper to create a content section
|
|
584
|
+
*/
|
|
585
|
+
protected createSection(text: string, type: ContentSection['type'], location: ContentSection['location']): ContentSection;
|
|
586
|
+
/**
|
|
587
|
+
* Apply text replacements to a string
|
|
588
|
+
*/
|
|
589
|
+
protected applyTextRedactions(text: string, redactions: Redaction[], sectionId: string): string;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
/**
|
|
593
|
+
* XlsxProcessor
|
|
594
|
+
* Excel document processor using xlsx library
|
|
595
|
+
*
|
|
596
|
+
* SECURITY: This processor rebuilds the output file from scratch rather than
|
|
597
|
+
* modifying the original. This approach strips:
|
|
598
|
+
* - Document properties (author, company, etc.)
|
|
599
|
+
* - Hidden sheets
|
|
600
|
+
* - Comments and notes
|
|
601
|
+
* - Formulas (converted to values only)
|
|
602
|
+
* - Custom XML parts
|
|
603
|
+
* - External links
|
|
604
|
+
*
|
|
605
|
+
* M-8 WARNING: Formula cells are converted to their calculated values.
|
|
606
|
+
* If a formula references hidden cells containing PII (e.g., =A1 where A1
|
|
607
|
+
* contains "John Doe's SSN: 123-45-6789"), the PII will appear in the
|
|
608
|
+
* formula cell's value. Users should review formula cells carefully and
|
|
609
|
+
* ensure hidden columns/sheets don't contain sensitive data that formulas
|
|
610
|
+
* reference.
|
|
611
|
+
*/
|
|
612
|
+
|
|
613
|
+
/**
|
|
614
|
+
* Options for parsing XLSX files
|
|
615
|
+
*/
|
|
616
|
+
interface XlsxParseOptions {
|
|
617
|
+
/** Column selection configuration for selective parsing */
|
|
618
|
+
columnSelection?: ColumnSelectionConfig;
|
|
619
|
+
/** Whether to generate attestations for skipped columns */
|
|
620
|
+
generateAttestation?: boolean;
|
|
621
|
+
}
|
|
622
|
+
declare class XlsxProcessor extends BaseProcessor {
|
|
623
|
+
readonly fileType: "xlsx";
|
|
624
|
+
readonly mimeTypes: string[];
|
|
625
|
+
readonly extensions: string[];
|
|
626
|
+
/**
|
|
627
|
+
* Parse Excel file and extract text from cells.
|
|
628
|
+
* Supports selective column parsing with attestation generation.
|
|
629
|
+
*
|
|
630
|
+
* @param file - The Excel file to parse
|
|
631
|
+
* @param options - Optional parsing options for column selection
|
|
632
|
+
*/
|
|
633
|
+
parse(file: File, options?: XlsxParseOptions): Promise<ParsedDocument>;
|
|
634
|
+
/**
|
|
635
|
+
* Generate metadata record for a skipped column.
|
|
636
|
+
*
|
|
637
|
+
* NOTE: This does NOT cryptographically prove anything meaningful.
|
|
638
|
+
* It just records column metadata (position, cell count). The hash
|
|
639
|
+
* is of this metadata string, not the actual cell values. This is
|
|
640
|
+
* for internal tracking only - we removed the misleading
|
|
641
|
+
* "cryptographic attestation" claim from the UI.
|
|
642
|
+
*/
|
|
643
|
+
private generateColumnAttestation;
|
|
644
|
+
/**
|
|
645
|
+
* Apply redactions and generate new Excel file
|
|
646
|
+
*
|
|
647
|
+
* SECURITY: This method rebuilds the workbook from scratch to strip metadata.
|
|
648
|
+
* Only cell values are copied - no formulas, comments, properties, or hidden sheets.
|
|
649
|
+
*/
|
|
650
|
+
applyRedactions(document: ParsedDocument, redactions: Redaction[]): Promise<Blob>;
|
|
651
|
+
}
|
|
652
|
+
declare const xlsxProcessor: XlsxProcessor;
|
|
653
|
+
|
|
654
|
+
/**
|
|
655
|
+
* Document Processors
|
|
656
|
+
* Export all available processors
|
|
657
|
+
*/
|
|
658
|
+
|
|
659
|
+
/**
|
|
660
|
+
* Get processor for a file type
|
|
661
|
+
*/
|
|
662
|
+
declare function getProcessor(fileType: SupportedFileType): DocumentProcessor | null;
|
|
663
|
+
/**
|
|
664
|
+
* Get processor for a file
|
|
665
|
+
*/
|
|
666
|
+
declare function getProcessorForFile(file: File): DocumentProcessor | null;
|
|
667
|
+
|
|
668
|
+
/**
|
|
669
|
+
* Adversarial Verification Engine
|
|
670
|
+
*
|
|
671
|
+
* This service thinks like an attacker - given redacted text, how confident
|
|
672
|
+
* would an adversary be in re-identifying the subject?
|
|
673
|
+
*
|
|
674
|
+
* The engine analyzes:
|
|
675
|
+
* 1. Leaked attributes (profession, affiliation, temporal markers, etc.)
|
|
676
|
+
* 2. Semantic fingerprinting (how unique is this description?)
|
|
677
|
+
* 3. Cross-reference vulnerability (could this be Googled?)
|
|
678
|
+
*/
|
|
679
|
+
|
|
680
|
+
declare class AdversarialVerifier {
|
|
681
|
+
private config;
|
|
682
|
+
constructor(config?: Partial<AdversarialConfig>);
|
|
683
|
+
/**
|
|
684
|
+
* Main entry point: analyze redacted content for re-identification risk
|
|
685
|
+
*/
|
|
686
|
+
analyze(sections: ContentSection[], appliedRedactions: Detection[]): Promise<AdversarialVerificationResult>;
|
|
687
|
+
/**
|
|
688
|
+
* Simulate what the document looks like after redaction
|
|
689
|
+
*/
|
|
690
|
+
private simulateRedactedOutput;
|
|
691
|
+
/**
|
|
692
|
+
* Extract attributes that leak identity from redacted text
|
|
693
|
+
*/
|
|
694
|
+
private extractLeakedAttributes;
|
|
695
|
+
/**
|
|
696
|
+
* Check if a position is inside a [REDACTED] marker
|
|
697
|
+
*/
|
|
698
|
+
private isInsideRedaction;
|
|
699
|
+
/**
|
|
700
|
+
* Calculate semantic fingerprint - how unique is this description?
|
|
701
|
+
*/
|
|
702
|
+
private calculateSemanticFingerprint;
|
|
703
|
+
/**
|
|
704
|
+
* Categorize the impact of a narrowing factor
|
|
705
|
+
*/
|
|
706
|
+
private categorizeImpact;
|
|
707
|
+
/**
|
|
708
|
+
* Create human-readable population description
|
|
709
|
+
*/
|
|
710
|
+
private describePopulation;
|
|
711
|
+
/**
|
|
712
|
+
* Assess cross-reference vulnerability
|
|
713
|
+
*/
|
|
714
|
+
private assessCrossReferenceRisk;
|
|
715
|
+
/**
|
|
716
|
+
* Predict what a search might return
|
|
717
|
+
*/
|
|
718
|
+
private predictSearchResults;
|
|
719
|
+
/**
|
|
720
|
+
* Assess likelihood of database match
|
|
721
|
+
*/
|
|
722
|
+
private assessMatchLikelihood;
|
|
723
|
+
/**
|
|
724
|
+
* Calculate overall re-identification confidence
|
|
725
|
+
*/
|
|
726
|
+
private calculateOverallConfidence;
|
|
727
|
+
/**
|
|
728
|
+
* Classify risk level based on confidence score
|
|
729
|
+
*/
|
|
730
|
+
private classifyRiskLevel;
|
|
731
|
+
/**
|
|
732
|
+
* Generate suggestions for reducing risk
|
|
733
|
+
*/
|
|
734
|
+
private generateSuggestions;
|
|
735
|
+
/**
|
|
736
|
+
* Estimate risk reduction from applying a suggestion
|
|
737
|
+
*/
|
|
738
|
+
private estimateRiskReduction;
|
|
739
|
+
/**
|
|
740
|
+
* Empty semantic fingerprint for when analysis is disabled
|
|
741
|
+
*/
|
|
742
|
+
private emptySemanticFingerprint;
|
|
743
|
+
/**
|
|
744
|
+
* Empty cross-reference risk for when analysis is disabled
|
|
745
|
+
*/
|
|
746
|
+
private emptyCrossReferenceRisk;
|
|
747
|
+
/**
|
|
748
|
+
* Update configuration
|
|
749
|
+
*/
|
|
750
|
+
setConfig(config: Partial<AdversarialConfig>): void;
|
|
751
|
+
/**
|
|
752
|
+
* Get current configuration
|
|
753
|
+
*/
|
|
754
|
+
getConfig(): AdversarialConfig;
|
|
755
|
+
}
|
|
756
|
+
declare const adversarialVerifier: AdversarialVerifier;
|
|
757
|
+
|
|
758
|
+
/**
|
|
759
|
+
* useDocumentProcessor Hook
|
|
760
|
+
* Main processing hook for document scrubbing workflow
|
|
761
|
+
*
|
|
762
|
+
* NOTE: The wipeMemory function zeros out tracked buffers as a visualization
|
|
763
|
+
* of cleanup, but JavaScript does not provide true secure memory wiping.
|
|
764
|
+
* The original file data may persist in browser memory until garbage collected.
|
|
765
|
+
*/
|
|
766
|
+
|
|
767
|
+
/**
|
|
768
|
+
* Result object returned by the useDocumentProcessor hook
|
|
769
|
+
*/
|
|
770
|
+
interface UseDocumentProcessorResult {
|
|
771
|
+
/** Whether document processing is currently in progress */
|
|
772
|
+
isProcessing: boolean;
|
|
773
|
+
/** Current processing progress (null when not processing) */
|
|
774
|
+
progress: ProcessingProgress | null;
|
|
775
|
+
/** Array of PII detections found in scanned documents */
|
|
776
|
+
detections: Detection[];
|
|
777
|
+
/** Memory allocation and cleanup statistics */
|
|
778
|
+
memoryStats: MemoryStats;
|
|
779
|
+
/** Before/after entropy comparison for visualization */
|
|
780
|
+
entropyComparison: EntropyComparison;
|
|
781
|
+
/** Whether entropy calculation is in progress */
|
|
782
|
+
isCalculatingEntropy: boolean;
|
|
783
|
+
/** Content sections from parsed documents (for adversarial analysis) */
|
|
784
|
+
contentSections: ContentSection[];
|
|
785
|
+
/**
|
|
786
|
+
* Scan files for PII detections
|
|
787
|
+
* @param files - Array of queued files to scan
|
|
788
|
+
* @param config - Configuration specifying which categories to detect
|
|
789
|
+
* @param bypassQuota - If true, skip offline quota checks (for online_acknowledged mode)
|
|
790
|
+
* @returns Promise resolving to array of detections found
|
|
791
|
+
*/
|
|
792
|
+
scanFiles: (files: QueuedFile[], config: ScrubConfig, bypassQuota?: boolean) => Promise<Detection[]>;
|
|
793
|
+
/**
|
|
794
|
+
* Process files and apply redactions to selected detections
|
|
795
|
+
* @param files - Array of queued files to process
|
|
796
|
+
* @param selectedDetections - Set of detection IDs to redact
|
|
797
|
+
* @param config - Configuration specifying redaction style
|
|
798
|
+
* @returns Promise resolving to array of processed files
|
|
799
|
+
*/
|
|
800
|
+
processFiles: (files: QueuedFile[], selectedDetections: Set<string>, config: ScrubConfig) => Promise<ProcessedFile[]>;
|
|
801
|
+
/**
|
|
802
|
+
* Wipe tracked memory buffers (visualization only - not cryptographically secure)
|
|
803
|
+
*/
|
|
804
|
+
wipeMemory: () => Promise<void>;
|
|
805
|
+
}
|
|
806
|
+
declare function useDocumentProcessor(): UseDocumentProcessorResult;
|
|
807
|
+
|
|
808
|
+
/**
|
|
809
|
+
* useOfflineEnforcement Hook
|
|
810
|
+
*
|
|
811
|
+
* Encourages offline mode for maximum privacy assurance during processing.
|
|
812
|
+
* While offline mode provides strong indication of no network activity,
|
|
813
|
+
* it cannot guarantee complete protection (extensions, etc. may still access data).
|
|
814
|
+
*
|
|
815
|
+
* States:
|
|
816
|
+
* - online_blocked: Recommends user go offline for better assurance
|
|
817
|
+
* - online_acknowledged: User explicitly accepted online risk (bypass with warning)
|
|
818
|
+
* - offline_ready: Offline detected, ready to accept files
|
|
819
|
+
* - processing: Currently processing files
|
|
820
|
+
* - reconnected_abort: Connection detected mid-process, processing stopped
|
|
821
|
+
* - complete: Done processing, prompts to download before reconnecting
|
|
822
|
+
* - reconnected_warning: Reconnected before download, shows reminder
|
|
823
|
+
*/
|
|
824
|
+
type OfflineEnforcementStatus = 'online_blocked' | 'online_acknowledged' | 'offline_ready' | 'sw_blocked' | 'quota_exhausted' | 'processing' | 'awaiting_download' | 'reconnected_abort' | 'complete' | 'reconnected_warning';
|
|
825
|
+
/**
|
|
826
|
+
* State object representing the current offline enforcement status
|
|
827
|
+
*/
|
|
828
|
+
interface OfflineEnforcementState {
|
|
829
|
+
/** Current state machine status */
|
|
830
|
+
status: OfflineEnforcementStatus;
|
|
831
|
+
/** Whether the browser is currently online */
|
|
832
|
+
isOnline: boolean;
|
|
833
|
+
/** Whether processing can proceed (offline and ready) */
|
|
834
|
+
canProcess: boolean;
|
|
835
|
+
/** Countdown seconds remaining (null when not counting) */
|
|
836
|
+
countdownSeconds: number | null;
|
|
837
|
+
/** Whether the user has downloaded processed files */
|
|
838
|
+
hasDownloaded: boolean;
|
|
839
|
+
/** Number of countdown extensions used */
|
|
840
|
+
extensionsUsed: number;
|
|
841
|
+
/** Maximum allowed countdown extensions */
|
|
842
|
+
maxExtensions: number;
|
|
843
|
+
/** Whether processing started in online (acknowledged) mode - affects close behavior */
|
|
844
|
+
startedOnline: boolean;
|
|
845
|
+
/** Remaining offline quota tokens */
|
|
846
|
+
quotaRemaining: number;
|
|
847
|
+
/** Whether offline quota is exhausted */
|
|
848
|
+
quotaExhausted: boolean;
|
|
849
|
+
}
|
|
850
|
+
/**
|
|
851
|
+
* Actions available for controlling offline enforcement behavior
|
|
852
|
+
*/
|
|
853
|
+
interface OfflineEnforcementActions {
|
|
854
|
+
/** Transition to processing state when files are dropped. Async because it checks for service workers in offline mode. */
|
|
855
|
+
startProcessing: () => Promise<void>;
|
|
856
|
+
/** Mark processing as complete and start countdown timer */
|
|
857
|
+
completeProcessing: () => void;
|
|
858
|
+
/** Mark that the user has downloaded their processed files */
|
|
859
|
+
markDownloaded: () => void;
|
|
860
|
+
/** Extend countdown by 30 seconds (max 2 times). Returns false if extensions exhausted. */
|
|
861
|
+
extendCountdown: () => boolean;
|
|
862
|
+
/** Force close the tab immediately by navigating to about:blank */
|
|
863
|
+
forceClose: () => void;
|
|
864
|
+
/** Reset state machine to initial state based on current online status */
|
|
865
|
+
reset: () => void;
|
|
866
|
+
/** User explicitly acknowledges online risk and wants to proceed anyway */
|
|
867
|
+
acknowledgeOnlineRisk: () => void;
|
|
868
|
+
}
|
|
869
|
+
/**
|
|
870
|
+
* Result object returned by the useOfflineEnforcement hook
|
|
871
|
+
*/
|
|
872
|
+
interface UseOfflineEnforcementResult {
|
|
873
|
+
/** Current enforcement state */
|
|
874
|
+
state: OfflineEnforcementState;
|
|
875
|
+
/** Actions to control enforcement behavior */
|
|
876
|
+
actions: OfflineEnforcementActions;
|
|
877
|
+
}
|
|
878
|
+
declare function useOfflineEnforcement(): UseOfflineEnforcementResult;
|
|
879
|
+
|
|
880
|
+
/**
|
|
881
|
+
* QuotaRefreshAPI
|
|
882
|
+
*
|
|
883
|
+
* Stubbed API client for quota refresh.
|
|
884
|
+
* Currently returns offline/unavailable status.
|
|
885
|
+
*
|
|
886
|
+
* Future implementation will:
|
|
887
|
+
* 1. POST to /api/quota/refresh with auth token
|
|
888
|
+
* 2. Server validates user tier and usage
|
|
889
|
+
* 3. Returns new token count or denial
|
|
890
|
+
*/
|
|
891
|
+
type RefreshResult = {
|
|
892
|
+
success: true;
|
|
893
|
+
tokens: number;
|
|
894
|
+
} | {
|
|
895
|
+
success: false;
|
|
896
|
+
reason: 'offline' | 'api_error' | 'not_authenticated' | 'rate_limited';
|
|
897
|
+
};
|
|
898
|
+
/**
|
|
899
|
+
* Requests a quota refresh from the server.
|
|
900
|
+
*
|
|
901
|
+
* STUB IMPLEMENTATION:
|
|
902
|
+
* - Returns { success: false, reason: 'offline' } when offline
|
|
903
|
+
* - Returns { success: false, reason: 'not_authenticated' } when online
|
|
904
|
+
* (since auth isn't implemented yet)
|
|
905
|
+
*
|
|
906
|
+
* FUTURE IMPLEMENTATION:
|
|
907
|
+
* - POST to /api/quota/refresh with JWT auth
|
|
908
|
+
* - Server validates user, tier, and usage limits
|
|
909
|
+
* - Returns new token allocation
|
|
910
|
+
*/
|
|
911
|
+
declare function requestQuotaRefresh(): Promise<RefreshResult>;
|
|
912
|
+
|
|
913
|
+
/**
|
|
914
|
+
* OfflineQuotaStore
|
|
915
|
+
*
|
|
916
|
+
* Tamper-resistant storage for offline usage quota.
|
|
917
|
+
* Uses HMAC signatures and IndexedDB backup for integrity.
|
|
918
|
+
*
|
|
919
|
+
* Security measures:
|
|
920
|
+
* 1. HMAC-SHA256 signature on all stored data
|
|
921
|
+
* 2. IndexedDB backup for cross-validation
|
|
922
|
+
* 3. Device fingerprint binding
|
|
923
|
+
* 4. Tampering detection with quota reset
|
|
924
|
+
*/
|
|
925
|
+
interface QuotaState {
|
|
926
|
+
/** Remaining processing tokens (0-50) */
|
|
927
|
+
tokensRemaining: number;
|
|
928
|
+
/** ISO timestamp of last refresh */
|
|
929
|
+
lastRefresh: string;
|
|
930
|
+
/** Device fingerprint this quota is bound to */
|
|
931
|
+
deviceFingerprint: string;
|
|
932
|
+
/** Schema version for migrations */
|
|
933
|
+
version: number;
|
|
934
|
+
}
|
|
935
|
+
declare class OfflineQuotaStore {
|
|
936
|
+
private cachedState;
|
|
937
|
+
private fingerprint;
|
|
938
|
+
/**
|
|
939
|
+
* Initializes the store, loading existing state or creating new
|
|
940
|
+
*/
|
|
941
|
+
initialize(): Promise<QuotaState>;
|
|
942
|
+
/**
|
|
943
|
+
* Gets current tokens remaining (cached for performance)
|
|
944
|
+
*/
|
|
945
|
+
getTokens(): number;
|
|
946
|
+
/**
|
|
947
|
+
* Checks if processing is allowed
|
|
948
|
+
*/
|
|
949
|
+
canProcess(): boolean;
|
|
950
|
+
/**
|
|
951
|
+
* Decrements quota by 1 token. Returns new remaining count.
|
|
952
|
+
* Throws if quota is exhausted.
|
|
953
|
+
*/
|
|
954
|
+
decrement(): Promise<number>;
|
|
955
|
+
/**
|
|
956
|
+
* Resets quota to specified token count (used by refresh API)
|
|
957
|
+
*/
|
|
958
|
+
reset(tokens: number): Promise<void>;
|
|
959
|
+
/**
|
|
960
|
+
* Loads and validates quota state from storage
|
|
961
|
+
*/
|
|
962
|
+
private load;
|
|
963
|
+
/**
|
|
964
|
+
* Saves quota state to both storage locations
|
|
965
|
+
*/
|
|
966
|
+
private save;
|
|
967
|
+
/**
|
|
968
|
+
* Creates initial state for new users
|
|
969
|
+
*/
|
|
970
|
+
private createInitialState;
|
|
971
|
+
/**
|
|
972
|
+
* Validates a signed state
|
|
973
|
+
*/
|
|
974
|
+
private validateState;
|
|
975
|
+
/**
|
|
976
|
+
* Handles detected tampering by resetting quota to 0
|
|
977
|
+
*/
|
|
978
|
+
private handleTampering;
|
|
979
|
+
/**
|
|
980
|
+
* LocalStorage helpers
|
|
981
|
+
*/
|
|
982
|
+
private loadFromLocalStorage;
|
|
983
|
+
private saveToLocalStorage;
|
|
984
|
+
/**
|
|
985
|
+
* Clears all quota data (for testing/debugging)
|
|
986
|
+
*/
|
|
987
|
+
clear(): Promise<void>;
|
|
988
|
+
}
|
|
989
|
+
declare class QuotaExhaustedError extends Error {
|
|
990
|
+
constructor(message: string);
|
|
991
|
+
}
|
|
992
|
+
declare const offlineQuotaStore: OfflineQuotaStore;
|
|
993
|
+
|
|
994
|
+
/**
|
|
995
|
+
* useOfflineQuota Hook
|
|
996
|
+
*
|
|
997
|
+
* React hook for offline quota enforcement.
|
|
998
|
+
* Provides quota state and actions for the document processor.
|
|
999
|
+
*/
|
|
1000
|
+
|
|
1001
|
+
interface UseOfflineQuotaReturn {
|
|
1002
|
+
/** Number of tokens remaining */
|
|
1003
|
+
tokensRemaining: number;
|
|
1004
|
+
/** Whether quota is exhausted (0 tokens) */
|
|
1005
|
+
isExhausted: boolean;
|
|
1006
|
+
/** Whether quota store is ready */
|
|
1007
|
+
isInitialized: boolean;
|
|
1008
|
+
/** Check if a file can be processed */
|
|
1009
|
+
canProcessFile: () => boolean;
|
|
1010
|
+
/** Consume one token (call after successful processing) */
|
|
1011
|
+
consumeToken: () => Promise<void>;
|
|
1012
|
+
/** Request quota refresh from server (stubbed) */
|
|
1013
|
+
requestRefresh: () => Promise<RefreshResult>;
|
|
1014
|
+
/** Error message if any */
|
|
1015
|
+
quotaError: string | null;
|
|
1016
|
+
/** Last refresh timestamp */
|
|
1017
|
+
lastRefresh: string | null;
|
|
1018
|
+
}
|
|
1019
|
+
declare function useOfflineQuota(): UseOfflineQuotaReturn;
|
|
1020
|
+
|
|
1021
|
+
/**
|
|
1022
|
+
* useFileEntropy Hook
|
|
1023
|
+
*
|
|
1024
|
+
* Calculates Shannon entropy for file data to visualize data randomness.
|
|
1025
|
+
* High entropy (near 8 bits) indicates random/encrypted/compressed data.
|
|
1026
|
+
* Low entropy (near 0 bits) indicates repetitive/uniform data.
|
|
1027
|
+
*
|
|
1028
|
+
* PII like names, SSNs, emails typically has moderate-high entropy.
|
|
1029
|
+
* Redacted text like "[REDACTED]" has low entropy due to repetition.
|
|
1030
|
+
*
|
|
1031
|
+
* References:
|
|
1032
|
+
* - Shannon entropy: https://en.wikipedia.org/wiki/Entropy_(information_theory)
|
|
1033
|
+
* - binvis.io: https://binvis.io/
|
|
1034
|
+
*/
|
|
1035
|
+
|
|
1036
|
+
interface UseFileEntropyOptions {
|
|
1037
|
+
/** Size of each block in bytes (default: 256) */
|
|
1038
|
+
blockSize?: number;
|
|
1039
|
+
}
|
|
1040
|
+
interface UseFileEntropyResult {
|
|
1041
|
+
/** Calculate entropy for raw bytes */
|
|
1042
|
+
calculateEntropy: (bytes: Uint8Array, detections?: Detection[]) => EntropyData;
|
|
1043
|
+
/** Calculate entropy for a File object */
|
|
1044
|
+
calculateFileEntropy: (file: File, detections?: Detection[]) => Promise<EntropyData>;
|
|
1045
|
+
/** Calculate entropy for text content */
|
|
1046
|
+
calculateTextEntropy: (text: string, detections?: Detection[]) => EntropyData;
|
|
1047
|
+
/** Compare before and after entropy */
|
|
1048
|
+
compareEntropy: (before: EntropyData | null, after: EntropyData | null) => EntropyComparison;
|
|
1049
|
+
/** Loading state for async operations */
|
|
1050
|
+
isCalculating: boolean;
|
|
1051
|
+
/** Any error during calculation */
|
|
1052
|
+
error: Error | null;
|
|
1053
|
+
}
|
|
1054
|
+
/**
|
|
1055
|
+
* Hook for calculating file entropy.
|
|
1056
|
+
*/
|
|
1057
|
+
declare function useFileEntropy(options?: UseFileEntropyOptions): UseFileEntropyResult;
|
|
1058
|
+
|
|
1059
|
+
/**
|
|
1060
|
+
* useFileHash Hook
|
|
1061
|
+
*
|
|
1062
|
+
* Calculates SHA-256 hash of files using Web Crypto API.
|
|
1063
|
+
*
|
|
1064
|
+
* IMPORTANT: This proves FILE INTEGRITY (wasn't modified during processing).
|
|
1065
|
+
* This does NOT prove the file wasn't copied/uploaded.
|
|
1066
|
+
* The UI should be honest about this distinction.
|
|
1067
|
+
*/
|
|
1068
|
+
interface FileHashState {
|
|
1069
|
+
/** SHA-256 hash of original file */
|
|
1070
|
+
originalHash: string | null;
|
|
1071
|
+
/** SHA-256 hash of processed file */
|
|
1072
|
+
processedHash: string | null;
|
|
1073
|
+
/** Timestamp when hashes were calculated */
|
|
1074
|
+
timestamp: string | null;
|
|
1075
|
+
/** Whether hashing is in progress */
|
|
1076
|
+
isHashing: boolean;
|
|
1077
|
+
/** Error message if hashing failed */
|
|
1078
|
+
error: string | null;
|
|
1079
|
+
}
|
|
1080
|
+
interface UseFileHashResult {
|
|
1081
|
+
state: FileHashState;
|
|
1082
|
+
/** Calculate hash of original file */
|
|
1083
|
+
hashOriginalFile: (file: File) => Promise<string>;
|
|
1084
|
+
/** Calculate hash of processed blob */
|
|
1085
|
+
hashProcessedBlob: (blob: Blob) => Promise<string>;
|
|
1086
|
+
/** Reset state */
|
|
1087
|
+
reset: () => void;
|
|
1088
|
+
/** Get hash comparison result */
|
|
1089
|
+
getComparisonResult: () => HashComparisonResult;
|
|
1090
|
+
}
|
|
1091
|
+
interface HashComparisonResult {
|
|
1092
|
+
/** Whether both hashes exist */
|
|
1093
|
+
hasBothHashes: boolean;
|
|
1094
|
+
/** Whether hashes match (file unchanged) */
|
|
1095
|
+
hashesMatch: boolean;
|
|
1096
|
+
/** Human-readable message */
|
|
1097
|
+
message: string;
|
|
1098
|
+
}
|
|
1099
|
+
/**
|
|
1100
|
+
* Truncate hash for display (first 8 + last 8 chars)
|
|
1101
|
+
*/
|
|
1102
|
+
declare function truncateHash(hash: string): string;
|
|
1103
|
+
declare function useFileHash(): UseFileHashResult;
|
|
1104
|
+
|
|
1105
|
+
/**
|
|
1106
|
+
* useAdversarialAnalysis Hook
|
|
1107
|
+
*
|
|
1108
|
+
* Orchestrates adversarial verification analysis on redacted content.
|
|
1109
|
+
* Connects the AdversarialVerifier service with the Purge store and UI.
|
|
1110
|
+
*/
|
|
1111
|
+
|
|
1112
|
+
interface UseAdversarialAnalysisOptions {
|
|
1113
|
+
/** Automatically run analysis when detections change */
|
|
1114
|
+
autoAnalyze?: boolean;
|
|
1115
|
+
/** Debounce delay in ms for auto-analysis */
|
|
1116
|
+
debounceMs?: number;
|
|
1117
|
+
}
|
|
1118
|
+
interface UseAdversarialAnalysisReturn {
|
|
1119
|
+
/** Current analysis result */
|
|
1120
|
+
result: AdversarialVerificationResult | null;
|
|
1121
|
+
/** Whether analysis is in progress */
|
|
1122
|
+
isAnalyzing: boolean;
|
|
1123
|
+
/** Current configuration */
|
|
1124
|
+
config: AdversarialConfig;
|
|
1125
|
+
/** Run analysis manually */
|
|
1126
|
+
analyze: (sections: ContentSection[], selectedDetections: Detection[]) => Promise<void>;
|
|
1127
|
+
/** Apply a single suggestion */
|
|
1128
|
+
applySuggestion: (suggestion: AdversarialSuggestion) => void;
|
|
1129
|
+
/** Apply all suggestions */
|
|
1130
|
+
applyAllSuggestions: () => void;
|
|
1131
|
+
/** Update configuration */
|
|
1132
|
+
updateConfig: (config: Partial<AdversarialConfig>) => void;
|
|
1133
|
+
/** Clear analysis state */
|
|
1134
|
+
clear: () => void;
|
|
1135
|
+
/** Dismiss the analysis panel */
|
|
1136
|
+
dismiss: () => void;
|
|
1137
|
+
}
|
|
1138
|
+
declare function useAdversarialAnalysis(options?: UseAdversarialAnalysisOptions): UseAdversarialAnalysisReturn;
|
|
1139
|
+
|
|
1140
|
+
/**
|
|
1141
|
+
* useSpreadsheetMetadata Hook
|
|
1142
|
+
* Extracts spreadsheet structure WITHOUT reading sensitive cell values.
|
|
1143
|
+
* This allows column selection UI while maintaining privacy.
|
|
1144
|
+
*/
|
|
1145
|
+
|
|
1146
|
+
interface UseSpreadsheetMetadataResult {
|
|
1147
|
+
/**
|
|
1148
|
+
* Extract metadata from an XLSX file
|
|
1149
|
+
* @param file - The File object to analyze
|
|
1150
|
+
* @param includeHeaders - Whether to include first row values as sample headers
|
|
1151
|
+
* @returns Promise resolving to spreadsheet metadata
|
|
1152
|
+
*/
|
|
1153
|
+
extractMetadata: (file: File, includeHeaders?: boolean) => Promise<SpreadsheetMetadata>;
|
|
1154
|
+
}
|
|
1155
|
+
/**
|
|
1156
|
+
* Hook for extracting spreadsheet structure metadata.
|
|
1157
|
+
* Used to show column selection UI before scanning.
|
|
1158
|
+
*/
|
|
1159
|
+
declare function useSpreadsheetMetadata(): UseSpreadsheetMetadataResult;
|
|
1160
|
+
|
|
1161
|
+
/**
|
|
1162
|
+
* useNetworkProof Hook
|
|
1163
|
+
* Monitors network requests to provide visibility into network activity.
|
|
1164
|
+
*
|
|
1165
|
+
* SECURITY NOTE: This hook monitors common network APIs (fetch, XHR, sendBeacon,
|
|
1166
|
+
* WebSocket, Worker, RTCPeerConnection) but cannot guarantee complete coverage.
|
|
1167
|
+
* Browser extensions, service workers, and other mechanisms may bypass monitoring.
|
|
1168
|
+
* This is a best-effort indicator, not a security guarantee.
|
|
1169
|
+
*/
|
|
1170
|
+
|
|
1171
|
+
/**
|
|
1172
|
+
* Result object returned by the useNetworkProof hook
|
|
1173
|
+
*/
|
|
1174
|
+
interface UseNetworkProofResult {
|
|
1175
|
+
/** Array of captured network requests */
|
|
1176
|
+
requests: NetworkRequest[];
|
|
1177
|
+
/** Whether the hook is currently recording requests */
|
|
1178
|
+
isRecording: boolean;
|
|
1179
|
+
/** Start monitoring network activity (patches global APIs) */
|
|
1180
|
+
startRecording: () => void;
|
|
1181
|
+
/** Stop monitoring and restore original APIs */
|
|
1182
|
+
stopRecording: () => void;
|
|
1183
|
+
/** Clear the recorded requests array */
|
|
1184
|
+
clearRequests: () => void;
|
|
1185
|
+
/** Total bytes transferred across all requests */
|
|
1186
|
+
totalBytes: number;
|
|
1187
|
+
/** Total number of captured requests */
|
|
1188
|
+
requestCount: number;
|
|
1189
|
+
}
|
|
1190
|
+
/**
|
|
1191
|
+
* Hook to monitor and display network activity.
|
|
1192
|
+
*
|
|
1193
|
+
* Uses monkey-patching of global APIs (fetch, XHR, WebSocket, Worker, RTCPeerConnection)
|
|
1194
|
+
* and PerformanceObserver to intercept network requests. This provides visibility into
|
|
1195
|
+
* network activity during document processing.
|
|
1196
|
+
*
|
|
1197
|
+
* @returns Object containing request data, recording state, and control functions
|
|
1198
|
+
*
|
|
1199
|
+
* @example
|
|
1200
|
+
* ```tsx
|
|
1201
|
+
* function MyComponent() {
|
|
1202
|
+
* const { requests, isRecording, startRecording, stopRecording } = useNetworkProof();
|
|
1203
|
+
*
|
|
1204
|
+
* useEffect(() => {
|
|
1205
|
+
* startRecording();
|
|
1206
|
+
* return () => stopRecording();
|
|
1207
|
+
* }, []);
|
|
1208
|
+
*
|
|
1209
|
+
* return <div>Requests: {requests.length}</div>;
|
|
1210
|
+
* }
|
|
1211
|
+
* ```
|
|
1212
|
+
*/
|
|
1213
|
+
declare function useNetworkProof(): UseNetworkProofResult;
|
|
1214
|
+
|
|
1215
|
+
/**
|
|
1216
|
+
* useEnhancedNetworkProof Hook
|
|
1217
|
+
*
|
|
1218
|
+
* Enhanced network monitoring that covers channels the basic hook misses:
|
|
1219
|
+
* - WebSocket connections
|
|
1220
|
+
* - WebRTC data channels (RTCPeerConnection)
|
|
1221
|
+
* - Service Worker registrations and background sync
|
|
1222
|
+
* - Beacon API
|
|
1223
|
+
* - sendBeacon
|
|
1224
|
+
*
|
|
1225
|
+
* SECURITY NOTE: This is monitoring for visibility, not security.
|
|
1226
|
+
* Extensions, service workers, and other mechanisms may bypass detection.
|
|
1227
|
+
*/
|
|
1228
|
+
interface EnhancedNetworkState {
|
|
1229
|
+
/** WebSocket connections detected */
|
|
1230
|
+
webSocketConnections: WebSocketConnection[];
|
|
1231
|
+
/** WebRTC peer connections detected */
|
|
1232
|
+
webRTCConnections: WebRTCConnection[];
|
|
1233
|
+
/** Service workers registered */
|
|
1234
|
+
serviceWorkers: ServiceWorkerInfo[];
|
|
1235
|
+
/** Beacon requests detected */
|
|
1236
|
+
beaconRequests: BeaconRequest[];
|
|
1237
|
+
/** Any concerning activity detected */
|
|
1238
|
+
hasConcerningActivity: boolean;
|
|
1239
|
+
/** Summary of all activity */
|
|
1240
|
+
summary: string;
|
|
1241
|
+
}
|
|
1242
|
+
interface WebSocketConnection {
|
|
1243
|
+
id: string;
|
|
1244
|
+
url: string;
|
|
1245
|
+
timestamp: number;
|
|
1246
|
+
state: 'connecting' | 'open' | 'closing' | 'closed';
|
|
1247
|
+
}
|
|
1248
|
+
interface WebRTCConnection {
|
|
1249
|
+
id: string;
|
|
1250
|
+
timestamp: number;
|
|
1251
|
+
type: 'offer' | 'answer' | 'datachannel';
|
|
1252
|
+
}
|
|
1253
|
+
interface ServiceWorkerInfo {
|
|
1254
|
+
scriptURL: string;
|
|
1255
|
+
state: ServiceWorkerState;
|
|
1256
|
+
timestamp: number;
|
|
1257
|
+
}
|
|
1258
|
+
interface BeaconRequest {
|
|
1259
|
+
id: string;
|
|
1260
|
+
url: string;
|
|
1261
|
+
timestamp: number;
|
|
1262
|
+
}
|
|
1263
|
+
interface UseEnhancedNetworkProofResult {
|
|
1264
|
+
state: EnhancedNetworkState;
|
|
1265
|
+
startMonitoring: () => void;
|
|
1266
|
+
stopMonitoring: () => void;
|
|
1267
|
+
checkServiceWorkers: () => Promise<void>;
|
|
1268
|
+
reset: () => void;
|
|
1269
|
+
isMonitoring: boolean;
|
|
1270
|
+
}
|
|
1271
|
+
declare function useEnhancedNetworkProof(): UseEnhancedNetworkProofResult;
|
|
1272
|
+
|
|
1273
|
+
/**
|
|
1274
|
+
* useStorageProof Hook
|
|
1275
|
+
* Monitors browser storage to show visibility into storage changes.
|
|
1276
|
+
*
|
|
1277
|
+
* SECURITY NOTE: This provides a snapshot comparison of storage counts.
|
|
1278
|
+
* It cannot detect all forms of data persistence (e.g., Service Worker
|
|
1279
|
+
* cache, browser history, etc.). This is a best-effort indicator.
|
|
1280
|
+
*/
|
|
1281
|
+
|
|
1282
|
+
interface UseStorageProofResult {
|
|
1283
|
+
beforeSnapshot: StorageSnapshot | null;
|
|
1284
|
+
afterSnapshot: StorageSnapshot | null;
|
|
1285
|
+
isDifferent: boolean;
|
|
1286
|
+
watermarkStatus: 'not_planted' | 'planted' | 'verified' | 'failed';
|
|
1287
|
+
takeBeforeSnapshot: () => void;
|
|
1288
|
+
takeAfterSnapshot: () => void;
|
|
1289
|
+
plantWatermark: () => void;
|
|
1290
|
+
verifyWatermark: () => boolean;
|
|
1291
|
+
}
|
|
1292
|
+
/**
|
|
1293
|
+
* Hook to monitor browser storage and prove no data is saved
|
|
1294
|
+
*/
|
|
1295
|
+
declare function useStorageProof(): UseStorageProofResult;
|
|
1296
|
+
|
|
1297
|
+
/**
|
|
1298
|
+
* useAirplaneMode Hook
|
|
1299
|
+
* Detects offline status for strong privacy indication
|
|
1300
|
+
*
|
|
1301
|
+
* When navigator.onLine is false, network uploads are highly unlikely.
|
|
1302
|
+
* This provides strong assurance, though not absolute proof due to potential
|
|
1303
|
+
* browser extensions, queued requests, or service workers.
|
|
1304
|
+
*/
|
|
1305
|
+
interface AirplaneModeState {
|
|
1306
|
+
/** Current connection status */
|
|
1307
|
+
isOnline: boolean;
|
|
1308
|
+
/** Was offline when processing started */
|
|
1309
|
+
processingStartedOffline: boolean;
|
|
1310
|
+
/** Connection was maintained offline throughout processing */
|
|
1311
|
+
stayedOfflineDuringProcessing: boolean;
|
|
1312
|
+
/** User accepted the airplane mode challenge */
|
|
1313
|
+
challengeAccepted: boolean;
|
|
1314
|
+
/** Processing is currently active */
|
|
1315
|
+
isProcessing: boolean;
|
|
1316
|
+
/** Timestamped log of connection events */
|
|
1317
|
+
connectionLog: ConnectionEvent[];
|
|
1318
|
+
/** Connection went online during processing (invalidates proof) */
|
|
1319
|
+
proofInvalidated: boolean;
|
|
1320
|
+
}
|
|
1321
|
+
interface ConnectionEvent {
|
|
1322
|
+
timestamp: number;
|
|
1323
|
+
status: 'online' | 'offline';
|
|
1324
|
+
event: 'initial' | 'change' | 'processing_start' | 'processing_end';
|
|
1325
|
+
}
|
|
1326
|
+
interface UseAirplaneModeResult {
|
|
1327
|
+
state: AirplaneModeState;
|
|
1328
|
+
/** Accept the airplane mode challenge */
|
|
1329
|
+
acceptChallenge: () => void;
|
|
1330
|
+
/** Mark processing as started (records current state) */
|
|
1331
|
+
startProcessing: () => void;
|
|
1332
|
+
/** Mark processing as complete */
|
|
1333
|
+
endProcessing: () => void;
|
|
1334
|
+
/** Reset state for new session */
|
|
1335
|
+
reset: () => void;
|
|
1336
|
+
/** Get human-readable status */
|
|
1337
|
+
getStatusMessage: () => string;
|
|
1338
|
+
/** Get platform-specific instructions to go offline */
|
|
1339
|
+
getOfflineInstructions: () => string;
|
|
1340
|
+
}
|
|
1341
|
+
declare function useAirplaneMode(): UseAirplaneModeResult;
|
|
1342
|
+
|
|
1343
|
+
/**
|
|
1344
|
+
* useFirstTimeUser Hook
|
|
1345
|
+
*
|
|
1346
|
+
* Detects if this is a first-time user and manages onboarding state.
|
|
1347
|
+
* Uses localStorage to persist the "has seen onboarding" flag.
|
|
1348
|
+
*/
|
|
1349
|
+
declare function useFirstTimeUser(): {
|
|
1350
|
+
isFirstTime: boolean;
|
|
1351
|
+
isLoading: boolean;
|
|
1352
|
+
markOnboardingComplete: () => void;
|
|
1353
|
+
resetOnboarding: () => void;
|
|
1354
|
+
};
|
|
1355
|
+
|
|
1356
|
+
/**
|
|
1357
|
+
* Secure Random Utilities
|
|
1358
|
+
*
|
|
1359
|
+
* Uses crypto.getRandomValues() for cryptographically secure random values.
|
|
1360
|
+
* This replaces Math.random() which is predictable and not suitable for
|
|
1361
|
+
* security-sensitive ID generation.
|
|
1362
|
+
*/
|
|
1363
|
+
/**
|
|
1364
|
+
* Generate a cryptographically secure random ID
|
|
1365
|
+
* Returns a 24-character hex string (96 bits of entropy)
|
|
1366
|
+
*/
|
|
1367
|
+
declare function generateSecureId(): string;
|
|
1368
|
+
/**
|
|
1369
|
+
* Generate a secure random ID with a prefix
|
|
1370
|
+
* @param prefix - String prefix for the ID (e.g., 'det', 'req', 'file')
|
|
1371
|
+
*/
|
|
1372
|
+
declare function generatePrefixedId(prefix: string): string;
|
|
1373
|
+
|
|
1374
|
+
/**
|
|
1375
|
+
* Partial Mask Utilities
|
|
1376
|
+
* Category-specific masking that preserves format while hiding sensitive data
|
|
1377
|
+
*
|
|
1378
|
+
* Each PII category gets a sensible default partial mask:
|
|
1379
|
+
* - SSN: show last 4 digits
|
|
1380
|
+
* - Credit Card: show last 4, preserve format
|
|
1381
|
+
* - Email: show first char + full domain
|
|
1382
|
+
* - Phone: show last 4, preserve format
|
|
1383
|
+
* - Address: hide house number only
|
|
1384
|
+
* - Person Name: first char of each name part
|
|
1385
|
+
* - IP Address: show last octet
|
|
1386
|
+
* - Date of Birth: show year only
|
|
1387
|
+
*/
|
|
1388
|
+
|
|
1389
|
+
/**
|
|
1390
|
+
* Main dispatch function for partial masking
|
|
1391
|
+
* Returns a format-preserving masked version of the PII value
|
|
1392
|
+
*/
|
|
1393
|
+
declare function getPartialMask(category: PIICategory, value: string): string;
|
|
1394
|
+
|
|
1395
|
+
/**
|
|
1396
|
+
* maskPII - Utility for masking PII values for safe display
|
|
1397
|
+
*
|
|
1398
|
+
* Provides category-aware masking that preserves format recognition
|
|
1399
|
+
* while hiding sensitive data. Used in the Detection Feed.
|
|
1400
|
+
*/
|
|
1401
|
+
|
|
1402
|
+
/**
|
|
1403
|
+
* Mask a PII value for display based on its category
|
|
1404
|
+
* Shows enough to confirm detection accuracy without exposing full value
|
|
1405
|
+
*
|
|
1406
|
+
* @param category - The detection category (email, ssn, etc.)
|
|
1407
|
+
* @param value - The raw PII value
|
|
1408
|
+
* @returns Masked string safe for display
|
|
1409
|
+
*/
|
|
1410
|
+
declare function maskPII(category: Detection['category'], value: string): string;
|
|
1411
|
+
/**
|
|
1412
|
+
* Get a display-friendly category label
|
|
1413
|
+
*/
|
|
1414
|
+
declare function getCategoryLabel(category: Detection['category']): string;
|
|
1415
|
+
/**
|
|
1416
|
+
* Get category icon (monospace-friendly)
|
|
1417
|
+
*/
|
|
1418
|
+
declare function getCategoryIcon$1(category: Detection['category']): string;
|
|
1419
|
+
|
|
1420
|
+
/**
|
|
1421
|
+
* Validate and compile a regex pattern
|
|
1422
|
+
* Returns the compiled RegExp if valid, null otherwise
|
|
1423
|
+
*
|
|
1424
|
+
* @param pattern - The regex pattern string
|
|
1425
|
+
* @param flags - Optional regex flags (default: 'g')
|
|
1426
|
+
* @returns Compiled RegExp or null with error
|
|
1427
|
+
*/
|
|
1428
|
+
declare function safeCompileRegex(pattern: string, flags?: string): {
|
|
1429
|
+
regex: RegExp | null;
|
|
1430
|
+
error?: string;
|
|
1431
|
+
};
|
|
1432
|
+
|
|
1433
|
+
/**
|
|
1434
|
+
* File Magic Byte Validation
|
|
1435
|
+
*
|
|
1436
|
+
* Validates file types by checking magic bytes (file signatures) in addition
|
|
1437
|
+
* to file extensions. This prevents attackers from bypassing file type checks
|
|
1438
|
+
* by simply renaming files.
|
|
1439
|
+
*
|
|
1440
|
+
* SECURITY: Extension-only validation is insufficient. A file named "malware.xlsx"
|
|
1441
|
+
* could actually be an executable. Magic bytes provide defense-in-depth.
|
|
1442
|
+
*/
|
|
1443
|
+
|
|
1444
|
+
/**
|
|
1445
|
+
* Result of file type validation
|
|
1446
|
+
*/
|
|
1447
|
+
interface FileTypeValidation {
|
|
1448
|
+
/** Whether the file passed all validation checks */
|
|
1449
|
+
valid: boolean;
|
|
1450
|
+
/** The detected file type (if valid) */
|
|
1451
|
+
fileType: SupportedFileType | null;
|
|
1452
|
+
/** Error message if validation failed */
|
|
1453
|
+
error?: string;
|
|
1454
|
+
/** Warnings that don't prevent processing but should be noted */
|
|
1455
|
+
warnings: string[];
|
|
1456
|
+
}
|
|
1457
|
+
/**
|
|
1458
|
+
* Validate file type using extension, MIME type, and magic bytes
|
|
1459
|
+
*
|
|
1460
|
+
* @param file - The file to validate
|
|
1461
|
+
* @returns Validation result with file type and any errors/warnings
|
|
1462
|
+
*/
|
|
1463
|
+
declare function validateFileType(file: File): Promise<FileTypeValidation>;
|
|
1464
|
+
/**
|
|
1465
|
+
* Quick check if a file is likely valid based on extension only
|
|
1466
|
+
* Use validateFileType for full validation with magic bytes
|
|
1467
|
+
*/
|
|
1468
|
+
declare function hasValidExtension(filename: string): boolean;
|
|
1469
|
+
/**
|
|
1470
|
+
* Get file type from extension (quick check, no magic byte validation)
|
|
1471
|
+
*/
|
|
1472
|
+
declare function getFileTypeFromExtension(filename: string): SupportedFileType | null;
|
|
1473
|
+
|
|
1474
|
+
/**
|
|
1475
|
+
* Download Utilities
|
|
1476
|
+
*
|
|
1477
|
+
* Provides file download functionality including ZIP bundling
|
|
1478
|
+
* for multiple processed files.
|
|
1479
|
+
*/
|
|
1480
|
+
|
|
1481
|
+
/**
|
|
1482
|
+
* Download a single file by creating a temporary link
|
|
1483
|
+
* @param file - The processed file to download
|
|
1484
|
+
*/
|
|
1485
|
+
declare function downloadFile(file: ProcessedFile): void;
|
|
1486
|
+
/**
|
|
1487
|
+
* Download multiple files bundled as a ZIP archive
|
|
1488
|
+
* Uses the fflate library for fast, pure-JS compression
|
|
1489
|
+
*
|
|
1490
|
+
* @param files - Array of processed files to bundle
|
|
1491
|
+
* @param zipName - Name of the output ZIP file (default: 'purged_files.zip')
|
|
1492
|
+
*/
|
|
1493
|
+
declare function downloadFilesAsZip(files: ProcessedFile[], zipName?: string): Promise<void>;
|
|
1494
|
+
|
|
1495
|
+
/**
|
|
1496
|
+
* Secure Logging Utilities
|
|
1497
|
+
*
|
|
1498
|
+
* Provides logging functions that sanitize error messages to prevent
|
|
1499
|
+
* accidental PII leakage through console output.
|
|
1500
|
+
*
|
|
1501
|
+
* SECURITY: Error objects may contain file content, user data, or PII.
|
|
1502
|
+
* These utilities strip sensitive details before logging.
|
|
1503
|
+
*/
|
|
1504
|
+
/**
|
|
1505
|
+
* Log a warning without exposing potentially sensitive error details
|
|
1506
|
+
* @param context - Description of what was happening (e.g., "Failed to hash file")
|
|
1507
|
+
* @param error - The error object (will be sanitized)
|
|
1508
|
+
*/
|
|
1509
|
+
declare function secureWarn(context: string, error?: unknown): void;
|
|
1510
|
+
/**
|
|
1511
|
+
* Log an error without exposing potentially sensitive error details
|
|
1512
|
+
* @param context - Description of what was happening
|
|
1513
|
+
* @param error - The error object (will be sanitized)
|
|
1514
|
+
*/
|
|
1515
|
+
declare function secureError(context: string, error?: unknown): void;
|
|
1516
|
+
/**
|
|
1517
|
+
* Log debug info - safe to use as it doesn't include error objects
|
|
1518
|
+
* @param message - Debug message (should not contain PII)
|
|
1519
|
+
*/
|
|
1520
|
+
declare function secureLog(message: string): void;
|
|
1521
|
+
/**
|
|
1522
|
+
* Safely get filename for logging (strips path, limits length)
|
|
1523
|
+
*/
|
|
1524
|
+
declare function safeFilename(filename: string): string;
|
|
1525
|
+
|
|
1526
|
+
/**
|
|
1527
|
+
* Hilbert Curve Utility
|
|
1528
|
+
*
|
|
1529
|
+
* Space-filling curve for converting 1D data into 2D visualization.
|
|
1530
|
+
* Preserves locality - nearby elements in 1D stay nearby in 2D.
|
|
1531
|
+
*
|
|
1532
|
+
* Used for entropy heat map visualization where we want to display
|
|
1533
|
+
* sequential file bytes in a 2D grid that maintains spatial relationships.
|
|
1534
|
+
*
|
|
1535
|
+
* References:
|
|
1536
|
+
* - https://en.wikipedia.org/wiki/Hilbert_curve
|
|
1537
|
+
* - https://corte.si/posts/visualisation/entropy/
|
|
1538
|
+
*/
|
|
1539
|
+
/**
|
|
1540
|
+
* Convert a 1D index (d) to 2D coordinates (x, y) on a Hilbert curve.
|
|
1541
|
+
*
|
|
1542
|
+
* @param n - Grid size (must be power of 2, e.g., 16, 32, 64)
|
|
1543
|
+
* @param d - 1D index (0 to n*n-1)
|
|
1544
|
+
* @returns 2D coordinates {x, y} where 0 <= x,y < n
|
|
1545
|
+
*/
|
|
1546
|
+
declare function hilbertD2XY(n: number, d: number): {
|
|
1547
|
+
x: number;
|
|
1548
|
+
y: number;
|
|
1549
|
+
};
|
|
1550
|
+
/**
|
|
1551
|
+
* Convert 2D coordinates (x, y) to a 1D index (d) on a Hilbert curve.
|
|
1552
|
+
*
|
|
1553
|
+
* @param n - Grid size (must be power of 2, e.g., 16, 32, 64)
|
|
1554
|
+
* @param x - X coordinate (0 to n-1)
|
|
1555
|
+
* @param y - Y coordinate (0 to n-1)
|
|
1556
|
+
* @returns 1D index d where 0 <= d < n*n
|
|
1557
|
+
*/
|
|
1558
|
+
declare function hilbertXY2D(n: number, x: number, y: number): number;
|
|
1559
|
+
/**
|
|
1560
|
+
* Calculate the optimal grid size for a given number of blocks.
|
|
1561
|
+
* Returns the smallest power of 2 that can fit all blocks.
|
|
1562
|
+
*
|
|
1563
|
+
* @param blockCount - Number of blocks to fit
|
|
1564
|
+
* @returns Power of 2 grid size (e.g., 16, 32, 64)
|
|
1565
|
+
*/
|
|
1566
|
+
declare function getOptimalGridSize(blockCount: number): number;
|
|
1567
|
+
|
|
1568
|
+
/**
|
|
1569
|
+
* PII Category Definitions
|
|
1570
|
+
* Categories of personally identifiable information that PURGE can detect
|
|
1571
|
+
*/
|
|
1572
|
+
|
|
1573
|
+
interface CategoryDefinition {
|
|
1574
|
+
id: PIICategory;
|
|
1575
|
+
name: string;
|
|
1576
|
+
description: string;
|
|
1577
|
+
icon: string;
|
|
1578
|
+
examples: string[];
|
|
1579
|
+
defaultEnabled: boolean;
|
|
1580
|
+
}
|
|
1581
|
+
declare const categories: CategoryDefinition[];
|
|
1582
|
+
declare const categoryMap: Map<PIICategory, CategoryDefinition>;
|
|
1583
|
+
declare function getCategoryName(id: PIICategory): string;
|
|
1584
|
+
declare function getCategoryIcon(id: PIICategory): string;
|
|
1585
|
+
|
|
1586
|
+
/**
|
|
1587
|
+
* Platform detection and offline instructions
|
|
1588
|
+
* Shared between OfflineBlockedState and NetworkModeToggle components
|
|
1589
|
+
*/
|
|
1590
|
+
type Platform = 'mac' | 'windows' | 'ios' | 'android' | 'unknown';
|
|
1591
|
+
/**
|
|
1592
|
+
* Detect user's platform for tailored instructions
|
|
1593
|
+
*/
|
|
1594
|
+
declare function detectPlatform(): Platform;
|
|
1595
|
+
/**
|
|
1596
|
+
* Platform-specific offline instructions
|
|
1597
|
+
*/
|
|
1598
|
+
declare const PLATFORM_INSTRUCTIONS: Record<Platform, {
|
|
1599
|
+
icon: string;
|
|
1600
|
+
steps: string[];
|
|
1601
|
+
}>;
|
|
1602
|
+
|
|
1603
|
+
/**
|
|
1604
|
+
* DeviceFingerprint
|
|
1605
|
+
*
|
|
1606
|
+
* Generates a lightweight browser fingerprint for quota binding.
|
|
1607
|
+
* This prevents copying quota state to a different device/browser.
|
|
1608
|
+
*
|
|
1609
|
+
* Note: This is not meant to be a tracking fingerprint - it's used
|
|
1610
|
+
* solely to bind quota state to a specific browser instance.
|
|
1611
|
+
*/
|
|
1612
|
+
/**
|
|
1613
|
+
* Generates a stable device fingerprint by hashing browser properties.
|
|
1614
|
+
* Returns a 16-character hex string.
|
|
1615
|
+
*/
|
|
1616
|
+
declare function generateDeviceFingerprint(): Promise<string>;
|
|
1617
|
+
/**
|
|
1618
|
+
* Verifies if a fingerprint matches the current device.
|
|
1619
|
+
* Allows for minor variations (returns similarity score).
|
|
1620
|
+
*/
|
|
1621
|
+
declare function verifyDeviceFingerprint(storedFingerprint: string): Promise<{
|
|
1622
|
+
matches: boolean;
|
|
1623
|
+
similarity: number;
|
|
1624
|
+
}>;
|
|
1625
|
+
|
|
1626
|
+
/**
|
|
1627
|
+
* Session Summary PDF Generator
|
|
1628
|
+
*
|
|
1629
|
+
* IMPORTANT: This is a SESSION SUMMARY, NOT a proof certificate.
|
|
1630
|
+
* The PDF must include disclaimers that this is self-reported data.
|
|
1631
|
+
* For genuine proof, users should use Airplane Mode or DevTools.
|
|
1632
|
+
*/
|
|
1633
|
+
interface SessionSummaryData {
|
|
1634
|
+
sessionId: string;
|
|
1635
|
+
timestamp: string;
|
|
1636
|
+
files: Array<{
|
|
1637
|
+
name: string;
|
|
1638
|
+
originalHash: string;
|
|
1639
|
+
processedHash: string;
|
|
1640
|
+
originalSize: number;
|
|
1641
|
+
processedSize: number;
|
|
1642
|
+
detectionsRemoved: number;
|
|
1643
|
+
}>;
|
|
1644
|
+
selfReportedMetrics: {
|
|
1645
|
+
networkRequestsDetected: number;
|
|
1646
|
+
storageChangesDetected: boolean;
|
|
1647
|
+
memoryWipeCompleted: boolean;
|
|
1648
|
+
wasOfflineDuringProcessing: boolean;
|
|
1649
|
+
};
|
|
1650
|
+
environment: {
|
|
1651
|
+
browser: string;
|
|
1652
|
+
platform: string;
|
|
1653
|
+
userAgent: string;
|
|
1654
|
+
};
|
|
1655
|
+
}
|
|
1656
|
+
/**
|
|
1657
|
+
* Generate session ID using cryptographically secure random
|
|
1658
|
+
*/
|
|
1659
|
+
declare function generateSessionId(): string;
|
|
1660
|
+
/**
|
|
1661
|
+
* Generate PDF session summary
|
|
1662
|
+
*/
|
|
1663
|
+
declare function generateSessionSummaryPDF(data: SessionSummaryData): Blob;
|
|
1664
|
+
/**
|
|
1665
|
+
* Download the session summary PDF
|
|
1666
|
+
*/
|
|
1667
|
+
declare function downloadSessionSummary(data: SessionSummaryData): void;
|
|
1668
|
+
|
|
1669
|
+
export { type AdversarialConfig, type AdversarialSuggestion, type AdversarialVerificationResult, type AirplaneModeState, BaseProcessor, type CategoryDefinition, type ColumnAccessProof, type ColumnAttestation, type ColumnSelectionConfig, type ContentSection, type Detection, type DetectionCapabilities, type DetectionEngine, type DetectionResult, type DocumentContent, type EntropyBlock, type EntropyComparison, type EntropyData, type FileTypeValidation, type LeakedAttribute, type MemoryStats, type NetworkRequest, type OfflineEnforcementActions, type OfflineEnforcementState, OfflineQuotaStore, type PIICategory, PLATFORM_INSTRUCTIONS, type ParsedDocument, type Platform, type ProcessedFile, type ProcessingProgress, type PurgeState, type QueuedFile, QuotaExhaustedError, QuotaExhaustedError as QuotaExhaustedErrorClass, type QuotaState, type Redaction, type RedactionStyle, type RefreshResult, RegexDetectionEngine, type ScrubConfig, type SessionSummaryData, type SheetMetadata, type SpreadsheetMetadata, type StorageSnapshot, type SupportedFileType, XlsxProcessor, adversarialVerifier, categories, categoryMap, detectPlatform, downloadFile, downloadFilesAsZip, downloadSessionSummary, generateDeviceFingerprint, generatePrefixedId, generateSectionId, generateSecureId, generateSessionId, generateSessionSummaryPDF, getCategoryIcon$1 as getCategoryIcon, getCategoryIcon as getCategoryIconFromCategories, getCategoryLabel, getCategoryName, getFileTypeFromExtension, getOptimalGridSize, getPartialMask, getPatternsForCategories, getProcessor, getProcessorForFile, getSensitivityThreshold, hasValidExtension, hilbertD2XY, hilbertXY2D, maskPII, offlineQuotaStore, regexDetectionEngine, requestQuotaRefresh, safeCompileRegex, safeFilename, secureError, secureLog, secureWarn, truncateHash, useAdversarialAnalysis, useAirplaneMode, useDocumentProcessor, useEnhancedNetworkProof, useFileEntropy, useFileHash, useFirstTimeUser, useNetworkProof, useOfflineEnforcement, useOfflineQuota, usePurgeStore, useSpreadsheetMetadata, useStorageProof, validateFileType, verifyDeviceFingerprint, xlsxProcessor };
|