CVE-2023-3191: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') (CWE-79)
value: Cross-site Scripting (XSS) - Stored in GitHub repository nilsteampassnet/teampass prior to 3.0.9.
CWE description: The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.
CVSS v3 base score: 5.4
Published: 2023-06-10T09:15Z
Repository: https://github.com/nilsteampassnet/teampass
Language: PHP
Files changed: includes/js/functions.js, pages/admin.js.php, pages/api.js.php, pages/api.php, pages/backups.js.php, pages/emails.js.php, pages/folders.js.php, pages/folders.php, pages/items.js.php, pages/profile.js.php, pages/roles.js.php, pages/users.js.php, pages/users.php, sources/admin.queries.php
Commit date: 2023-06-10 09:30:21 +0200
Commit message: 3.0.9

Fix xss vulnerabilities in several text inputs

Diff: diff --git a/includes/js/functions.js b/includes/js/functions.js
index c820cd7d3f..9a307a8993 100755
--- a/includes/js/functions.js
+++ b/includes/js/functions.js
@@ -388,50 +388,147 @@ if (typeof String.prototype.utf8Decode == 'undefined') {
             return this; // invalid UTF-8? return as-is
         }
     };
 }
 
-function fieldSanitizeStep1(
-    field,
-    bHtml=true,
-    bSvg=true,
-    bSvgFilters=true,
-    text=''
+function simplePurifier(
+    text,
+    bHtml = false,
+    bSvg = false,
+    bSvgFilters = false
 )
 {
-    if (field === undefined ||field === '') {
-        return false;
-    }
-    let string = '';
-    text = (text === '') ? $(field).val() : text;
-/*
-    // Sanitize string
-    var tagsToReplace = {
-        '&': '&amp;',
-        '<': '&lt;',
-        '>': '&gt;',
-        "'" : '&#39;',
-        '"' : '&quot;'
-    };
-    text = text.replace(/[&<>'"]/g, function(tag) {
-        return tagsToReplace[tag] || tag;
-    });
-    */
-    // Purify string
-    string = DOMPurify.sanitize(
+    return DOMPurify.sanitize(
         text
             .replaceAll('&lt;', '<')
             .replaceAll('&gt;', '>')
             .replaceAll('&amp;', '&')
             .replaceAll('&quot;', '"')
             .replaceAll('&#39;', "'"),
         {USE_PROFILES: {html:bHtml, svg:bSvg, svgFilters: bSvgFilters}}
     );
+}
+
+/**
+ * Permits to purify the content of a string using domPurify
+ * @param {*} field 
+ * @param {*} bHtml 
+ * @param {*} bSvg 
+ * @param {*} bSvgFilters 
+ * @param {*} text 
+ * @returns bool||string
+ */
+function fieldDomPurifier(
+    field,
+    bHtml = false,
+    bSvg = false,
+    bSvgFilters = false,
+    text = ''
+)
+{
+    if (field === undefined ||field === '') {
+        return false;
+    }
+    let string = '';
+    text = (text === '') ? $(field).val() : text;
+
+    // Purify string
+    string = simplePurifier(text, bHtml, bSvg, bSvgFilters);
     
     // Clear field if string is empty and warn user
     if (string === '' && text !== '') {
         $(field).val('');
         return false;
     }
 
+    return string;
+}
+
+/**
+ * Permits to get all fields of a class and purify them
+ * @param {*} elementClass 
+ * @returns array
+ */
+function fieldDomPurifierLoop(elementClass)
+{
+    let purifyStop = false,
+        arrFields = [];
+    $.each($(elementClass), function(index, element) {
+        purifiedField = fieldDomPurifier(
+            '#' + $(element).attr('id'), 
+            $(element).hasClass('purifyHtml') === true ? true : false,
+            $(element).hasClass('purifySvg') === true ? true : false,
+            $(element).hasClass('purifySvgFilter') === true ? true : false,
+            typeof $(element).data('purify-text') !== undefined ? $(element).data('purify-text') : ''
+        );
+
+        if (purifiedField === false) {
+            // Label is empty
+            toastr.remove();
+            toastr.warning(
+                'XSS attempt detected. Please remove all special characters from your input.',
+                'Error', {
+                    timeOut: 5000,
+                    progressBar: true
+                }
+            );
+            $('#' + $(element).attr('id')).focus();
+            purifyStop = true;
+            return {
+                'purifyStop' : purifyStop,
+                'arrFields' : arrFields
+            };
+        } else {
+            $(element).val(purifiedField);
+            arrFields[$(element).data('field')] = purifiedField;
+        }
+    });
+    
+    // return
+    return {
+        'purifyStop' : purifyStop,
+        'arrFields' : arrFields
+    };
+}
+
+/**
+ * Permits to purify the content of a string using domPurify
+ * @param {*} field 
+ * @param {*} bHtml 
+ * @param {*} bSvg 
+ * @param {*} bSvgFilters 
+ * @returns bool||string
+ */
+function fieldDomPurifierWithWarning(
+    field,
+    bHtml = false,
+    bSvg = false,
+    bSvgFilters = false,
+)
+{
+    if (field === undefined || field === '') {
+        return false;
+    }
+    if ($(field).val() === '') {
+        return '';
+    }
+    let string = '';
+
+    // Purify string
+    string = simplePurifier($(field).val(), bHtml, bSvg, bSvgFilters);
+    
+    // Clear field if string is empty and warn user
+    if (string === '') {
+        toastr.remove();
+        toastr.warning(
+            'XSS attempt detected. Please remove all special characters from your input.',
+            'Error', {
+                timeOut: 5000,
+                progressBar: true
+            }
+        );
+        $(field).focus();
+        return false;
+    }
+
     return string;
 }
\ No newline at end of file
diff --git a/pages/admin.js.php b/pages/admin.js.php
index 0aad38e396..852dab00a9 100755
--- a/pages/admin.js.php
+++ b/pages/admin.js.php
@@ -132,10 +132,17 @@ declare(strict_types=1);
         // prevent launch of similar query in case of doubleclick
         if (requestRunning === true) {
             return false;
         }
 
+        // Sanitize value
+        value = fieldDomPurifierWithWarning('#' + field);
+        if (value === false) {
+            return false;
+        }
+        $('#' + field).val(value);
+
         requestRunning = true;
 
         var data = {
             "field": field,
             "value": value,
diff --git a/pages/api.js.php b/pages/api.js.php
index 425d1fe497..1c8c1e5383 100755
--- a/pages/api.js.php
+++ b/pages/api.js.php
@@ -63,14 +63,21 @@ if (checkUser($_SESSION['user_id'], $_SESSION['key'], 'api', $SETTINGS) === fals
     $(document).on('click', '#button-new-api-key', function() {
         // IS form field?
         if ($('#new_api_key_label') === '') {
             return false;
         }
+        
+        // Sanitize text fields
+        purifyRes = fieldDomPurifierLoop('#new_api_key_label');
+        if (purifyRes.purifyStop === true) {
+            // if purify failed, stop
+            return false;
+        }
 
         // Prepare data
         var data = {
-            'label': $('#new_api_key_label').val(),
+            'label': purifyRes.arrFields['label'], //$('#new_api_key_label').val(),
             'action': 'add',
         }
 
         $('#table-api-keys').removeClass('hidden');
         $('#api-no-keys').addClass('hidden');
@@ -194,20 +201,20 @@ if (checkUser($_SESSION['user_id'], $_SESSION['key'], 'api', $SETTINGS) === fals
         $('.new-api-key').val(oldLabel);
     });
 
     $(document).on('click', '#new-api-key-save', function() {
         var keyId = $(this).closest('tr').data('id'),
-            label = $(this).prev('input').val(),
+            label = simplePurifier($(this).prev('input').val()),
             cell = $(this).closest('td');
 
         // Prepare data
         var data = {
             'id': keyId,
             'label': label,
             'action': 'update',
         }
-
+        
         // Launch action
         $.post(
             'sources/admin.queries.php', {
                 type: 'admin_action_api_save_key',
                 data: prepareExchangedData(JSON.stringify(data), "encode", "<?php echo $_SESSION['key']; ?>"),
@@ -256,13 +263,20 @@ if (checkUser($_SESSION['user_id'], $_SESSION['key'], 'api', $SETTINGS) === fals
                 }
             );
             return false;
         }
 
+        // Sanitize text fields
+        purifyRes = fieldDomPurifierLoop('#new-api-ip .purify');
+        if (purifyRes.purifyStop === true) {
+            // if purify failed, stop
+            return false;
+        }
+
         // Prepare data
         var data = {
-            'label': $('#new_api_ip_label').val(),
+            'label': purifyRes.arrFields['label'],
             'ip': $('#new_api_ip_value').val(),
             'action': 'add',
         }
 
         $('#table-api-ips').removeClass('hidden');
@@ -396,11 +410,11 @@ if (checkUser($_SESSION['user_id'], $_SESSION['key'], 'api', $SETTINGS) === fals
             cell = $(this).closest('td');
 
         // Prepare data
         var data = {
             'id': ipId,
-            'value': label,
+            'value': simplePurifier(label),
             'field': field,
             'action': 'update',
         }
 
         // Launch action
diff --git a/pages/api.php b/pages/api.php
index 09883013c5..a81ca0df99 100755
--- a/pages/api.php
+++ b/pages/api.php
@@ -159,11 +159,11 @@ require_once $SETTINGS['cpassman_dir'] . '/sources/main.functions.php';
                                 <div class="form-group mt-4">
                                     <div class="callout callout-info">
                                         <span class="text-bold"><?php echo langHdl('adding_new_api_key'); ?></span>
 
                                         <div class="row mt-1 ml-1">
-                                            <input type="text" placeholder="<?php echo langHdl('label'); ?>" class="col-4 form-control form-control-sm" id="new_api_key_label">
+                                            <input type="text" placeholder="<?php echo langHdl('label'); ?>" class="col-4 form-control form-control-sm purify" id="new_api_key_label" data-field="label">
                                             <span class="fa-stack ml-2 infotip pointer" title="<?php echo langHdl('adding_new_api_key'); ?>" id="button-new-api-key">
                                                 <i class="fas fa-square fa-stack-2x"></i>
                                                 <i class="fas fa-plus fa-stack-1x fa-inverse"></i>
                                             </span>
                                         </div>
@@ -209,17 +209,17 @@ require_once $SETTINGS['cpassman_dir'] . '/sources/main.functions.php';
                                     <div class="mt-2<?php echo DB::count() > 0 ? ' hidden' : ''; ?>" id="api-no-ips">
                                         <i class="fas fa-info mr-2 text-warning"></i><?php echo langHdl('no_data_defined'); ?>
                                     </div>
                                 </div>
 
-                                <div class="form-group mt-4">
+                                <div class="form-group mt-4" id="new-api-ip">
                                     <div class="callout callout-info">
                                         <span class="text-bold"><?php echo langHdl('adding_new_api_ip'); ?></span>
 
                                         <div class="row mt-1 ml-1">
-                                            <input type="text" placeholder="<?php echo langHdl('ip'); ?>" class="col-4 form-control" id="new_api_ip_value" data-inputmask="'alias': 'ip'" data-mask>
-                                            <input type="text" placeholder="<?php echo langHdl('label'); ?>" class="col-4 form-control ml-2" id="new_api_ip_label">
+                                            <input type="text" placeholder="<?php echo langHdl('ip'); ?>" class="col-4 form-control" id="new_api_ip_value" data-inputmask="'alias': 'ip'">
+                                            <input type="text" placeholder="<?php echo langHdl('label'); ?>" class="col-4 form-control ml-2 purify" id="new_api_ip_label" data-field="label">
                                             <span class="fa-stack ml-2 infotip pointer" title="<?php echo langHdl('settings_api_add_ip'); ?>" id="button-new-api-ip">
                                                 <i class="fas fa-square fa-stack-2x"></i>
                                                 <i class="fas fa-plus fa-stack-1x fa-inverse"></i>
                                             </span>
                                         </div>
diff --git a/pages/backups.js.php b/pages/backups.js.php
index 7ec96ea7ef..dda4d792c7 100755
--- a/pages/backups.js.php
+++ b/pages/backups.js.php
@@ -94,11 +94,11 @@ if (checkUser($_SESSION['user_id'], $_SESSION['key'], '2fa', $SETTINGS) === fals
                 toastr.remove();
                 toastr.info('<?php echo langHdl('in_progress'); ?> ... <i class="fas fa-circle-notch fa-spin fa-2x"></i>');
 
                 // Prepare data
                 var data = {
-                    'encryptionKey': $('#onthefly-backup-key').val(),
+                    'encryptionKey': simplePurifier($('#onthefly-backup-key').val()),
                 };
 
                 //send query
                 $.post(
                     "sources/backups.queries.php", {
@@ -123,11 +123,11 @@ if (checkUser($_SESSION['user_id'], $_SESSION['key'], '2fa', $SETTINGS) === fals
                             );
                         } else {
                             // Store KEY in DB
                             var newData = {
                                 "field": 'bck_script_passkey',
-                                "value": $('#onthefly-backup-key').val(),
+                                "value": simplePurifier($('#onthefly-backup-key').val()),
                             }
 
                             $.post(
                                 "sources/admin.queries.php", {
                                     type: "save_option_change",
@@ -191,11 +191,11 @@ if (checkUser($_SESSION['user_id'], $_SESSION['key'], '2fa', $SETTINGS) === fals
                 toastr.remove();
                 toastr.info('<?php echo langHdl('in_progress'); ?> ... <i class="fas fa-circle-notch fa-spin fa-2x"></i>');
 
                 // Prepare data
                 var data = {
-                    'encryptionKey': $('#onthefly-restore-key').val(),
+                    'encryptionKey': simplePurifier($('#onthefly-restore-key').val()),
                     'backupFile': $('#onthefly-restore-file').data('operation-id')
                 };
                 console.log(data);
                 //send query
                 $.post(
diff --git a/pages/emails.js.php b/pages/emails.js.php
index 578d3f48ae..71f4f2cda5 100755
--- a/pages/emails.js.php
+++ b/pages/emails.js.php
@@ -143,54 +143,7 @@ if (checkUser($_SESSION['user_id'], $_SESSION['key'], '2fa', $SETTINGS) === fals
                 }
             }
         );
     }
 
-
-    $(document).on('click', '#button-duo-save', function() {
-        // Prepare data
-        var data = {
-            'akey': $('#duo_akey').val(),
-            'ikey': $('#duo_ikey').val(),
-            'skey': $('#duo_skey').val(),
-            'host': $('#duo_host').val(),
-        }
-        console.log(data);
-
-        // Launch action
-        $.post(
-            'sources/admin.queries.php', {
-                type: 'save_duo_in_sk_file',
-                data: prepareExchangedData(JSON.stringify(data), "encode", "<?php echo $_SESSION['key']; ?>"),
-                key: '<?php echo $_SESSION['key']; ?>'
-            },
-            function(data) {
-                //decrypt data
-                data = decodeQueryReturn(data, '<?php echo $_SESSION['key']; ?>');
-
-                if (data.error === true) {
-                    // ERROR
-                    toastr.remove();
-                    toastr.warning(
-                        '<?php echo langHdl('none_selected_text'); ?>',
-                        '', {
-                            timeOut: 5000,
-                            progressBar: true
-                        }
-                    );
-                } else {
-                    // Inform user
-                    toastr.remove();
-                    toastr.success(
-                        '<?php echo langHdl('done'); ?>',
-                        '', {
-                            timeOut: 1000
-                        }
-                    );
-                }
-            }
-        );
-    });
-
-
     //]]>
 </script>
diff --git a/pages/folders.js.php b/pages/folders.js.php
index f0dba143cf..adcfb3bb3d 100755
--- a/pages/folders.js.php
+++ b/pages/folders.js.php
@@ -94,21 +94,29 @@ if (checkUser($_SESSION['user_id'], $_SESSION['key'], 'folders', $SETTINGS) ===
             $('#folders-list').addClass('hidden');
             $('#new-title').focus();
 
         } else if ($(this).data('action') === 'new-submit') {
             //--- SAVE NEW FOLDER
+
+            // Sanitize text fields
+            purifyRes = fieldDomPurifierLoop('#folder-new .purify');
+            if (purifyRes.purifyStop === true) {
+                // if purify failed, stop
+                return false;
+            }
+
             // Prepare data
             var data = {
-                'title': DOMPurify.sanitize($('#new-title').val()),
+                'title': purifyRes.arrFields['title'],
                 'parentId': parseInt($('#new-parent').val()),
                 'complexity': parseInt($('#new-complexity').val()),
                 'accessRight': $('#new-access-right').val(),
-                'renewalPeriod': DOMPurify.sanitize($('#new-renewal').val() === '' ? 0 : parseInt($('#new-renewal').val())),
+                'renewalPeriod': $('#new-renewal').val() === '' ? 0 : parseInt($('#new-renewal').val()),
                 'addRestriction': $('#new-add-restriction').prop("checked") === true ? 1 : 0,
                 'editRestriction': $('#new-edit-restriction').prop("checked") === true ? 1 : 0,
-                'icon': DOMPurify.sanitize($('#new-folder-add-icon').val(), {USE_PROFILES: {html: true}}),
-                'iconSelected': DOMPurify.sanitize($('#new-folder-add-icon-selected').val(), {USE_PROFILES: {html: true}}),
+                'icon': purifyRes.arrFields['icon'],
+                'iconSelected': purifyRes.arrFields['iconSelected'],
             }
             console.log(data)
             // Launch action
             $.post(
                 'sources/folders.queries.php', {
@@ -645,11 +653,11 @@ if (checkUser($_SESSION['user_id'], $_SESSION['key'], 'folders', $SETTINGS) ===
             '<tr class="temp-row"><td colspan="' + $(currentRow).children('td').length + '">' +
             '<div class="card card-warning card-outline form">' +
             '<div class="card-body">' +
             '<div class="form-group ml-2">' +
             '<label for="folder-edit-title"><?php echo langHdl('label'); ?></label>' +
-            '<input type="text" class="form-control clear-me" id="folder-edit-title" value="' + folderTitle + '">' +
+            '<input type="text" class="form-control clear-me purify" id="folder-edit-title" data-field="title" value="' + folderTitle + '">' +
             '</div>' +
             '<div class="form-group ml-2">' +
             '<label for="folder-edit-parent"><?php echo langHdl('parent'); ?></label><br>' +
             '<select id="folder-edit-parent" class="form-control form-item-control select2 clear-me">' + store.get('teampassApplication').foldersSelect + '</select>' +
             '</div>' +
@@ -661,18 +669,18 @@ if (checkUser($_SESSION['user_id'], $_SESSION['key'], 'folders', $SETTINGS) ===
             '<label for="folder-edit-renewal"><?php echo langHdl('renewal_delay'); ?></label>' +
             '<input type="text" class="form-control clear-me" id="folder-edit-renewal" value="' + folderRenewal + '">' +
             '</div>' +
             '<div class="form-group ml-2">' +
             '<label><?php echo langHdl('icon'); ?></label>' +
-            '<input type="text" class="form-control form-folder-control" id="folder-edit-icon" value="'+folderIcon+'">' +
+            '<input type="text" class="form-control form-folder-control purify" id="folder-edit-icon" data-field="icon" value="'+folderIcon+'">' +
             '<small class="form-text text-muted">' +
             '<?php echo langHdl('fontawesome_icon_tip'); ?><a href="<?php echo FONTAWESOME_URL;?>" target="_blank"><i class="fas fa-external-link-alt ml-1"></i></a>' +
             '</small>' +
             '</div>' +
             '<div class="form-group ml-2">' +
             '<label><?php echo langHdl('icon_on_selection'); ?></label>' +
-            '<input type="text" class="form-control form-folder-control" id="folder-edit-icon-selected" value="'+folderIconSelection+'">' +
+            '<input type="text" class="form-control form-folder-control purify" id="folder-edit-icon-selected" data-field="iconSelected" value="'+folderIconSelection+'">' +
             '<small class="form-text text-muted">' +
             '<?php echo langHdl('fontawesome_icon_tip'); ?><a href="<?php echo FONTAWESOME_URL;?>" target="_blank"><i class="fas fa-external-link-alt ml-1"></i></a>' +
             '</small>' +
             '</div>' +
             '<div class="form-group ml-2" id="folder-rights-tuned">' +
@@ -736,21 +744,28 @@ if (checkUser($_SESSION['user_id'], $_SESSION['key'], 'folders', $SETTINGS) ===
             var selectedFolders = [];
             $("input:checkbox[class=checkbox-folder]:checked").each(function() {
                 selectedFolders.push($(this).data('id'));
             });
 
+            // Sanitize text fields
+            purifyRes = fieldDomPurifierLoop('#table-folders .purify');
+            if (purifyRes.purifyStop === true) {
+                // if purify failed, stop
+                return false;
+            }
+
             // Prepare data
             var data = {
                 'id': currentFolderId,
-                'title': DOMPurify.sanitize($('#folder-edit-title').val()),
+                'title': purifyRes.arrFields['title'],    //$('#folder-edit-title').val(),
                 'parentId': $('#folder-edit-parent').val(),
                 'complexity': $('#folder-edit-complexity').val(),
-                'renewalPeriod': DOMPurify.sanitize($('#folder-edit-renewal').val()),
+                'renewalPeriod': $('#new-renewal').val() === '' ? 0 : parseInt($('#new-renewal').val()),
                 'addRestriction': $('#folder-edit-add-restriction').prop("checked") === true ? 1 : 0,
                 'editRestriction': $('#folder-edit-edit-restriction').prop("checked") === true ? 1 : 0,
-                'icon': DOMPurify.sanitize($('#folder-edit-icon').val(), {USE_PROFILES: {html: true}}),
-                'iconSelected': DOMPurify.sanitize($('#folder-edit-icon-selected').val(), {USE_PROFILES: {html: true}})
+                'icon': purifyRes.arrFields['icon'],    //$('#folder-edit-icon').val(),
+                'iconSelected': purifyRes.arrFields['iconSelected'],    //$('#folder-edit-icon-selected').val(),
             }
             console.log(data)
             // Launch action
             $.post(
                 'sources/folders.queries.php', {
diff --git a/pages/folders.php b/pages/folders.php
index 90d2b195c0..80b8c357b7 100755
--- a/pages/folders.php
+++ b/pages/folders.php
@@ -141,11 +141,11 @@ foreach ($tst as $t) {
                         <!-- form start -->
                         <form role="form">
                             <div class="card-body">
                                 <div class="form-group">
                                     <label for="new-title"><?php echo langHdl('label'); ?></label>
-                                    <input type="text" class="form-control clear-me" id="new-title">
+                                    <input type="text" class="form-control clear-me purify" id="new-title" data-field="title">
                                 </div>
                                 <div class="form-group">
                                     <label for="new-parent"><?php echo langHdl('parent'); ?></label>
                                     <select id="new-parent" class="form-control form-item-control select2 no-root" style="width:100%;">
                                         <?php echo $droplist; ?>
@@ -169,18 +169,18 @@ foreach ($tst as $t) {
                                     <label for="new-renewal"><?php echo langHdl('renewal_delay'); ?></label>
                                     <input type="number" class="form-control clear-me" id="new-renewal" value="0" min="0" data-bind="value:replyNumber">
                                 </div>
                                 <div class="form-group">
                                     <label><?php echo langHdl('icon'); ?></label>
-                                    <input type="text" class="form-control form-folder-control" id="new-folder-add-icon">
+                                    <input type="text" class="form-control form-folder-control purify" id="new-folder-add-icon" data-field="icon">
                                     <small class='form-text text-muted'>
                                         <?php echo langHdl('fontawesome_icon_tip'); ?><a href="<?php echo FONTAWESOME_URL;?>" target="_blank"><i class="fas fa-external-link-alt ml-1"></i></a>
                                     </small>
                                 </div>
                                 <div class="form-group">
                                     <label><?php echo langHdl('icon_on_selection'); ?></label>
-                                    <input type="text" class="form-control form-folder-control" id="new-folder-add-icon-selected">
+                                    <input type="text" class="form-control form-folder-control purify" id="new-folder-add-icon-selected" data-field="iconSelected">
                                     <small class='form-text text-muted'>
                                         <?php echo langHdl('fontawesome_icon_tip'); ?><a href="<?php echo FONTAWESOME_URL;?>" target="_blank"><i class="fas fa-external-link-alt ml-1"></i></a>
                                     </small>
                                 </div>
                                 <div class="form-group">
@@ -191,11 +191,11 @@ foreach ($tst as $t) {
                                     </div>
                                     <div class="form-check">
                                         <input type="checkbox" class="form-check-input form-control" id="new-edit-restriction">
                                         <label for="new-edit-restriction" class="form-check-label pointer ml-2"><?php echo langHdl('edit_without_password_minimal_complexity_target'); ?></label>
                                     </div>
-</div>
+                                </div>
                             </div>
                             <!-- /.card-body -->
 
                             <div class="card-footer">
                                 <button type="button" class="btn btn-primary tp-action" data-action="new-submit"><?php echo langHdl('submit'); ?></button>
diff --git a/pages/items.js.php b/pages/items.js.php
index 4eaa57b7f4..fb80138d6b 100755
--- a/pages/items.js.php
+++ b/pages/items.js.php
@@ -1678,13 +1678,13 @@ $var['hidden_asterisk'] = '<i class="fas fa-asterisk mr-2"></i><i class="fas fa-
         // Force user did a change to false
         userDidAChange = false;
         userUploadedFile = false;
 
         // Sanitize text fields
-        let formLabel = fieldSanitizeStep1('#form-folder-add-label', false, false, false),
-            formIcon = fieldSanitizeStep1('#form-folder-add-icon', false, false, false),
-            formIconSelected = fieldSanitizeStep1('#form-folder-add-icon-selected', false, false, false);
+        let formLabel = fieldDomPurifier('#form-folder-add-label', false, false, false),
+            formIcon = fieldDomPurifier('#form-folder-add-icon', false, false, false),
+            formIconSelected = fieldDomPurifier('#form-folder-add-icon-selected', false, false, false);
         if (formLabel === false || formIcon === false || formIconSelected === false) {
             // Label is empty
             toastr.remove();
             toastr.warning(
                 'XSS attempt detected. Field has been emptied.',
@@ -2113,10 +2113,11 @@ $var['hidden_asterisk'] = '<i class="fas fa-asterisk mr-2"></i><i class="fas fa-
             toastr.remove();
             toastr.info('<?php echo langHdl('loading_item'); ?> ... <i class="fas fa-circle-notch fa-spin fa-2x"></i>');
 
             // Load item info
             Details(
+                //$(this).hasClass('but-prev-item') === true ? $('#list-item-row_' + $(this).attr('data-prev-item-key')) : $('#list-item-row_' + $(this).attr('data-next-item-key')),
                 $(this).hasClass('but-prev-item') === true ? $('#list-item-row_' + $(this).attr('data-prev-item-id')) : $('#list-item-row_' + $(this).attr('data-next-item-id')),
                 'show'
             );
 
             $('.but-navigate-item').addClass('hidden');
@@ -2675,16 +2676,16 @@ $var['hidden_asterisk'] = '<i class="fas fa-asterisk mr-2"></i><i class="fas fa-
         // Do checks
         if (arrayQuery.length > 0 || userDidAChange === true) {
             var reg = new RegExp("[.|,|;|:|!|=|+|-|*|/|#|\"|'|&]");
 
             // Sanitize text fields
-            let formLabel = fieldSanitizeStep1('#form-item-label', false, false, false),
-                formDescription = $('#form-item-description').summernote('code') !== "<p><br></p>" ? fieldSanitizeStep1('#form-item-description', true, false, false, $('#form-item-description').summernote('code')) : '',
-                formEmail = fieldSanitizeStep1('#form-item-email'),
-                formTags = fieldSanitizeStep1('#form-item-tags'),
-                formUrl = fieldSanitizeStep1('#form-item-url'),
-                formIcon = fieldSanitizeStep1('#form-item-icon');
+            let formLabel = fieldDomPurifier('#form-item-label', false, false, false),
+                formDescription = $('#form-item-description').summernote('code') !== "<p><br></p>" ? fieldDomPurifier('#form-item-description', true, false, false, $('#form-item-description').summernote('code')) : '',
+                formEmail = fieldDomPurifier('#form-item-email'),
+                formTags = fieldDomPurifier('#form-item-tags'),
+                formUrl = fieldDomPurifier('#form-item-url'),
+                formIcon = fieldDomPurifier('#form-item-icon');
             if (formLabel === false || formDescription === false || formEmail === false || formTags === false || formUrl === false || formIcon === false) {
                 // Label is empty
                 toastr.remove();
                 toastr.warning(
                     'XSS attempt detected. Field has been emptied.',
@@ -3946,12 +3947,16 @@ $var['hidden_asterisk'] = '<i class="fas fa-asterisk mr-2"></i><i class="fas fa-
                 if (debugJavascript === true) {
                     console.log('current id: '+value.item_id);
                     console.log(prevIdForNextItem);
                 }
                 if (prevIdForNextItem !== -1) {
-                    $('#list-item-row_' + value.item_id).attr('data-next-item-id', prevIdForNextItem.item_id);
-                    $('#list-item-row_' + value.item_id).attr('data-next-item-label', value.label);
+                    //$('#list-item-row_' + value.item_id).attr('data-next-item-id', prevIdForNextItem.item_id);
+                    //$('#list-item-row_' + value.item_id).attr('data-next-item-label', value.label);
+                    $('[data-item-key="'+value.item_key+'"]')
+                        //.attr('data-next-item-id', prevIdForNextItem.item_id)
+                        .attr('data-next-item-key', prevIdForNextItem.item_key)
+                        .attr('data-next-item-label', value.label);
                 }
                 
                 // Prepare anyone can modify icon
                 if (value.anyone_can_modify === 1 || value.open_edit === 1) {
                     icon_all_can_modify = '<span class="fa-stack fa-clickable pointer infotip list-item-clicktoedit mr-2" title="<?php echo langHdl('edit'); ?>"><i class="fas fa-circle fa-stack-2x"></i><i class="fas fa-pen fa-stack-1x fa-inverse"></i></span>';
@@ -3984,23 +3989,23 @@ $var['hidden_asterisk'] = '<i class="fas fa-asterisk mr-2"></i><i class="fas fa-
                 // Prepare Favorite icon
                 if (store.get('teampassSettings') !== undefined && parseInt(store.get('teampassSettings').enable_favourites) === 1 &&
                     value.rights > 10
                 ) {
                     if (value.is_favourited === 1) {
-                        icon_favorite = '<span class="fa-stack fa-clickable item-favourite pointer infotip mr-2" title="<?php echo langHdl('unfavorite'); ?>" data-item-id="' + value.item_id + '" data-item-favourited="1"><i class="fas fa-circle fa-stack-2x"></i><i class="fas fa-star fa-stack-1x fa-inverse text-warning"></i></span>';
+                        icon_favorite = '<span class="fa-stack fa-clickable item-favourite pointer infotip mr-2" title="<?php echo langHdl('unfavorite'); ?>" data-item-id="' + value.item_id + '" data-item-key="' + value.item_key + '" data-item-favourited="1"><i class="fas fa-circle fa-stack-2x"></i><i class="fas fa-star fa-stack-1x fa-inverse text-warning"></i></span>';
                     } else {
-                        icon_favorite = '<span class="fa-stack fa-clickable item-favourite pointer infotip mr-2" title="<?php echo langHdl('favorite'); ?>" data-item-id="' + value.item_id + '" data-item-favourited="0"><i class="fas fa-circle fa-stack-2x"></i><i class="far fa-star fa-stack-1x fa-inverse"></i></span>';
+                        icon_favorite = '<span class="fa-stack fa-clickable item-favourite pointer infotip mr-2" title="<?php echo langHdl('favorite'); ?>" data-item-id="' + value.item_id + '" data-item-key="' + value.item_key + '" data-item-favourited="0"><i class="fas fa-circle fa-stack-2x"></i><i class="far fa-star fa-stack-1x fa-inverse"></i></span>';
                     }
                 }
 
                 // Prepare Description
                 if (value.desc !== '') {
                     value.desc = ' <span class="text-secondary small">- ' + value.desc + '</span>';
                 }
 
                 $('#teampass_items_list').append(
-                    '<tr class="list-item-row' + (value.canMove === 1 ? ' is-draggable' : '') + '" id="list-item-row_' + value.item_id + '" data-item-edition="' + value.open_edit + '" data-item-id="' + value.item_id + '" data-item-sk="' + value.sk + '" data-item-expired="' + value.expired + '" data-item-rights="' + value.rights + '" data-item-display="' + value.display + '" data-item-open-edit="' + value.open_edit + '" data-item-tree-id="' + value.tree_id + '" data-is-search-result="' + value.is_result_of_search + '" data-label="' + escape(value.label) + '">' +
+                    '<tr class="list-item-row' + (value.canMove === 1 ? ' is-draggable' : '') + '" id="list-item-row_' + value.item_id + '" data-item-key="' + value.item_key + '" data-item-edition="' + value.open_edit + '" data-item-id="' + value.item_id + '" data-item-sk="' + value.sk + '" data-item-expired="' + value.expired + '" data-item-rights="' + value.rights + '" data-item-display="' + value.display + '" data-item-open-edit="' + value.open_edit + '" data-item-tree-id="' + value.tree_id + '" data-is-search-result="' + value.is_result_of_search + '" data-label="' + escape(value.label) + '">' +
                     '<td class="list-item-description" style="width: 100%;">' +
                     // Show user a grippy bar to move item
                     (value.canMove === 1  ? '<i class="fas fa-ellipsis-v mr-2 dragndrop"></i>' : '') + //&& value.is_result_of_search === 0
                     // Show user a ban icon if expired
                     (value.expired === 1 ? '<i class="far fa-calendar-times mr-2 text-warning infotip" title="<?php echo langHdl('not_allowed_to_see_pw_is_expired'); ?>"></i>' : '') +
@@ -4009,11 +4014,11 @@ $var['hidden_asterisk'] = '<i class="fas fa-asterisk mr-2"></i><i class="fas fa-
                     // Show user that password is badly encrypted
                     (value.pw_status === 'encryption_error' ? '<i class="fas fa-exclamation-triangle fa-xs text-danger infotip mr-1" title="<?php echo langHdl('pw_encryption_error'); ?>"></i>' : '') +
                     // Show item fa_icon if set
                     (value.fa_icon !== '' ? '<i class="'+value.fa_icon+' mr-1"></i>' : '') +
                     // Prepare item info
-                    '<span class="list-item-clicktoshow' + (value.rights === 10 ? '' : ' pointer') + '" data-item-id="' + value.item_id + '">' +
+                    '<span class="list-item-clicktoshow' + (value.rights === 10 ? '' : ' pointer') + '" data-item-id="' + value.item_id + '" data-item-key="' + value.item_key + '">' +
                     '<span class="list-item-row-description' + (value.rights === 10 ? ' font-weight-light' : '') + '">' + value.label + '</span>' + (value.rights === 10 ? '' : value.desc) + '</span>' +
                     '<span class="list-item-actions hidden">' +
                     (value.rights === 10 ?
                         '<span class="fa-stack fa-clickable fa-clickable-access-request pointer infotip mr-2" title="<?php echo langHdl('need_access'); ?>"><i class="fas fa-circle fa-stack-2x text-danger"></i><i class="far fa-handshake fa-stack-1x fa-inverse"></i></span>' :
                         pwd_error + icon_all_can_modify + icon_login + icon_pwd + icon_link + icon_favorite) +
@@ -4025,11 +4030,12 @@ $var['hidden_asterisk'] = '<i class="fas fa-asterisk mr-2"></i><i class="fas fa-
                     '</tr>'
                 );
 
                 // Save id for usage
                 prevIdForNextItem = {
-                    'item_id' : value.item_id,
+                    //'item_id' : value.item_id,
+                    'item_key' : value.item_key,
                     'label': value.label,
                 };
 
                 //---------------------
             }
@@ -4214,25 +4220,30 @@ $var['hidden_asterisk'] = '<i class="fas fa-asterisk mr-2"></i><i class="fas fa-
 
     /**
      *
      */
     function Details(itemDefinition, actionType, hotlink = false) {
-        if (debugJavascript === true) console.info('EXPECTED ACTION on ' + itemDefinition + ' is ' + actionType + ' -- ')
+        if (debugJavascript === true) {
+            console.info('EXPECTED ACTION on ' + itemDefinition + ' is ' + actionType + ' -- ');
+            console.log(itemDefinition);
+        }
 
         // Init
         if (hotlink === false) {
             var itemId = parseInt($(itemDefinition).data('item-id')) || '';
+            var itemKey = parseInt($(itemDefinition).data('item-key')) || '';
             var itemTreeId = parseInt($(itemDefinition).data('item-tree-id')) || '';
             var itemSk = parseInt($(itemDefinition).data('item-sk')) || 0;
             var itemExpired = parseInt($(itemDefinition).data('item-expired')) || '';
             var itemRestricted = parseInt($(itemDefinition).data('item-restricted-id')) || '';
             var itemDisplay = parseInt($(itemDefinition).data('item-display')) || 0;
             var itemOpenEdit = parseInt($(itemDefinition).data('item-open-edit')) || 0;
             var itemReload = parseInt($(itemDefinition).data('item-reload')) || 0;
             var itemRights = parseInt($(itemDefinition).data('item-rights')) || 10;
         } else {
             var itemId = itemDefinition || '';
+            var itemKey = itemDefinition || '';
             var itemTreeId = store.get('teampassApplication').selectedFolder || '';
             var itemSk = 0;
             var itemExpired = '';
             var itemRestricted = '';
             var itemDisplay = 1;
@@ -4883,12 +4894,28 @@ $var['hidden_asterisk'] = '<i class="fas fa-asterisk mr-2"></i><i class="fas fa-
                     $('.but-next-item')
                         .html('<i class="fas fa-arrow-right mr-2"></i>' + unescape($('#list-item-row_'+data.id).next('.list-item-row').attr('data-label')))
                         .attr('data-next-item-id', $('#list-item-row_'+data.id).next('.list-item-row').attr('data-item-id'))
                         .removeClass('hidden');
                 }
+
+                /*
+                dataItemKey = $('[data-item-key="'+data.item_key+'"]');
+                if (dataItemKey.prev('.list-item-row').attr('data-item-key') !== undefined) {
+                    $('.but-prev-item')
+                        .html('<i class="fas fa-arrow-left mr-2"></i>' + unescape(dataItemKey.prev('.list-item-row').attr('data-label')))
+                        .attr('data-prev-item-key', dataItemKey.attr('data-item-key'))
+                        .removeClass('hidden');
+                }
+                if (dataItemKey.next('.list-item-row').attr('data-item-key') !== undefined) {
+                    $('.but-next-item')
+                        .html('<i class="fas fa-arrow-right mr-2"></i>' + unescape(dataItemKey.next('.list-item-row').attr('data-label')))
+                        .attr('data-next-item-id', dataItemKey.next('.list-item-row').attr('data-item-id'))
+                        .removeClass('hidden');
+                }
+                */
                 if (debugJavascript === true) {
-                    console.log("PREV: " + $('#list-item-row_'+data.id).prev('.list-item-row').attr('data-item-id') + " - NEXT: " + $('#list-item-row_'+data.id).next('.list-item-row').attr('data-item-id'));
+                    //console.log("PREV: " + dataItemKey.attr('data-item-key') + " - NEXT: " + $('#list-item-row_'+data.id).next('.list-item-row').attr('data-item-id'));
                 }
 
                 // Inform user
                 toastr.remove();
                 toastr.info(
diff --git a/pages/profile.js.php b/pages/profile.js.php
index c846c2aa8c..e6964df174 100755
--- a/pages/profile.js.php
+++ b/pages/profile.js.php
@@ -163,13 +163,13 @@ if (checkUser($_SESSION['user_id'], $_SESSION['key'], 'profile', $SETTINGS) ===
 
 
     // Save user settings
     $('#profile-user-save-settings').click(function() {
         // Sanitize text fields
-        let formName = fieldSanitizeStep1('#profile-user-name', false, false, false),
-            formLastname = fieldSanitizeStep1('#profile-user-lastname', false, false, false),
-            formEmail = fieldSanitizeStep1('#profile-user-email', false, false, false);
+        let formName = fieldDomPurifier('#profile-user-name', false, false, false),
+            formLastname = fieldDomPurifier('#profile-user-lastname', false, false, false),
+            formEmail = fieldDomPurifier('#profile-user-email', false, false, false);
         if (formName === false || formLastname === false || formEmail === false) {
             // Label is empty
             toastr.remove();
             toastr.warning(
                 'XSS attempt detected. Field has been emptied.',
diff --git a/pages/roles.js.php b/pages/roles.js.php
index 68ea82b645..42aa311e06 100755
--- a/pages/roles.js.php
+++ b/pages/roles.js.php
@@ -472,13 +472,20 @@ if (checkUser($_SESSION['user_id'], $_SESSION['key'], 'profile', $SETTINGS) ===
 
             // Show spinner
             toastr.remove();
             toastr.info('<?php echo langHdl('in_progress'); ?> ... <i class="fas fa-circle-notch fa-spin fa-2x"></i>');
 
+            // Sanitize value
+            value = fieldDomPurifierWithWarning('#form-role-label');
+            if (value === false) {
+                return false;
+            }
+            $('#form-role-label').val(value);
+
             // Prepare data
             var data = {
-                'label': DOMPurify.sanitize($('#form-role-label').val()),
+                'label': value,
                 'complexity': $('#form-complexity-list').val() === null ? 0 : $('#form-complexity-list').val(),
                 'folderId': $('#roles-list').find(':selected').val(),
                 'allowEdit': $('#form-role-privilege').is(":checked") === true ? 1 : 0,
                 'action': store.get('teampassApplication').formUserAction
             }
diff --git a/pages/users.js.php b/pages/users.js.php
index 7b08685a66..365f92ac79 100755
--- a/pages/users.js.php
+++ b/pages/users.js.php
@@ -878,28 +878,10 @@ if (checkUser($_SESSION['user_id'], $_SESSION['key'], 'folders', $SETTINGS) ===
                         'value': $(this).val(),
                     });
                 }
             });
 
-            // Sanitize text fields
-            let formLogin = fieldSanitizeStep1('#form-login', false, false, false),
-                formName = fieldSanitizeStep1('#form-name', false, false, false),
-                formLastname = fieldSanitizeStep1('#form-lastname', false, false, false),
-                formEmail = fieldSanitizeStep1('#form-email', false, false, false);
-            if (formLogin === false || formName === false || formLastname === false || formEmail === false) {
-                // Label is empty
-                toastr.remove();
-                toastr.warning(
-                    'XSS attempt detected. Field has been emptied.',
-                    'Error', {
-                        timeOut: 5000,
-                        progressBar: true
-                    }
-                );
-                return false;
-            }
-
             if (arrayQuery.length > 0) {
                 // Now save
                 // get lists
                 var forbidFld = [],
                     authFld = [],
@@ -973,17 +955,24 @@ if (checkUser($_SESSION['user_id'], $_SESSION['key'], 'folders', $SETTINGS) ===
                             }
                         );
                     }
                 );
 
+                // Sanitize text fields
+                purifyRes = fieldDomPurifierLoop('#form-user .purify');
+                if (purifyRes.purifyStop === true) {
+                    // if purify failed, stop
+                    return false;
+                }
+
                 //prepare data
                 var data = {
                     'user_id': store.get('teampassApplication').formUserId,
-                    'login': formLogin,
-                    'name': formName,
-                    'lastname': formLastname,
-                    'email': formEmail,
+                    'login': purifyRes.arrFields['login'],
+                    'name': purifyRes.arrFields['name'],
+                    'lastname': purifyRes.arrFields['lastname'],
+                    'email': purifyRes.arrFields['email'],
                     'admin': $('#privilege-admin').prop('checked'),
                     'manager': $('#privilege-manager').prop('checked'),
                     'hr': $('#privilege-hr').prop('checked'),
                     'read_only': $('#privilege-ro').prop('checked'),
                     'personal_folder': $('#form-create-personal-folder').prop('checked'),
@@ -1676,11 +1665,11 @@ if (checkUser($_SESSION['user_id'], $_SESSION['key'], 'folders', $SETTINGS) ===
             } else {
                 // Add new role to Teampass
 
                 // Prepare data
                 var data = {
-                    'label': DOMPurify.sanitize($('#ldap-new-role-selection').val()),
+                    'label': simplePurifier($('#ldap-new-role-selection').val()),
                     'complexity': $('#ldap-new-role-complexity').val(),
                     'allowEdit': 0,
                     'action': 'add_role',
                     'folderId' : -1,
                 }
@@ -2271,16 +2260,23 @@ if (checkUser($_SESSION['user_id'], $_SESSION['key'], 'folders', $SETTINGS) ===
         var roles = [];
         $("#ldap-user-roles option:selected").each(function() {
             roles.push($(this).val())
         });
 
+        // Sanitize text fields
+        purifyRes = fieldDomPurifierLoop('#form-user .purify');
+        if (purifyRes.purifyStop === true) {
+            // if purify failed, stop
+            return false;
+        }
+
         // prepare data
         var data = {
-            'login': DOMPurify.sanitize($('.selected-user').data('user-login')),
-            'name': DOMPurify.sanitize($('.selected-user').data('user-name') === '' ? $('#ldap-user-name').val() : $('.selected-user').data('user-name')),
-            'lastname': DOMPurify.sanitize($('.selected-user').data('user-lastname')),
-            'email': DOMPurify.sanitize($('.selected-user').data('user-email')),
+            'login': simplePurifier($('.selected-user').data('user-login')),
+            'name': simplePurifier($('.selected-user').data('user-name') === '' ? $('#ldap-user-name').val() : $('.selected-user').data('user-name')),
+            'lastname': simplePurifier($('.selected-user').data('user-lastname')),
+            'email': simplePurifier($('.selected-user').data('user-email')),
             'roles' : roles,
         };
         if (debugJavascript === true) console.log(data)
 
         $.post(
diff --git a/pages/users.php b/pages/users.php
index 0b92b6f729..1746e5e4b5 100755
--- a/pages/users.php
+++ b/pages/users.php
@@ -269,27 +269,27 @@ foreach ($treeDesc as $t) {
                     <div class="card-body">
                         <div class="row">
                             <div class="col-lg-6">
                                 <div class="form-group">
                                     <label for="form-name"><?php echo langHdl('name'); ?></label>
-                                    <input type="text" class="form-control clear-me required track-change" id="form-name">
+                                    <input type="text" class="form-control clear-me required track-change purify" id="form-name" data-field="name">
                                 </div>
                                 <div class="form-group">
                                     <label for="form-login"><?php echo langHdl('login'); ?></label>
-                                    <input type="text" class="form-control clear-me required build-login track-change" id="form-login">
+                                    <input type="text" class="form-control clear-me required build-login track-change purify" id="form-login" data-field="login">
                                     <input type="hidden" id="form-login-conform" value="0">
                                 </div>
                             </div>
                             
                             <div class="col-lg-6">
                                 <div class="form-group">
                                     <label for="form-lastname"><?php echo langHdl('lastname'); ?></label>
-                                    <input type="text" class="form-control clear-me required track-change" id="form-lastname">
+                                    <input type="text" class="form-control clear-me required track-change purify" id="form-lastname" data-field="lastname">
                                 </div>
                                 <div class="form-group">
                                     <label for="form-login"><?php echo langHdl('email'); ?></label>
-                                    <input type="email" class="form-control clear-me required track-change validate-email" id="form-email">
+                                    <input type="email" class="form-control clear-me required track-change validate-email purify" id="form-email" data-field="email">
                                 </div>
                             </div>
                         </div>
                         <div class="form-group">
                             <label for="form-login" class="mr-2"><?php echo langHdl('privileges'); ?></label>
@@ -340,11 +340,11 @@ foreach ($treeDesc as $t) {
                             <label class="form-check-label mr-2" for="form-create-personal-folder"><?php echo langHdl('enable_personal_folder_for_this_user'); ?></label>
                         </div>
                         <div class="form-group not-for-admin" id="group-create-special-folder">
                             <input type="checkbox" class="form-check-input form-control flat-blue track-change" id="form-create-special-folder">
                             <label class="form-check-label mr-2" for="form-create-special-folder"><?php echo langHdl('auto_create_folder_role'); ?></label>
-                            <input type="text" class="form-control clear-me mt-1" id="form-special-folder" disabled="true" placeholder="<?php echo langHdl('label'); ?>">
+                            <input type="text" class="form-control clear-me mt-1 purify" id="form-special-folder" data-field="" disabled="true" placeholder="<?php echo langHdl('label'); ?>">
                         </div>
                         <div class="form-group not-for-admin" id="form-create-mfa-enabled-div">
                             <input type="checkbox" class="form-check-input form-control flat-blue track-change" id="form-create-mfa-enabled">
                             <label class="form-check-label mr-2" for="form-create-mfa-enabled"><?php echo langHdl('mfa_enabled'); ?></label>
                         </div>
diff --git a/sources/admin.queries.php b/sources/admin.queries.php
index e59771d15e..1e8ca57f67 100755
--- a/sources/admin.queries.php
+++ b/sources/admin.queries.php
@@ -1872,24 +1872,24 @@ switch ($post_type) {
             );
 
             $post_id = DB::insertId();
             // Update existing key
         } elseif (null !== $post_action && $post_action === 'update') {
-            $post_id = filter_var($dataReceived['id'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
+            $post_id = filter_var($dataReceived['id'], FILTER_SANITIZE_NUMBER_INT);
 
             DB::update(
                 prefixTable('api'),
                 array(
                     'label' => $post_label,
                     'timestamp' => $timestamp,
                 ),
-                'id=%i',
+                'increment_id=%i',
                 $post_id
             );
             // Delete existing key
         } elseif (null !== $post_action && $post_action === 'delete') {
-            $post_id = filter_var($dataReceived['id'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
+            $post_id = filter_var($dataReceived['id'], FILTER_SANITIZE_NUMBER_INT);
 
             DB::query(
                 'DELETE FROM ' . prefixTable('api') . ' WHERE increment_id = %i',
                 $post_id
             );

Vulnerable code: function fieldSanitizeStep1(
    field,
    bHtml=true,
    bSvg=true,
    bSvgFilters=true,
    text=''
    if (field === undefined ||field === '') {
        return false;
    }
    let string = '';
    text = (text === '') ? $(field).val() : text;
/*
    // Sanitize string
    var tagsToReplace = {
        '&': '&amp;',
        '<': '&lt;',
        '>': '&gt;',
        "'" : '&#39;',
        '"' : '&quot;'
    };
    text = text.replace(/[&<>'"]/g, function(tag) {
        return tagsToReplace[tag] || tag;
    });
    */
    // Purify string
    string = DOMPurify.sanitize(
            'label': $('#new_api_key_label').val(),
            label = $(this).prev('input').val(),

            'label': $('#new_api_ip_label').val(),
            'value': label,
                                            <input type="text" placeholder="<?php echo langHdl('label'); ?>" class="col-4 form-control form-control-sm" id="new_api_key_label">
                                <div class="form-group mt-4">
                                            <input type="text" placeholder="<?php echo langHdl('ip'); ?>" class="col-4 form-control" id="new_api_ip_value" data-inputmask="'alias': 'ip'" data-mask>
                                            <input type="text" placeholder="<?php echo langHdl('label'); ?>" class="col-4 form-control ml-2" id="new_api_ip_label">
                    'encryptionKey': $('#onthefly-backup-key').val(),
                                "value": $('#onthefly-backup-key').val(),
                    'encryptionKey': $('#onthefly-restore-key').val(),

    $(document).on('click', '#button-duo-save', function() {
        // Prepare data
        var data = {
            'akey': $('#duo_akey').val(),
            'ikey': $('#duo_ikey').val(),
            'skey': $('#duo_skey').val(),
            'host': $('#duo_host').val(),
        }
        console.log(data);

        // Launch action
        $.post(
            'sources/admin.queries.php', {
                type: 'save_duo_in_sk_file',
                data: prepareExchangedData(JSON.stringify(data), "encode", "<?php echo $_SESSION['key']; ?>"),
                key: '<?php echo $_SESSION['key']; ?>'
            },
            function(data) {
                //decrypt data
                data = decodeQueryReturn(data, '<?php echo $_SESSION['key']; ?>');

                if (data.error === true) {
                    // ERROR
                    toastr.remove();
                    toastr.warning(
                        '<?php echo langHdl('none_selected_text'); ?>',
                        '', {
                            timeOut: 5000,
                            progressBar: true
                        }
                    );
                } else {
                    // Inform user
                    toastr.remove();
                    toastr.success(
                        '<?php echo langHdl('done'); ?>',
                        '', {
                            timeOut: 1000
                        }
                    );
                }
            }
        );
    });


                'title': DOMPurify.sanitize($('#new-title').val()),
                'renewalPeriod': DOMPurify.sanitize($('#new-renewal').val() === '' ? 0 : parseInt($('#new-renewal').val())),
                'icon': DOMPurify.sanitize($('#new-folder-add-icon').val(), {USE_PROFILES: {html: true}}),
                'iconSelected': DOMPurify.sanitize($('#new-folder-add-icon-selected').val(), {USE_PROFILES: {html: true}}),
            '<input type="text" class="form-control clear-me" id="folder-edit-title" value="' + folderTitle + '">' +
            '<input type="text" class="form-control form-folder-control" id="folder-edit-icon" value="'+folderIcon+'">' +
            '<input type="text" class="form-control form-folder-control" id="folder-edit-icon-selected" value="'+folderIconSelection+'">' +
                'title': DOMPurify.sanitize($('#folder-edit-title').val()),
                'renewalPeriod': DOMPurify.sanitize($('#folder-edit-renewal').val()),
                'icon': DOMPurify.sanitize($('#folder-edit-icon').val(), {USE_PROFILES: {html: true}}),
                'iconSelected': DOMPurify.sanitize($('#folder-edit-icon-selected').val(), {USE_PROFILES: {html: true}})
                                    <input type="text" class="form-control clear-me" id="new-title">
                                    <input type="text" class="form-control form-folder-control" id="new-folder-add-icon">
                                    <input type="text" class="form-control form-folder-control" id="new-folder-add-icon-selected">
</div>
        let formLabel = fieldSanitizeStep1('#form-folder-add-label', false, false, false),
            formIcon = fieldSanitizeStep1('#form-folder-add-icon', false, false, false),
            formIconSelected = fieldSanitizeStep1('#form-folder-add-icon-selected', false, false, false);
            let formLabel = fieldSanitizeStep1('#form-item-label', false, false, false),
                formDescription = $('#form-item-description').summernote('code') !== "<p><br></p>" ? fieldSanitizeStep1('#form-item-description', true, false, false, $('#form-item-description').summernote('code')) : '',
                formEmail = fieldSanitizeStep1('#form-item-email'),
                formTags = fieldSanitizeStep1('#form-item-tags'),
                formUrl = fieldSanitizeStep1('#form-item-url'),
                formIcon = fieldSanitizeStep1('#form-item-icon');
                    $('#list-item-row_' + value.item_id).attr('data-next-item-id', prevIdForNextItem.item_id);
                    $('#list-item-row_' + value.item_id).attr('data-next-item-label', value.label);
                        icon_favorite = '<span class="fa-stack fa-clickable item-favourite pointer infotip mr-2" title="<?php echo langHdl('unfavorite'); ?>" data-item-id="' + value.item_id + '" data-item-favourited="1"><i class="fas fa-circle fa-stack-2x"></i><i class="fas fa-star fa-stack-1x fa-inverse text-warning"></i></span>';
                        icon_favorite = '<span class="fa-stack fa-clickable item-favourite pointer infotip mr-2" title="<?php echo langHdl('favorite'); ?>" data-item-id="' + value.item_id + '" data-item-favourited="0"><i class="fas fa-circle fa-stack-2x"></i><i class="far fa-star fa-stack-1x fa-inverse"></i></span>';
                    '<tr class="list-item-row' + (value.canMove === 1 ? ' is-draggable' : '') + '" id="list-item-row_' + value.item_id + '" data-item-edition="' + value.open_edit + '" data-item-id="' + value.item_id + '" data-item-sk="' + value.sk + '" data-item-expired="' + value.expired + '" data-item-rights="' + value.rights + '" data-item-display="' + value.display + '" data-item-open-edit="' + value.open_edit + '" data-item-tree-id="' + value.tree_id + '" data-is-search-result="' + value.is_result_of_search + '" data-label="' + escape(value.label) + '">' +
                    '<span class="list-item-clicktoshow' + (value.rights === 10 ? '' : ' pointer') + '" data-item-id="' + value.item_id + '">' +
                    'item_id' : value.item_id,
        if (debugJavascript === true) console.info('EXPECTED ACTION on ' + itemDefinition + ' is ' + actionType + ' -- ')
                    console.log("PREV: " + $('#list-item-row_'+data.id).prev('.list-item-row').attr('data-item-id') + " - NEXT: " + $('#list-item-row_'+data.id).next('.list-item-row').attr('data-item-id'));
        let formName = fieldSanitizeStep1('#profile-user-name', false, false, false),
            formLastname = fieldSanitizeStep1('#profile-user-lastname', false, false, false),
            formEmail = fieldSanitizeStep1('#profile-user-email', false, false, false);
                'label': DOMPurify.sanitize($('#form-role-label').val()),
            // Sanitize text fields
            let formLogin = fieldSanitizeStep1('#form-login', false, false, false),
                formName = fieldSanitizeStep1('#form-name', false, false, false),
                formLastname = fieldSanitizeStep1('#form-lastname', false, false, false),
                formEmail = fieldSanitizeStep1('#form-email', false, false, false);
            if (formLogin === false || formName === false || formLastname === false || formEmail === false) {
                // Label is empty
                toastr.remove();
                toastr.warning(
                    'XSS attempt detected. Field has been emptied.',
                    'Error', {
                        timeOut: 5000,
                        progressBar: true
                    }
                );
                return false;
            }

                    'login': formLogin,
                    'name': formName,
                    'lastname': formLastname,
                    'email': formEmail,
                    'label': DOMPurify.sanitize($('#ldap-new-role-selection').val()),
            'login': DOMPurify.sanitize($('.selected-user').data('user-login')),
            'name': DOMPurify.sanitize($('.selected-user').data('user-name') === '' ? $('#ldap-user-name').val() : $('.selected-user').data('user-name')),
            'lastname': DOMPurify.sanitize($('.selected-user').data('user-lastname')),
            'email': DOMPurify.sanitize($('.selected-user').data('user-email')),
                                    <input type="text" class="form-control clear-me required track-change" id="form-name">
                                    <input type="text" class="form-control clear-me required build-login track-change" id="form-login">
                                    <input type="text" class="form-control clear-me required track-change" id="form-lastname">
                                    <input type="email" class="form-control clear-me required track-change validate-email" id="form-email">
                            <input type="text" class="form-control clear-me mt-1" id="form-special-folder" disabled="true" placeholder="<?php echo langHdl('label'); ?>">
            $post_id = filter_var($dataReceived['id'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
                'id=%i',
            $post_id = filter_var($dataReceived['id'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
Fixed code: function simplePurifier(
    text,
    bHtml = false,
    bSvg = false,
    bSvgFilters = false
    return DOMPurify.sanitize(
}

/**
 * Permits to purify the content of a string using domPurify
 * @param {*} field 
 * @param {*} bHtml 
 * @param {*} bSvg 
 * @param {*} bSvgFilters 
 * @param {*} text 
 * @returns bool||string
 */
function fieldDomPurifier(
    field,
    bHtml = false,
    bSvg = false,
    bSvgFilters = false,
    text = ''
)
{
    if (field === undefined ||field === '') {
        return false;
    }
    let string = '';
    text = (text === '') ? $(field).val() : text;

    // Purify string
    string = simplePurifier(text, bHtml, bSvg, bSvgFilters);
    return string;
}

/**
 * Permits to get all fields of a class and purify them
 * @param {*} elementClass 
 * @returns array
 */
function fieldDomPurifierLoop(elementClass)
{
    let purifyStop = false,
        arrFields = [];
    $.each($(elementClass), function(index, element) {
        purifiedField = fieldDomPurifier(
            '#' + $(element).attr('id'), 
            $(element).hasClass('purifyHtml') === true ? true : false,
            $(element).hasClass('purifySvg') === true ? true : false,
            $(element).hasClass('purifySvgFilter') === true ? true : false,
            typeof $(element).data('purify-text') !== undefined ? $(element).data('purify-text') : ''
        );

        if (purifiedField === false) {
            // Label is empty
            toastr.remove();
            toastr.warning(
                'XSS attempt detected. Please remove all special characters from your input.',
                'Error', {
                    timeOut: 5000,
                    progressBar: true
                }
            );
            $('#' + $(element).attr('id')).focus();
            purifyStop = true;
            return {
                'purifyStop' : purifyStop,
                'arrFields' : arrFields
            };
        } else {
            $(element).val(purifiedField);
            arrFields[$(element).data('field')] = purifiedField;
        }
    });
    
    // return
    return {
        'purifyStop' : purifyStop,
        'arrFields' : arrFields
    };
}

/**
 * Permits to purify the content of a string using domPurify
 * @param {*} field 
 * @param {*} bHtml 
 * @param {*} bSvg 
 * @param {*} bSvgFilters 
 * @returns bool||string
 */
function fieldDomPurifierWithWarning(
    field,
    bHtml = false,
    bSvg = false,
    bSvgFilters = false,
)
{
    if (field === undefined || field === '') {
        return false;
    }
    if ($(field).val() === '') {
        return '';
    }
    let string = '';

    // Purify string
    string = simplePurifier($(field).val(), bHtml, bSvg, bSvgFilters);
    
    // Clear field if string is empty and warn user
    if (string === '') {
        toastr.remove();
        toastr.warning(
            'XSS attempt detected. Please remove all special characters from your input.',
            'Error', {
                timeOut: 5000,
                progressBar: true
            }
        );
        $(field).focus();
        return false;
    }

        // Sanitize value
        value = fieldDomPurifierWithWarning('#' + field);
        if (value === false) {
            return false;
        }
        $('#' + field).val(value);

        
        // Sanitize text fields
        purifyRes = fieldDomPurifierLoop('#new_api_key_label');
        if (purifyRes.purifyStop === true) {
            // if purify failed, stop
            return false;
        }
            'label': purifyRes.arrFields['label'], //$('#new_api_key_label').val(),
            label = simplePurifier($(this).prev('input').val()),
        
        // Sanitize text fields
        purifyRes = fieldDomPurifierLoop('#new-api-ip .purify');
        if (purifyRes.purifyStop === true) {
            // if purify failed, stop
            return false;
        }

            'label': purifyRes.arrFields['label'],
            'value': simplePurifier(label),
                                            <input type="text" placeholder="<?php echo langHdl('label'); ?>" class="col-4 form-control form-control-sm purify" id="new_api_key_label" data-field="label">
                                <div class="form-group mt-4" id="new-api-ip">
                                            <input type="text" placeholder="<?php echo langHdl('ip'); ?>" class="col-4 form-control" id="new_api_ip_value" data-inputmask="'alias': 'ip'">
                                            <input type="text" placeholder="<?php echo langHdl('label'); ?>" class="col-4 form-control ml-2 purify" id="new_api_ip_label" data-field="label">
                    'encryptionKey': simplePurifier($('#onthefly-backup-key').val()),
                                "value": simplePurifier($('#onthefly-backup-key').val()),
                    'encryptionKey': simplePurifier($('#onthefly-restore-key').val()),

            // Sanitize text fields
            purifyRes = fieldDomPurifierLoop('#folder-new .purify');
            if (purifyRes.purifyStop === true) {
                // if purify failed, stop
                return false;
            }

                'title': purifyRes.arrFields['title'],
                'renewalPeriod': $('#new-renewal').val() === '' ? 0 : parseInt($('#new-renewal').val()),
                'icon': purifyRes.arrFields['icon'],
                'iconSelected': purifyRes.arrFields['iconSelected'],
            '<input type="text" class="form-control clear-me purify" id="folder-edit-title" data-field="title" value="' + folderTitle + '">' +
            '<input type="text" class="form-control form-folder-control purify" id="folder-edit-icon" data-field="icon" value="'+folderIcon+'">' +
            '<input type="text" class="form-control form-folder-control purify" id="folder-edit-icon-selected" data-field="iconSelected" value="'+folderIconSelection+'">' +
            // Sanitize text fields
            purifyRes = fieldDomPurifierLoop('#table-folders .purify');
            if (purifyRes.purifyStop === true) {
                // if purify failed, stop
                return false;
            }

                'title': purifyRes.arrFields['title'],    //$('#folder-edit-title').val(),
                'renewalPeriod': $('#new-renewal').val() === '' ? 0 : parseInt($('#new-renewal').val()),
                'icon': purifyRes.arrFields['icon'],    //$('#folder-edit-icon').val(),
                'iconSelected': purifyRes.arrFields['iconSelected'],    //$('#folder-edit-icon-selected').val(),
                                    <input type="text" class="form-control clear-me purify" id="new-title" data-field="title">
                                    <input type="text" class="form-control form-folder-control purify" id="new-folder-add-icon" data-field="icon">
                                    <input type="text" class="form-control form-folder-control purify" id="new-folder-add-icon-selected" data-field="iconSelected">
                                </div>
        let formLabel = fieldDomPurifier('#form-folder-add-label', false, false, false),
            formIcon = fieldDomPurifier('#form-folder-add-icon', false, false, false),
            formIconSelected = fieldDomPurifier('#form-folder-add-icon-selected', false, false, false);
                //$(this).hasClass('but-prev-item') === true ? $('#list-item-row_' + $(this).attr('data-prev-item-key')) : $('#list-item-row_' + $(this).attr('data-next-item-key')),
            let formLabel = fieldDomPurifier('#form-item-label', false, false, false),
                formDescription = $('#form-item-description').summernote('code') !== "<p><br></p>" ? fieldDomPurifier('#form-item-description', true, false, false, $('#form-item-description').summernote('code')) : '',
                formEmail = fieldDomPurifier('#form-item-email'),
                formTags = fieldDomPurifier('#form-item-tags'),
                formUrl = fieldDomPurifier('#form-item-url'),
                formIcon = fieldDomPurifier('#form-item-icon');
                    //$('#list-item-row_' + value.item_id).attr('data-next-item-id', prevIdForNextItem.item_id);
                    //$('#list-item-row_' + value.item_id).attr('data-next-item-label', value.label);
                    $('[data-item-key="'+value.item_key+'"]')
                        //.attr('data-next-item-id', prevIdForNextItem.item_id)
                        .attr('data-next-item-key', prevIdForNextItem.item_key)
                        .attr('data-next-item-label', value.label);
                        icon_favorite = '<span class="fa-stack fa-clickable item-favourite pointer infotip mr-2" title="<?php echo langHdl('unfavorite'); ?>" data-item-id="' + value.item_id + '" data-item-key="' + value.item_key + '" data-item-favourited="1"><i class="fas fa-circle fa-stack-2x"></i><i class="fas fa-star fa-stack-1x fa-inverse text-warning"></i></span>';
                        icon_favorite = '<span class="fa-stack fa-clickable item-favourite pointer infotip mr-2" title="<?php echo langHdl('favorite'); ?>" data-item-id="' + value.item_id + '" data-item-key="' + value.item_key + '" data-item-favourited="0"><i class="fas fa-circle fa-stack-2x"></i><i class="far fa-star fa-stack-1x fa-inverse"></i></span>';
                    '<tr class="list-item-row' + (value.canMove === 1 ? ' is-draggable' : '') + '" id="list-item-row_' + value.item_id + '" data-item-key="' + value.item_key + '" data-item-edition="' + value.open_edit + '" data-item-id="' + value.item_id + '" data-item-sk="' + value.sk + '" data-item-expired="' + value.expired + '" data-item-rights="' + value.rights + '" data-item-display="' + value.display + '" data-item-open-edit="' + value.open_edit + '" data-item-tree-id="' + value.tree_id + '" data-is-search-result="' + value.is_result_of_search + '" data-label="' + escape(value.label) + '">' +
                    '<span class="list-item-clicktoshow' + (value.rights === 10 ? '' : ' pointer') + '" data-item-id="' + value.item_id + '" data-item-key="' + value.item_key + '">' +
                    //'item_id' : value.item_id,
                    'item_key' : value.item_key,
        if (debugJavascript === true) {
            console.info('EXPECTED ACTION on ' + itemDefinition + ' is ' + actionType + ' -- ');
            console.log(itemDefinition);
        }
            var itemKey = parseInt($(itemDefinition).data('item-key')) || '';
            var itemKey = itemDefinition || '';

                /*
                dataItemKey = $('[data-item-key="'+data.item_key+'"]');
                if (dataItemKey.prev('.list-item-row').attr('data-item-key') !== undefined) {
                    $('.but-prev-item')
                        .html('<i class="fas fa-arrow-left mr-2"></i>' + unescape(dataItemKey.prev('.list-item-row').attr('data-label')))
                        .attr('data-prev-item-key', dataItemKey.attr('data-item-key'))
                        .removeClass('hidden');
                }
                if (dataItemKey.next('.list-item-row').attr('data-item-key') !== undefined) {
                    $('.but-next-item')
                        .html('<i class="fas fa-arrow-right mr-2"></i>' + unescape(dataItemKey.next('.list-item-row').attr('data-label')))
                        .attr('data-next-item-id', dataItemKey.next('.list-item-row').attr('data-item-id'))
                        .removeClass('hidden');
                }
                */
                    //console.log("PREV: " + dataItemKey.attr('data-item-key') + " - NEXT: " + $('#list-item-row_'+data.id).next('.list-item-row').attr('data-item-id'));
        let formName = fieldDomPurifier('#profile-user-name', false, false, false),
            formLastname = fieldDomPurifier('#profile-user-lastname', false, false, false),
            formEmail = fieldDomPurifier('#profile-user-email', false, false, false);
            // Sanitize value
            value = fieldDomPurifierWithWarning('#form-role-label');
            if (value === false) {
                return false;
            }
            $('#form-role-label').val(value);

                'label': value,
                // Sanitize text fields
                purifyRes = fieldDomPurifierLoop('#form-user .purify');
                if (purifyRes.purifyStop === true) {
                    // if purify failed, stop
                    return false;
                }

                    'login': purifyRes.arrFields['login'],
                    'name': purifyRes.arrFields['name'],
                    'lastname': purifyRes.arrFields['lastname'],
                    'email': purifyRes.arrFields['email'],
                    'label': simplePurifier($('#ldap-new-role-selection').val()),
        // Sanitize text fields
        purifyRes = fieldDomPurifierLoop('#form-user .purify');
        if (purifyRes.purifyStop === true) {
            // if purify failed, stop
            return false;
        }

            'login': simplePurifier($('.selected-user').data('user-login')),
            'name': simplePurifier($('.selected-user').data('user-name') === '' ? $('#ldap-user-name').val() : $('.selected-user').data('user-name')),
            'lastname': simplePurifier($('.selected-user').data('user-lastname')),
            'email': simplePurifier($('.selected-user').data('user-email')),
                                    <input type="text" class="form-control clear-me required track-change purify" id="form-name" data-field="name">
                                    <input type="text" class="form-control clear-me required build-login track-change purify" id="form-login" data-field="login">
                                    <input type="text" class="form-control clear-me required track-change purify" id="form-lastname" data-field="lastname">
                                    <input type="email" class="form-control clear-me required track-change validate-email purify" id="form-email" data-field="email">
                            <input type="text" class="form-control clear-me mt-1 purify" id="form-special-folder" data-field="" disabled="true" placeholder="<?php echo langHdl('label'); ?>">
            $post_id = filter_var($dataReceived['id'], FILTER_SANITIZE_NUMBER_INT);
                'increment_id=%i',
            $post_id = filter_var($dataReceived['id'], FILTER_SANITIZE_NUMBER_INT);