add light anonymization option to the existing full anonymization

This commit is contained in:
zadam 2022-02-06 13:49:33 +01:00
parent 600f74576d
commit 97fd550402
5 changed files with 55 additions and 14 deletions

View file

@ -4,4 +4,4 @@ const anonymizationService = require('../src/services/anonymization');
const fs = require('fs');
const path = require('path');
fs.writeFileSync(path.resolve(__dirname, 'tpl', 'anonymize-database.sql'), anonymizationService.getAnonymizationScript());
fs.writeFileSync(path.resolve(__dirname, 'tpl', 'anonymize-database.sql'), anonymizationService.getFullAnonymizationScript());

View file

@ -25,10 +25,20 @@ const TPL = `
<h4>Anonymize database</h4>
<h5>Full anonymization</h5>
<p>This action will create a new copy of the database and anonymize it (remove all note content and leave only structure and some non-sensitive metadata)
for sharing online for debugging purposes without fear of leaking your personal data.</p>
<button id="anonymize-button" class="btn">Save anonymized database</button><br/><br/>
<button id="anonymize-full-button" class="btn">Save fully anonymized database</button><br/><br/>
<h5>Light anonymization</h5>
<p>This action will create a new copy of the database and do a light anonymization on it - specifically only content of all notes will be removed, but titles and attributes will remaing. Additionally, custom JS frontend/backend script notes and custom widgets will remain. This provides more context to debug the issues.</p>
<p>You can decide yourself if you want to provide fully or lightly anonymized database. Even fully anonymized DB is very useful, however in some cases lightly anonymized database can speed up the process of bug identification and fixing.</p>
<button id="anonymize-light-button" class="btn">Save lightly anonymized database</button><br/><br/>
<h4>Vacuum database</h4>
@ -42,7 +52,8 @@ export default class AdvancedOptions {
this.$forceFullSyncButton = $("#force-full-sync-button");
this.$fillEntityChangesButton = $("#fill-entity-changes-button");
this.$anonymizeButton = $("#anonymize-button");
this.$anonymizeFullButton = $("#anonymize-full-button");
this.$anonymizeLightButton = $("#anonymize-light-button");
this.$vacuumDatabaseButton = $("#vacuum-database-button");
this.$findAndFixConsistencyIssuesButton = $("#find-and-fix-consistency-issues-button");
this.$checkIntegrityButton = $("#check-integrity-button");
@ -59,14 +70,25 @@ export default class AdvancedOptions {
toastService.showMessage("Sync rows filled successfully");
});
this.$anonymizeButton.on('click', async () => {
const resp = await server.post('database/anonymize');
this.$anonymizeFullButton.on('click', async () => {
const resp = await server.post('database/anonymize/full');
if (!resp.success) {
toastService.showError("Could not create anonymized database, check backend logs for details");
}
else {
toastService.showMessage(`Created anonymized database in ${resp.anonymizedFilePath}`, 10000);
toastService.showMessage(`Created fully anonymized database in ${resp.anonymizedFilePath}`, 10000);
}
});
this.$anonymizeLightButton.on('click', async () => {
const resp = await server.post('database/anonymize/light');
if (!resp.success) {
toastService.showError("Could not create anonymized database, check backend logs for details");
}
else {
toastService.showMessage(`Created lightly anonymized database in ${resp.anonymizedFilePath}`, 10000);
}
});

View file

@ -6,8 +6,8 @@ const backupService = require('../../services/backup');
const anonymizationService = require('../../services/anonymization');
const consistencyChecksService = require('../../services/consistency_checks');
async function anonymize() {
return await anonymizationService.createAnonymizedCopy();
async function anonymize(req) {
return await anonymizationService.createAnonymizedCopy(req.params.type);
}
async function backupDatabase() {

View file

@ -324,7 +324,7 @@ function register(app) {
apiRoute(GET, '/api/sql/schema', sqlRoute.getSchema);
apiRoute(POST, '/api/sql/execute/:noteId', sqlRoute.execute);
route(POST, '/api/database/anonymize', [auth.checkApiAuthOrElectron, csrfMiddleware], databaseRoute.anonymize, apiResultHandler, false);
route(POST, '/api/database/anonymize/:type', [auth.checkApiAuthOrElectron, csrfMiddleware], databaseRoute.anonymize, apiResultHandler, false);
// backup requires execution outside of transaction
route(POST, '/api/database/backup-database', [auth.checkApiAuthOrElectron, csrfMiddleware], databaseRoute.backupDatabase, apiResultHandler, false);

View file

@ -5,7 +5,7 @@ const dateUtils = require("./date_utils");
const Database = require("better-sqlite3");
const sql = require("./sql");
function getAnonymizationScript() {
function getFullAnonymizationScript() {
// we want to delete all non-builtin attributes because they can contain sensitive names and values
// on the other hand builtin/system attrs should not contain any sensitive info
const builtinAttrNames = BUILTIN_ATTRIBUTES
@ -33,18 +33,37 @@ VACUUM;
return anonymizeScript;
}
async function createAnonymizedCopy() {
function getLightAnonymizationScript() {
return `
UPDATE note_contents SET content = 'text' WHERE content IS NOT NULL AND noteId NOT IN (
SELECT noteId FROM notes WHERE mime IN ('application/javascript;env=backend', 'application/javascript;env=frontend')
);
UPDATE note_revision_contents SET content = 'text' WHERE content IS NOT NULL AND noteRevisionId NOT IN (
SELECT noteRevisionId FROM note_revisions WHERE mime IN ('application/javascript;env=backend', 'application/javascript;env=frontend')
);
`;
}
async function createAnonymizedCopy(type) {
if (!['full', 'light'].includes(type)) {
throw new Error(`Unrecognized anonymization type '${type}'`);
}
if (!fs.existsSync(dataDir.ANONYMIZED_DB_DIR)) {
fs.mkdirSync(dataDir.ANONYMIZED_DB_DIR, 0o700);
}
const anonymizedFile = dataDir.ANONYMIZED_DB_DIR + "/" + "anonymized-" + dateUtils.getDateTimeForFile() + ".db";
const anonymizedFile = `${dataDir.ANONYMIZED_DB_DIR}/anonymized-${type}-${dateUtils.getDateTimeForFile()}.db`;
await sql.copyDatabase(anonymizedFile);
const db = new Database(anonymizedFile);
db.exec(getAnonymizationScript());
const anonymizationScript = type === 'light'
? getLightAnonymizationScript()
: getFullAnonymizationScript();
db.exec(anonymizationScript);
db.close();
@ -55,6 +74,6 @@ async function createAnonymizedCopy() {
}
module.exports = {
getAnonymizationScript,
getFullAnonymizationScript,
createAnonymizedCopy
}