From 5b480ce501d32a19fa3fb47093e05b18c6c9aa88 Mon Sep 17 00:00:00 2001 From: Sebastian Richner Date: Mon, 4 Mar 2024 17:38:21 +0100 Subject: [PATCH] Add obfuscation term feature --- src/electron/electron/ipc/IpcHandler.ts | 9 +- .../main/services/DataExportService.ts | 137 ++++++++++-------- .../DataExportWindowActivityTracker.vue | 63 ++++++-- src/electron/src/utils/Commands.ts | 3 +- src/electron/src/views/DataExportView.vue | 110 ++++++++++---- 5 files changed, 220 insertions(+), 102 deletions(-) diff --git a/src/electron/electron/ipc/IpcHandler.ts b/src/electron/electron/ipc/IpcHandler.ts index e9097f6f..75e2be6e 100644 --- a/src/electron/electron/ipc/IpcHandler.ts +++ b/src/electron/electron/ipc/IpcHandler.ts @@ -139,9 +139,14 @@ export class IpcHandler { private async startDataExport( windowActivityExportType: DataExportType, - userInputExportType: DataExportType + userInputExportType: DataExportType, + obfuscationTerms: string[] ): Promise { - return this.dataExportService.startDataExport(windowActivityExportType, userInputExportType); + return this.dataExportService.startDataExport( + windowActivityExportType, + userInputExportType, + obfuscationTerms + ); } private async revealItemInFolder(path: string): Promise { diff --git a/src/electron/electron/main/services/DataExportService.ts b/src/electron/electron/main/services/DataExportService.ts index 6df73cf0..08a2739f 100644 --- a/src/electron/electron/main/services/DataExportService.ts +++ b/src/electron/electron/main/services/DataExportService.ts @@ -16,73 +16,96 @@ export class DataExportService { new WindowActivityTrackerService(); public async startDataExport( windowActivityExportType: DataExportType, - userInputExportType: DataExportType + userInputExportType: DataExportType, + obfuscationTerms: string[] ): Promise { LOG.info('startDataExport called'); - const dbName = 'database.sqlite'; - let dbPath = dbName; - if (!(is.dev && process.env['VITE_DEV_SERVER_URL'])) { - const userDataPath = app.getPath('userData'); - dbPath = path.join(userDataPath, dbName); - } + try { + const dbName = 'database.sqlite'; + let dbPath = dbName; + if (!(is.dev && process.env['VITE_DEV_SERVER_URL'])) { + const userDataPath = app.getPath('userData'); + dbPath = path.join(userDataPath, dbName); + } - const settings: Settings = await Settings.findOneBy({ onlyOneEntityShouldExist: 1 }); + const settings: Settings = await Settings.findOneBy({ onlyOneEntityShouldExist: 1 }); - const userDataPath = app.getPath('userData'); - const exportFolderPath = path.join(userDataPath, 'exports'); - if (!fs.existsSync(exportFolderPath)) { - fs.mkdirSync(exportFolderPath); - } - const now = new Date(); - const nowStr = now.toISOString().replace(/:/g, '-').replace('T', '_').slice(0, 16); - // Also update the DataExportView if you change the file name here - const exportDbPath = path.join( - userDataPath, - 'exports', - `PA_${settings.subjectId}_${nowStr}.sqlite` - ); - fs.copyFileSync(dbPath, exportDbPath); - LOG.info(`Database copied to ${exportDbPath}`); - const db = new Database(exportDbPath); - // see https://github.com/WiseLibs/better-sqlite3/blob/master/docs/performance.md - db.pragma('journal_mode = WAL'); + const userDataPath = app.getPath('userData'); + const exportFolderPath = path.join(userDataPath, 'exports'); + if (!fs.existsSync(exportFolderPath)) { + fs.mkdirSync(exportFolderPath); + } + const now = new Date(); + const nowStr = now.toISOString().replace(/:/g, '-').replace('T', '_').slice(0, 16); + // Also update the DataExportView if you change the file name here + const exportDbPath = path.join( + userDataPath, + 'exports', + `PA_${settings.subjectId}_${nowStr}.sqlite` + ); + fs.copyFileSync(dbPath, exportDbPath); + LOG.info(`Database copied to ${exportDbPath}`); + const db = new Database(exportDbPath); + // see https://github.com/WiseLibs/better-sqlite3/blob/master/docs/performance.md + db.pragma('journal_mode = WAL'); - // see https://github.com/m4heshd/better-sqlite3-multiple-ciphers/issues/5#issuecomment-1008330548 - db.pragma(`cipher='sqlcipher'`); - db.pragma(`legacy=4`); + // see https://github.com/m4heshd/better-sqlite3-multiple-ciphers/issues/5#issuecomment-1008330548 + db.pragma(`cipher='sqlcipher'`); + db.pragma(`legacy=4`); - db.pragma(`rekey='PersonalAnalytics_${settings.subjectId}'`); + db.pragma(`rekey='PersonalAnalytics_${settings.subjectId}'`); - if (windowActivityExportType === DataExportType.Obfuscate) { - const items: { windowTitle: string; url: string; id: string }[] = - await WindowActivityEntity.getRepository() - .createQueryBuilder('window_activity') - .select('id, windowTitle, url') - .getRawMany(); - for (const item of items) { - const randomizeWindowTitle = this.windowActivityTrackerService.randomizeWindowTitle( - item.windowTitle - ); - const randomizeUrl = this.windowActivityTrackerService.randomizeUrl(item.url); - const obfuscateWindowActivities = db.prepare( - 'UPDATE window_activity SET windowTitle = ?, url = ? WHERE id = ?' - ); - obfuscateWindowActivities.run(randomizeWindowTitle, randomizeUrl, item.id); + if (windowActivityExportType === DataExportType.Obfuscate || obfuscationTerms?.length > 0) { + const items: { windowTitle: string; url: string; id: string }[] = + await WindowActivityEntity.getRepository() + .createQueryBuilder('window_activity') + .select('id, windowTitle, url') + .getRawMany(); + for (const item of items) { + if (windowActivityExportType === DataExportType.Obfuscate) { + const randomizeWindowTitle = this.windowActivityTrackerService.randomizeWindowTitle( + item.windowTitle + ); + const randomizeUrl = this.windowActivityTrackerService.randomizeUrl(item.url); + const obfuscateWindowActivities = db.prepare( + 'UPDATE window_activity SET windowTitle = ?, url = ? WHERE id = ?' + ); + obfuscateWindowActivities.run(randomizeWindowTitle, randomizeUrl, item.id); + } else if (obfuscationTerms.length > 0) { + const lowerCaseObfuscationTerms: string[] = obfuscationTerms.map((term: string) => + term.toLowerCase() + ); + lowerCaseObfuscationTerms.forEach((term: string) => { + if ( + item.windowTitle?.toLowerCase().includes(term) || + item.url?.toLowerCase().includes(term) + ) { + const obfuscateWindowActivities = db.prepare( + 'UPDATE window_activity SET windowTitle = ?, url = ? WHERE id = ?' + ); + obfuscateWindowActivities.run('[anonymized]', '[anonymized]', item.id); + } + }); + } + } + } else if (windowActivityExportType === DataExportType.None) { + // remove all window activities + const removeWindowActivities = db.prepare('DROP TABLE IF EXISTS window_activity'); + removeWindowActivities.run(); } - } else if (windowActivityExportType === DataExportType.None) { - // remove all window activities - const removeWindowActivities = db.prepare('DROP TABLE IF EXISTS window_activity'); - removeWindowActivities.run(); - } - if (userInputExportType === DataExportType.None) { - // remove all user input - const removeUserInput = db.prepare('DROP TABLE IF EXISTS user_input'); - removeUserInput.run(); - } + if (userInputExportType === DataExportType.None) { + // remove all user input + const removeUserInput = db.prepare('DROP TABLE IF EXISTS user_input'); + removeUserInput.run(); + } - db.close(); + db.close(); - return exportDbPath; + return exportDbPath; + } catch (error) { + LOG.error('Error exporting the data', error); + throw error; + } } } diff --git a/src/electron/src/components/DataExportWindowActivityTracker.vue b/src/electron/src/components/DataExportWindowActivityTracker.vue index 6e73896f..4b3f22e5 100644 --- a/src/electron/src/components/DataExportWindowActivityTracker.vue +++ b/src/electron/src/components/DataExportWindowActivityTracker.vue @@ -20,12 +20,21 @@ const props = defineProps({ } }); -const emits = defineEmits(['change']); +const emits = defineEmits(['optionChanged', 'obfuscationTermsChanged', 'obfuscateSampleData']); const selectedOption = ref(props.defaultValue); +const obfuscationTermsInput = ref(''); -const emitChange = async () => { - emits('change', selectedOption.value); +const emitOptionChanged = async () => { + emits('optionChanged', selectedOption.value); +}; + +const emitObfuscationTermsChanged = async () => { + emits('obfuscationTermsChanged', obfuscationTermsInput.value); +}; + +const emitObfuscateSampleData = async () => { + emits('obfuscateSampleData'); };