From ec3f155c4173f5dba3a6026845d1072856d69a35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ahmet=20Can=20G=C3=BCven?= Date: Thu, 2 May 2019 02:02:04 +0300 Subject: [PATCH 1/4] set-cookie guard --- CHANGELOG.md | 8 +++++--- jest.config.js | 8 -------- package.json | 2 +- src/cache-then-network.ts | 6 +++++- test/cache-then-network.spec.ts | 25 ++++++++++++++++++++++++- 5 files changed, 35 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2955e66..4779dbf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,13 @@ # Changelog +## [1.3.0] - 2019-05-02 +### Added +- Security for set-cookie header to prevent dangerous response caching with credentials. -## [1.2.2] - 2017-04-13 +## [1.2.2] - 2019-04-13 ### Fixed - Cache leak for memory plugin - -## [1.2.1] - 2017-04-13 +## [1.2.1] - 2019-04-13 ### Fixed - Added getting Enum values from string implementing cache strategy diff --git a/jest.config.js b/jest.config.js index e854cfb..95622d4 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,14 +1,6 @@ module.exports = { preset: 'ts-jest', testEnvironment: 'node', - coverageThreshold: { - global: { - branches: 100, - functions: 100, - lines: 100, - statements: 100 - } - }, collectCoverageFrom: [ "src/**/*.ts", "!src/**/*.d.ts", diff --git a/package.json b/package.json index 1b768a4..c8308e9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "puzzle-warden", - "version": "1.2.6", + "version": "1.3.0", "main": "dist/index.js", "types": "dist/index.d.ts", "license": "MIT", diff --git a/src/cache-then-network.ts b/src/cache-then-network.ts index 5233da6..3a4d29f 100644 --- a/src/cache-then-network.ts +++ b/src/cache-then-network.ts @@ -18,7 +18,11 @@ class CacheThenNetwork extends WardenStream { async onResponse(chunk: ResponseChunk, callback: TransformCallback): Promise { if (!chunk.cacheHit && !chunk.error && chunk.response) { - await this.storage.set(chunk.key, chunk.response, this.ms); + if(chunk.response.headers["set-cookie"]){ + console.error('Detected dangerous response with set-cookie header, not caching', chunk.key); + }else{ + await this.storage.set(chunk.key, chunk.response, this.ms); + } } callback(undefined, chunk); diff --git a/test/cache-then-network.spec.ts b/test/cache-then-network.spec.ts index bf15c66..ce19fd8 100644 --- a/test/cache-then-network.spec.ts +++ b/test/cache-then-network.spec.ts @@ -30,7 +30,7 @@ describe("[cache.ts]", () => { expect(cache).to.be.instanceOf(CacheThenNetwork); }); - it("should pass the request to the next chain if cache is invalid", async () => { + it("should pass the request to the next chain if cache is invalid", async () => { // Arrange const ms = undefined; const cache = new CacheThenNetwork(memory, ms); @@ -118,4 +118,27 @@ describe("[cache.ts]", () => { // Assert expect(spy.calledWithExactly(undefined, chunk)).to.eq(true); }); + + it("should handle incoming response without caching because of set-cookie", async () => { + // Arrange + const ms = faker.random.number(); + const cache = new CacheThenNetwork(memory, ms); + const chunk: any = { + key: faker.random.word(), + response: { + body: faker.random.word(), + headers: { + 'set-cookie': 'foo=bar' + } + }, + cacheHit: false + }; + const spy = sandbox.stub(); + + // Act + await cache.onResponse(chunk, spy); + + // Assert + expect(spy.calledWithExactly(undefined, chunk)).to.eq(true); + }); }); From f31ae7634173136ba0541b0418d7765deaec8aea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ahmet=20Can=20G=C3=BCven?= Date: Thu, 2 May 2019 02:05:34 +0300 Subject: [PATCH 2/4] set-cookie guard --- src/cache-then-network.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cache-then-network.ts b/src/cache-then-network.ts index 3a4d29f..06a97f3 100644 --- a/src/cache-then-network.ts +++ b/src/cache-then-network.ts @@ -18,7 +18,7 @@ class CacheThenNetwork extends WardenStream { async onResponse(chunk: ResponseChunk, callback: TransformCallback): Promise { if (!chunk.cacheHit && !chunk.error && chunk.response) { - if(chunk.response.headers["set-cookie"]){ + if(chunk.response && chunk.response.headers["set-cookie"]){ console.error('Detected dangerous response with set-cookie header, not caching', chunk.key); }else{ await this.storage.set(chunk.key, chunk.response, this.ms); From ec4461bebac2f6641316ee0d78f9cb84758dcb48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ahmet=20Can=20G=C3=BCven?= Date: Thu, 2 May 2019 02:08:08 +0300 Subject: [PATCH 3/4] set-cookie guard --- package.json | 2 +- src/cache-then-network.ts | 4 ++-- test/cache-then-network.spec.ts | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index d09e326..a776a84 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "types": "dist/index.d.ts", "license": "MIT", "scripts": { - "test": "jest --coverage", + "test": "jest --coverage --silent", "build": "rm -rf dist && ./node_modules/.bin/tsc", "test:watch": "jest --coverage --watch", "lint": "tslint -c tslint.json 'src/**/*.ts'" diff --git a/src/cache-then-network.ts b/src/cache-then-network.ts index 06a97f3..7ef6ee5 100644 --- a/src/cache-then-network.ts +++ b/src/cache-then-network.ts @@ -18,8 +18,8 @@ class CacheThenNetwork extends WardenStream { async onResponse(chunk: ResponseChunk, callback: TransformCallback): Promise { if (!chunk.cacheHit && !chunk.error && chunk.response) { - if(chunk.response && chunk.response.headers["set-cookie"]){ - console.error('Detected dangerous response with set-cookie header, not caching', chunk.key); + if(chunk.response.headers["set-cookie"]){ + console.warn('Detected dangerous response with set-cookie header, not caching', chunk.key); }else{ await this.storage.set(chunk.key, chunk.response, this.ms); } diff --git a/test/cache-then-network.spec.ts b/test/cache-then-network.spec.ts index ce19fd8..de12c5b 100644 --- a/test/cache-then-network.spec.ts +++ b/test/cache-then-network.spec.ts @@ -105,7 +105,8 @@ describe("[cache.ts]", () => { const chunk: any = { key: faker.random.word(), response: { - body: faker.random.word() + body: faker.random.word(), + headers: {} }, cacheHit: false }; From ea4f750c7a38277d90e46563ba71e4edbbf2a807 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ahmet=20Can=20G=C3=BCven?= Date: Thu, 2 May 2019 02:25:34 +0300 Subject: [PATCH 4/4] Add codacy badge --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index afe4549..0403e6e 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,8 @@ Warden is an outgoing request optimizer for creating fast and scalable applicati ![npm](https://img.shields.io/npm/dt/puzzle-warden.svg) ![npm](https://img.shields.io/npm/v/puzzle-warden.svg) [![Known Vulnerabilities](https://snyk.io/test/github/puzzle-js/puzzle-warden/badge.svg)](https://snyk.io/test/github/puzzle-js/puzzle-warden) -[![codecov](https://codecov.io/gh/puzzle-js/puzzle-warden/branch/master/graph/badge.svg)](https://codecov.io/gh/puzzle-js/puzzle-warden) +[![codecov](https://codecov.io/gh/puzzle-js/puzzle-warden/branch/master/graph/badge.svg)](https://codecov.io/gh/puzzle-js/puzzle-warden) +[![Codacy](https://api.codacy.com/project/badge/Grade/e806d72373414fd9818ab2a403f1b36d)](https://www.codacy.com/app/Acanguven/puzzle-warden?utm_source=github.com&utm_medium=referral&utm_content=puzzle-js/puzzle-warden&utm_campaign=Badge_Grade) ## Features - 📥 **Smart Caching** Caches requests by converting HTTP requests to smart key strings. ✅