From 00cc53e0458e378fda74c5c5e82acb99f2f62542 Mon Sep 17 00:00:00 2001 From: Grzegorz Leoniec Date: Sun, 28 Oct 2018 18:47:24 +0100 Subject: [PATCH] added xss protection, automatic links and, date middleware --- package.json | 5 + src/bootstrap/directives.js | 16 ++ src/bootstrap/scalars.js | 13 ++ src/index.js | 50 ++---- src/middleware/dateTimeMiddleware.js | 54 +++++++ src/middleware/index.js | 4 + src/middleware/xssMiddleware.js | 124 +++++++++++++++ src/nodes/users/userMiddleware.js | 0 src/schema.graphql | 6 + yarn.lock | 228 +++++++++++++++++++++++++-- 10 files changed, 450 insertions(+), 50 deletions(-) create mode 100644 src/bootstrap/directives.js create mode 100644 src/bootstrap/scalars.js create mode 100644 src/middleware/dateTimeMiddleware.js create mode 100644 src/middleware/xssMiddleware.js create mode 100644 src/nodes/users/userMiddleware.js diff --git a/package.json b/package.json index bfddc0aa8..dcde920d2 100644 --- a/package.json +++ b/package.json @@ -18,12 +18,16 @@ "apollo-link-http": "^1.5.4", "apollo-server": "^2.0.4", "bcryptjs": "^2.4.3", + "cheerio": "^1.0.0-rc.2", + "date-fns": "^2.0.0-alpha.24", "dotenv": "^6.0.0", "graphql-custom-directives": "^0.2.13", + "graphql-iso-date": "^3.6.1", "graphql-middleware": "1.7.6", "graphql-tag": "^2.9.2", "graphql-yoga": "1.16.2", "jsonwebtoken": "^8.3.0", + "linkifyjs": "^2.1.7", "lodash": "^4.17.11", "ms": "^2.1.1", "neo4j-driver": "^1.6.1", @@ -31,6 +35,7 @@ "node-fetch": "^2.1.2", "passport": "^0.4.0", "passport-jwt": "^4.0.0", + "sanitize-html": "^1.19.1", "slug": "^0.9.1", "trunc-html": "^1.1.2" }, diff --git a/src/bootstrap/directives.js b/src/bootstrap/directives.js new file mode 100644 index 000000000..280713721 --- /dev/null +++ b/src/bootstrap/directives.js @@ -0,0 +1,16 @@ +import { + GraphQLLowerCaseDirective, + GraphQLTrimDirective, + GraphQLDefaultToDirective +} from 'graphql-custom-directives' + +export default function applyDirectives(augmentedSchema) { + const directives = [ + GraphQLLowerCaseDirective, + GraphQLTrimDirective, + GraphQLDefaultToDirective + ] + augmentedSchema._directives.push.apply(augmentedSchema._directives, directives) + + return augmentedSchema +} diff --git a/src/bootstrap/scalars.js b/src/bootstrap/scalars.js new file mode 100644 index 000000000..2676f69ae --- /dev/null +++ b/src/bootstrap/scalars.js @@ -0,0 +1,13 @@ +import { + GraphQLDate, + GraphQLTime, + GraphQLDateTime +} from 'graphql-iso-date' + +export default function applyScalars(augmentedSchema) { + augmentedSchema._typeMap.Date = GraphQLDate + augmentedSchema._typeMap.Time = GraphQLTime + augmentedSchema._typeMap.DateTime = GraphQLDateTime + + return augmentedSchema +} diff --git a/src/index.js b/src/index.js index e975ba57d..7aaf8904d 100644 --- a/src/index.js +++ b/src/index.js @@ -6,17 +6,13 @@ import { v1 as neo4j } from 'neo4j-driver' import dotenv from 'dotenv' import mocks from './mocks' import middleware from './middleware' +import applyDirectives from './bootstrap/directives' +import applyScalars from './bootstrap/scalars' import passport from 'passport' import jwtStrategy from './jwt/strategy' import jwt from 'jsonwebtoken' -// import { -// GraphQLLowerCaseDirective, -// GraphQLTrimDirective, -// GraphQLDefaultToDirective -// } from 'graphql-custom-directives'; - dotenv.config() const schema = makeExecutableSchema({ @@ -24,17 +20,6 @@ const schema = makeExecutableSchema({ resolvers }) -// augmentSchema will add auto generated mutations based on types in schema -// const augmentedSchema = augmentSchema(schema) - -// add custom directives -// const directives = [ -// GraphQLLowerCaseDirective, -// GraphQLTrimDirective, -// GraphQLDefaultToDirective -// ] -// augmentedSchema._directives.push.apply(augmentedSchema._directives, directives) - const driver = neo4j.driver( process.env.NEO4J_URI || 'bolt://localhost:7687', neo4j.auth.basic( @@ -46,6 +31,15 @@ const driver = neo4j.driver( const MOCK = (process.env.MOCK === 'true') console.log('MOCK:', MOCK) +const augumentedSchema = augmentSchema(schema, { + query: { + exclude: ['Statistics', 'LoggedInUser'] + }, + mutation: { + exclude: ['Statistics', 'LoggedInUser'] + } +}) + const server = new GraphQLServer({ context: async (req) => { const payload = { @@ -60,15 +54,7 @@ const server = new GraphQLServer({ return payload }, - // schema: augmentSchema(schema), - schema: augmentSchema(schema, { - query: { - exclude: ['Statistics', 'LoggedInUser'] - }, - mutation: { - exclude: ['Statistics', 'LoggedInUser'] - } - }), + schema: applyScalars(applyDirectives(augumentedSchema)), tracing: true, middlewares: middleware, mocks: MOCK ? mocks : false @@ -79,18 +65,6 @@ server.express.use(passport.initialize()) server.express.post('/graphql', passport.authenticate(['jwt'], { session: false })) -// session middleware -// server.express.use(session({ -// name: 'qid', -// secret: process.env.JWT_SECRET, -// resave: true, -// saveUninitialized: true, -// cookie: { -// secure: process.env.NODE_ENV === 'production', -// maxAge: ms('1d') -// } -// })) - const serverConfig = { port: 4000 // cors: { diff --git a/src/middleware/dateTimeMiddleware.js b/src/middleware/dateTimeMiddleware.js new file mode 100644 index 000000000..8fd3c074b --- /dev/null +++ b/src/middleware/dateTimeMiddleware.js @@ -0,0 +1,54 @@ +import format from 'date-fns/format' + +export default { + Mutation: { + CreateUser: async (resolve, root, args, context, info) => { + args.createdAt = format(new Date()) + args.disabled = false + args.deleted = false + const result = await resolve(root, args, context, info) + return result + }, + CreatePost: async (resolve, root, args, context, info) => { + args.createdAt = format(new Date()) + args.disabled = false + args.deleted = false + const result = await resolve(root, args, context, info) + return result + }, + CreateComment: async (resolve, root, args, context, info) => { + args.createdAt = format(new Date()) + args.disabled = false + args.deleted = false + const result = await resolve(root, args, context, info) + return result + }, + CreateOrganization: async (resolve, root, args, context, info) => { + args.createdAt = format(new Date()) + args.disabled = false + args.deleted = false + const result = await resolve(root, args, context, info) + return result + }, + UpdateUser: async (resolve, root, args, context, info) => { + args.updatedAt = format(new Date()) + const result = await resolve(root, args, context, info) + return result + }, + UpdatePost: async (resolve, root, args, context, info) => { + args.updatedAt = format(new Date()) + const result = await resolve(root, args, context, info) + return result + }, + UpdateComment: async (resolve, root, args, context, info) => { + args.updatedAt = format(new Date()) + const result = await resolve(root, args, context, info) + return result + }, + UpdateOrganization: async (resolve, root, args, context, info) => { + args.updatedAt = format(new Date()) + const result = await resolve(root, args, context, info) + return result + } + } +} diff --git a/src/middleware/index.js b/src/middleware/index.js index 9a32862c3..4088c428b 100644 --- a/src/middleware/index.js +++ b/src/middleware/index.js @@ -3,8 +3,12 @@ import softDeleteMiddleware from './softDeleteMiddleware' import sluggifyMiddleware from './sluggifyMiddleware' import fixImageUrlsMiddleware from './fixImageUrlsMiddleware' import excerptMiddleware from './excerptMiddleware' +import dateTimeMiddleware from './dateTimeMiddleware'; +import xssMiddleware from './xssMiddleware'; export default [ + xssMiddleware, + dateTimeMiddleware, passwordMiddleware, sluggifyMiddleware, excerptMiddleware, diff --git a/src/middleware/xssMiddleware.js b/src/middleware/xssMiddleware.js new file mode 100644 index 000000000..240301cf7 --- /dev/null +++ b/src/middleware/xssMiddleware.js @@ -0,0 +1,124 @@ +import trunc from 'trunc-html' +// import { getByDot, setByDot, getItems, replaceItems } from 'feathers-hooks-common' +import sanitizeHtml from 'sanitize-html' +// import { isEmpty, intersection } from 'lodash' +import cheerio from 'cheerio' +import linkifyHtml from 'linkifyjs/html' + +const embedToAnchor = (content) => { + const $ = cheerio.load(content) + $('div[data-url-embed]').each((i, el) => { + let url = el.attribs['data-url-embed'] + let aTag = $(`${url}`) + $(el).replaceWith(aTag) + }) + return $('body').html() +} + +function clean (dirty) { + if (!dirty) { + return dirty + } + + // Convert embeds to a-tags + dirty = embedToAnchor(dirty) + dirty = linkifyHtml(dirty) + dirty = sanitizeHtml(dirty, { + allowedTags: ['iframe', 'img', 'p', 'br', 'b', 'i', 'em', 'strong', 'a', 'pre', 'ul', 'li', 'ol', 's', 'strike', 'span', 'blockquote'], + allowedAttributes: { + a: ['href', 'class', 'target', 'data-*' , 'contenteditable'], + span: ['contenteditable', 'class', 'data-*'], + img: ['src'], + iframe: ['src', 'class', 'frameborder', 'allowfullscreen'] + }, + allowedIframeHostnames: ['www.youtube.com', 'player.vimeo.com'], + parser: { + lowerCaseTags: true + }, + transformTags: { + iframe: function (tagName, attribs) { + return { + tagName: 'a', + text: attribs.src, + attribs: { + href: attribs.src, + target: '_blank', + 'data-url-embed': '' + } + } + }, + i: 'em', + // a: function (tagName, attribs) { + // return { + // tagName: 'a', + // attribs: { + // href: attribs.href, + // target: '_blank', + // } + // } + // }, + b: 'strong', + s: 'strike', + img: function (tagName, attribs) { + let src = attribs.src + + // if (isEmpty(hook.result)) { + // const config = hook.app.get('thumbor') + // if (config && src.indexOf(config < 0)) { + // // download image + // // const ThumborUrlHelper = require('../helper/thumbor-helper') + // // const Thumbor = new ThumborUrlHelper(config.key || null, config.url || null) + // // src = Thumbor + // // .setImagePath(src) + // // .buildUrl('740x0') + // } + // } + return { + tagName: 'img', + attribs: { + src: `http://localhost:3050/images?url=${src}` + } + } + } + } + }) + + // remove empty html tags and duplicated returns + dirty = dirty + .replace(/<[a-z]>[\s]*<\/[a-z]>/igm, '') + .replace(/(]*)(>)[^>]*\/*>/igm, '') + .replace(/

