@striae-org/striae 4.3.3 → 4.3.4

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.
@@ -14,7 +14,7 @@ import {
14
14
  signForensicManifest,
15
15
  removeCaseConfirmationSummary
16
16
  } from '~/utils/data';
17
- import { type CaseData, type ReadOnlyCaseData, type FileData, type AuditTrail, type CaseExportData } from '~/types';
17
+ import { type CaseData, type ReadOnlyCaseData, type FileData, type AuditTrail, type CaseExportData, type ValidationAuditEntry } from '~/types';
18
18
  import { auditService } from '~/services/audit';
19
19
  import { fetchImageApi } from '~/utils/api';
20
20
  import { exportCaseData, formatDateForFilename } from '~/components/actions/case-export';
@@ -27,7 +27,7 @@ import {
27
27
  getVerificationPublicKey,
28
28
  } from '~/utils/forensics';
29
29
  import { signAuditExport } from '~/services/audit/audit-export-signing';
30
- import { generateAuditSummary } from '~/services/audit/audit-query-helpers';
30
+ import { generateAuditSummary, sortAuditEntriesNewestFirst } from '~/services/audit/audit-query-helpers';
31
31
 
32
32
  /**
33
33
  * Delete a file without individual audit logging (for bulk operations)
@@ -393,15 +393,23 @@ export const renameCase = async (
393
393
  // 5) Delete old case number in user's KV entry
394
394
  await removeUserCase(user, oldCaseNumber);
395
395
 
396
- // Log successful case rename
396
+ // Log successful case rename under the original case number context
397
397
  const endTime = Date.now();
398
398
  await auditService.logCaseRename(
399
399
  user,
400
- newCaseNumber, // Use new case number as the current context
400
+ oldCaseNumber,
401
401
  oldCaseNumber,
402
402
  newCaseNumber
403
403
  );
404
404
 
405
+ // Log creation of the new case number as a rename-derived case
406
+ await auditService.logCaseCreation(
407
+ user,
408
+ newCaseNumber,
409
+ newCaseNumber,
410
+ oldCaseNumber
411
+ );
412
+
405
413
  console.log(`✅ Case renamed: ${oldCaseNumber} → ${newCaseNumber} (${endTime - startTime}ms)`);
406
414
 
407
415
  } catch (error) {
@@ -808,11 +816,43 @@ export const archiveCase = async (
808
816
  startDate: caseData.createdAt,
809
817
  endDate: archivedAt,
810
818
  });
819
+
820
+ // Ensure the bundled archive trail includes the archival event itself.
821
+ const archiveAuditEntry: ValidationAuditEntry = {
822
+ timestamp: archivedAt,
823
+ userId: user.uid,
824
+ userEmail: user.email || '',
825
+ action: 'case-archive',
826
+ result: 'success',
827
+ details: {
828
+ fileName: `${caseNumber}.case`,
829
+ fileType: 'case-package',
830
+ validationErrors: [],
831
+ caseNumber,
832
+ workflowPhase: 'casework',
833
+ caseDetails: {
834
+ newCaseName: caseNumber,
835
+ archiveReason: archiveReason?.trim() || 'No reason provided',
836
+ totalFiles: archiveData.files?.length || 0,
837
+ lastModified: archivedAt,
838
+ },
839
+ performanceMetrics: {
840
+ processingTimeMs: Date.now() - startTime,
841
+ fileSizeBytes: 0,
842
+ },
843
+ },
844
+ };
845
+
846
+ const auditEntriesWithArchive = sortAuditEntriesNewestFirst([
847
+ ...auditEntries,
848
+ archiveAuditEntry,
849
+ ]);
850
+
811
851
  const auditTrail: AuditTrail = {
812
852
  caseNumber,
813
853
  workflowId: `${caseNumber}-archive-${Date.now()}`,
814
- entries: auditEntries,
815
- summary: generateAuditSummary(auditEntries),
854
+ entries: auditEntriesWithArchive,
855
+ summary: generateAuditSummary(auditEntriesWithArchive),
816
856
  };
817
857
 
818
858
  const auditTrailPayload = {
@@ -522,11 +522,60 @@
522
522
  white-space: nowrap;
523
523
  }
524
524
 
525
+ .entryHeaderActions {
526
+ display: flex;
527
+ align-items: center;
528
+ gap: 8px;
529
+ margin-left: auto;
530
+ }
531
+
532
+ .entryDetailsToggle {
533
+ background: color-mix(in lab, var(--primary) 10%, transparent);
534
+ color: color-mix(in lab, var(--primary) 65%, var(--text));
535
+ border: 1px solid color-mix(in lab, var(--primary) 30%, transparent);
536
+ padding: 4px 8px;
537
+ border-radius: 999px;
538
+ font-size: 0.75rem;
539
+ font-weight: var(--fontWeightMedium);
540
+ cursor: pointer;
541
+ transition: background-color var(--durationS) var(--bezierFastoutSlowin);
542
+ }
543
+
544
+ .entryDetailsToggle:hover {
545
+ background: color-mix(in lab, var(--primary) 16%, transparent);
546
+ }
547
+
548
+ .entryDetailsToggle:focus-visible {
549
+ outline: 2px solid color-mix(in lab, var(--primary) 45%, transparent);
550
+ outline-offset: 2px;
551
+ }
552
+
525
553
  /* Entry Details */
