CVE-2020-4038 (published 2020-06-08T21:15Z)

GraphQL Playground (graphql-playground-html NPM package) before version 1.6.22 have a severe XSS Reflection attack vulnerability. All unsanitized user input passed into renderPlaygroundPage() method could trigger this vulnerability. This has been patched in graphql-playground-html version 1.6.22. Note that some of the associated dependent middleware packages are also affected including but not limited to graphql-playground-middleware-express before version 1.7.16, graphql-playground-middleware-koa before version 1.6.15, graphql-playground-middleware-lambda before version 1.7.17, and graphql-playground-middleware-hapi before 1.6.13.

Severity: MEDIUM
CVSS v2 base score: 4.3 (AV:N/AC:M/Au:N/C:N/I:P/A:N)
CVSS v3 base score: 7.4, HIGH (CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:N/I:H/A:N)

Weakness information:
cwe_id: CWE-79
cwe_name: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
description: The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.
extended_description: 
            Cross-site scripting (XSS) vulnerabilities occur when:
            
               Untrusted data enters a web application, typically from a web request.
               The web application dynamically generates a web page that contains this untrusted data.
               During page generation, the application does not prevent the data from containing content that is executable by a web browser, such as JavaScript, HTML tags, HTML attributes, mouse events, Flash, ActiveX, etc.
               A victim visits the generated web page through a web browser, which contains malicious script that was injected using the untrusted data.
               Since the script comes from a web page that was sent by the web server, the victim's web browser executes the malicious script in the context of the web server's domain.
               This effectively violates the intention of the web browser's same-origin policy, which states that scripts in one domain should not be able to access resources or run code in a different domain.
            
            There are three main kinds of XSS:
            
               
                  Type 1: Reflected XSS (or Non-Persistent) - 
         			The server reads data directly from the HTTP request and reflects it back in the HTTP response. Reflected XSS exploits occur when an attacker causes a victim to supply dangerous content to a vulnerable web application, which is then reflected back to the victim and executed by the web browser. The most common mechanism for delivering malicious content is to include it as a parameter in a URL that is posted publicly or e-mailed directly to the victim. URLs constructed in this manner constitute the core of many phishing schemes, whereby an attacker convinces a victim to visit a URL that refers to a vulnerable site. After the site reflects the attacker's content back to the victim, the content is executed by the victim's browser.
               
                  Type 2: Stored XSS (or Persistent) - 
               The application stores dangerous data in a database, message forum, visitor log, or other trusted data store. At a later time, the dangerous data is subsequently read back into the application and included in dynamic content. From an attacker's perspective, the optimal place to inject malicious content is in an area that is displayed to either many users or particularly interesting users. Interesting users typically have elevated privileges in the application or interact with sensitive data that is valuable to the attacker. If one of these users executes malicious content, the attacker may be able to perform privileged operations on behalf of the user or gain access to sensitive data belonging to the user. For example, the attacker might inject XSS into a log message, which might not be handled properly when an administrator views the logs.
            
               
                  Type 0: DOM-Based XSS - 
               In DOM-based XSS, the client performs the injection of XSS into the page; in the other types, the server performs the injection. DOM-based XSS generally involves server-controlled, trusted script that is sent to the client, such as Javascript that performs sanity checks on a form before the user submits it. If the server-supplied script processes user-supplied data and then injects it back into the web page (such as with dynamic HTML), then DOM-based XSS is possible.
            
            
            Once the malicious script is injected, the attacker can perform a variety of malicious activities. The attacker could transfer private information, such as cookies that may include session information, from the victim's machine to the attacker. The attacker could send malicious requests to a web site on behalf of the victim, which could be especially dangerous to the site if the victim has administrator privileges to manage that site. Phishing attacks could be used to emulate trusted web sites and trick the victim into entering a password, allowing the attacker to compromise the victim's account on that web site. Finally, the script could exploit a vulnerability in the web browser itself possibly taking over the victim's machine, sometimes referred to as "drive-by hacking."
            In many cases, the attack can be launched without the victim even being aware of it. Even with careful users, attackers frequently use a variety of methods to encode the malicious portion of the attack, such as URL encoding or Unicode, so the request looks less suspicious.
         
         

