mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 11:39:01 +01:00 
			
		
		
		
	safe import implementation
This commit is contained in:
		
							parent
							
								
									caa7dd9619
								
							
						
					
					
						commit
						14f7a8b7b9
					
				| @ -12,6 +12,7 @@ const $fileUploadInput = $("#import-file-upload-input"); | |||||||
| const $importProgressCountWrapper = $("#import-progress-count-wrapper"); | const $importProgressCountWrapper = $("#import-progress-count-wrapper"); | ||||||
| const $importProgressCount = $("#import-progress-count"); | const $importProgressCount = $("#import-progress-count"); | ||||||
| const $importButton = $("#import-button"); | const $importButton = $("#import-button"); | ||||||
|  | const $safeImport = $("#safe-import"); | ||||||
| 
 | 
 | ||||||
| let importId; | let importId; | ||||||
| 
 | 
 | ||||||
| @ -21,6 +22,7 @@ async function showDialog() { | |||||||
|     $importProgressCountWrapper.hide(); |     $importProgressCountWrapper.hide(); | ||||||
|     $importProgressCount.text('0'); |     $importProgressCount.text('0'); | ||||||
|     $fileUploadInput.val('').change(); // to trigger Import button disabling listener below
 |     $fileUploadInput.val('').change(); // to trigger Import button disabling listener below
 | ||||||
|  |     $safeImport.attr("checked", "checked"); | ||||||
| 
 | 
 | ||||||
|     glob.activeDialog = $dialog; |     glob.activeDialog = $dialog; | ||||||
| 
 | 
 | ||||||
| @ -49,8 +51,10 @@ function importIntoNote(importNoteId) { | |||||||
|     // dialog (which shouldn't happen, but still ...)
 |     // dialog (which shouldn't happen, but still ...)
 | ||||||
|     importId = utils.randomString(10); |     importId = utils.randomString(10); | ||||||
| 
 | 
 | ||||||
|  |     const safeImport = $safeImport.is(":checked") ? 1 : 0; | ||||||
|  | 
 | ||||||
|     $.ajax({ |     $.ajax({ | ||||||
|         url: baseApiUrl + 'notes/' + importNoteId + '/import/' + importId, |         url: baseApiUrl + 'notes/' + importNoteId + '/import/' + importId + '/safe/' + safeImport, | ||||||
|         headers: server.getHeaders(), |         headers: server.getHeaders(), | ||||||
|         data: formData, |         data: formData, | ||||||
|         dataType: 'json', |         dataType: 'json', | ||||||
|  | |||||||
| @ -12,10 +12,13 @@ const noteCacheService = require('../../services/note_cache'); | |||||||
| const log = require('../../services/log'); | const log = require('../../services/log'); | ||||||
| 
 | 
 | ||||||
| class ImportContext { | class ImportContext { | ||||||
|     constructor(importId) { |     constructor(importId, safeImport) { | ||||||
|         // importId is to distinguish between different import events - it is possible (though not recommended)
 |         // importId is to distinguish between different import events - it is possible (though not recommended)
 | ||||||
|         // to have multiple imports going at the same time
 |         // to have multiple imports going at the same time
 | ||||||
|         this.importId = importId; |         this.importId = importId; | ||||||
|  | 
 | ||||||
|  |         this.safeImport = safeImport; | ||||||
|  | 
 | ||||||
|         // // count is mean to represent count of exported notes where practical, otherwise it's just some measure of progress
 |         // // count is mean to represent count of exported notes where practical, otherwise it's just some measure of progress
 | ||||||
|         this.progressCount = 0; |         this.progressCount = 0; | ||||||
|         this.lastSentCountTs = Date.now(); |         this.lastSentCountTs = Date.now(); | ||||||
| @ -53,7 +56,10 @@ class ImportContext { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| async function importToBranch(req) { | async function importToBranch(req) { | ||||||
|     const {parentNoteId, importId} = req.params; |     let {parentNoteId, importId, safeImport} = req.params; | ||||||
|  | 
 | ||||||
|  |     safeImport = safeImport !== '0'; | ||||||
|  | 
 | ||||||
|     const file = req.file; |     const file = req.file; | ||||||
| 
 | 
 | ||||||
|     if (!file) { |     if (!file) { | ||||||
| @ -74,7 +80,7 @@ async function importToBranch(req) { | |||||||
| 
 | 
 | ||||||
|     let note; // typically root of the import - client can show it after finishing the import
 |     let note; // typically root of the import - client can show it after finishing the import
 | ||||||
| 
 | 
 | ||||||
|     const importContext = new ImportContext(importId); |     const importContext = new ImportContext(importId, safeImport); | ||||||
| 
 | 
 | ||||||
|     try { |     try { | ||||||
|         if (extension === '.tar') { |         if (extension === '.tar') { | ||||||
|  | |||||||
| @ -129,7 +129,7 @@ function register(app) { | |||||||
|     apiRoute(PUT, '/api/notes/:noteId/clone-after/:afterBranchId', cloningApiRoute.cloneNoteAfter); |     apiRoute(PUT, '/api/notes/:noteId/clone-after/:afterBranchId', cloningApiRoute.cloneNoteAfter); | ||||||
| 
 | 
 | ||||||
|     route(GET, '/api/notes/:branchId/export/:type/:format/:exportId', [auth.checkApiAuthOrElectron], exportRoute.exportBranch); |     route(GET, '/api/notes/:branchId/export/:type/:format/:exportId', [auth.checkApiAuthOrElectron], exportRoute.exportBranch); | ||||||
|     route(POST, '/api/notes/:parentNoteId/import/:importId', [auth.checkApiAuthOrElectron, uploadMiddleware], importRoute.importToBranch, apiResultHandler); |     route(POST, '/api/notes/:parentNoteId/import/:importId/safe/:safeImport', [auth.checkApiAuthOrElectron, uploadMiddleware], importRoute.importToBranch, apiResultHandler); | ||||||
| 
 | 
 | ||||||
|     route(POST, '/api/notes/:parentNoteId/upload', [auth.checkApiAuthOrElectron, uploadMiddleware], |     route(POST, '/api/notes/:parentNoteId/upload', [auth.checkApiAuthOrElectron, uploadMiddleware], | ||||||
|         filesRoute.uploadFile, apiResultHandler); |         filesRoute.uploadFile, apiResultHandler); | ||||||
|  | |||||||
| @ -5,13 +5,14 @@ const sql = require('./sql'); | |||||||
| const utils = require('./utils'); | const utils = require('./utils'); | ||||||
| const Attribute = require('../entities/attribute'); | const Attribute = require('../entities/attribute'); | ||||||
| 
 | 
 | ||||||
|  | const ATTRIBUTE_TYPES = [ 'label', 'label-definition', 'relation', 'relation-definition' ]; | ||||||
|  | 
 | ||||||
| const BUILTIN_ATTRIBUTES = [ | const BUILTIN_ATTRIBUTES = [ | ||||||
|     // label names
 |     // label names
 | ||||||
|     { type: 'label', name: 'disableVersioning' }, |     { type: 'label', name: 'disableVersioning' }, | ||||||
|     { type: 'label', name: 'calendarRoot' }, |     { type: 'label', name: 'calendarRoot' }, | ||||||
|     { type: 'label', name: 'archived' }, |     { type: 'label', name: 'archived' }, | ||||||
|     { type: 'label', name: 'excludeFromExport' }, |     { type: 'label', name: 'excludeFromExport' }, | ||||||
|     { type: 'label', name: 'run' }, |  | ||||||
|     { type: 'label', name: 'manualTransactionHandling' }, |     { type: 'label', name: 'manualTransactionHandling' }, | ||||||
|     { type: 'label', name: 'disableInclusion' }, |     { type: 'label', name: 'disableInclusion' }, | ||||||
|     { type: 'label', name: 'appCss' }, |     { type: 'label', name: 'appCss' }, | ||||||
| @ -19,19 +20,20 @@ const BUILTIN_ATTRIBUTES = [ | |||||||
|     { type: 'label', name: 'hideChildrenOverview' }, |     { type: 'label', name: 'hideChildrenOverview' }, | ||||||
|     { type: 'label', name: 'hidePromotedAttributes' }, |     { type: 'label', name: 'hidePromotedAttributes' }, | ||||||
|     { type: 'label', name: 'readOnly' }, |     { type: 'label', name: 'readOnly' }, | ||||||
|     { type: 'label', name: 'customRequestHandler' }, |     { type: 'label', name: 'run', isDangerous: true }, | ||||||
|     { type: 'label', name: 'customResourceProvider' }, |     { type: 'label', name: 'customRequestHandler', isDangerous: true }, | ||||||
|  |     { type: 'label', name: 'customResourceProvider', isDangerous: true }, | ||||||
| 
 | 
 | ||||||
|     // relation names
 |     // relation names
 | ||||||
|     { type: 'relation', name: 'runOnNoteView' }, |     { type: 'relation', name: 'runOnNoteView', isDangerous: true }, | ||||||
|     { type: 'relation', name: 'runOnNoteCreation' }, |     { type: 'relation', name: 'runOnNoteCreation', isDangerous: true }, | ||||||
|     { type: 'relation', name: 'runOnNoteTitleChange' }, |     { type: 'relation', name: 'runOnNoteTitleChange', isDangerous: true }, | ||||||
|     { type: 'relation', name: 'runOnNoteChange' }, |     { type: 'relation', name: 'runOnNoteChange', isDangerous: true }, | ||||||
|     { type: 'relation', name: 'runOnChildNoteCreation' }, |     { type: 'relation', name: 'runOnChildNoteCreation', isDangerous: true }, | ||||||
|     { type: 'relation', name: 'runOnAttributeCreation' }, |     { type: 'relation', name: 'runOnAttributeCreation', isDangerous: true }, | ||||||
|     { type: 'relation', name: 'runOnAttributeChange' }, |     { type: 'relation', name: 'runOnAttributeChange', isDangerous: true }, | ||||||
|     { type: 'relation', name: 'template' }, |     { type: 'relation', name: 'template' }, | ||||||
|     { type: 'relation', name: 'renderNote' } |     { type: 'relation', name: 'renderNote', isDangerous: true } | ||||||
| ]; | ]; | ||||||
| 
 | 
 | ||||||
| async function getNotesWithLabel(name, value) { | async function getNotesWithLabel(name, value) { | ||||||
| @ -94,11 +96,25 @@ async function getAttributeNames(type, nameLike) { | |||||||
|     return names; |     return names; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | function isAttributeType(type) { | ||||||
|  |     return ATTRIBUTE_TYPES.includes(type); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function isAttributeDangerous(type, name) { | ||||||
|  |     return BUILTIN_ATTRIBUTES.some(attr =>  | ||||||
|  |         attr.type === attr.type &&  | ||||||
|  |         attr.name.toLowerCase() === name.trim().toLowerCase() && | ||||||
|  |         attr.isDangerous | ||||||
|  |     ); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| module.exports = { | module.exports = { | ||||||
|     getNotesWithLabel, |     getNotesWithLabel, | ||||||
|     getNotesWithLabels, |     getNotesWithLabels, | ||||||
|     getNoteWithLabel, |     getNoteWithLabel, | ||||||
|     createLabel, |     createLabel, | ||||||
|     createAttribute, |     createAttribute, | ||||||
|     getAttributeNames |     getAttributeNames, | ||||||
|  |     isAttributeType, | ||||||
|  |     isAttributeDangerous | ||||||
| }; | }; | ||||||
| @ -6,6 +6,7 @@ const utils = require('../../services/utils'); | |||||||
| const log = require('../../services/log'); | const log = require('../../services/log'); | ||||||
| const repository = require('../../services/repository'); | const repository = require('../../services/repository'); | ||||||
| const noteService = require('../../services/notes'); | const noteService = require('../../services/notes'); | ||||||
|  | const attributeService = require('../../services/attributes'); | ||||||
| const Branch = require('../../entities/branch'); | const Branch = require('../../entities/branch'); | ||||||
| const tar = require('tar-stream'); | const tar = require('tar-stream'); | ||||||
| const stream = require('stream'); | const stream = require('stream'); | ||||||
| @ -153,10 +154,19 @@ async function importTar(importContext, fileBuffer, importRootNote) { | |||||||
|         for (const attr of noteMeta.attributes) { |         for (const attr of noteMeta.attributes) { | ||||||
|             attr.noteId = note.noteId; |             attr.noteId = note.noteId; | ||||||
| 
 | 
 | ||||||
|  |             if (!attributeService.isAttributeType(attr.type)) { | ||||||
|  |                 log.error("Unrecognized attribute type " + attr.type); | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|             if (attr.type === 'relation') { |             if (attr.type === 'relation') { | ||||||
|                 attr.value = getNewNoteId(attr.value); |                 attr.value = getNewNoteId(attr.value); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  |             if (importContext.safeImport && attributeService.isAttributeDangerous(attr.type, attr.name)) { | ||||||
|  |                 attr.name = 'disabled-' + attr.name; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|             attributes.push(attr); |             attributes.push(attr); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 zadam
						zadam