526
554
  .entryDetails {
527
555
  padding: 12px 14px;
528
556
  }
529
557
 
558
+ .expandedDetails {
559
+ margin-top: 10px;
560
+ padding-top: 10px;
561
+ border-top: 1px dashed color-mix(in lab, var(--textLight) 25%, transparent);
562
+ }
563
+
564
+ .expandedDetailsCode {
565
+ margin: 4px 0 0;
566
+ padding: 10px;
567
+ border-radius: 6px;
568
+ border: 1px solid color-mix(in lab, var(--textLight) 20%, transparent);
569
+ background: color-mix(in lab, var(--backgroundLight) 75%, transparent);
570
+ color: var(--text);
571
+ font-size: 0.78rem;
572
+ line-height: 1.4;
573
+ white-space: pre-wrap;
574
+ word-break: break-word;
575
+ max-height: 280px;
576
+ overflow: auto;
577
+ }
578
+
530
579
  .detailRow {
531
580
  display: flex;
532
581
  align-items: center;
@@ -1,3 +1,4 @@
1
+ import { useMemo, useState, type MouseEvent } from 'react';
1
2
  import { type ValidationAuditEntry } from '~/types';
2
3
  import { formatAuditTimestamp, getAuditActionIcon, getAuditStatusIcon } from './audit-viewer-utils';
3
4
  import styles from '../user-audit.module.css';
@@ -13,7 +14,57 @@ const isConfirmationImportEntry = (entry: ValidationAuditEntry): boolean => {
13
14
  );
14
15
  };
15
16
 
