Skip to content

Commit

Permalink
Ported http-security-headers middlewares by @willfarrell to 1.x
Browse files Browse the repository at this point in the history
  • Loading branch information
lmammino authored and benjifs committed May 21, 2020
1 parent 4353922 commit a8aa601
Show file tree
Hide file tree
Showing 26 changed files with 171 additions and 212 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,7 @@ Currently available middlewares:
- [`http-json-body-parser`](/packages/http-json-body-parser): Automatically parses HTTP requests with JSON body and converts the body into an object. Also handles gracefully broken JSON if used in combination of
`httpErrorHandler`.
- [`http-partial-response`](/packages/http-partial-response): Filter response objects attributes based on query string parameters.
- [`http-security-headers`](/packages/http-security-headers): Applies best practice security headers to responses. It's a simplified port of HelmetJS.
- [`http-urlencode-body-parser`](/packages/http-urlencode-body-parser): Automatically parses HTTP requests with URL encoded body (typically the result of a form submit).
- [`s3-key-normalizer`](/packages/s3-key-normalizer): Normalizes key names in s3 events.
- [`ssm`](/packages/ssm): Fetches parameters from [AWS Systems Manager Parameter Store](https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-paramstore.html).
Expand Down
2 changes: 1 addition & 1 deletion lerna.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@
"packages": [
"packages/*"
],
"version": "1.0.0-alpha.14"
"version": "1.0.0-alpha.15"
}
182 changes: 91 additions & 91 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "middy-monorepo",
"version": "1.0.0-alpha.14",
"version": "1.0.0-alpha.15",
"description": "🛵 The stylish Node.js middleware engine for AWS Lambda",
"engines": {
"node": ">=6.10"
Expand Down
2 changes: 1 addition & 1 deletion packages/cache/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@middy/cache",
"version": "1.0.0-alpha.14",
"version": "1.0.0-alpha.15",
"description": "Cache middleware for the middy framework",
"engines": {
"node": ">=6.10"
Expand Down
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@middy/core",
"version": "1.0.0-alpha.14",
"version": "1.0.0-alpha.15",
"description": "🛵 The stylish Node.js middleware engine for AWS Lambda (core package)",
"engines": {
"node": ">=6.10"
Expand Down
2 changes: 1 addition & 1 deletion packages/do-not-wait-for-empty-event-loop/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@middy/do-not-wait-for-empty-event-loop",
"version": "1.0.0-alpha.14",
"version": "1.0.0-alpha.15",
"description": "Middleware for the middy framework that allows to easily disable the wait for empty event loop in a Lambda function",
"engines": {
"node": ">=6.10"
Expand Down
2 changes: 1 addition & 1 deletion packages/error-logger/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@middy/error-logger",
"version": "1.0.0-alpha.14",
"version": "1.0.0-alpha.15",
"description": "Input and output logger middleware for the middy framework",
"engines": {
"node": ">=6.10"
Expand Down
2 changes: 1 addition & 1 deletion packages/http-content-negotiation/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@middy/http-content-negotiation",
"version": "1.0.0-alpha.14",
"version": "1.0.0-alpha.15",
"description": "Http content negotiation middleware for the middy framework",
"engines": {
"node": ">=6.10"
Expand Down
2 changes: 1 addition & 1 deletion packages/http-cors/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@middy/http-cors",
"version": "1.0.0-alpha.14",
"version": "1.0.0-alpha.15",
"description": "CORS (Cross-Origin Resource Sharing) middleware for the middy framework",
"engines": {
"node": ">=6.10"
Expand Down
2 changes: 1 addition & 1 deletion packages/http-error-handler/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@middy/http-error-handler",
"version": "1.0.0-alpha.14",
"version": "1.0.0-alpha.15",
"description": "Http error handler middleware for the middy framework",
"engines": {
"node": ">=6.10"
Expand Down
2 changes: 1 addition & 1 deletion packages/http-event-normalizer/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@middy/http-event-normalizer",
"version": "1.0.0-alpha.14",
"version": "1.0.0-alpha.15",
"description": "Http event normalizer middleware for the middy framework",
"engines": {
"node": ">=6.10"
Expand Down
2 changes: 1 addition & 1 deletion packages/http-header-normalizer/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@middy/http-header-normalizer",
"version": "1.0.0-alpha.14",
"version": "1.0.0-alpha.15",
"description": "Http header normalizer middleware for the middy framework",
"engines": {
"node": ">=6.10"
Expand Down
2 changes: 1 addition & 1 deletion packages/http-json-body-parser/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@middy/http-json-body-parser",
"version": "1.0.0-alpha.14",
"version": "1.0.0-alpha.15",
"description": "Http JSON body parser middleware for the middy framework",
"engines": {
"node": ">=6.10"
Expand Down
2 changes: 1 addition & 1 deletion packages/http-partial-response/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@middy/http-partial-response",
"version": "1.0.0-alpha.14",
"version": "1.0.0-alpha.15",
"description": "Http partial response middleware for the middy framework",
"engines": {
"node": ">=6.10"
Expand Down
97 changes: 36 additions & 61 deletions packages/http-security-headers/__tests__/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
const { invoke } = require('../../test-helpers')
const middy = require('../../core')
const httpSecurityHeaders = require('../')

Expand All @@ -7,7 +6,7 @@ const createDefaultObjectResponse = () =>
{},
{
statusCode: 200,
body: { firstname: 'john', lastname: 'doe' }
body: {firstname: 'john', lastname: 'doe'}
}
)

Expand All @@ -29,16 +28,16 @@ const createHeaderObjectResponse = () =>
{},
{
statusCode: 200,
body: { firstname: 'john', lastname: 'doe' },
body: {firstname: 'john', lastname: 'doe'},
headers: {
Server: 'AMZN',
'Server': 'AMZN',
'X-Powered-By': 'MiddyJS'
}
}
)

describe('🔒 Middleware Http Security Headers', () => {
test('It should return default security headers', async () => {
test('It should modify default security headers', () => {
const handler = middy((event, context, cb) =>
cb(null, createDefaultObjectResponse())
)
Expand All @@ -49,22 +48,20 @@ describe('🔒 Middleware Http Security Headers', () => {
httpMethod: 'GET'
}

const response = await invoke(handler, event)
handler(event, {}, (_, response) => {
expect(response.headers['X-DNS-Prefetch-Control']).toEqual('off')
expect(response.headers['X-Powered-By']).toEqual(undefined)
expect(response.headers['Strict-Transport-Security']).toEqual('max-age=15552000; includeSubDomains; preload')
expect(response.headers['X-Download-Options']).toEqual('noopen')
expect(response.headers['X-Content-Type-Options']).toEqual('nosniff')
expect(response.headers['Referrer-Policy']).toEqual('no-referrer')

expect(response.statusCode).toEqual(200)
expect(response.headers['X-DNS-Prefetch-Control']).toEqual('off')
expect(response.headers['X-Powered-By']).toEqual(undefined)
expect(response.headers['Strict-Transport-Security']).toEqual('max-age=15552000; includeSubDomains; preload')
expect(response.headers['X-Download-Options']).toEqual('noopen')
expect(response.headers['X-Content-Type-Options']).toEqual('nosniff')
expect(response.headers['Referrer-Policy']).toEqual('no-referrer')
expect(response.headers['X-Permitted-Cross-Domain-Policies']).toEqual('none')

expect(response.headers['X-Frame-Options']).toEqual(undefined)
expect(response.headers['X-XSS-Protection']).toEqual(undefined)
expect(response.headers['X-Frame-Options']).toEqual(undefined)
expect(response.headers['X-XSS-Protection']).toEqual(undefined)
})
})

test('It should return default security headers when HTML', async () => {
test('It should modify default security headers when HTML', () => {
const handler = middy((event, context, cb) =>
cb(null, createHtmlObjectResponse())
)
Expand All @@ -75,21 +72,20 @@ describe('🔒 Middleware Http Security Headers', () => {
httpMethod: 'GET'
}

const response = await invoke(handler, event)

expect(response.headers['X-DNS-Prefetch-Control']).toEqual('off')
expect(response.headers['X-Powered-By']).toEqual(undefined)
expect(response.headers['Strict-Transport-Security']).toEqual('max-age=15552000; includeSubDomains; preload')
expect(response.headers['X-Download-Options']).toEqual('noopen')
expect(response.headers['X-Content-Type-Options']).toEqual('nosniff')
expect(response.headers['Referrer-Policy']).toEqual('no-referrer')
expect(response.headers['X-Permitted-Cross-Domain-Policies']).toEqual('none')
handler(event, {}, (_, response) => {
expect(response.headers['X-DNS-Prefetch-Control']).toEqual('off')
expect(response.headers['X-Powered-By']).toEqual(undefined)
expect(response.headers['Strict-Transport-Security']).toEqual('max-age=15552000; includeSubDomains; preload')
expect(response.headers['X-Download-Options']).toEqual('noopen')
expect(response.headers['X-Content-Type-Options']).toEqual('nosniff')
expect(response.headers['Referrer-Policy']).toEqual('no-referrer')

expect(response.headers['X-Frame-Options']).toEqual('DENY')
expect(response.headers['X-XSS-Protection']).toEqual('1; mode=block')
expect(response.headers['X-Frame-Options']).toEqual('DENY')
expect(response.headers['X-XSS-Protection']).toEqual('1; mode=block')
})
})

test('It should modify default security headers', async () => {
test('It should modify default security headers', () => {
const handler = middy((event, context, cb) =>
cb(null, createHeaderObjectResponse())
)
Expand All @@ -100,14 +96,13 @@ describe('🔒 Middleware Http Security Headers', () => {
httpMethod: 'GET'
}

const response = await invoke(handler, event)

expect(response.statusCode).toEqual(200)
expect(response.headers.Server).toEqual(undefined)
expect(response.headers['X-Powered-By']).toEqual(undefined)
handler(event, {}, (_, response) => {
expect(response.headers['Server']).toEqual(undefined)
expect(response.headers['X-Powered-By']).toEqual(undefined)
})
})

test('It should modify default security headers', async () => {
test('It should modify default security headers', () => {
const handler = middy((event, context, cb) =>
cb(null, createHtmlObjectResponse())
)
Expand All @@ -123,9 +118,6 @@ describe('🔒 Middleware Http Security Headers', () => {
hidePoweredBy: {
setTo: 'Other'
},
permittedCrossDomainPolicies: {
policy: 'all'
},
xssFilter: {
reportUri: 'https://example.com/report'
}
Expand All @@ -135,28 +127,11 @@ describe('🔒 Middleware Http Security Headers', () => {
httpMethod: 'GET'
}

const response = await invoke(handler, event)

expect(response.statusCode).toEqual(200)
expect(response.headers['X-Permitted-Cross-Domain-Policies']).toEqual('all')
expect(response.headers['X-DNS-Prefetch-Control']).toEqual('on')
expect(response.headers['X-Powered-By']).toEqual('Other')
expect(response.headers['Strict-Transport-Security']).toEqual('max-age=15552000')
expect(response.headers['X-XSS-Protection']).toEqual('1; mode=block; report=https://example.com/report')
})

test('It should catch thrown errors', async () => {
const handler = middy(async () => {
throw new Error('This error should not return 200')
handler(event, {}, (_, response) => {
expect(response.headers['X-DNS-Prefetch-Control']).toEqual('on')
expect(response.headers['X-Powered-By']).toEqual('Other')
expect(response.headers['Strict-Transport-Security']).toEqual('max-age=15552000')
expect(response.headers['X-XSS-Protection']).toEqual('1; mode=block; report=https://example.com/report')
})

handler.use(httpSecurityHeaders())

const event = {
httpMethod: 'GET'
}

const response = await invoke(handler, event)
expect(response.statusCode).toEqual(500)
})
})
4 changes: 2 additions & 2 deletions packages/http-security-headers/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import middy from '@middy/core'
import middy from '../core'

interface IHTTPSecurityHeadersOptions {
dnsPrefetchControl?: {
Expand Down Expand Up @@ -31,6 +31,6 @@ interface IHTTPSecurityHeadersOptions {
xssFilter?: Object
}

declare const httpSecurityHeaders : middy.Middleware<IHTTPSecurityHeadersOptions, any, any>
declare function httpSecurityHeaders(opts?: IHTTPSecurityHeadersOptions): middy.IMiddyMiddlewareObject;

export default httpSecurityHeaders
45 changes: 17 additions & 28 deletions packages/http-security-headers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,6 @@ const defaults = {
noSniff: {
action: 'nosniff'
},
permittedCrossDomainPolicies: {
policy: 'none' // none, master-only, by-content-type, by-ftp-filename, all
},
referrerPolicy: {
policy: 'no-referrer'
},
Expand Down Expand Up @@ -105,12 +102,6 @@ helmet.referrerPolicy = (headers, options) => {
return headers
}

// https://github.com/helmetjs/crossdomain
helmet.permittedCrossDomainPolicies = (headers, options) => {
headers['X-Permitted-Cross-Domain-Policies'] = options.policy
return headers
}

// https://github.com/helmetjs/x-xss-protection
helmetHtmlOnly.xssFilter = (headers, options) => {
let header = '1; mode=block'
Expand All @@ -121,30 +112,28 @@ helmetHtmlOnly.xssFilter = (headers, options) => {
return headers
}

module.exports = (opts = {}) => {
const response = (opts, handler, next) => {
opts = Object.assign({}, defaults, opts)

const response = (handler, next) => {
handler.response = handler.response || { statusCode: 500 } // catch thrown errors, prevent default statusCode
handler.response.headers = handler.response.headers || {}
handler.response = handler.response || {}
handler.response.headers = handler.response.headers || {}

Object.keys(helmet).forEach(key => {
const options = Object.assign({}, defaults[key], opts[key])
handler.response.headers = helmet[key](handler.response.headers, options)
})

Object.keys(helmet).forEach(key => {
if (handler.response.headers['Content-Type'] && handler.response.headers['Content-Type'].indexOf('text/html') !== -1) {
Object.keys(helmetHtmlOnly).forEach(key => {
const options = Object.assign({}, defaults[key], opts[key])
handler.response.headers = helmet[key](handler.response.headers, options)
handler.response.headers = helmetHtmlOnly[key](handler.response.headers, options)
})

if (handler.response.headers['Content-Type'] && handler.response.headers['Content-Type'].indexOf('text/html') !== -1) {
Object.keys(helmetHtmlOnly).forEach(key => {
const options = Object.assign({}, defaults[key], opts[key])
handler.response.headers = helmetHtmlOnly[key](handler.response.headers, options)
})
}

next()
}

return {
after: response,
onError: response
}
next()
}

module.exports = (opts = {}) => ({
after: response.bind(null, opts),
onError: response.bind(null, opts)
})
13 changes: 4 additions & 9 deletions packages/http-security-headers/package.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"name": "@middy/http-security-headers",
"version": "1.0.0",
"name": "@middy/http-security-header",
"version": "1.0.0-alpha.15",
"description": "Applies best practice security headers to responses. It's a simplified port of HelmetJS",
"engines": {
"node": ">=10"
"node": ">=6.10"
},
"engineStrict": true,
"publishConfig": {
Expand Down Expand Up @@ -44,10 +44,5 @@
"homepage": "https://github.com/middyjs/middy#readme",
"peerDependencies": {
"@middy/core": ">=1.0.0-alpha"
},
"devDependencies": {
"@middy/core": "^1.0.0",
"es6-promisify": "^6.0.2"
},
"gitHead": "7a6c0fbb8ab71d6a2171e678697de9f237568431"
}
}
3 changes: 1 addition & 2 deletions packages/http-security-headers/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
"compilerOptions": {
"module": "commonjs",
"lib": ["es2015"],
"target": "es2015",
"esModuleInterop": true
"target": "es2015"
},
"files": [
"index.d.ts"
Expand Down
Loading

0 comments on commit a8aa601

Please sign in to comment.