CVE-2023-3190 (published 2023-06-10T09:15Z)

Improper Encoding or Escaping of Output in GitHub repository nilsteampassnet/teampass prior to 3.0.9.

CVSS v3 base score: 4.6, MEDIUM (CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:L/I:L/A:N)

Weakness information:
cwe_id: CWE-116
cwe_name: Improper Encoding or Escaping of Output
description: The product prepares a structured message for communication with another component, but encoding or escaping of the data is either missing or done incorrectly. As a result, the intended structure of the message is not preserved.
extended_description: 
            Improper encoding or escaping can allow attackers to change the commands that are sent to another component, inserting malicious commands instead.
            Most products follow a certain protocol that uses structured messages for communication between components, such as queries or commands. These structured messages can contain raw data interspersed with metadata or control information. For example, "GET /index.html HTTP/1.1" is a structured message containing a command ("GET") with a single argument ("/index.html") and metadata about which protocol version is being used ("HTTP/1.1").
            If an application uses attacker-supplied inputs to construct a structured message without properly encoding or escaping, then the attacker could insert special characters that will cause the data to be interpreted as control information or metadata. Consequently, the component that receives the output will perform the wrong operations, or otherwise interpret the data incorrectly.
         
         

Fix information:
repo_url: https://github.com/nilsteampassnet/teampass
commit_details: repo_url: https://github.com/nilsteampassnet/teampass
author: nilsteampassnet
author_date: 2023-06-10 09:30:21+02:00
msg: 3.0.9

Fix xss vulnerabilities in several text inputs
repository: repo_url: https://github.com/nilsteampassnet/teampass
repo_name: nilsteampassnet/TeamPass
description: Collaborative Passwords Manager
repo_language: PHP
owner: nilsteampassnet
file_changes: filename: functions.js
change_type: ModificationType.MODIFY
diff: @@ -390,34 +390,14 @@ if (typeof String.prototype.utf8Decode == 'undefined') {
     };
 }
 