17
+ const isConfirmationEvent = (entry: ValidationAuditEntry): boolean => {
18
+ return (
19
+ entry.action === 'confirmation-create' ||
20
+ entry.action === 'confirmation-export' ||
21
+ entry.action === 'confirmation-import' ||
22
+ entry.action === 'confirm' ||
23
+ (entry.action === 'import' && entry.details.workflowPhase === 'confirmation') ||
24
+ (entry.action === 'export' && entry.details.workflowPhase === 'confirmation')
25
+ );
26
+ };
27
+
28
+ const supportsFullDetailsToggle = (entry: ValidationAuditEntry): boolean => {
29
+ return (
30
+ entry.action === 'annotation-create' ||
31
+ entry.action === 'annotation-edit' ||
32
+ entry.action === 'annotation-delete' ||
33
+ isConfirmationEvent(entry)
34
+ );
35
+ };
36
+
37
+ const getEntryKey = (entry: ValidationAuditEntry): string => {
38
+ return `${entry.timestamp}-${entry.userId}-${entry.action}-${entry.details.fileName || ''}-${entry.details.confirmationId || ''}`;
39
+ };
40
+
16
41
  export const AuditEntriesList = ({ entries }: AuditEntriesListProps) => {
42
+ const [expandedEntryKeys, setExpandedEntryKeys] = useState<Set<string>>(new Set());
43
+
44
+ const expandableEntries = useMemo(() => {
45
+ return new Set(entries.filter(supportsFullDetailsToggle).map(getEntryKey));
46
+ }, [entries]);
47
+
48
+ const toggleExpanded = (entryKey: string) => {
49
+ setExpandedEntryKeys((current) => {
50
+ const next = new Set(current);
51
+
52
+ if (next.has(entryKey)) {
53
+ next.delete(entryKey);
54
+ } else {
55
+ next.add(entryKey);
56
+ }
57
+
58
+ return next;
59
+ });
60
+ };
61
+
62
+ const handleToggleClick = (event: MouseEvent<HTMLButtonElement>, entryKey: string) => {
63
+ event.preventDefault();
64
+ event.stopPropagation();
65
+ toggleExpanded(entryKey);
66
+ };
67
+
17
68
  return (
18
69
  <div className={styles.entriesList}>
19
70
  <h3>Activity Log ({entries.length} entries)</h3>
@@ -22,30 +73,49 @@ export const AuditEntriesList = ({ entries }: AuditEntriesListProps) => {
22
73
  <p>No activities match the current filters.</p>
23
74
  </div>
24
75
  ) : (
25
- entries.map((entry) => (
26
- <div
27
- key={`${entry.timestamp}-${entry.userId}-${entry.action}-${entry.details.fileName || ''}`}
28
- className={`${styles.entry} ${styles[entry.result]}`}
29
- >
30
- <div className={styles.entryHeader}>
31
- <div className={styles.entryIcons}>
32
- <span className={styles.actionIcon}>{getAuditActionIcon(entry.action)}</span>
33
- <span className={styles.statusIcon}>{getAuditStatusIcon(entry.result)}</span>
34
- </div>
35
- <div className={styles.entryTitle}>
36
- <span className={styles.action}>{entry.action.toUpperCase().replace(/-/g, ' ')}</span>
37
- <span className={styles.fileName}>{entry.details.fileName}</span>
38
- </div>
39
- <div className={styles.entryTimestamp}>{formatAuditTimestamp(entry.timestamp)}</div>
40
- </div>
76
+ entries.map((entry) => {
77
+ const entryKey = getEntryKey(entry);
78
+ const isExpandable = expandableEntries.has(entryKey);
79
+ const isExpanded = expandedEntryKeys.has(entryKey);
41
80
 
42
- <div className={styles.entryDetails}>
43
- {entry.details.caseNumber && (
44
- <div className={styles.detailRow}>
45
- <span className={styles.detailLabel}>Case:</span>
46
- <span className={styles.detailValue}>{entry.details.caseNumber}</span>
81
+ return (
82
+ <div
83
+ key={entryKey}
84
+ className={`${styles.entry} ${styles[entry.result]}`}
85
+ >
86
+ <div className={styles.entryHeader}>
87
+ <div className={styles.entryIcons}>
88
+ <span className={styles.actionIcon}>{getAuditActionIcon(entry.action)}</span>
89
+ <span className={styles.statusIcon}>{getAuditStatusIcon(entry.result)}</span>
47
90
  </div>
48
- )}
91
+ <div className={styles.entryTitle}>
92
+ <span className={styles.action}>{entry.action.toUpperCase().replace(/-/g, ' ')}</span>
93
+ <span className={styles.fileName}>{entry.details.fileName}</span>
94
+ </div>
95
+
96
+ <div className={styles.entryHeaderActions}>
97
+ <div className={styles.entryTimestamp}>{formatAuditTimestamp(entry.timestamp)}</div>
98
+ {isExpandable && (
99
+ <button
100
+ type="button"
101
+ className={styles.entryDetailsToggle}
102
+ aria-expanded={isExpanded}
103
+ aria-label={isExpanded ? 'Hide full entry details' : 'Show full entry details'}
104
+ onClick={(event) => handleToggleClick(event, entryKey)}
105
+ >
106
+ {isExpanded ? 'Hide details' : 'Show details'}
107
+ </button>
108
+ )}
109
+ </div>
110
+ </div>
111
+
112
+ <div className={styles.entryDetails}>
113
+ {entry.details.caseNumber && (
114
+ <div className={styles.detailRow}>
115
+ <span className={styles.detailLabel}>Case:</span>
116
+ <span className={styles.detailValue}>{entry.details.caseNumber}</span>
117
+ </div>
118
+ )}
49
119
 
50
120
  {entry.details.userProfileDetails?.badgeId && (
51
121
  <div className={styles.detailRow}>
@@ -191,37 +261,49 @@ export const AuditEntriesList = ({ entries }: AuditEntriesListProps) => {
191
261
  </>
192
262
  )}
193
263
 
194
- {(entry.action === 'pdf-generate' || entry.action === 'confirm') && entry.details.fileDetails && (
195
- <>
196
- {entry.details.fileDetails.fileId && (
197
- <div className={styles.detailRow}>
198
- <span className={styles.detailLabel}>
199
- {entry.action === 'pdf-generate' ? 'Source File ID:' : 'Original Image ID:'}
200
- </span>
201
- <span className={styles.detailValue}>{entry.details.fileDetails.fileId}</span>
202
- </div>
203
- )}
264
+ {(entry.action === 'pdf-generate' || entry.action === 'confirm') && entry.details.fileDetails && (
265
+ <>
266
+ {entry.details.fileDetails.fileId && (
267
+ <div className={styles.detailRow}>
268
+ <span className={styles.detailLabel}>
269
+ {entry.action === 'pdf-generate' ? 'Source File ID:' : 'Original Image ID:'}
270
+ </span>
271
+ <span className={styles.detailValue}>{entry.details.fileDetails.fileId}</span>
272
+ </div>
273
+ )}
204
274
 
205
- {entry.details.fileDetails.originalFileName && (
206
- <div className={styles.detailRow}>
207
- <span className={styles.detailLabel}>
208
- {entry.action === 'pdf-generate' ? 'Source Filename:' : 'Original Filename:'}
209
- </span>
210
- <span className={styles.detailValue}>{entry.details.fileDetails.originalFileName}</span>
211
- </div>
212
- )}
275
+ {entry.details.fileDetails.originalFileName && (
276
+ <div className={styles.detailRow}>
277
+ <span className={styles.detailLabel}>
278
+ {entry.action === 'pdf-generate' ? 'Source Filename:' : 'Original Filename:'}
279
+ </span>
280
+ <span className={styles.detailValue}>{entry.details.fileDetails.originalFileName}</span>
281
+ </div>
282
+ )}
283
+
284
+ {entry.action === 'confirm' && entry.details.confirmationId && (
285
+ <div className={styles.detailRow}>
286
+ <span className={styles.detailLabel}>Confirmation ID:</span>
287
+ <span className={styles.detailValue}>{entry.details.confirmationId}</span>
288
+ </div>
289
+ )}
290
+ </>
291
+ )}
213
292
 
214
- {entry.action === 'confirm' && entry.details.confirmationId && (
293
+ {isExpandable && isExpanded && (
294
+ <div className={styles.expandedDetails}>
215
295
  <div className={styles.detailRow}>
216
- <span className={styles.detailLabel}>Confirmation ID:</span>
217
- <span className={styles.detailValue}>{entry.details.confirmationId}</span>
296
+ <span className={styles.detailLabel}>Full Entry Details:</span>
218
297
  </div>
219
- )}
220
- </>
221
- )}
298
+ <pre className={styles.expandedDetailsCode}>
299
+ {JSON.stringify(entry, null, 2)}
300
+ </pre>
301
+ </div>
302
+ )}
303
+ </div>
222
304
  </div>
223
- </div>
224
- ))
305
+ );
306
+ })
225
307
  )}
226
308
  </div>
227
309
  );
@@ -1,6 +1,6 @@
1
1
  import { type ValidationAuditEntry } from '~/types';
2
2
 
3
- export const getAuditSecurityIssuesForConsole = (
3
+ const getAuditSecurityIssuesForConsole = (
4
4
  entry: ValidationAuditEntry
5
5
  ): string[] => {
6
6
  const checks = entry.details.securityChecks;
@@ -42,7 +42,7 @@ export const AUDIT_CSV_ENTRY_HEADERS = [
42
42
  'Confirmed Files'
43
43
  ];
44
44
 
45
- export const formatForCSV = (value?: string | number | null): string => {
45
+ const formatForCSV = (value?: string | number | null): string => {
46
46
  if (value === undefined || value === null) return '';
47
47
  const str = String(value);
48
48
  if (str.includes(',') || str.includes('"') || str.includes('\n')) {
@@ -22,14 +22,14 @@ interface SignAuditExportInput {
22
22
  hash: string;
23
23
  }
24
24
 
25
- export interface AuditExportSignature {
25
+ interface AuditExportSignature {
26
26
  algorithm: string;
27
27
  keyId: string;
28
28
  signedAt: string;
29
29
  value: string;
30
30
  }
31
31
 
32
- export interface SignedAuditExportPayload {
32
+ interface SignedAuditExportPayload {
33
33
  signatureMetadata: AuditExportSigningPayload;
34
34
  signature: AuditExportSignature;
35
35
  }
@@ -8,7 +8,7 @@ import { type AuditExportContext, signAuditExport } from './audit-export-signing
8
8
  * Audit Export Service
9
9
  * Handles exporting audit trails to various formats for compliance and forensic analysis
10
10
  */
11
- export class AuditExportService {
11
+ class AuditExportService {
12
12
  private static instance: AuditExportService;
13
13
 
14
14
  private constructor() {}
@@ -18,7 +18,7 @@ interface PersistAuditEntryResponse {
18
18
  filename: string;
19
19
  }
20
20
 
21
- export type PersistAuditEntryResult =
21
+ type PersistAuditEntryResult =
22
22
  | {
23
23
  ok: true;
24
24
  entryCount: number;
@@ -2,7 +2,6 @@ import type { User } from 'firebase/auth';
2
2
  import type {
3
3
  ValidationAuditEntry,
4
4
  CreateAuditEntryParams,
5
- AuditTrail,
6
5
  AuditQueryParams,
7
6
  WorkflowPhase,
8
7
  AuditAction,
@@ -18,7 +17,6 @@ import {
18
17
  import {
19
18
  applyAuditEntryFilters,
20
19
  applyAuditPagination,
21
- generateAuditSummary,
22
20
  sortAuditEntriesNewestFirst
23
21
  } from './audit-query-helpers';
24
22
  import { logAuditEntryToConsole } from './audit-console-logger';
@@ -58,7 +56,7 @@ import {
58
56
  * Audit Service for ValidationAuditEntry system
59
57
  * Provides comprehensive audit logging throughout the confirmation workflow
60
58
  */
61
- export class AuditService {
59
+ class AuditService {
62
60
  private static instance: AuditService;
63
61
  private auditBuffer: ValidationAuditEntry[] = [];
64
62
  private workflowId: string | null = null;
@@ -383,13 +381,15 @@ export class AuditService {
383
381
  public async logCaseCreation(
384
382
  user: User,
385
383
  caseNumber: string,
386
- caseName: string
384
+ caseName: string,
385
+ renamedFromCaseNumber?: string
387
386
  ): Promise<void> {
388
387
  await this.logEventForUser(user,
389
388
  buildCaseCreationAuditParams({
390
389
  user,
391
390
  caseNumber,
392
- caseName
391
+ caseName,
392
+ renamedFromCaseNumber
393
393
  })
394
394
  );
395
395
  }
@@ -721,37 +721,6 @@ export class AuditService {
721
721
  );
722
722
  }
723
723
 
724
- /**
725
- * Log user account deletion event
726
- */
727
- public async logAccountDeletion(
728
- user: User,
729
- result: AuditResult,
730
- deletionReason: 'user-requested' | 'admin-initiated' | 'policy-violation' | 'inactive-account' = 'user-requested',
731
- confirmationMethod: 'uid-email' | 'password' | 'admin-override' = 'uid-email',
732
- casesCount?: number,
733
- filesCount?: number,
734
- dataRetentionPeriod?: number,
735
- emailNotificationSent?: boolean,
736
- sessionId?: string,
737
- errors: string[] = []
738
- ): Promise<void> {
739
- // Wrapper that extracts user data and calls the simplified version
740
- return this.logAccountDeletionSimple(
741
- user.uid,
742
- user.email || '',
743
- result,
744
- deletionReason,
745
- confirmationMethod,
746
- casesCount,
747
- filesCount,
748
- dataRetentionPeriod,
749
- emailNotificationSent,
750
- sessionId,
751
- errors
752
- );
753
- }
754
-
755
724
  /**
756
725
  * Log user account deletion event with simplified user data
757
726
  */
@@ -1011,32 +980,6 @@ export class AuditService {
1011
980
  return await this.getAuditEntries(queryParams, params?.requestingUser);
1012
981
  }
1013
982
 
1014
- /**
1015
- * Get audit trail for a case
1016
- */
1017
- public async getAuditTrail(caseNumber: string): Promise<AuditTrail | null> {
1018
- try {
1019
- // Implement retrieval from storage
1020
- const entries = await this.getAuditEntries({ caseNumber });
1021
- if (!entries || entries.length === 0) {
1022
- return null;
1023
- }
1024
-
1025
- const summary = generateAuditSummary(entries);
1026
- const workflowId = this.workflowId || `${caseNumber}-archived`;
1027
-
1028
- return {
1029
- caseNumber,
1030
- workflowId,
1031
- entries,
1032
- summary
1033
- };
1034
- } catch (error) {
1035
- console.error('🚨 Audit: Failed to get audit trail:', error);
1036
- return null;
1037
- }
1038
- }
1039
-
1040
983
  /**
1041
984
  * Get audit entries based on query parameters
1042
985
  */
@@ -1143,19 +1086,6 @@ export class AuditService {
1143
1086
  }
1144
1087
  }
1145
1088
 
1146
- /**
1147
- * Clear audit buffer (for testing)
1148
- */
1149
- public clearBuffer(): void {
1150
- this.auditBuffer = [];
1151
- }
1152
-
1153
- /**
1154
- * Get current buffer size (for monitoring)
1155
- */
1156
- public getBufferSize(): number {
1157
- return this.auditBuffer.length;
1158
- }
1159
1089
  }
1160
1090
 
1161
1091
  // Export singleton instance
@@ -6,6 +6,7 @@ interface BuildCaseCreationAuditParamsInput {
6
6
  user: User;
7
7
  caseNumber: string;
8
8
  caseName: string;
9
+ renamedFromCaseNumber?: string;
9
10
  }
10
11
 
11
12
  export const buildCaseCreationAuditParams = (
@@ -22,7 +23,9 @@ export const buildCaseCreationAuditParams = (
22
23
  caseNumber: input.caseNumber,
23
24
  workflowPhase: 'casework',
24
25
  caseDetails: {
26
+ oldCaseName: input.renamedFromCaseNumber,
25
27
  newCaseName: input.caseName,
28
+ createdByRename: Boolean(input.renamedFromCaseNumber),
26
29
  createdDate: new Date().toISOString(),
27
30
  totalFiles: 0,
28
31
  totalAnnotations: 0
@@ -1,2 +1,2 @@
1
- export { AuditService, auditService } from './audit.service';
2
- export { AuditExportService, auditExportService } from './audit-export.service';
1
+ export { auditService } from './audit.service';
2
+ export { auditExportService } from './audit-export.service';
@@ -44,7 +44,7 @@ export interface ValidationAuditEntry {
44
44
  * Detailed information for each audit entry
45
45
  * Contains action-specific data and metadata
46
46
  */
47
- export interface AuditDetails {
47
+ interface AuditDetails {
48
48
  // Core identification
49
49
  fileName?: string;
50
50
  fileType?: AuditFileType;
@@ -194,9 +194,10 @@ export interface AuditQueryParams {
194
194
  /**
195
195
  * Case management specific audit details
196
196
  */
197
- export interface CaseAuditDetails {
197
+ interface CaseAuditDetails {
198
198
  oldCaseName?: string;
199
199
  newCaseName?: string;
200
+ createdByRename?: boolean;
200
201
  totalFiles?: number;
201
202
  totalAnnotations?: number;
202
203
  confirmedFileNames?: string[];
@@ -210,7 +211,7 @@ export interface CaseAuditDetails {
210
211
  /**
211
212
  * File operation specific audit details
212
213
  */
213
- export interface FileAuditDetails {
214
+ interface FileAuditDetails {
214
215
  fileId?: string;
215
216
  originalFileName?: string;
216
217
  fileSize: number;
@@ -225,7 +226,7 @@ export interface FileAuditDetails {
225
226
  /**
226
227
  * Annotation operation specific audit details
227
228
  */
228
- export interface AnnotationAuditDetails {
229
+ interface AnnotationAuditDetails {
229
230
  annotationId?: string;
230
231
  annotationType?: 'measurement' | 'identification' | 'comparison' | 'note' | 'region';
231
232
  annotationData?: unknown; // The actual annotation data structure
@@ -238,7 +239,7 @@ export interface AnnotationAuditDetails {
238
239
  /**
239
240
  * User session specific audit details
240
241
  */
241
- export interface SessionAuditDetails {
242
+ interface SessionAuditDetails {
242
243
  sessionId?: string;
243
244
  userAgent?: string;
244
245
  sessionDuration?: number;
@@ -249,7 +250,7 @@ export interface SessionAuditDetails {
249
250
  /**
250
251
  * Security incident specific audit details
251
252
  */
252
- export interface SecurityAuditDetails {
253
+ interface SecurityAuditDetails {
253
254
  incidentType?: 'unauthorized-access' | 'data-breach' | 'malware' | 'injection' | 'brute-force' | 'privilege-escalation';
254
255
  severity?: 'low' | 'medium' | 'high' | 'critical';
255
256
  targetResource?: string;
@@ -272,7 +273,7 @@ export interface SecurityAuditDetails {
272
273
  /**
273
274
  * User profile and authentication specific audit details
274
275
  */
275
- export interface UserProfileAuditDetails {
276
+ interface UserProfileAuditDetails {
276
277
  profileField?: 'displayName' | 'email' | 'organization' | 'role' | 'preferences' | 'avatar' | 'badgeId';
277
278
  oldValue?: string;
278
279
  newValue?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@striae-org/striae",
3
- "version": "4.3.3",
3
+ "version": "4.3.4",
4
4
  "private": false,
5
5
  "description": "Striae is a specialized, cloud-native platform designed to streamline forensic firearms identification by providing an intuitive environment for digital comparison image annotation, authenticated confirmations, and automated report generation.",
6
6
  "license": "Apache-2.0",