VYPR
Critical severityNVD Advisory· Published Feb 12, 2025· Updated Feb 12, 2025

Koa has Inefficient Regular Expression Complexity

CVE-2025-25200

Description

Koa is expressive middleware for Node.js using ES2017 async functions. Prior to versions 0.21.2, 1.7.1, 2.15.4, and 3.0.0-alpha.3, Koa uses an evil regex to parse the X-Forwarded-Proto and X-Forwarded-Host HTTP headers. This can be exploited to carry out a Denial-of-Service attack. Versions 0.21.2, 1.7.1, 2.15.4, and 3.0.0-alpha.3 fix the issue.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
koanpm
>= 2.0.0, < 2.15.42.15.4
koanpm
>= 3.0.0-alpha.0, < 3.0.0-alpha.33.0.0-alpha.3
koanpm
>= 1.0.0, < 1.7.11.7.1
koanpm
< 0.21.20.21.2

Affected products

1

Patches

3
5f294bb1c7c8

Merge commit from fork

https://github.com/koajs/koafengmk2Feb 12, 2025via ghsa
3 files changed · +21 4
  • History.md+5 0 modified
    @@ -1,4 +1,9 @@
     
    +2.15.4 / 2025-02-11
    +==================
    +
    +fix: avoid redos on host and protocol getter
    +
     2.15.3 / 2024-04-11
     ==================
     
    
  • lib/request.js+15 3 modified
    @@ -257,7 +257,7 @@ module.exports = {
           if (!host) host = this.get('Host');
         }
         if (!host) return '';
    -    return host.split(/\s*,\s*/, 1)[0];
    +    return splitCommaSeparatedValues(host, 1)[0];
       },
     
       /**
    @@ -402,7 +402,7 @@ module.exports = {
         if (this.socket.encrypted) return 'https';
         if (!this.app.proxy) return 'http';
         const proto = this.get('X-Forwarded-Proto');
    -    return proto ? proto.split(/\s*,\s*/, 1)[0] : 'http';
    +    return proto ? splitCommaSeparatedValues(proto, 1)[0] : 'http';
       },
     
       /**
    @@ -434,7 +434,7 @@ module.exports = {
         const proxy = this.app.proxy;
         const val = this.get(this.app.proxyIpHeader);
         let ips = proxy && val
    -      ? val.split(/\s*,\s*/)
    +      ? splitCommaSeparatedValues(val)
           : [];
         if (this.app.maxIpsCount > 0) {
           ips = ips.slice(-this.app.maxIpsCount);
    @@ -724,3 +724,15 @@ module.exports = {
     if (util.inspect.custom) {
       module.exports[util.inspect.custom] = module.exports.inspect;
     }
    +
    +/**
    + * Split a comma-separated value string into an array of values, with an optional limit.
    + * All the values are trimmed of whitespace.
    + *
    + * @param {string} value - The comma-separated value string to split.
    + * @param {number} [limit] - The maximum number of values to return.
    + * @returns {string[]} An array of values from the comma-separated string.
    + */
    +function splitCommaSeparatedValues(value, limit) {
    +  return value.split(',', limit).map(v => v.trim());
    +}
    
  • package.json+1 1 modified
    @@ -1,6 +1,6 @@
     {
       "name": "koa",
    -  "version": "2.15.3",
    +  "version": "2.15.4",
       "description": "Koa web app framework",
       "main": "lib/application.js",
       "exports": {
    
93fe903fc966

Merge commit from fork

https://github.com/koajs/koafengmk2Feb 12, 2025via ghsa
4 files changed · +33 6
  • History.md+5 0 modified
    @@ -1,4 +1,9 @@
     
    +1.7.1 / 2025-02-11
    +==================
    +
    +* fix: avoid redos on host and protocol getter
    +
     1.7.0 / 2019-10-17
     ==================
     
    
  • lib/request.js+15 3 modified
    @@ -230,7 +230,7 @@ module.exports = {
         var host = proxy && this.get('X-Forwarded-Host');
         host = host || this.get('Host');
         if (!host) return '';
    -    return host.split(/\s*,\s*/)[0];
    +    return splitCommaSeparatedValues(host, 1)[0];
       },
     
       /**
    @@ -358,7 +358,7 @@ module.exports = {
         if (this.socket.encrypted) return 'https';
         if (!proxy) return 'http';
         var proto = this.get('X-Forwarded-Proto') || 'http';
    -    return proto.split(/\s*,\s*/)[0];
    +    return splitCommaSeparatedValues(proto, 1)[0];
       },
     
       /**
    @@ -403,7 +403,7 @@ module.exports = {
         var proxy = this.app.proxy;
         var val = this.get('X-Forwarded-For');
         return proxy && val
    -      ? val.split(/\s*,\s*/)
    +      ? splitCommaSeparatedValues(val)
           : [];
       },
     
    @@ -634,3 +634,15 @@ module.exports = {
         };
       }
     };
    +
    +/**
    + * Split a comma-separated value string into an array of values, with an optional limit.
    + * All the values are trimmed of whitespace.
    + *
    + * @param {string} value - The comma-separated value string to split.
    + * @param {number} [limit] - The maximum number of values to return.
    + * @returns {string[]} An array of values from the comma-separated string.
    + */
    +function splitCommaSeparatedValues(value, limit) {
    +  return value.split(',', limit).map(v => v.trim());
    +}
    
  • package.json+1 1 modified
    @@ -1,6 +1,6 @@
     {
       "name": "koa",
    -  "version": "1.7.0",
    +  "version": "1.7.1",
       "description": "Koa web app framework",
       "main": "lib/application.js",
       "scripts": {
    
  • test/application.js+12 2 modified
    @@ -79,7 +79,15 @@ describe('app.inspect()', function(){
         var app = koa();
         var util = require('util');
         var str = util.inspect(app);
    -    assert.equal("{ subdomainOffset: 2, proxy: false, env: 'test' }", str);
    +    assert.equal('Application {\n' +
    +  "  env: 'test',\n" +
    +  '  subdomainOffset: 2,\n' +
    +  '  middleware: [],\n' +
    +  '  proxy: false,\n' +
    +  '  context: {},\n' +
    +  '  request: {},\n' +
    +  '  response: {}\n' +
    +  '}', str);
       })
     })
     
    @@ -215,7 +223,9 @@ describe('app.onerror(err)', function(){
     
     describe('app.respond', function(){
       describe('when this.respond === false', function(){
    -    it('should bypass app.respond', function(done){
    +    // no more work on `supertest`, skip it
    +    // TypeError: Cannot read properties of null (reading 'text')
    +    it.skip('should bypass app.respond', function(done){
           var app = koa();
     
           app.use(function *(){
    
5054af6e31ff

Merge commit from fork

https://github.com/koajs/koafengmk2Feb 12, 2025via ghsa
3 files changed · +22 4
  • History.md+6 0 modified
    @@ -1,4 +1,10 @@
     
    +3.0.0-alpha.3 / 2025-02-11
    +==================
    +
    +**fixes**
    +- Avoid redos on host and protocol getter
    +
     3.0.0-alpha.2 / 2024-11-04
     ==================
     
    
  • lib/request.js+15 3 modified
    @@ -256,7 +256,7 @@ module.exports = {
           if (!host) host = this.get('Host')
         }
         if (!host) return ''
    -    return host.split(/\s*,\s*/, 1)[0]
    +    return splitCommaSeparatedValues(host, 1)[0]
       },
     
       /**
    @@ -401,7 +401,7 @@ module.exports = {
         if (this.socket.encrypted) return 'https'
         if (!this.app.proxy) return 'http'
         const proto = this.get('X-Forwarded-Proto')
    -    return proto ? proto.split(/\s*,\s*/, 1)[0] : 'http'
    +    return proto ? splitCommaSeparatedValues(proto, 1)[0] : 'http'
       },
     
       /**
    @@ -433,7 +433,7 @@ module.exports = {
         const proxy = this.app.proxy
         const val = this.get(this.app.proxyIpHeader)
         let ips = proxy && val
    -      ? val.split(/\s*,\s*/)
    +      ? splitCommaSeparatedValues(val)
           : []
         if (this.app.maxIpsCount > 0) {
           ips = ips.slice(-this.app.maxIpsCount)
    @@ -723,3 +723,15 @@ module.exports = {
     if (util.inspect.custom) {
       module.exports[util.inspect.custom] = module.exports.inspect
     }
    +
    +/**
    + * Split a comma-separated value string into an array of values, with an optional limit.
    + * All the values are trimmed of whitespace.
    + *
    + * @param {string} value - The comma-separated value string to split.
    + * @param {number} [limit] - The maximum number of values to return.
    + * @returns {string[]} An array of values from the comma-separated string.
    + */
    +function splitCommaSeparatedValues(value, limit) {
    +  return value.split(',', limit).map(v => v.trim());
    +}
    
  • package.json+1 1 modified
    @@ -1,6 +1,6 @@
     {
       "name": "koa",
    -  "version": "3.0.0-alpha.2",
    +  "version": "3.0.0-alpha.3",
       "publishConfig": {
         "tag": "experimental"
       },
    

Vulnerability mechanics

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

References

9

News mentions

0

No linked articles in our index yet.