[\s]*(
)+[\s]*<\/p>/igm, '
') + .replace(/(
){2,}/igm, '
') + .replace(/[\n]{3,}/igm, '\n\n') + .replace(/(\r\n|\n\r|\r|\n)/g, '
$1') + .replace(/


<\/p>/g, '') + return dirty +} + +// iterate through all fields and clean the values +function cleanAll (result, key, recursive, fields = ['content', 'contentExcerpt']) { + if (result && typeof result === 'string' && fields.includes(key)) { + result = clean(result) + } else if (result && Array.isArray(result)) { + result.forEach((res, index) => { + result[index] = cleanAll(result[index], index, true, fields) + }) + } else if (result && typeof result === 'object') { + Object.keys(result).forEach(key => { + result[key] = cleanAll(result[key], key, true, fields) + }) + } + return result +} + +export default { + Mutation: async (resolve, root, args, context, info) => { + args = cleanAll(args) + const result = await resolve(root, args, context, info) + return result + }, + Query: async (resolve, root, args, context, info) => { + const result = await resolve(root, args, context, info) + return cleanAll(result) + } +} diff --git a/src/nodes/users/userMiddleware.js b/src/nodes/users/userMiddleware.js new file mode 100644 index 000000000..e69de29bb diff --git a/src/schema.graphql b/src/schema.graphql index fb2279f9c..e6bb4f350 100644 --- a/src/schema.graphql +++ b/src/schema.graphql @@ -28,6 +28,10 @@ type Statistics { countShouts: Int! } +scalar Date +scalar Time +scalar DateTime + enum VisibilityEnum { public friends @@ -65,6 +69,8 @@ type User { deleted: Boolean disabled: Boolean role: UserGroupEnum + createdAt: String + updatedAt: String friends: [User]! @relation(name: "FRIENDS", direction: "BOTH") friendsCount: Int! @cypher(statement: "MATCH (this)<-[:FRIENDS]->(r:User) RETURN COUNT(r)") diff --git a/yarn.lock b/yarn.lock index 42b231e58..a855f8d7e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -490,6 +490,10 @@ array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" +array-uniq@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" + array-unique@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" @@ -1090,6 +1094,10 @@ body-parser@1.18.3, body-parser@^1.18.2, body-parser@^1.18.3: raw-body "2.3.3" type-is "~1.6.16" +boolbase@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + boxen@^1.2.1: version "1.3.0" resolved "https://registry.yarnpkg.com/boxen/-/boxen-1.3.0.tgz#55c6c39a8ba58d9c61ad22cd877532deb665a20b" @@ -1202,7 +1210,7 @@ chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.0.1: +chalk@^2.0.1, chalk@^2.3.0, chalk@^2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" dependencies: @@ -1210,6 +1218,17 @@ chalk@^2.0.1: escape-string-regexp "^1.0.5" supports-color "^5.3.0" +cheerio@^1.0.0-rc.2: + version "1.0.0-rc.2" + resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.2.tgz#4b9f53a81b27e4d5dac31c0ffd0cfa03cc6830db" + dependencies: + css-select "~1.2.0" + dom-serializer "~0.1.0" + entities "~1.1.1" + htmlparser2 "^3.9.1" + lodash "^4.15.0" + parse5 "^3.0.1" + chokidar@^1.6.1: version "1.7.0" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468" @@ -1379,6 +1398,23 @@ crypto-random-string@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e" +css-select@~1.2.0: + version "1.2.0" + resolved "http://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858" + dependencies: + boolbase "~1.0.0" + css-what "2.1" + domutils "1.5.1" + nth-check "~1.0.1" + +css-what@2.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.2.tgz#c0876d9d0480927d7d4920dcd72af3595649554d" + +date-fns@^2.0.0-alpha.24: + version "2.0.0-alpha.24" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.0.0-alpha.24.tgz#2988c137c72275af29d7d21cc53eb52b3a8c2586" + debug@2.6.9, debug@^2.1.2, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -1457,6 +1493,45 @@ dicer@0.2.5: readable-stream "1.1.x" streamsearch "0.1.2" +dom-serializer@0, dom-serializer@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82" + dependencies: + domelementtype "~1.1.1" + entities "~1.1.1" + +domelementtype@1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.2.1.tgz#578558ef23befac043a1abb0db07635509393479" + +domelementtype@^1.3.0: + version "1.3.0" + resolved "http://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz#b17aed82e8ab59e52dd9c19b1756e0fc187204c2" + +domelementtype@~1.1.1: + version "1.1.3" + resolved "http://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299c5cd822185b" + +domhandler@^2.3.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803" + dependencies: + domelementtype "1" + +domutils@1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" + dependencies: + dom-serializer "0" + domelementtype "1" + +domutils@^1.5.1: + version "1.7.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" + dependencies: + dom-serializer "0" + domelementtype "1" + dot-prop@^4.1.0: version "4.2.0" resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.0.tgz#1f19e0c2e1aa0e32797c49799f2837ac6af69c57" @@ -1497,6 +1572,10 @@ encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" +entities@^1.1.1, entities@~1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56" + es-abstract@^1.5.1: version "1.12.0" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.12.0.tgz#9dbbdd27c6856f0001421ca18782d786bf8a6165" @@ -1898,6 +1977,10 @@ graphql-import@^0.6.0: dependencies: lodash "^4.17.4" +graphql-iso-date@^3.6.1: + version "3.6.1" + resolved "https://registry.yarnpkg.com/graphql-iso-date/-/graphql-iso-date-3.6.1.tgz#bd2d0dc886e0f954cbbbc496bbf1d480b57ffa96" + graphql-middleware@1.6.6: version "1.6.6" resolved "https://registry.yarnpkg.com/graphql-middleware/-/graphql-middleware-1.6.6.tgz#f40f4e836ced479df718dbc1869ea4cd3b64027b" @@ -2071,6 +2154,17 @@ home-or-tmp@^2.0.0: os-homedir "^1.0.0" os-tmpdir "^1.0.1" +htmlparser2@^3.9.0, htmlparser2@^3.9.1: + version "3.10.0" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.0.tgz#5f5e422dcf6119c0d983ed36260ce9ded0bee464" + dependencies: + domelementtype "^1.3.0" + domhandler "^2.3.0" + domutils "^1.5.1" + entities "^1.1.1" + inherits "^2.0.1" + readable-stream "^3.0.6" + http-errors@1.6.3, http-errors@~1.6.2, http-errors@~1.6.3: version "1.6.3" resolved "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" @@ -2389,6 +2483,10 @@ jmespath@0.15.0: version "0.15.0" resolved "https://registry.yarnpkg.com/jmespath/-/jmespath-0.15.0.tgz#a3f222a9aae9f966f5d27c796510e28091764217" +jquery@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.3.1.tgz#958ce29e81c9790f31be7792df5d4d95fc57fbca" + "js-tokens@^3.0.0 || ^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -2471,10 +2569,26 @@ latest-version@^3.0.0: dependencies: package-json "^4.0.0" +linkifyjs@^2.1.7: + version "2.1.7" + resolved "https://registry.yarnpkg.com/linkifyjs/-/linkifyjs-2.1.7.tgz#e5d68d2ae30b9c055e1d74cc40f9a31d3abb4012" + optionalDependencies: + jquery "^3.3.1" + react "^16.4.2" + react-dom "^16.4.2" + +lodash.clonedeep@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" + lodash.debounce@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" +lodash.escaperegexp@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz#64762c48618082518ac3df4ccf5d5886dae20347" + lodash.includes@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" @@ -2499,11 +2613,15 @@ lodash.isstring@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" +lodash.mergewith@^4.6.0: + version "4.6.1" + resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz#639057e726c3afbdb3e7d42741caa8d6e4335927" + lodash.once@^4.0.0: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" -lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.4, lodash@^4.17.5: +lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.4, lodash@^4.17.5: version "4.17.11" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" @@ -2511,7 +2629,7 @@ long@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" -loose-envify@^1.0.0: +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" dependencies: @@ -2800,6 +2918,12 @@ npmlog@^4.0.2: gauge "~2.7.3" set-blocking "~2.0.0" +nth-check@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" + dependencies: + boolbase "~1.0.0" + number-is-nan@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" @@ -2808,7 +2932,7 @@ numeral@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/numeral/-/numeral-2.0.6.tgz#4ad080936d443c2561aed9f2197efffe25f4e506" -object-assign@^4, object-assign@^4.1.0: +object-assign@^4, object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" @@ -2917,6 +3041,12 @@ parse-glob@^3.0.4: is-extglob "^1.0.0" is-glob "^2.0.0" +parse5@^3.0.1: + version "3.0.3" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-3.0.3.tgz#042f792ffdd36851551cf4e9e066b3874ab45b5c" + dependencies: + "@types/node" "*" + parseurl@~1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3" @@ -2981,6 +3111,14 @@ posix-character-classes@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" +postcss@^6.0.14: + version "6.0.23" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.23.tgz#61c82cc328ac60e677645f979054eb98bc0e3324" + dependencies: + chalk "^2.4.1" + source-map "^0.6.1" + supports-color "^5.4.0" + prepend-http@^1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" @@ -2997,6 +3135,13 @@ process-nextick-args@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" +prop-types@^15.6.2: + version "15.6.2" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.2.tgz#05d5ca77b4453e985d60fc7ff8c859094a497102" + dependencies: + loose-envify "^1.3.1" + object-assign "^4.1.1" + protobufjs@^6.8.6: version "6.8.8" resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.8.8.tgz#c8b4f1282fd7a90e6f5b109ed11c84af82908e7c" @@ -3084,6 +3229,24 @@ rc@^1.0.1, rc@^1.1.6, rc@^1.2.7: minimist "^1.2.0" strip-json-comments "~2.0.1" +react-dom@^16.4.2: + version "16.6.0" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.6.0.tgz#6375b8391e019a632a89a0988bce85f0cc87a92f" + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + prop-types "^15.6.2" + scheduler "^0.10.0" + +react@^16.4.2: + version "16.6.0" + resolved "https://registry.yarnpkg.com/react/-/react-16.6.0.tgz#b34761cfaf3e30f5508bc732fb4736730b7da246" + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + prop-types "^15.6.2" + scheduler "^0.10.0" + readable-stream@1.1.x: version "1.1.14" resolved "http://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" @@ -3105,6 +3268,14 @@ readable-stream@^2.0.2, readable-stream@^2.0.6: string_decoder "~1.1.1" util-deprecate "~1.0.1" +readable-stream@^3.0.6: + version "3.0.6" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.0.6.tgz#351302e4c68b5abd6a2ed55376a7f9a25be3057a" + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + readdirp@^2.0.0: version "2.2.1" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" @@ -3231,6 +3402,21 @@ safe-regex@^1.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" +sanitize-html@^1.19.1: + version "1.19.1" + resolved "https://registry.yarnpkg.com/sanitize-html/-/sanitize-html-1.19.1.tgz#e8b33c69578054d6ee4f57ea152d6497f3f6fb7d" + dependencies: + chalk "^2.3.0" + htmlparser2 "^3.9.0" + lodash.clonedeep "^4.5.0" + lodash.escaperegexp "^4.1.2" + lodash.isplainobject "^4.0.6" + lodash.isstring "^4.0.1" + lodash.mergewith "^4.6.0" + postcss "^6.0.14" + srcset "^1.0.0" + xtend "^4.0.0" + sax@1.2.1: version "1.2.1" resolved "http://registry.npmjs.org/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a" @@ -3239,6 +3425,13 @@ sax@>=0.6.0, sax@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" +scheduler@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.10.0.tgz#7988de90fe7edccc774ea175a783e69c40c521e1" + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + semver-diff@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-2.1.0.tgz#4bbb8437c8d37e4b0cf1a68fd726ec6d645d6d36" @@ -3384,7 +3577,7 @@ source-map@^0.5.6, source-map@^0.5.7: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" -source-map@^0.6.0: +source-map@^0.6.0, source-map@^0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" @@ -3404,6 +3597,13 @@ sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" +srcset@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/srcset/-/srcset-1.0.0.tgz#a5669de12b42f3b1d5e83ed03c71046fc48f41ef" + dependencies: + array-uniq "^1.0.2" + number-is-nan "^1.0.0" + static-extend@^0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" @@ -3445,16 +3645,16 @@ string-width@^1.0.1: is-fullwidth-code-point "^2.0.0" strip-ansi "^4.0.0" -string_decoder@~0.10.x: - version "0.10.31" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" - -string_decoder@~1.1.1: +string_decoder@^1.1.1, string_decoder@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" dependencies: safe-buffer "~5.1.0" +string_decoder@~0.10.x: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + strip-ansi@^3.0.0, strip-ansi@^3.0.1: version "3.0.1" resolved "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" @@ -3489,7 +3689,7 @@ supports-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" -supports-color@^5.2.0, supports-color@^5.3.0: +supports-color@^5.2.0, supports-color@^5.3.0, supports-color@^5.4.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" dependencies: @@ -3670,7 +3870,7 @@ user-home@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/user-home/-/user-home-1.1.1.tgz#2b5be23a32b63a7c9deb8d0f28d485724a3df190" -util-deprecate@~1.0.1: +util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" @@ -3758,6 +3958,10 @@ xmlbuilder@~9.0.1: version "9.0.7" resolved "http://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d" +xtend@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" + yallist@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"