Fix information:
repo_url: https://github.com/prisma-labs/graphql-playground
commit_details: repo_url: https://github.com/prisma-labs/graphql-playground
author: Rikki Schulte
author_date: 2020-06-06 21:17:58-04:00
msg: Merge pull request from GHSA-4852-vrh7-28rf

* fix: security vulnerability allowing XSS reflection with user input

* fix: update docs, replace santize-html and sanitizeUrl with xss module
repository: repo_url: https://github.com/prisma-labs/graphql-playground
repo_name: graphql/graphql-playground
description: 🎮  GraphQL IDE for better development workflows (GraphQL Subscriptions, interactive docs & collaboration)
repo_language: TypeScript
owner: prisma-labs
file_changes: filename: README.md
change_type: ModificationType.MODIFY
diff: @@ -1,6 +1,8 @@
+> **SECURITY WARNING:** This `graphql-playground-html` and [all of it's middleware dependents](#impacted-packages) in this repository **had a severe XSS Reflection attack vulnerability to unsanitized user input** until version `graphql-playground-html@1.6.20`. Impacted are any and all **user-defined** input to `renderPlaygroundPage()`, `koaPlayground()`,`expressPlayground()`, `koaPlayground()`, or `lambdaPlayground()`. If you used static values, such as `graphql-playground-electron` does in [it's webpack config](https://github.com/prisma-labs/graphql-playground/blob/master/packages/graphql-playground-electron/webpack.config.build.js#L16), you were not vulnerable to the attack. [More Details](./SECURITY.md)
+
 <p align="center"><img src="https://imgur.com/5fzMbyV.png" width="269"></p>
 
-[![npm version](https://badge.fury.io/js/graphql-playground-react.svg)](https://badge.fury.io/js/graphql-playground-react) 
+[![npm version](https://badge.fury.io/js/graphql-playground-react.svg)](https://badge.fury.io/js/graphql-playground-react)
 [![prisma-labs](https://circleci.com/gh/prisma-labs/graphql-playground.svg?style=shield)](https://circleci.com/gh/prisma-labs/graphql-playground)
 
 **Future of this repository**: see [the announcement issue](https://github.com/prisma-labs/graphql-playground/issues/1143) for details.
@@ -25,6 +27,32 @@ $ brew cask install graphql-playground
 - ⚙ GraphQL Config support with multiple Projects & Endpoints
 - 🚥 Apollo Tracing support
 
+## Security Details
+
+**NOTE: only _unsanitized user input_ to the functions in these packages is vulnerable** to the recently reported XSS Reflection attack.
+
+### Impact
+
+The only reason this vulnerability exists is because we are using template strings in `renderPlaygroundPage()` with potentially user defined variables. This allows an attacker to inject html and javascript into a page on execution.
+
+Common examples may be user-defined path parameters, query string, unsanitized UI provided values in database, etc that are used to build template strings or passed directly to a `renderPlaygroundPage()` or the matching middleware function equivalent.
+
+### Impacted Packages
+
+**All versions of these packages are impacted until the ones specified below**, which are now safe for user defined input:
+
+- `graphql-playground-html`: **☔ safe** @ `1.6.20`
+- `graphql-playground-express` **☔ safe** @ `1.7.15`
+- `graphql-playground-koa` **☔ safe** @ `1.6.14`
+- `graphql-playground-hapi` **☔ safe** @ `1.6.12`
+- `graphql-playground-lambda` **☔ safe** @ `1.7.16`
+- `graphql-playground-electron` has always been **☔ safe** from XSS attacks! This is because configuration is statically defined [it's webpack config](https://github.com/prisma-labs/graphql-playground/blob/master/packages/graphql-playground-electron/webpack.config.build.js#L16)
+- `graphql-playground-react` is safe because it does not use `renderPlaygroundPage()` anywhere, and thus is not susceptible to template string XSS reflection attacks.
+
+### More Information
+
+See the [security docs](./SECURITY.md) for more details on how your implementation might be impacted by this vulnerability. It contains safe examples, unsafe examples, workarounds, and more details.
+
 ## FAQ
 
 ### How is this different from [GraphiQL](https://github.com/graphql/graphiql)?
@@ -122,12 +150,12 @@ interface ISettings {
 
 ```ts
 interface Tab {
-	endpoint: string
-	query: string
-	name?: string
-	variables?: string
-	responses?: string[]
-	headers?: { [key: string]: string }
+  endpoint: string
+  query: string
+  name?: string
+  variables?: string
+  responses?: string[]
+  headers?: { [key: string]: string }
 }
 ```
 
@@ -169,8 +197,8 @@ Including Fonts (`1.`)
 
 ```html
 <link
-	href="https://fonts.googleapis.com/css?family=Open+Sans:300,400,600,700|Source+Code+Pro:400,700"
-	rel="stylesheet"
+  href="https://fonts.googleapis.com/css?family=Open+Sans:300,400,600,700|Source+Code+Pro:400,700"
+  rel="stylesheet"
 />
 ```
 
@@ -183,10 +211,10 @@ import { Provider } from 'react-redux'
 import { Playground, store } from 'graphql-playground-react'
 
 ReactDOM.render(
-	<Provider store={store}>
-		<Playground endpoint="https://api.graph.cool/simple/v1/swapi" />
-	</Provider>,
-	document.body,
+  <Provider store={store}>
+    <Playground endpoint='https://api.graph.cool/simple/v1/swapi' />
+  </Provider>,
+  document.body,
 )
 ```
 
@@ -232,18 +260,18 @@ import lambdaPlayground from 'graphql-playground-middleware-lambda'
 // const lambdaPlayground = require('graphql-playground-middleware-lambda').default
 
 exports.graphqlHandler = function graphqlHandler(event, context, callback) {
-	function callbackFilter(error, output) {
-		// eslint-disable-next-line no-param-reassign
-		output.headers['Access-Control-Allow-Origin'] = '*'
-		callback(error, output)
-	}
-
-	const handler = graphqlLambda({ schema: myGraphQLSchema })
-	return handler(event, context, callbackFilter)
+  function callbackFilter(error, output) {
+    // eslint-disable-next-line no-param-reassign
+    output.headers['Access-Control-Allow-Origin'] = '*'
+    callback(error, output)
+  }
+
+  const handler = graphqlLambda({ schema: myGraphQLSchema })
+  return handler(event, context, callbackFilter)
 }
 
 exports.playgroundHandler = lambdaPlayground({
-	endpoint: '/dev/graphql',
+  endpoint: '/dev/graphql',
 })
 ```
 
@@ -267,6 +295,10 @@ functions:
           cors: true
 ```
 
+#### Security Issue
+
+There is an [XSS Reflection Vulnerability](./SECURITY.md) when using these middlewares with unsanitized user input before
+
 ## Development
 
 ```sh
@@ -285,27 +317,27 @@ These are the available options:
 
 ```ts
 export interface EditorColours {
-	property: string
-	comment: string
-	punctuation: string
-	keyword: string
-	def: string
-	qualifier: string
-	attribute: string
-	number: string
-	string: string
-	builtin: string
-	string2: string
-	variable: string
-	meta: string
-	atom: string
-	ws: string
-	selection: string
-	cursorColor: string
-	editorBackground: string
-	resultBackground: string
-	leftDrawerBackground: string
-	rightDrawerBackground: string
+  property: string
+  comment: string
+  punctuation: string
+  keyword: string
+  def: string
+  qualifier: string
+  attribute: string
+  number: string
+  string: string
+  builtin: string
+  string2: string
+  variable: string
+  meta: string
+  atom: string
+  ws: string
+  selection: string
+  cursorColor: string
+  editorBackground: string
+  resultBackground: string
+  leftDrawerBackground: string
+  rightDrawerBackground: string
 }
 ```
 

programming_language: Markdown
filename: SECURITY.md
change_type: ModificationType.ADD
diff: @@ -0,0 +1,132 @@
+# Known Vulnerabilities
+
+## XSS Reflection Vulnerability
+
+the origin of the vulnerability is in `renderPlaygroundPage`, found in `graphql-playground-html`
+
+### Impact
+
+When using
+
+- `renderPlaygroundPage()`,
+- `koaPlayground()`
+- `expressPlayground()`
+- `koaPlayground()`
+- `lambdaPlayground()`
+- any downstream dependents that use these functions
+
+without sanitization of user input, your application is vulnerable to an XSS Reflecton Attack. This is a serious vulnerability that could allow for exfiltration of data or user credentials, or to disrupt systems.
+
+### Impacted Packages
+
+**All versions of these packages are impacted until those specified below**, which are now safe for user defined input:
+
+- `graphql-playground-html`: **☔ safe** @ `1.6.20`
+- `graphql-playground-express` **☔ safe** @ `1.7.15`
+- `graphql-playground-koa` **☔ safe** @ `1.6.14`
+- `graphql-playground-hapi` **☔ safe** @ `1.6.12`
+- `graphql-playground-lambda` **☔ safe** @ `1.7.16`
+
+### Static input was always safe
+
+These examples are safe for _all versions_ **because input is static**
+
+with `express` and `renderPlaygroundPage`:
+
+```js
+app.get('/playground', (req) => {
+  res.html(
+    renderPlaygroundPage({
+      endpoint: `/our/graphql`,
+    }),
+  )
+  next()
+})
+```
+
+with `expressPlayground`:
+
+```js
+// params
+app.get('/playground', (req) =>
+  expressPlayground({
+    endpoint: `/our/graphql`,
+    settings: { 'editor.theme': req.query.darkMode ? 'dark' : 'light' },
+  }),
+)
+```
+
+with `koaPlayground`:
+
+```js
+const koa = require('koa')
+const koaRouter = require('koa-router')
+const koaPlayground = require('graphql-playground-middleware-koa')
+
+const app = new koa()
+const router = new koaRouter()
+
+router.all('/playground', koaPlayground({ endpoint: '/graphql' }))
+```
+
+### Vulnerable Examples
+
+Here are some examples where the vulnerability would be present before the patch, because of unfiltered user input
+
+```js
+const express = require('express')
+const expressPlayground = require('graphql-playground-middleware-express')
+  .default
+
+const app = express()
+
+app.use(express.json())
+
+// params
+app.get('/playground/:id', (req) =>
+  expressPlayground({
+    endpoint: `/our/graphql/${req.params.id}`,
+  }),
+)
+
+// params
+app.get('/playground', (req) =>
+  expressPlayground({
+    endpoint: `/our/graphql`,
+    // any settings that are unsanitized user input, not just `endpoint`
+    settings: { 'editor.fontFamily': req.query.font },
+  }),
+)
+```
+
+### Workaround
+
+To fix this issue without the update, you can sanitize however you want.
+
+We suggest using [`xss`](https://www.npmjs.com/package/xss) (what we use for our own fix)
+
+For example, with `graphql-playground-middleware-express`:
+
+```js
+const express = require('express')
+const { filterXSS } = require('xss')
+const expressPlayground = require('graphql-playground-middleware-express')
+  .default
+
+
+const app = express()
+
+const filter = (val) => filterXSS(val, {
+  whitelist: [],
+  stripIgnoreTag: true,
+  stripIgnoreTagBody: ['script']
+})
+
+// simple example
+app.get('/playground/:id', (req) =>
+  expressPlayground({ endpoint: `/graphql/${filter(req.params.id)}` })
+
+// advanced params
+app.get('/playground', (req) =>
+  expressPlayground(JSON.parse(filter(JSON.stringify(req.query))))
+```

programming_language: Markdown
filename: README.md
change_type: ModificationType.MODIFY
diff: @@ -1,5 +1,7 @@
 # graphql-playground-html
 
+> **SECURITY WARNING:** This package and all of it's dependendents had a severe XSS Reflection attack vulnerability until version `1.6.20` of this package. You must sanitize any and all user input values to `renderPlaygroundPage()` values. If you used static values in your middlewares, including ours, you were not vulnerable to the attack.
+
 This package is being used by the GraphQL Playground middlewares.
 
 For local development, you can `yarn link` this package, then use `yarn link graphql-playground-html` in the

programming_language: Markdown
filename: package.json
change_type: ModificationType.MODIFY
diff: @@ -33,5 +33,7 @@
   "typescript": {
     "definition": "dist/index.d.ts"
   },
-  "dependencies": {}
+  "dependencies": {
+    "xss": "^1.0.6"
+  }
 }

programming_language: JavaScript
filename: render-playground-page.ts
change_type: ModificationType.MODIFY
diff: @@ -1,3 +1,5 @@
+import { filterXSS } from 'xss';
+
 import getLoadingMarkup from './get-loading-markup'
 
 export interface MiddlewareOptions {
@@ -72,20 +74,38 @@ export interface Tab {
   headers?: { [key: string]: string }
 }
 
+const filter = (val) => {
+  return filterXSS(val, {
+    // @ts-ignore
+    whiteList: [],
+    stripIgnoreTag: true,
+    stripIgnoreTagBody: ["script"]
+  })
+}
+
+
 const loading = getLoadingMarkup()
 
-const getCdnMarkup = ({ version, cdnUrl = '//cdn.jsdelivr.net/npm', faviconUrl }) => `
-    <link rel="stylesheet" href="${cdnUrl}/graphql-playground-react${
-  version ? `@${version}` : ''
-}/build/static/css/index.css" />
-    ${typeof faviconUrl === 'string' ? `<link rel="shortcut icon" href="${faviconUrl}" />` : ''}
-    ${faviconUrl === undefined ? `<link rel="shortcut icon" href="${cdnUrl}/graphql-playground-react${
-      version ? `@${version}` : ''
-    }/build/favicon.png" />` : ''}
-    <script src="${cdnUrl}/graphql-playground-react${
-  version ? `@${version}` : ''
-}/build/static/js/middleware.js"></script>
-`
+const getCdnMarkup = ({ version, cdnUrl = '//cdn.jsdelivr.net/npm', faviconUrl }) => {
+  const buildCDNUrl = (packageName: string, suffix: string) => filter(`${cdnUrl}/${packageName}/${version ? `@${version}/` : ''}${suffix}` || '')
+  return `
+    <link 
+      rel="stylesheet" 
+      href="${buildCDNUrl('graphql-playground-react', 'build/static/css/index.css')}"
+    />
+    ${typeof faviconUrl === 'string' ? `<link rel="shortcut icon" href="${filter(faviconUrl || '')}" />` : ''}
+    ${faviconUrl === undefined ? `<link rel="shortcut icon" href="${buildCDNUrl('graphql-playground-react', 'build/favicon.png')}" />` : ''}
+    <script 
+      src="${buildCDNUrl('graphql-playground-react', 'build/static/js/middleware.js')}"
+    ></script>
+`}
+
+
+const renderConfig = (config) => {
+  return filterXSS(`<div id="config">${JSON.stringify(config)}</div>`, {
+    whiteList: { div: ['id'] },
+  })
+}
 
 export function renderPlaygroundPage(options: RenderPageOptions) {
   const extendedOptions: any = {
@@ -94,7 +114,7 @@ export function renderPlaygroundPage(options: RenderPageOptions) {
   }
   // for compatibility
   if ((options as any).subscriptionsEndpoint) {
-    extendedOptions.subscriptionEndpoint = (options as any).subscriptionsEndpoint
+    extendedOptions.subscriptionEndpoint = filter((options as any).subscriptionsEndpoint || '')
   }
   if (options.config) {
     extendedOptions.configString = JSON.stringify(options.config, null, 2)
@@ -105,6 +125,9 @@ export function renderPlaygroundPage(options: RenderPageOptions) {
       `WARNING: You didn't provide an endpoint and don't have a .graphqlconfig. Make sure you have at least one of them.`,
     )
   }
+  else if (extendedOptions.endpoint) {
+    extendedOptions.endpoint = filter(extendedOptions.endpoint || '')
+  }
 
   return `
   <!DOCTYPE html>
@@ -115,9 +138,9 @@ export function renderPlaygroundPage(options: RenderPageOptions) {
     <link href="https://fonts.googleapis.com/css?family=Open+Sans:300,400,600,700|Source+Code+Pro:400,700" rel="stylesheet">
     <title>${extendedOptions.title || 'GraphQL Playground'}</title>
     ${
-      extendedOptions.env === 'react' || extendedOptions.env === 'electron'
-        ? ''
-        : getCdnMarkup(extendedOptions)
+    extendedOptions.env === 'react' || extendedOptions.env === 'electron'
+      ? ''
+      : getCdnMarkup(extendedOptions)
     }
   </head>
   <body>
@@ -168,6 +191,7 @@ export function renderPlaygroundPage(options: RenderPageOptions) {
       }
     </style>
     ${loading.container}
+    ${renderConfig(extendedOptions)}
     <div id="root" />
     <script type="text/javascript">
       window.addEventListener('load', function (event) {
@@ -175,12 +199,15 @@ export function renderPlaygroundPage(options: RenderPageOptions) {
   
         const root = document.getElementById('root');
         root.classList.add('playgroundIn');
-  
-        GraphQLPlayground.init(root, ${JSON.stringify(
-          extendedOptions,
-          null,
-          2,
-        )})
+        const configText = document.getElementById('config').innerText
+        if(configText && configText.length) {
+          try {
+            GraphQLPlayground.init(root, JSON.parse(configText))
+          }
+          catch(err) {
+            console.error("could not find config")
+          }
+        }
       })
     </script>
   </body>

programming_language: TypeScript
filename: README.md
change_type: ModificationType.MODIFY
diff: @@ -1,6 +1,7 @@
 # graphql-playground-middleware-express
 
 > Express middleware to expose an endpoint for the GraphQL Playground IDE
+> **SECURITY NOTE**: All versions of `graphql-playground-express` until `1.7.15` or later have a security vulnerability when unsanitized user input is used while invoking `expressPlayground()`. [Read more below](#security-notes)
 
 ## Installation
 
@@ -29,3 +30,17 @@ const app = express()
 
 app.get('/playground', expressPlayground({ endpoint: '/graphql' }))
 ```
+
+## Security Notes
+
+All versions before `1.7.15` were vulnerable to user-defined input to `expressPlayground()`. Read more in [the security notes](https://github.com/prisma/graphql-playground/tree/master/SECURITY.md)
+
+### Security Upgrade Steps
+
+To fix the issue, you can upgrade to `1.6.12` or later. If you aren't able to upgrade, see the security notes for a workaround.
+
+**yarn:**
+`yarn add graphql-playground-express@^1.7.15`
+
+**npm:**
+`npm install --save graphql-playground-express@^1.7.15`

programming_language: Markdown
filename: index.js
change_type: ModificationType.MODIFY
diff: @@ -1,6 +1,6 @@
-const express = require("express");
-const { ApolloServer, gql } = require("apollo-server-express");
-const expressPlayground = require("../../dist/index").default;
+const express = require('express')
+const { ApolloServer, gql } = require('apollo-server-express')
+const expressPlayground = require('../../dist/index').default
 
 const typeDefs = gql`
   type Query {
@@ -9,22 +9,27 @@ const typeDefs = gql`
   schema {
     query: Query
   }
-`;
+`
 const resolvers = {
   Query: {
-    hello: () => "world"
-  }
-};
+    hello: () => 'world',
+  },
+}
 
-const PORT = 4000;
+const PORT = 4000
 
-const server = new ApolloServer({ typeDefs, resolvers });
-const app = express();
-server.applyMiddleware({ app });
+const server = new ApolloServer({ typeDefs, resolvers })
+const app = express()
+server.applyMiddleware({ app })
 
-app.get("/playground", expressPlayground({ endpoint: "/graphql" }));
-app.listen(PORT);
+app.get(
+  '/playground',
+  expressPlayground({
+    endpoint: '/graphql/</script><script>alert(1)</script><script>',
+  }),
+)
+app.listen(PORT)
 
 console.log(
-  `Serving the GraphQL Playground on http://localhost:${PORT}/playground`
-);
+  `Serving the GraphQL Playground on http://localhost:${PORT}/playground`,
+)

programming_language: JavaScript
filename: README.md
change_type: ModificationType.ADD
diff: @@ -0,0 +1,57 @@
+# graphql-playground-middleware-hapi
+
+> Koa middleware to expose an endpoint for the GraphQL Playground IDE
+> **SECURITY NOTE**: All versions of `graphql-playground-middleware-hapi` until `1.6.12` or later have a security vulnerability when unsanitized user input is used while invoking `hapiPlayground()`. [Read more below](#security-notes)
+
+## Installation
+
+Using yarn:
+
+```console
+yarn add graphql-playground-middleware-hapi
+```
+
+Or npm:
+
+```console
+npm install graphql-playground-middleware-hapi --save
+```
+
+## Usage
+
+See full example in [examples/basic](https://github.com/prisma/graphql-playground/tree/master/packages/graphql-playground-middleware-hapi/examples/basic).
+
+minimal example:
+
+```js
+const hapiPlayground = require('graphql-playground-middleware-hapi').default
+
+const playground = {
+  plugin: hapiPlayground,
+  options: {
+    path: '/playground',
+    endpoint: '/graphql',
+  },
+}
+
+const app = new Hapi.server({
+  port: 3000,
+})
+
+app.register(playground)
+;(async () => await app.start())()
+```
+
+## Security Notes
+
+All versions before `1.6.12` were vulnerable to user-defined input to `hapiPlayground()`. Read more in [the security notes](https://github.com/prisma/graphql-playground/tree/master/SECURITY.md)
+
+### Security Upgrade Steps
+
+To fix the issue, you can upgrade to `1.6.12` or later. If you aren't able to upgrade, see the security notes for a workaround.
+
+**yarn:**
+`yarn add graphql-playground-middleware-hapi@^1.6.12`
+
+**npm:**
+`npm install --save graphql-playground-middleware-hapi@^1.6.12`

programming_language: Markdown
filename: README.md
change_type: ModificationType.MODIFY
diff: @@ -1,5 +1,7 @@
 # graphql-playground-middleware-koa
+
 > Koa middleware to expose an endpoint for the GraphQL Playground IDE
+> **SECURITY NOTE**: All versions of `graphql-playground-koa` until `1.6.14` or later have a security vulnerability when unsanitized user input is used while invoking `koaPlayground()`. [Read more below](#security-notes)
 
 ## Installation
 
@@ -29,3 +31,17 @@ const router = new koaRouter()
 
 router.all('/playground', koaPlayground({ endpoint: '/graphql' }))
 ```
+
+## Security Notes
+
+All versions before `1.6.14` were vulnerable to user-defined input to `koaPlayground()`. Read more in [the security notes](https://github.com/prisma/graphql-playground/tree/master/SECURITY.md)
+
+### Security Upgrade Steps
+
+To fix the issue, you can upgrade to `1.6.14` or later. If you aren't able to upgrade, see the security notes for a workaround.
+
+**yarn:**
+`yarn add graphql-playground-koa@^1.6.14`
+
+**npm:**
+`npm install --save graphql-playground-koa@^1.6.14`

programming_language: Markdown
filename: README.md
change_type: ModificationType.ADD
diff: @@ -0,0 +1,46 @@
+# graphql-playground-middleware-lambda
+
+> Koa middleware to expose an endpoint for the GraphQL Playground IDE
+> **SECURITY NOTE**: All versions of `graphql-playground-middleware-lambda` until `1.7.16` or later have a security vulnerability when unsanitized user input is used while invoking `lambdaPlayground()`. [Read more below](#security-notes)
+
+## Installation
+
+Using yarn:
+
+```console
+yarn add graphql-playground-middleware-lambda
+```
+
+Or npm:
+
+```console
+npm install graphql-playground-middleware-lambda --save
+```
+
+## Usage
+
+See full example in [examples/basic](https://github.com/prisma/graphql-playground/tree/master/packages/graphql-playground-middleware-lambda/examples/basic).
+
+minimal example:
+
+```js
+const lambdaPlayground = require('graphql-playground-middleware-lambda').default
+
+exports.handler = lambdaPlayground({
+  endpoint: '/dev',
+})
+```
+
+## Security Notes
+
+All versions before `1.7.16` were vulnerable to user-defined input to `lambdaPlayground()`. Read more in [the security notes](https://github.com/prisma/graphql-playground/tree/SECURITY.md)
+
+### Security Upgrade Steps
+
+To fix the issue, you can upgrade to `1.7.16`. If you aren't able to upgrade, see the security notes for a workaround.
+
+**yarn:**
+`yarn add graphql-playground-middleware-lambda@^1.7.16`
+
+**npm:**
+`npm install --save graphql-playground-middleware-lambda@^1.7.16`

programming_language: Markdown
filename: README.md
change_type: ModificationType.MODIFY
diff: @@ -1,11 +1,16 @@
 # graphql playground serverless example
 
 ## Deploy
+
 ```
 $ sls deploy
 ```
 
 ## Hosted Example
+
 This example is also hosted here:
 https://1rp2h4rth8.execute-api.us-east-1.amazonaws.com/dev/playground
 
+exports.playgroundHandler = lambdaPlayground({
+endpoint: '/dev',
+})

programming_language: Markdown
filename: README.md
change_type: ModificationType.MODIFY
diff: @@ -73,7 +73,10 @@ The GraphQL Playground requires **React 16**.
 Including Fonts (`1.`)
 
 ```html
-<link href="https://fonts.googleapis.com/css?family=Open+Sans:300,400,600,700|Source+Code+Pro:400,700" rel="stylesheet">
+<link
+  href="https://fonts.googleapis.com/css?family=Open+Sans:300,400,600,700|Source+Code+Pro:400,700"
+  rel="stylesheet"
+/>
 ```
 
 Including stylesheet and the component (`2., 3.`)
@@ -84,7 +87,7 @@ import ReactDOM from 'react-dom'
 import Playground from 'graphql-playground'
 
 ReactDOM.render(
-  <Playground endpoint="https://api.graph.cool/simple/v1/swapi" />,
+  <Playground endpoint='https://api.graph.cool/simple/v1/swapi' />,
   document.body,
 )
 ```
@@ -153,17 +156,17 @@ functions:
   graphql:
     handler: handler.graphqlHandler
     events:
-    - http:
-        path: graphql
-        method: post
-        cors: true
+      - http:
+          path: graphql
+          method: post
+          cors: true
   playground:
     handler: handler.playgroundHandler
     events:
-    - http:
-        path: playground
-        method: get
-        cors: true
+      - http:
+          path: playground
+          method: get
+          cors: true
 ```
 
 ## Development [![npm version](https://badge.fury.io/js/graphql-playground-react.svg)](https://badge.fury.io/js/graphql-playground-react)

programming_language: Markdown
filename: build.sh
change_type: ModificationType.MODIFY
diff: @@ -5,8 +5,8 @@ set -e
 cd packages
 
 packages=(
-  graphql-playground-react
   graphql-playground-html
+  graphql-playground-react
   # graphql-playground-electron
   graphql-playground-middleware-express
   graphql-playground-middleware-hapi

programming_language: Shell
filename: yarn.lock
change_type: ModificationType.MODIFY
diff: @@ -4601,6 +4601,11 @@ cssesc@^3.0.0:
   resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
   integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==
 
+cssfilter@0.0.10:
+  version "0.0.10"
+  resolved "https://registry.yarnpkg.com/cssfilter/-/cssfilter-0.0.10.tgz#c6d2672632a2e5c83e013e6864a42ce8defd20ae"
+  integrity sha1-xtJnJjKi5cg+AT5oZKQs6N79IK4=
+
 cssnano@^3.10.0:
   version "3.10.0"
   resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-3.10.0.tgz#4f38f6cea2b9b17fa01490f23f1dc68ea65c1c38"
@@ -15128,6 +15133,14 @@ xregexp@4.0.0:
   resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-4.0.0.tgz#e698189de49dd2a18cc5687b05e17c8e43943020"
   integrity sha512-PHyM+sQouu7xspQQwELlGwwd05mXUFqwFYfqPO0cC7x4fxyHnnuetmQr6CjJiafIDoH4MogHb9dOoJzR/Y4rFg==
 
+xss@^1.0.6:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/xss/-/xss-1.0.6.tgz#eaf11e9fc476e3ae289944a1009efddd8a124b51"
+  integrity sha512-6Q9TPBeNyoTRxgZFk5Ggaepk/4vUOYdOsIUYvLehcsIZTFjaavbVnsuAkLA5lIFuug5hw8zxcB9tm01gsjph2A==
+  dependencies:
+    commander "^2.9.0"
+    cssfilter "0.0.10"
+
 xtend@^4.0.0, xtend@~4.0.1:
   version "4.0.2"
   resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"

programming_language: Shell