CVE-2018-25061: Inefficient Regular Expression Complexity (CWE-1333)
value: A vulnerability was found in rgb2hex up to 0.1.5. It has been rated as problematic. This issue affects some unknown processing. The manipulation leads to inefficient regular expression complexity. The attack may be initiated remotely. Upgrading to version 0.1.6 is able to address this issue. The patch is named 9e0c38594432edfa64136fdf7bb651835e17c34f. It is recommended to upgrade the affected component. The associated identifier of this vulnerability is VDB-217151.
CWE description: The product uses a regular expression with an inefficient, possibly exponential worst-case computational complexity that consumes excessive CPU cycles.
CVSS v3 base score: 7.5
Published: 2022-12-31T20:15Z
Repository: https://github.com/christian-bromann/rgb2hex
Language: JavaScript
Files changed: index.js, rgb2hex.js, rgb2hex.min.js, test/rgb2hex.test.js
Commit date: 2018-07-05 11:40:04 +0200
Commit message: prevent Regular Expression Denial of Service attacks

Diff: diff --git a/index.js b/index.js
index 73cdf34..422b18a 100644
--- a/index.js
+++ b/index.js
@@ -26,25 +26,25 @@ var rgb2hex = module.exports = function rgb2hex(color) {
     var strippedColor = color.replace(/\s+/g,'');
 
     /**
      * parse input
      */
-    var digits = /rgba?\((\d+),(\d+),(\d+)(,(\d+)?\.?(\d+))?\);?/.exec(strippedColor);
+    var digits = /(.*?)rgb(a)??\((\d{1,3}),(\d{1,3}),(\d{1,3})(,[01]??\.([0-9]{0,3}))??\)/.exec(strippedColor);
 
     if(!digits) {
         // or throw error if input isn't a valid rgb(a) color
         throw new Error('given color (' + color + ') isn\'t a valid rgb or rgba color');
     }
 
-    var red = parseInt(digits[1], 10);
-    var green = parseInt(digits[2], 10);
-    var blue = parseInt(digits[3], 10);
-    var alpha = digits[4] ? /([0-9\.]+)/.exec(digits[4])[0] : '1';
+    var red = parseInt(digits[3], 10);
+    var green = parseInt(digits[4], 10);
+    var blue = parseInt(digits[5], 10);
+    var alpha = digits[6] ? /([0-9\.]+)/.exec(digits[6])[0] : '1';
     var rgb = ((blue | green << 8 | red << 16) | 1 << 24).toString(16).slice(1);
 
     // parse alpha value into float
-    if(alpha.substr(0,1) === '.') {
+    if(alpha.substr(0,2) === ',.') {
         alpha = parseFloat('0' + alpha);
     }
 
     // limit alpha value to 1
     if(alpha > 1) {
@@ -56,6 +56,6 @@ var rgb2hex = module.exports = function rgb2hex(color) {
 
     return {
         hex: '#' + rgb.toString(16),
         alpha: alpha
     };
-};
+};
\ No newline at end of file
diff --git a/rgb2hex.js b/rgb2hex.js
index bff5dc9..77d56f0 100644
--- a/rgb2hex.js
+++ b/rgb2hex.js
@@ -19,25 +19,25 @@
         var strippedColor = color.replace(/\s+/g,'');
 
         /**
          * parse input
          */
-        var digits = /rgba?\((\d+),(\d+),(\d+)(,(\d+)?\.?(\d+))?\);?/.exec(strippedColor);
+        var digits = /(.*?)rgb(a)??\((\d{1,3}),(\d{1,3}),(\d{1,3})(,[01]??\.([0-9]{0,3}))??\)/.exec(strippedColor);
 
         if(!digits) {
             // or throw error if input isn't a valid rgb(a) color
             throw new Error('given color (' + color + ') isn\'t a valid rgb or rgba color');
         }
 
-        var red = parseInt(digits[1], 10);
-        var green = parseInt(digits[2], 10);
-        var blue = parseInt(digits[3], 10);
-        var alpha = digits[4] ? /([0-9\.]+)/.exec(digits[4])[0] : '1';
+        var red = parseInt(digits[3], 10);
+        var green = parseInt(digits[4], 10);
+        var blue = parseInt(digits[5], 10);
+        var alpha = digits[6] ? /([0-9\.]+)/.exec(digits[6])[0] : '1';
         var rgb = ((blue | green << 8 | red << 16) | 1 << 24).toString(16).slice(1);
 
         // parse alpha value into float
-        if(alpha.substr(0,1) === '.') {
+        if(alpha.substr(0,2) === ',.') {
             alpha = parseFloat('0' + alpha);
         }
 
         // limit alpha value to 1
         if(alpha > 1) {
diff --git a/rgb2hex.min.js b/rgb2hex.min.js
index cbcb990..b0e3aef 100644
--- a/rgb2hex.min.js
+++ b/rgb2hex.min.js
@@ -1 +1 @@
-!function(r){var e=function(r){if("string"!=typeof r)throw new Error("color has to be type of `string`");if("#"===r.substr(0,1))return{hex:r,alpha:1};var e=r.replace(/\s+/g,""),t=/rgba?\((\d+),(\d+),(\d+)(,(\d+)?\.?(\d+))?\);?/.exec(e);if(!t)throw new Error("given color ("+r+") isn't a valid rgb or rgba color");var n=parseInt(t[1],10),o=parseInt(t[2],10),a=parseInt(t[3],10),i=t[4]?/([0-9\.]+)/.exec(t[4])[0]:"1",s=(a|o<<8|n<<16|1<<24).toString(16).slice(1);return"."===i.substr(0,1)&&(i=parseFloat("0"+i)),1<i&&(i=1),i=parseFloat(Math.round(100*i))/100,{hex:"#"+s.toString(16),alpha:i}};"function"==typeof define&&define.amd?define("rgb2hex",function(){return e}):r.rgb2hex=e}(window);
\ No newline at end of file
+!function(r){var e=function(r){if("string"!=typeof r)throw new Error("color has to be type of `string`");if("#"===r.substr(0,1))return{hex:r,alpha:1};var e=r.replace(/\s+/g,""),t=/(.*?)rgb(a)??\((\d{1,3}),(\d{1,3}),(\d{1,3})(,[01]??\.([0-9]{0,3}))??\)/.exec(e);if(!t)throw new Error("given color ("+r+") isn't a valid rgb or rgba color");var n=parseInt(t[3],10),o=parseInt(t[4],10),a=parseInt(t[5],10),i=t[6]?/([0-9\.]+)/.exec(t[6])[0]:"1",s=(a|o<<8|n<<16|1<<24).toString(16).slice(1);return",."===i.substr(0,2)&&(i=parseFloat("0"+i)),1<i&&(i=1),i=parseFloat(Math.round(100*i))/100,{hex:"#"+s.toString(16),alpha:i}};"function"==typeof define&&define.amd?define("rgb2hex",function(){return e}):r.rgb2hex=e}(window);
\ No newline at end of file
diff --git a/test/rgb2hex.test.js b/test/rgb2hex.test.js
index a8b69f3..90a2b1d 100644
--- a/test/rgb2hex.test.js
+++ b/test/rgb2hex.test.js
@@ -78,14 +78,26 @@ describe('rgb2hex should', () => {
             expect(typeof parsedValue.alpha).toEqual('number')
             expect(parsedValue.alpha).toEqual(0.67)
         })
 
         it('by limiting alpha value to 1', () => {
-            var input = 'rgba(12,173,22,12312.67)'
+            var input = 'rgba(12,173,22,1.67)'
             expect(rgb2hex(input).alpha).not.toBeGreaterThan(1)
         })
 
+        it('by not accepting to big values', () => {
+            var input = 'rgba(1123, 54, 4, 0.33)'
+            expect(() => rgb2hex(input)).toThrow(invalidErrorMessage(input))
+            input = 'rgba(113, 1154, 4, 0.33)'
+            expect(() => rgb2hex(input)).toThrow(invalidErrorMessage(input))
+            input = 'rgba(113, 154, 1114, 0.33)'
+            expect(() => rgb2hex(input)).toThrow(invalidErrorMessage(input))
+            input = 'rgba(113, 54, 4, 2.33)'
+            expect(() => rgb2hex(input)).toThrow(invalidErrorMessage(input))
+            input = 'rgbaaaaaa(113, 54, 4, .33)'
+            expect(() => rgb2hex(input)).toThrow(invalidErrorMessage(input))
+        })
     })
 
     describe('not care about', () => {
         it('rgb or rgba prefix', () => {
             const rgb = 'rgb(0, 0, 0)'
@@ -130,6 +142,6 @@ describe('rgb2hex should', () => {
             const values = `url("${url}")no-repeatscroll0%0%/100%padding-boxborder-box`
             expect(rgb2hex(`${values}rgb(226,230,233)${values}`).hex)
             expect(rgb2hex(`${values}rgba(226,230,233,0.4)${values}`).hex)
         })
     })
-})
+})
\ No newline at end of file

