@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.
@@ -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 };