VYPR
Moderate severityOSV Advisory· Published Jan 9, 2019· Updated Aug 5, 2024

CVE-2018-20676

CVE-2018-20676

Description

In Bootstrap before 3.4.0, XSS is possible in the tooltip data-viewport attribute.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Bootstrap before 3.4.0 allows XSS via the tooltip `data-viewport` attribute due to insufficient input sanitization.

Vulnerability

Bootstrap versions before 3.4.0 contain a cross-site scripting (XSS) vulnerability in the tooltip component. The flaw resides in how the data-viewport attribute is processed; user-supplied values are not properly sanitized before being inserted into the DOM. An attacker can inject arbitrary HTML or JavaScript through a crafted data-viewport value. This affects all Bootstrap releases prior to 3.4.0 [1] [2].

Exploitation

An attacker needs to be able to provide input that controls the tooltip's data-viewport attribute. This can be achieved if the application reflects user input into the attribute without sanitization, or if the attacker can plant malicious content (e.g., via a stored comment or post) that includes a manipulated data-viewport. No elevated privileges are required beyond the ability to inject HTML attributes. The tooltip component must be enabled and initialized on the target element [1].

Impact

Successful exploitation allows an attacker to execute arbitrary JavaScript in the context of the victim's browser. This can lead to session theft, page defacement, redirection to malicious sites, or other client-side attacks that compromise confidentiality and integrity [1].

Mitigation

The vulnerability is fixed in Bootstrap 3.4.0, released on December 18, 2018 [2]. Users should upgrade to version 3.4.0 or later. No workarounds are provided for unpatched versions. Red Hat issued RHBA-2019:1076 to address the issue in affected products [1].

AI Insight generated on May 22, 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.

PackageAffected versionsPatched versions
bootstrapnpm
< 3.4.03.4.0
bootstrap-sassnpm
< 3.4.03.4.0
twbs/bootstrapPackagist
< 3.4.03.4.0
org.webjars:bootstrapMaven
< 3.4.03.4.0
bootstrapRubyGems
< 3.4.03.4.0
bootstrap-sassRubyGems
< 3.4.03.4.0
bootstrapNuGet
< 3.4.03.4.0

Affected products

49

Patches

1
2a5ba23ce8f0

Fix/xss issues on data attributes (#27047)

https://github.com/twbs/bootstrapdon-spykerAug 13, 2018via ghsa
6 files changed · +49 4
  • js/affix.js+3 1 modified
    @@ -16,7 +16,9 @@
       var Affix = function (element, options) {
         this.options = $.extend({}, Affix.DEFAULTS, options)
     
    -    this.$target = $(this.options.target)
    +    var target = this.options.target === Affix.DEFAULTS.target ? $(this.options.target) : $(document).find(this.options.target)
    +
    +    this.$target = target
           .on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this))
           .on('click.bs.affix.data-api',  $.proxy(this.checkPositionWithEventLoop, this))
     
    
  • js/collapse.js+1 1 modified
    @@ -137,7 +137,7 @@
       }
     
       Collapse.prototype.getParent = function () {
    -    return $(this.options.parent)
    +    return $(document).find(this.options.parent)
           .find('[data-toggle="collapse"][data-parent="' + this.options.parent + '"]')
           .each($.proxy(function (i, element) {
             var $element = $(element)
    
  • js/tests/unit/affix.js+15 0 modified
    @@ -104,4 +104,19 @@ $(function () {
           }, 250)
         }, 250)
       })
    +
    +  QUnit.test('should raise exception to avoid xss on target', function (assert) {
    +    assert.expect(1)
    +    assert.throws(function () {
    +
    +      var templateHTML = '<div id="affixTarget"></div>'
    +      $(templateHTML).appendTo(document.body)
    +
    +      $('#affixTarget').bootstrapAffix({
    +        target: '<img src=1 onerror=\'alert(0)\'>'
    +      })
    +
    +    },  new Error('Syntax error, unrecognized expression: <img src=1 onerror=\'alert(0)\'>'))
    +  })
    +
     })
    
  • js/tests/unit/collapse.js+10 0 modified
    @@ -440,4 +440,14 @@ $(function () {
           .bootstrapCollapse('show')
       })
     
    +  QUnit.test('should raise exception to avoid xss on data-parent', function (assert) {
    +    assert.expect(1)
    +    assert.throws(function () {
    +      $('<a role="button" data-toggle="collapse" data-parent="<img src=1 onerror=\'alert(0)\'>" href="#collapseThree">')
    +        .appendTo('#qunit-fixture')
    +        .bootstrapCollapse('show')
    +        .trigger('click');
    +    },  new Error('Syntax error, unrecognized expression: <img src=1 onerror=\'alert(0)\'>'))
    +  })
    +
     })
    
  • js/tests/unit/tooltip.js+18 0 modified
    @@ -1322,4 +1322,22 @@ $(function () {
         })
       })
     
    +  QUnit.test('should raise exception to avoid xss on data-container', function (assert) {
    +    assert.expect(1)
    +    assert.throws(function () {
    +      $('<button data-toggle="tooltip" data-container="<img src=1 onerror=\'alert(0)\'>" title="Tooltip on right">Tooltip on right</button>')
    +        .appendTo('#qunit-fixture')
    +        .bootstrapTooltip('show')
    +    },  new Error('Syntax error, unrecognized expression: <img src=1 onerror=\'alert(0)\'>'))
    +  })
    +
    +  QUnit.test('should raise exception to avoid xss on data-viewport', function (assert) {
    +    assert.expect(1)
    +    assert.throws(function () {
    +      $('<button data-toggle="tooltip" data-viewport="<img src=1 onerror=\'alert(0)\'>" title="Tooltip on right">Tooltip on right</button>')
    +        .appendTo('#qunit-fixture')
    +        .bootstrapTooltip('show')
    +    },  new Error('Syntax error, unrecognized expression: <img src=1 onerror=\'alert(0)\'>'))
    +  })
    +
     })
    
  • js/tooltip.js+2 2 modified
    @@ -51,7 +51,7 @@
         this.type      = type
         this.$element  = $(element)
         this.options   = this.getOptions(options)
    -    this.$viewport = this.options.viewport && $($.isFunction(this.options.viewport) ? this.options.viewport.call(this, this.$element) : (this.options.viewport.selector || this.options.viewport))
    +    this.$viewport = this.options.viewport && $(document).find($.isFunction(this.options.viewport) ? this.options.viewport.call(this, this.$element) : (this.options.viewport.selector || this.options.viewport))
         this.inState   = { click: false, hover: false, focus: false }
     
         if (this.$element[0] instanceof document.constructor && !this.options.selector) {
    @@ -204,7 +204,7 @@
             .addClass(placement)
             .data('bs.' + this.type, this)
     
    -      this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element)
    +      this.options.container ? $tip.appendTo($(document).find(this.options.container)) : $tip.insertAfter(this.$element)
           this.$element.trigger('inserted.bs.' + this.type)
     
           var pos          = this.getPosition()
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

20

News mentions

0

No linked articles in our index yet.