-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;', '>')
@@ -426,6 +406,33 @@ function fieldSanitizeStep1(
             .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 !== '') {
@@ -433,5 +440,95 @@ function fieldSanitizeStep1(
         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

programming_language: JavaScript
filename: admin.js.php
change_type: ModificationType.MODIFY
diff: @@ -134,6 +134,13 @@ declare(strict_types=1);
             return false;
         }
 
+        // Sanitize value
+        value = fieldDomPurifierWithWarning('#' + field);
+        if (value === false) {
+            return false;
+        }
+        $('#' + field).val(value);
+
         requestRunning = true;
 
         var data = {

programming_language: PHP
filename: api.js.php
change_type: ModificationType.MODIFY
diff: @@ -65,10 +65,17 @@ if (checkUser($_SESSION['user_id'], $_SESSION['key'], 'api', $SETTINGS) === fals
         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',
         }
 
@@ -196,7 +203,7 @@ if (checkUser($_SESSION['user_id'], $_SESSION['key'], 'api', $SETTINGS) === fals
 
     $(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
@@ -205,7 +212,7 @@ if (checkUser($_SESSION['user_id'], $_SESSION['key'], 'api', $SETTINGS) === fals
             'label': label,
             'action': 'update',
         }
-
+        
         // Launch action
         $.post(
             'sources/admin.queries.php', {
@@ -258,9 +265,16 @@ 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',
         }
@@ -398,7 +412,7 @@ if (checkUser($_SESSION['user_id'], $_SESSION['key'], 'api', $SETTINGS) === fals
         // Prepare data
         var data = {
             'id': ipId,
-            'value': label,
+            'value': simplePurifier(label),
             'field': field,
             'action': 'update',
         }

programming_language: PHP
filename: api.php
change_type: ModificationType.MODIFY
diff: @@ -161,7 +161,7 @@ require_once $SETTINGS['cpassman_dir'] . '/sources/main.functions.php';
                                         <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>
@@ -211,13 +211,13 @@ require_once $SETTINGS['cpassman_dir'] . '/sources/main.functions.php';
                                     </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>

programming_language: PHP
filename: backups.js.php
change_type: ModificationType.MODIFY
diff: @@ -96,7 +96,7 @@ if (checkUser($_SESSION['user_id'], $_SESSION['key'], '2fa', $SETTINGS) === fals
 
                 // Prepare data
                 var data = {
-                    'encryptionKey': $('#onthefly-backup-key').val(),
+                    'encryptionKey': simplePurifier($('#onthefly-backup-key').val()),
                 };
 
                 //send query
@@ -125,7 +125,7 @@ if (checkUser($_SESSION['user_id'], $_SESSION['key'], '2fa', $SETTINGS) === fals
                             // Store KEY in DB
                             var newData = {
                                 "field": 'bck_script_passkey',
-                                "value": $('#onthefly-backup-key').val(),
+                                "value": simplePurifier($('#onthefly-backup-key').val()),
                             }
 
                             $.post(
@@ -193,7 +193,7 @@ if (checkUser($_SESSION['user_id'], $_SESSION['key'], '2fa', $SETTINGS) === fals
 
                 // 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);

programming_language: PHP
filename: emails.js.php
change_type: ModificationType.MODIFY
diff: @@ -145,52 +145,5 @@ 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>

programming_language: PHP
filename: folders.js.php
change_type: ModificationType.MODIFY
diff: @@ -96,17 +96,25 @@ if (checkUser($_SESSION['user_id'], $_SESSION['key'], 'folders', $SETTINGS) ===
 
         } 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
@@ -647,7 +655,7 @@ if (checkUser($_SESSION['user_id'], $_SESSION['key'], 'folders', $SETTINGS) ===
             '<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>' +
@@ -663,14 +671,14 @@ if (checkUser($_SESSION['user_id'], $_SESSION['key'], 'folders', $SETTINGS) ===
             '</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>' +
@@ -738,17 +746,24 @@ if (checkUser($_SESSION['user_id'], $_SESSION['key'], 'folders', $SETTINGS) ===
                 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

programming_language: PHP
filename: folders.php
change_type: ModificationType.MODIFY
diff: @@ -143,7 +143,7 @@ foreach ($tst as $t) {
                             <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>
@@ -171,14 +171,14 @@ foreach ($tst as $t) {
                                 </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>
@@ -193,7 +193,7 @@ foreach ($tst as $t) {
                                         <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 -->
 

programming_language: PHP
filename: items.js.php
change_type: ModificationType.MODIFY
diff: @@ -1680,9 +1680,9 @@ $var['hidden_asterisk'] = '<i class="fas fa-asterisk mr-2"></i><i class="fas fa-
         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();
@@ -2115,6 +2115,7 @@ $var['hidden_asterisk'] = '<i class="fas fa-asterisk mr-2"></i><i class="fas fa-
 
             // 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'
             );
@@ -2677,12 +2678,12 @@ $var['hidden_asterisk'] = '<i class="fas fa-asterisk mr-2"></i><i class="fas fa-
             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();
@@ -3948,8 +3949,12 @@ $var['hidden_asterisk'] = '<i class="fas fa-asterisk mr-2"></i><i class="fas fa-
                     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
@@ -3986,9 +3991,9 @@ $var['hidden_asterisk'] = '<i class="fas fa-asterisk mr-2"></i><i class="fas fa-
                     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>';
                     }
                 }
 
@@ -3998,7 +4003,7 @@ $var['hidden_asterisk'] = '<i class="fas fa-asterisk mr-2"></i><i class="fas fa-
                 }
 
                 $('#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
@@ -4011,7 +4016,7 @@ $var['hidden_asterisk'] = '<i class="fas fa-asterisk mr-2"></i><i class="fas fa-
                     // 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 ?
@@ -4027,7 +4032,8 @@ $var['hidden_asterisk'] = '<i class="fas fa-asterisk mr-2"></i><i class="fas fa-
 
                 // Save id for usage
                 prevIdForNextItem = {
-                    'item_id' : value.item_id,
+                    //'item_id' : value.item_id,
+                    'item_key' : value.item_key,
                     'label': value.label,
                 };
 
@@ -4216,11 +4222,15 @@ $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')) || '';
@@ -4231,6 +4241,7 @@ $var['hidden_asterisk'] = '<i class="fas fa-asterisk mr-2"></i><i class="fas fa-
             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 = '';
@@ -4885,8 +4896,24 @@ $var['hidden_asterisk'] = '<i class="fas fa-asterisk mr-2"></i><i class="fas fa-
                         .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

programming_language: JavaScript
filename: profile.js.php
change_type: ModificationType.MODIFY
diff: @@ -165,9 +165,9 @@ 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();

programming_language: PHP
filename: roles.js.php
change_type: ModificationType.MODIFY
diff: @@ -474,9 +474,16 @@ if (checkUser($_SESSION['user_id'], $_SESSION['key'], 'profile', $SETTINGS) ===
             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,

programming_language: PHP
filename: users.js.php
change_type: ModificationType.MODIFY
diff: @@ -880,24 +880,6 @@ if (checkUser($_SESSION['user_id'], $_SESSION['key'], 'folders', $SETTINGS) ===
                 }
             });
 
-            // 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
@@ -975,13 +957,20 @@ 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'),
@@ -1678,7 +1667,7 @@ if (checkUser($_SESSION['user_id'], $_SESSION['key'], 'folders', $SETTINGS) ===
 
                 // 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',
@@ -2273,12 +2262,19 @@ if (checkUser($_SESSION['user_id'], $_SESSION['key'], 'folders', $SETTINGS) ===
             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)

programming_language: PHP
filename: users.php
change_type: ModificationType.MODIFY
diff: @@ -271,11 +271,11 @@ foreach ($treeDesc as $t) {
                             <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>
@@ -283,11 +283,11 @@ foreach ($treeDesc as $t) {
                             <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>
@@ -342,7 +342,7 @@ foreach ($treeDesc as $t) {
                         <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">

programming_language: PHP
filename: admin.queries.php
change_type: ModificationType.MODIFY
diff: @@ -1874,7 +1874,7 @@ 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'),
@@ -1882,12 +1882,12 @@ switch ($post_type) {
                     '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',

programming_language: PHP