Hi, I would like to report a potential XSS filtering bypass in the default result rendering path.
Describe the bug
jquery-typeahead attempts to clean result strings with cleanStringFromScript() before rendering them in the suggestion list. The helper is documented in the source as:
/**
* Clean strings from possible XSS (script and iframe tags)
*/
cleanStringFromScript: function (string) {
return (
(typeof string === "string" &&
string.replace(/<\/?(?:script|iframe)\b[^>]*>/gm, "")) ||
string
);
},
However, the filter only removes script and iframe tags. Other executable HTML, such as event-handler attributes on allowed tags, is not removed. The filtered value is later inserted into the result list as HTML, so a payload such as <img src=x onerror=alert('XSS')> can execute when the suggestion list is rendered.
To Reproduce
Minimal reproduction using a locally downloaded copy of the latest GitHub project:
npm install jquery
git clone https://github.com/running-coder/jquery-typeahead.git jquery-typeahead-master
Place the HTML file next to the jquery-typeahead-master folder. In my local setup, jQuery is installed in ../node_modules, and the Typeahead files are loaded from the downloaded GitHub project.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>jquery-typeahead XSS filter bypass reproduction</title>
<script src="../node_modules/jquery/dist/jquery.min.js"></script>
<link rel="stylesheet" href="./jquery-typeahead-master/dist/jquery.typeahead.min.css">
<script src="./jquery-typeahead-master/dist/jquery.typeahead.min.js"></script>
</head>
<body>
<form>
<div class="typeahead__container">
<div class="typeahead__field">
<div class="typeahead__query">
<input id="search" type="search" autocomplete="off">
</div>
</div>
</div>
</form>
<script>
const localData = [
{ id: 1, name: "apple", category: "safe" },
{ id: 2, name: "<script>alert('blocked by cleanStringFromScript')<\/script>", category: "blocked" },
{ id: 3, name: "<img src=x onerror=alert('XSS')>", category: "bypass" }
];
$("#search").typeahead({
minLength: 1,
maxItem: 8,
display: "name",
source: {
data: localData
}
});
</script>
</body>
</html>
Steps to reproduce the behavior:
- Open the HTML file in a browser.
- Type
a in the input.
- The suggestion list is rendered.
- The
script tag payload is stripped by cleanStringFromScript(), but the <img onerror> payload is not stripped.
- The image error handler executes when the result is inserted into the DOM.
Expected behavior
If the library cleans result strings from possible XSS before inserting them as HTML, the cleaning should not be limited to only script and iframe tags.
Expected safer behavior would be one of the following:
- escape result values by default and insert them as text;
- use a robust sanitizer before inserting values as HTML;
- clearly document that
cleanStringFromScript() is not a sanitizer and should not be treated as an XSS protection boundary.
Screenshots
Desktop:
- Browser: Chrome
- Version: 126.0.6478.127
Additional context
The relevant internal flow is:
- display values are passed through
cleanStringFromScript();
- the helper only removes
script and iframe tags;
- the resulting string is used to build
_aHtml;
_aHtml is inserted into the suggestion list with jQuery HTML insertion.
This means the current filter blocks some common payloads, but it is not a complete XSS mitigation.
Hi, I would like to report a potential XSS filtering bypass in the default result rendering path.
Describe the bug
jquery-typeaheadattempts to clean result strings withcleanStringFromScript()before rendering them in the suggestion list. The helper is documented in the source as:However, the filter only removes
scriptandiframetags. Other executable HTML, such as event-handler attributes on allowed tags, is not removed. The filtered value is later inserted into the result list as HTML, so a payload such as<img src=x onerror=alert('XSS')>can execute when the suggestion list is rendered.To Reproduce
Minimal reproduction using a locally downloaded copy of the latest GitHub project:
Place the HTML file next to the
jquery-typeahead-masterfolder. In my local setup, jQuery is installed in../node_modules, and the Typeahead files are loaded from the downloaded GitHub project.Steps to reproduce the behavior:
ain the input.scripttag payload is stripped bycleanStringFromScript(), but the<img onerror>payload is not stripped.Expected behavior
If the library cleans result strings from possible XSS before inserting them as HTML, the cleaning should not be limited to only
scriptandiframetags.Expected safer behavior would be one of the following:
cleanStringFromScript()is not a sanitizer and should not be treated as an XSS protection boundary.Screenshots
Desktop:
Additional context
The relevant internal flow is:
cleanStringFromScript();scriptandiframetags;_aHtml;_aHtmlis inserted into the suggestion list with jQuery HTML insertion.This means the current filter blocks some common payloads, but it is not a complete XSS mitigation.