Vulnerable code:     var digits = /rgba?\((\d+),(\d+),(\d+)(,(\d+)?\.?(\d+))?\);?/.exec(strippedColor);
    var red = parseInt(digits[1], 10);
    var green = parseInt(digits[2], 10);
    var blue = parseInt(digits[3], 10);
    var alpha = digits[4] ? /([0-9\.]+)/.exec(digits[4])[0] : '1';
    if(alpha.substr(0,1) === '.') {
};
        var digits = /rgba?\((\d+),(\d+),(\d+)(,(\d+)?\.?(\d+))?\);?/.exec(strippedColor);
        var red = parseInt(digits[1], 10);
        var green = parseInt(digits[2], 10);
        var blue = parseInt(digits[3], 10);
        var alpha = digits[4] ? /([0-9\.]+)/.exec(digits[4])[0] : '1';
        if(alpha.substr(0,1) === '.') {
!function(r){var e=function(r){if("string"!=typeof r)throw new Error("color has to be type of `string`");if("#"===r.substr(0,1))return{hex:r,alpha:1};var e=r.replace(/\s+/g,""),t=/rgba?\((\d+),(\d+),(\d+)(,(\d+)?\.?(\d+))?\);?/.exec(e);if(!t)throw new Error("given color ("+r+") isn't a valid rgb or rgba color");var n=parseInt(t[1],10),o=parseInt(t[2],10),a=parseInt(t[3],10),i=t[4]?/([0-9\.]+)/.exec(t[4])[0]:"1",s=(a|o<<8|n<<16|1<<24).toString(16).slice(1);return"."===i.substr(0,1)&&(i=parseFloat("0"+i)),1<i&&(i=1),i=parseFloat(Math.round(100*i))/100,{hex:"#"+s.toString(16),alpha:i}};"function"==typeof define&&define.amd?define("rgb2hex",function(){return e}):r.rgb2hex=e}(window);
            var input = 'rgba(12,173,22,12312.67)'
})
Fixed code:     var digits = /(.*?)rgb(a)??\((\d{1,3}),(\d{1,3}),(\d{1,3})(,[01]??\.([0-9]{0,3}))??\)/.exec(strippedColor);
    var red = parseInt(digits[3], 10);
    var green = parseInt(digits[4], 10);
    var blue = parseInt(digits[5], 10);
    var alpha = digits[6] ? /([0-9\.]+)/.exec(digits[6])[0] : '1';
    if(alpha.substr(0,2) === ',.') {
};
        var digits = /(.*?)rgb(a)??\((\d{1,3}),(\d{1,3}),(\d{1,3})(,[01]??\.([0-9]{0,3}))??\)/.exec(strippedColor);
        var red = parseInt(digits[3], 10);
        var green = parseInt(digits[4], 10);
        var blue = parseInt(digits[5], 10);
        var alpha = digits[6] ? /([0-9\.]+)/.exec(digits[6])[0] : '1';
        if(alpha.substr(0,2) === ',.') {
!function(r){var e=function(r){if("string"!=typeof r)throw new Error("color has to be type of `string`");if("#"===r.substr(0,1))return{hex:r,alpha:1};var e=r.replace(/\s+/g,""),t=/(.*?)rgb(a)??\((\d{1,3}),(\d{1,3}),(\d{1,3})(,[01]??\.([0-9]{0,3}))??\)/.exec(e);if(!t)throw new Error("given color ("+r+") isn't a valid rgb or rgba color");var n=parseInt(t[3],10),o=parseInt(t[4],10),a=parseInt(t[5],10),i=t[6]?/([0-9\.]+)/.exec(t[6])[0]:"1",s=(a|o<<8|n<<16|1<<24).toString(16).slice(1);return",."===i.substr(0,2)&&(i=parseFloat("0"+i)),1<i&&(i=1),i=parseFloat(Math.round(100*i))/100,{hex:"#"+s.toString(16),alpha:i}};"function"==typeof define&&define.amd?define("rgb2hex",function(){return e}):r.rgb2hex=e}(window);
            var input = 'rgba(12,173,22,1.67)'
        it('by not accepting to big values', () => {
            var input = 'rgba(1123, 54, 4, 0.33)'
            expect(() => rgb2hex(input)).toThrow(invalidErrorMessage(input))
            input = 'rgba(113, 1154, 4, 0.33)'
            expect(() => rgb2hex(input)).toThrow(invalidErrorMessage(input))
            input = 'rgba(113, 154, 1114, 0.33)'
            expect(() => rgb2hex(input)).toThrow(invalidErrorMessage(input))
            input = 'rgba(113, 54, 4, 2.33)'
            expect(() => rgb2hex(input)).toThrow(invalidErrorMessage(input))
            input = 'rgbaaaaaa(113, 54, 4, .33)'
            expect(() => rgb2hex(input)).toThrow(invalidErrorMessage(input))
        })
})