Cross-site Scripting (XSS) - Stored in livehelperchat/livehelperchat
Description
livehelperchat is vulnerable to Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Live Helper Chat before a specific commit is vulnerable to stored XSS via user account fields that are not properly neutralized.
Vulnerability
Live Helper Chat (LHC), an open-source live support chat application, is vulnerable to stored Cross-Site Scripting (XSS) in user account fields due to improper neutralization of user-supplied input. The vulnerability exists in versions prior to the commit 55b1e3b. The affected fields include Email and possibly others, as the fix adds the ng-non-bindable attribute and htmlspecialchars() to prevent AngularJS template injection and HTML script execution [1], [2], [3].
Exploitation
An attacker who can modify user account fields, such as the Email address, can inject malicious JavaScript code. The injection occurs when the crafted input is rendered in a context where AngularJS evaluates the content. The attacker must have access to edit user account details, which can be achieved by compromising an operator account or, in some configurations, by a user with sufficient privileges [2], [4]. The injected script then executes when the page is rendered in an AngularJS environment, requiring no specific user interaction besides viewing the compromised profile or administration panel.
Impact
Successful exploitation allows the attacker to execute arbitrary JavaScript in the context of the victim's browser session. This can lead to session hijacking, credential theft, defacement of the Live Helper Chat interface, or redirecting users to malicious sites. The attack can potentially compromise all operators and administrators who view the affected user account fields, leading to a full takeover of the chat system [4].
Mitigation
The fix was implemented in commit 55b1e3b on an unspecified date before 2021-12-17 [2]. The mitigation adds the ng-non-bindable attribute to prevent AngularJS from evaluating the field content and applies htmlspecialchars() to escape HTML entities. Users should update their Live Helper Chat installation to the latest version that includes this commit or later. There is no known workaround for unpatched versions besides restricting write access to user account fields to trusted operators only [4].
- GitHub - LiveHelperChat/livehelperchat: Live Helper Chat - live support for your website. Featuring web and mobile apps, Voice & Video & ScreenShare. Supports Telegram, Twilio (whatsapp), Facebook messenger including building a bot.
- ng-non-bindable for user account fields · LiveHelperChat/livehelperchat@55b1e3b
- NVD - CVE-2021-4132
- The world’s first bug bounty platform for AI/ML
AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
remdex/livehelperchatPackagist | < 3.91 | 3.91 |
Affected products
3- osv-coords2 versions
< 3.90.0+ 1 more
- (no CPE)range: < 3.90.0
- (no CPE)range: < 3.91
- livehelperchat/livehelperchat/livehelperchatv5Range: unspecified
Patches
155b1e3bf62c5ng-non-bindable for user account fields
4 files changed · +37 −37
lhc_web/design/defaulttheme/tpl/lhuser/edit.tpl.php+25 −25 modified@@ -47,14 +47,14 @@ <br /> <form action="<?php echo erLhcoreClassDesign::baseurl('user/edit')?>/<?php echo $user->id?>#account" method="post" autocomplete="off" enctype="multipart/form-data"> - + <?php include(erLhcoreClassDesign::designtpl('lhuser/account/above_new_account_form_multiinclude.tpl.php'));?> - + <div class="form-group"> <label><?php echo erTranslationClassLhTranslation::getInstance()->getTranslation('user/edit','Username');?>*</label> <input <?php if ($can_edit_groups === false) : ?>disabled="disabled"<?php endif;?> class="form-control" type="text" ng-non-bindable name="Username" value="<?php echo htmlspecialchars($user->username);?>" /> </div> - + <div class="form-group"> <label><?php echo erTranslationClassLhTranslation::getInstance()->getTranslation('user/edit','Password');?></label> <input ng-non-bindable autocomplete="new-password" type="password" <?php if ($can_edit_groups === false) : ?>disabled="disabled"<?php endif;?> class="form-control" name="Password" value="<?php echo htmlspecialchars(isset($user->password_temp_1) ? $user->password_temp_1 : '');?>" /> @@ -82,33 +82,33 @@ <label><?php echo erTranslationClassLhTranslation::getInstance()->getTranslation('user/edit','E-mail');?></label> <input type="text" ng-non-bindable <?php if ($can_edit_groups === false) : ?>disabled="disabled"<?php endif;?> class="form-control" name="Email" value="<?php echo $user->email;?>"/> </div> - + <div class="form-group"> <label><?php echo erTranslationClassLhTranslation::getInstance()->getTranslation('user/edit','Chat nickname');?></label> <input type="text" ng-non-bindable <?php if ($can_edit_groups === false) : ?>disabled="disabled"<?php endif;?> class="form-control" name="ChatNickname" value="<?php echo htmlspecialchars($user->chat_nickname);?>" /> </div> - + <div class="form-group"> <label><?php echo erTranslationClassLhTranslation::getInstance()->getTranslation('user/edit','Name');?></label> <input type="text" ng-non-bindable <?php if ($can_edit_groups === false) : ?>disabled="disabled"<?php endif;?> class="form-control" name="Name" value="<?php echo htmlspecialchars($user->name);?>"/> </div> - + <div class="form-group"> <label><?php echo erTranslationClassLhTranslation::getInstance()->getTranslation('user/edit','Surname');?></label> <input type="text" ng-non-bindable <?php if ($can_edit_groups === false) : ?>disabled="disabled"<?php endif;?> class="form-control" name="Surname" value="<?php echo htmlspecialchars($user->surname);?>"/> </div> - + <div class="form-group"> <label><?php echo erTranslationClassLhTranslation::getInstance()->getTranslation('user/account','Job title');?></label> <input type="text" ng-non-bindable <?php if ($can_edit_groups === false) : ?>disabled="disabled"<?php endif;?> class="form-control" name="JobTitle" value="<?php echo htmlspecialchars($user->job_title);?>"/> </div> - + <?php include(erLhcoreClassDesign::designtpl('lhuser/parts/time_zone.tpl.php'));?> - + <div class="row"> - + <?php include(erLhcoreClassDesign::designtpl('lhuser/account/part/visibility_content.tpl.php'));?> - + <?php include(erLhcoreClassDesign::designtpl('lhuser/account/part/after_visibility_content.tpl.php'));?> <?php include(erLhcoreClassDesign::designtpl('lhuser/account/part/hidability.tpl.php'));?> @@ -119,9 +119,9 @@ </div> </div> </div> - + <?php include(erLhcoreClassDesign::designtpl('lhuser/account/part/after_permission.tpl.php'));?> - + <div class="row form-group"> <div class="col-md-6"> <label><?php echo erTranslationClassLhTranslation::getInstance()->getTranslation('user/account','Skype');?></label> @@ -195,9 +195,9 @@ <hr> <label><input type="checkbox" value="on" name="UserDisabled" <?php echo $user->disabled == 1 ? 'checked="checked"' : '' ?> /> <?php echo erTranslationClassLhTranslation::getInstance()->getTranslation('user/new','Disabled')?></label><br> <?php endif; ?> - + <?php include(erLhcoreClassDesign::designtpl('lhkernel/csfr_token.tpl.php'));?> - + <?php include(erLhcoreClassDesign::designtpl('lhuser/account/below_account_edit_multiinclude.tpl.php'));?> <div class="btn-group" role="group" aria-label="..." <?php if (empty($groupsRequired)) :?>ng-init="accval.validForm=true"<?php endif?> > @@ -208,8 +208,8 @@ <?php endif; ?> <input type="submit" class="btn btn-secondary" name="Cancel_account" value="<?php echo erTranslationClassLhTranslation::getInstance()->getTranslation('user/edit','Cancel');?>"/> - </div> - + </div> + </form> </div> @@ -226,8 +226,8 @@ <?php if (isset($account_updated_departaments) && $account_updated_departaments == 'done') : $msg = erTranslationClassLhTranslation::getInstance()->getTranslation('user/account','Account updated'); ?> <?php include(erLhcoreClassDesign::designtpl('lhkernel/alert_success.tpl.php'));?> <?php endif; ?> - - <?php + + <?php $userDepartaments = erLhcoreClassUserDep::getUserDepartamentsIndividual($user->id); $userDepartamentsRead = erLhcoreClassUserDep::getUserDepartamentsIndividual($user->id, true); $userDepartamentsGroup = erLhcoreClassModelDepartamentGroupUser::getUserGroupsIds($user->id); @@ -271,13 +271,13 @@ ); } ?> - + <form action="<?php echo erLhcoreClassDesign::baseurl('user/edit')?>/<?php echo $user->id?>#departments" method="post" enctype="multipart/form-data"> - + <?php include(erLhcoreClassDesign::designtpl('lhuser/account/departments_assignment.tpl.php'));?> - + <input type="submit" class="btn btn-secondary" name="UpdateDepartaments_account" value="<?php echo erTranslationClassLhTranslation::getInstance()->getTranslation('user/edit','Update');?>"/> - </form> + </form> </div> <?php endif; ?> @@ -318,7 +318,7 @@ <p><?php echo erTranslationClassLhTranslation::getInstance()->getTranslation('user/account','In order to change operator permissions you have to edit');?> <a href="<?php echo erLhcoreClassDesign::baseurl('permission/roles')?>"><?php echo erTranslationClassLhTranslation::getInstance()->getTranslation('user/account','roles');?></a>.</p> <input type="button" class="btn btn-secondary" name="UpdateSpeech_account" onclick="lhinst.showMyPermissions('<?php echo $user->id?>')" value="<?php echo erTranslationClassLhTranslation::getInstance()->getTranslation('user/account','Show permissions');?>" /> - <div id="permissions-summary"></div> + <div id="permissions-summary"></div> </div> <?php endif;?> @@ -350,5 +350,5 @@ <?php endif; ?> <?php include(erLhcoreClassDesign::designtpl('lhuser/menu_tabs_content/custom_multiinclude_tab.tpl.php'));?> - + </div>
lhc_web/design/defaulttheme/tpl/lhuser/new.tpl.php+9 −9 modified@@ -28,17 +28,17 @@ <div class="form-group"> <label><?php echo erTranslationClassLhTranslation::getInstance()->getTranslation('user/new','E-mail');?></label> - <input type="text" class="form-control" name="Email" value="<?php echo htmlspecialchars($user->email);?>"/> + <input type="text" ng-non-bindable class="form-control" name="Email" value="<?php echo htmlspecialchars($user->email);?>"/> </div> <div class="form-group"> <label><?php echo erTranslationClassLhTranslation::getInstance()->getTranslation('user/new','Password');?></label> - <input type="password" class="form-control" autocomplete="new-password" name="Password" value="<?php echo htmlspecialchars(isset($user->password_temp_1) ? $user->password_temp_1 : '');?>" /> + <input type="password" ng-non-bindable class="form-control" autocomplete="new-password" name="Password" value="<?php echo htmlspecialchars(isset($user->password_temp_1) ? $user->password_temp_1 : '');?>" /> </div> <div class="form-group"> <label><?php echo erTranslationClassLhTranslation::getInstance()->getTranslation('user/new','Repeat the new password');?></label> - <input type="password" class="form-control" autocomplete="new-password" name="Password1" value="<?php echo htmlspecialchars(isset($user->password_temp_2) ? $user->password_temp_2 : '');?>" /> + <input type="password" ng-non-bindable class="form-control" autocomplete="new-password" name="Password1" value="<?php echo htmlspecialchars(isset($user->password_temp_2) ? $user->password_temp_2 : '');?>" /> </div> <div class="form-group"> @@ -47,22 +47,22 @@ <div class="form-group"> <label><?php echo erTranslationClassLhTranslation::getInstance()->getTranslation('user/new','Chat nickname');?></label> - <input type="text" class="form-control" name="ChatNickname" value="<?php echo htmlspecialchars($user->chat_nickname);?>" /> + <input type="text" class="form-control" ng-non-bindable name="ChatNickname" value="<?php echo htmlspecialchars($user->chat_nickname);?>" /> </div> <div class="form-group"> <label><?php echo erTranslationClassLhTranslation::getInstance()->getTranslation('user/new','Name');?></label> - <input class="form-control" type="text" name="Name" value="<?php echo htmlspecialchars($user->name);?>" /> + <input class="form-control" ng-non-bindable type="text" name="Name" value="<?php echo htmlspecialchars($user->name);?>" /> </div> <div class="form-group"> <label><?php echo erTranslationClassLhTranslation::getInstance()->getTranslation('user/new','Surname');?></label> - <input class="form-control" type="text" name="Surname" value="<?php echo htmlspecialchars($user->surname);?>" /> + <input class="form-control" ng-non-bindable type="text" name="Surname" value="<?php echo htmlspecialchars($user->surname);?>" /> </div> <div class="form-group"> <label><?php echo erTranslationClassLhTranslation::getInstance()->getTranslation('user/account','Job title');?></label> - <input type="text" class="form-control" name="JobTitle" value="<?php echo htmlspecialchars($user->job_title);?>"/> + <input type="text" ng-non-bindable class="form-control" name="JobTitle" value="<?php echo htmlspecialchars($user->job_title);?>"/> </div> <?php include(erLhcoreClassDesign::designtpl('lhuser/parts/time_zone.tpl.php'));?> @@ -87,11 +87,11 @@ <div class="row form-group"> <div class="col-md-6"> <label><?php echo erTranslationClassLhTranslation::getInstance()->getTranslation('user/account','Skype');?></label> - <input class="form-control" type="text" name="Skype" value="<?php echo htmlspecialchars($user->skype);?>"/> + <input class="form-control" ng-non-bindable maxlength="50" type="text" name="Skype" value="<?php echo htmlspecialchars($user->skype);?>"/> </div> <div class="col-md-6"> <label><?php echo erTranslationClassLhTranslation::getInstance()->getTranslation('user/account','XMPP username');?></label> - <input class="form-control" type="text" name="XMPPUsername" value="<?php echo htmlspecialchars($user->xmpp_username);?>"/> + <input class="form-control" ng-non-bindable type="text" name="XMPPUsername" value="<?php echo htmlspecialchars($user->xmpp_username);?>"/> </div> </div>
lhc_web/design/defaulttheme/tpl/lhuser/parts/avatar_build.tpl.php+1 −1 modified@@ -1,4 +1,4 @@ -<div class="row"> +<div class="row" ng-non-bindable> <?php if (!(isset($can_edit_groups) && $can_edit_groups === false)) : ?> <div class="col-9"> <div class="input-group mb-3">
lhc_web/lib/core/lhuser/lhuservalidator.php+2 −2 modified@@ -177,7 +177,7 @@ public static function validateUser(& $userData, $params = array()) { } if ( $form->hasValidData( 'Skype' ) && $form->Skype != '') { - $userData->skype = $form->Skype; + $userData->skype = mb_substr($form->Skype,0,50); } else { $userData->skype = ''; } @@ -915,7 +915,7 @@ public static function validateAccount(& $userData) { if ( erLhcoreClassUser::instance()->hasAccessTo('lhuser','changeskypenick') ) { if ( $form->hasValidData( 'Skype' ) && $form->Skype != '' ) { - $userData->skype = $form->Skype; + $userData->skype = mb_substr($form->Skype,0,50); } else { $userData->skype = ''; }
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
4- github.com/advisories/GHSA-f7xw-46vh-5jw2ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2021-4132ghsaADVISORY
- github.com/livehelperchat/livehelperchat/commit/55b1e3bf62c564db7c919f5293ec1e755c2958d6ghsax_refsource_MISCWEB
- huntr.dev/bounties/7eb80e7c-bb7a-478d-9760-0ea2fa9dc0c2ghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.