From 99716984adab05bc02344fa62e6ab44895fc26b3 Mon Sep 17 00:00:00 2001 From: Paul Mineev Date: Fri, 1 Jul 2022 11:14:03 -0700 Subject: [PATCH 1/2] up node version, add nvmrc, recommend nvm --- .github/workflows/ci-frontend.yml | 8 ++++---- Dockerfile | 4 ++-- Dockerfile.artifacts | 4 ++-- frontend/.nvmrc | 1 + frontend/package.json | 2 +- site/src/docs/contributing/development/frontend/index.md | 4 ++-- 6 files changed, 12 insertions(+), 11 deletions(-) create mode 100644 frontend/.nvmrc diff --git a/.github/workflows/ci-frontend.yml b/.github/workflows/ci-frontend.yml index c177507ecb..f68b733ce6 100644 --- a/.github/workflows/ci-frontend.yml +++ b/.github/workflows/ci-frontend.yml @@ -19,7 +19,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node: [16.13.2] + node: [16.15.1] steps: - uses: actions/checkout@v2 @@ -44,7 +44,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node: [16.13.2] + node: [16.15.1] steps: - uses: actions/checkout@v2 @@ -69,7 +69,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node: [16.13.2] + node: [16.15.1] steps: - uses: actions/checkout@v2 @@ -87,7 +87,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node: [16.13.2] + node: [16.15.1] steps: - uses: actions/checkout@v2 diff --git a/Dockerfile b/Dockerfile index 95e6339acf..ff82d4993a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -33,7 +33,7 @@ RUN \ echo "version=$version" && \ go build -o remark42 -ldflags "-X main.revision=${version} -s -w" ./app -FROM --platform=$BUILDPLATFORM node:16.13.2-alpine as build-frontend-deps +FROM --platform=$BUILDPLATFORM node:16.15.1-alpine as build-frontend-deps ARG CI ARG SKIP_FRONTEND_BUILD @@ -52,7 +52,7 @@ RUN if [ -z "$SKIP_FRONTEND_BUILD" ] ; then \ echo "skip frontend build" \ ; fi -FROM --platform=$BUILDPLATFORM node:16.13.2-alpine as build-frontend +FROM --platform=$BUILDPLATFORM node:16.15.1-alpine as build-frontend ARG CI ARG SKIP_FRONTEND_TEST diff --git a/Dockerfile.artifacts b/Dockerfile.artifacts index dcac71933a..d7e60d406c 100644 --- a/Dockerfile.artifacts +++ b/Dockerfile.artifacts @@ -1,4 +1,4 @@ -FROM node:12.16-alpine as build-frontend-deps +FROM node:16.15.1-alpine as build-frontend-deps ARG CI @@ -9,7 +9,7 @@ ADD frontend/package.json /srv/frontend/package.json ADD frontend/package-lock.json /srv/frontend/package-lock.json RUN cd /srv/frontend && CI=true npm ci -FROM node:12.16-alpine as build-frontend +FROM node:16.15.1-alpine as build-frontend ARG CI ARG NODE_ENV=production diff --git a/frontend/.nvmrc b/frontend/.nvmrc new file mode 100644 index 0000000000..d9289897d3 --- /dev/null +++ b/frontend/.nvmrc @@ -0,0 +1 @@ +16.15.1 diff --git a/frontend/package.json b/frontend/package.json index 638ab0bc61..6e05d7b536 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -22,7 +22,7 @@ "translation:check": "node ./tasks/checkTranslation.js" }, "engines": { - "node": ">=16.13.0 <=17.*", + "node": ">=16.15 <=17.*", "npm": ">=8.1.0 <=8.*" }, "dependencies": { diff --git a/site/src/docs/contributing/development/frontend/index.md b/site/src/docs/contributing/development/frontend/index.md index 014f04ccfb..777565b553 100644 --- a/site/src/docs/contributing/development/frontend/index.md +++ b/site/src/docs/contributing/development/frontend/index.md @@ -8,8 +8,8 @@ title: Frontend Development Guidelines You must have at least 2GB RAM or swap enabled for building. -- install [Node.js 16.13.0](https://nodejs.org/en/) or higher -- install [NPM 8.1.0](https://www.npmjs.com/package/npm) +- install [Node.js 16.15](https://nodejs.org/en/) or higher (we recommend using [NVM](https://github.com/nvm-sh/nvm) for node version autoswitch) +- install [NPM 8.1](https://www.npmjs.com/package/npm) - run `npm install` inside `./frontend` Running `npm install` will set up pre-commit hooks into your git repository. They are used to reformat your frontend code using `prettier` and lint with `eslint` and `stylelint` before every commit. From 1f96a0e4d3ee1102d7c7cff5a771abdc6a03330a Mon Sep 17 00:00:00 2001 From: Dmitry Verkhoturov Date: Mon, 4 Jul 2022 23:39:43 +0200 Subject: [PATCH 2/2] update go-pkgz/auth module to fix dev provider work Fix for error introduced in the following commit: https://github.com/go-pkgz/auth/commit/06e72788bcbb23d958c60655b42892b95457477e After text/template was replaced with the html/template, the dev provider started escaping parameters which were not supposed to be escaped. --- .github/workflows/ci-test-backend.yml | 2 +- backend/_example/memory_store/go.mod | 2 +- backend/_example/memory_store/go.sum | 5 +- backend/go.mod | 4 +- backend/go.sum | 4 + .../go-pkgz/auth/provider/dev_provider.go | 6 +- .../go-pkgz/auth/provider/telegram.go | 2 +- .../microcosm-cc/bluemonday/LICENSE.md | 3 + .../microcosm-cc/bluemonday/README.md | 2 +- .../microcosm-cc/bluemonday/css/handlers.go | 269 +++++++----------- .../microcosm-cc/bluemonday/helpers.go | 2 +- .../microcosm-cc/bluemonday/sanitize.go | 23 +- backend/vendor/modules.txt | 6 +- 13 files changed, 122 insertions(+), 208 deletions(-) diff --git a/.github/workflows/ci-test-backend.yml b/.github/workflows/ci-test-backend.yml index 8a3f659365..6bf14c3cd7 100644 --- a/.github/workflows/ci-test-backend.yml +++ b/.github/workflows/ci-test-backend.yml @@ -34,7 +34,7 @@ jobs: - name: install golangci-lint and goveralls run: | - curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh| sh -s -- -b $GITHUB_WORKSPACE v1.44.0 + curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh| sh -s -- -b $GITHUB_WORKSPACE v1.46.1 go get -u github.com/mattn/goveralls - name: test and lint backend diff --git a/backend/_example/memory_store/go.mod b/backend/_example/memory_store/go.mod index b7fc265146..5bd4dab655 100644 --- a/backend/_example/memory_store/go.mod +++ b/backend/_example/memory_store/go.mod @@ -27,7 +27,7 @@ require ( github.com/gorilla/css v1.0.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/microcosm-cc/bluemonday v1.0.18 // indirect + github.com/microcosm-cc/bluemonday v1.0.19 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rs/xid v1.4.0 // indirect diff --git a/backend/_example/memory_store/go.sum b/backend/_example/memory_store/go.sum index 718fe18fd0..09674c474a 100644 --- a/backend/_example/memory_store/go.sum +++ b/backend/_example/memory_store/go.sum @@ -56,8 +56,8 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/microcosm-cc/bluemonday v1.0.18 h1:6HcxvXDAi3ARt3slx6nTesbvorIc3QeTzBNRvWktHBo= -github.com/microcosm-cc/bluemonday v1.0.18/go.mod h1:Z0r70sCuXHig8YpBzCc5eGHAap2K7e/u082ZUpDRRqM= +github.com/microcosm-cc/bluemonday v1.0.19 h1:OI7hoF5FY4pFz2VA//RN8TfM0YJ2dJcl4P4APrCWy6c= +github.com/microcosm-cc/bluemonday v1.0.19/go.mod h1:QNzV2UbLK2/53oIIwTOyLUSABMkjZ4tqiyC1g/DyqxE= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -86,7 +86,6 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/image v0.0.0-20220413100746-70e8d0d3baa9 h1:LRtI4W37N+KFebI/qV0OFiLUv4GLOWeEW5hn/KEJvxE= golang.org/x/image v0.0.0-20220413100746-70e8d0d3baa9/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2 h1:NWy5+hlRbC7HK+PmcXVUmW1IMyFce7to56IUvhUFm7Y= golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= diff --git a/backend/go.mod b/backend/go.mod index 20ecdff31a..c57904dc65 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -11,7 +11,7 @@ require ( github.com/go-chi/chi/v5 v5.0.7 github.com/go-chi/cors v1.2.1 github.com/go-chi/render v1.0.1 - github.com/go-pkgz/auth v1.19.1-0.20220605174438-06e72788bcbb + github.com/go-pkgz/auth v1.19.1-0.20220624002401-8b91a0eee69a github.com/go-pkgz/jrpc v0.2.0 github.com/go-pkgz/lcw v0.8.1 github.com/go-pkgz/lgr v0.10.4 @@ -25,7 +25,7 @@ require ( github.com/hashicorp/go-multierror v1.1.1 github.com/jessevdk/go-flags v1.5.0 github.com/kyokomi/emoji/v2 v2.2.9 - github.com/microcosm-cc/bluemonday v1.0.18 + github.com/microcosm-cc/bluemonday v1.0.19 github.com/rakyll/statik v0.1.7 github.com/rs/xid v1.4.0 github.com/russross/blackfriday/v2 v2.1.0 diff --git a/backend/go.sum b/backend/go.sum index 6cea29e774..29455474ab 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -143,6 +143,8 @@ github.com/go-oauth2/oauth2/v4 v4.4.3 h1:IiU3iAK5NEeZ4mUf5HV9tN7iNlx4AGUkIJ5M1C4 github.com/go-oauth2/oauth2/v4 v4.4.3/go.mod h1:NR9Hugz5/Qe2OGxoPBhsTRNjnm/amC+z9+XTwt63rhs= github.com/go-pkgz/auth v1.19.1-0.20220605174438-06e72788bcbb h1:HYMG+Tto0JVSMPtYkdXR3sT2FbKImFuXctgo9e4OdQw= github.com/go-pkgz/auth v1.19.1-0.20220605174438-06e72788bcbb/go.mod h1:ncL+ePcAf9A7lCKKDAQ6aIs4MTz/Q/g5XIhRx5hap6E= +github.com/go-pkgz/auth v1.19.1-0.20220624002401-8b91a0eee69a h1:gp0mLtawviHOwmrd6E60yxA+9GAKtpBjI1WCc/4oyAE= +github.com/go-pkgz/auth v1.19.1-0.20220624002401-8b91a0eee69a/go.mod h1:ncL+ePcAf9A7lCKKDAQ6aIs4MTz/Q/g5XIhRx5hap6E= github.com/go-pkgz/email v0.3.0 h1:89FbVF6trHkdn2VVoXlBfZHkdYQ1vLmQ54OhdosaFtc= github.com/go-pkgz/email v0.3.0/go.mod h1:TpnmSLkQW3FyICit2hn7WIhCUDrhCX6btzz5wS3wHRI= github.com/go-pkgz/expirable-cache v0.0.3 h1:rTh6qNPp78z0bQE6HDhXBHUwqnV9i09Vm6dksJLXQDc= @@ -306,6 +308,8 @@ github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/microcosm-cc/bluemonday v1.0.18 h1:6HcxvXDAi3ARt3slx6nTesbvorIc3QeTzBNRvWktHBo= github.com/microcosm-cc/bluemonday v1.0.18/go.mod h1:Z0r70sCuXHig8YpBzCc5eGHAap2K7e/u082ZUpDRRqM= +github.com/microcosm-cc/bluemonday v1.0.19 h1:OI7hoF5FY4pFz2VA//RN8TfM0YJ2dJcl4P4APrCWy6c= +github.com/microcosm-cc/bluemonday v1.0.19/go.mod h1:QNzV2UbLK2/53oIIwTOyLUSABMkjZ4tqiyC1g/DyqxE= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/moul/http2curl v1.0.0 h1:dRMWoAtb+ePxMlLkrCbAqh4TlPHXvoGUSQ323/9Zahs= github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= diff --git a/backend/vendor/github.com/go-pkgz/auth/provider/dev_provider.go b/backend/vendor/github.com/go-pkgz/auth/provider/dev_provider.go index b3d2b7699d..222c7ae7d8 100644 --- a/backend/vendor/github.com/go-pkgz/auth/provider/dev_provider.go +++ b/backend/vendor/github.com/go-pkgz/auth/provider/dev_provider.go @@ -35,7 +35,7 @@ type DevAuthServer struct { } // Run oauth2 dev server on port devAuthPort -func (d *DevAuthServer) Run(ctx context.Context) { //nolint (gocyclo) +func (d *DevAuthServer) Run(ctx context.Context) { // nolint (gocyclo) if d.Provider.Port == 0 { d.Provider.Port = defDevAuthPort } @@ -60,9 +60,7 @@ func (d *DevAuthServer) Run(ctx context.Context) { //nolint (gocyclo) // first time it will be called without username and will ask for one if !d.Automatic && (r.ParseForm() != nil || r.Form.Get("username") == "") { - - formData := struct{ Query string }{Query: r.URL.RawQuery} - + formData := struct{ Query template.URL }{Query: template.URL(r.URL.RawQuery)} //nolint:gosec // query is safes if err = userFormTmpl.Execute(w, formData); err != nil { d.Logf("[WARN] can't write, %s", err) } diff --git a/backend/vendor/github.com/go-pkgz/auth/provider/telegram.go b/backend/vendor/github.com/go-pkgz/auth/provider/telegram.go index 5a2bd4591b..f4ee0ecdaa 100644 --- a/backend/vendor/github.com/go-pkgz/auth/provider/telegram.go +++ b/backend/vendor/github.com/go-pkgz/auth/provider/telegram.go @@ -309,7 +309,7 @@ func (th *TelegramHandler) LoginHandler(w http.ResponseWriter, r *http.Request) ExpiresAt: time.Now().Add(30 * time.Minute).Unix(), NotBefore: time.Now().Add(-1 * time.Minute).Unix(), }, - SessionOnly: false, // TODO + SessionOnly: false, // TODO review? } if _, err := th.TokenService.Set(w, claims); err != nil { diff --git a/backend/vendor/github.com/microcosm-cc/bluemonday/LICENSE.md b/backend/vendor/github.com/microcosm-cc/bluemonday/LICENSE.md index f822458ed0..2e6c493ba8 100644 --- a/backend/vendor/github.com/microcosm-cc/bluemonday/LICENSE.md +++ b/backend/vendor/github.com/microcosm-cc/bluemonday/LICENSE.md @@ -1,3 +1,6 @@ +SPDX short identifier: BSD-3-Clause +https://opensource.org/licenses/BSD-3-Clause + Copyright (c) 2014, David Kitchen All rights reserved. diff --git a/backend/vendor/github.com/microcosm-cc/bluemonday/README.md b/backend/vendor/github.com/microcosm-cc/bluemonday/README.md index d20debf0ec..8e658fea75 100644 --- a/backend/vendor/github.com/microcosm-cc/bluemonday/README.md +++ b/backend/vendor/github.com/microcosm-cc/bluemonday/README.md @@ -1,4 +1,4 @@ -# bluemonday [![Build Status](https://travis-ci.org/microcosm-cc/bluemonday.svg?branch=master)](https://travis-ci.org/microcosm-cc/bluemonday) [![GoDoc](https://godoc.org/github.com/microcosm-cc/bluemonday?status.png)](https://godoc.org/github.com/microcosm-cc/bluemonday) [![Sourcegraph](https://sourcegraph.com/github.com/microcosm-cc/bluemonday/-/badge.svg)](https://sourcegraph.com/github.com/microcosm-cc/bluemonday?badge) +# bluemonday [![GoDoc](https://godoc.org/github.com/microcosm-cc/bluemonday?status.png)](https://godoc.org/github.com/microcosm-cc/bluemonday) [![Sourcegraph](https://sourcegraph.com/github.com/microcosm-cc/bluemonday/-/badge.svg)](https://sourcegraph.com/github.com/microcosm-cc/bluemonday?badge) bluemonday is a HTML sanitizer implemented in Go. It is fast and highly configurable. diff --git a/backend/vendor/github.com/microcosm-cc/bluemonday/css/handlers.go b/backend/vendor/github.com/microcosm-cc/bluemonday/css/handlers.go index 200a6729a2..e0429cf25c 100644 --- a/backend/vendor/github.com/microcosm-cc/bluemonday/css/handlers.go +++ b/backend/vendor/github.com/microcosm-cc/bluemonday/css/handlers.go @@ -280,6 +280,49 @@ var ( "slategray", "slategrey", "snow", "springgreen", "steelblue", "tan", "teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white", "whitesmoke", "yellow", "yellowgreen"} + + Alpha = regexp.MustCompile(`^[a-z]+$`) + Blur = regexp.MustCompile(`^blur\([0-9]+px\)$`) + BrightnessCont = regexp.MustCompile(`^(brightness|contrast)\([0-9]+\%\)$`) + Count = regexp.MustCompile(`^[0-9]+[\.]?[0-9]*$`) + CubicBezier = regexp.MustCompile(`^cubic-bezier\(([ ]*(0(.[0-9]+)?|1(.0)?),){3}[ ]*(0(.[0-9]+)?|1)\)$`) + Digits = regexp.MustCompile(`^digits [2-4]$`) + DropShadow = regexp.MustCompile(`drop-shadow\(([-]?[0-9]+px) ([-]?[0-9]+px)( [-]?[0-9]+px)?( ([-]?[0-9]+px))?`) + Font = regexp.MustCompile(`^('[a-z \-]+'|[a-z \-]+)$`) + Grayscale = regexp.MustCompile(`^grayscale\(([0-9]{1,2}|100)%\)$`) + GridTemplateAreas = regexp.MustCompile(`^['"]?[a-z ]+['"]?$`) + HexRGB = regexp.MustCompile(`^#([0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$`) + HSL = regexp.MustCompile(`^hsl\([ ]*([012]?[0-9]{1,2}|3[0-5][0-9]|360),[ ]*([0-9]{0,2}|100)\%,[ ]*([0-9]{0,2}|100)\%\)$`) + HSLA = regexp.MustCompile(`^hsla\(([ ]*[012]?[0-9]{1,2}|3[0-5][0-9]|360),[ ]*([0-9]{0,2}|100)\%,[ ]*([0-9]{0,2}|100)\%,[ ]*(1|1\.0|0|(0\.[0-9]+))\)$`) + HueRotate = regexp.MustCompile(`^hue-rotate\(([12]?[0-9]{1,2}|3[0-5][0-9]|360)?\)$`) + Invert = regexp.MustCompile(`^invert\(([0-9]{1,2}|100)%\)$`) + Length = regexp.MustCompile(`^[\-]?([0-9]+|[0-9]*[\.][0-9]+)(%|cm|mm|in|px|pt|pc|em|ex|ch|rem|vw|vh|vmin|vmax|deg|rad|turn)?$`) + Matrix = regexp.MustCompile(`^matrix\(([ ]*[0-9]+[\.]?[0-9]*,){5}([ ]*[0-9]+[\.]?[0-9]*)\)$`) + Matrix3D = regexp.MustCompile(`^matrix3d\(([ ]*[0-9]+[\.]?[0-9]*,){15}([ ]*[0-9]+[\.]?[0-9]*)\)$`) + NegTime = regexp.MustCompile(`^[\-]?[0-9]+[\.]?[0-9]*(s|ms)?$`) + Numeric = regexp.MustCompile(`^[0-9]+$`) + NumericDecimal = regexp.MustCompile(`^[0-9\.]+$`) + Opactiy = regexp.MustCompile(`^opacity\(([0-9]{1,2}|100)%\)$`) + Perspective = regexp.MustCompile(`perspective\(`) + Position = regexp.MustCompile(`^[\-]*[0-9]+[cm|mm|in|px|pt|pc\%]* [[\-]*[0-9]+[cm|mm|in|px|pt|pc\%]*]*$`) + Opacity = regexp.MustCompile(`^(0[.]?[0-9]*)|(1.0)$`) + QuotedAlpha = regexp.MustCompile(`^["'][a-z]+["']$`) + Quotes = regexp.MustCompile(`^([ ]*["'][\x{0022}\x{0027}\x{2039}\x{2039}\x{203A}\x{00AB}\x{00BB}\x{2018}\x{2019}\x{201C}-\x{201E}]["'] ["'][\x{0022}\x{0027}\x{2039}\x{2039}\x{203A}\x{00AB}\x{00BB}\x{2018}\x{2019}\x{201C}-\x{201E}]["'])+$`) + Rect = regexp.MustCompile(`^rect\([0-9]+px,[ ]*[0-9]+px,[ ]*[0-9]+px,[ ]*[0-9]+px\)$`) + RGB = regexp.MustCompile(`^rgb\(([ ]*((([0-9]{1,2}|100)\%)|(([01]?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5]))),){2}([ ]*((([0-9]{1,2}|100)\%)|(([01]?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5]))))\)$`) + RGBA = regexp.MustCompile(`^rgba\(([ ]*((([0-9]{1,2}|100)\%)|(([01]?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5]))),){3}[ ]*(1(\.0)?|0|(0\.[0-9]+))\)$`) + Rotate = regexp.MustCompile(`^rotate(x|y|z)?\(([12]?|3[0-5][0-9]|360)\)$`) + Rotate3D = regexp.MustCompile(`^rotate3d\(([ ]?(1(\.0)?|0\.[0-9]+),){3}([12]?|3[0-5][0-9]|360)\)$`) + Saturate = regexp.MustCompile(`^saturate\([0-9]+%\)$`) + Sepia = regexp.MustCompile(`^sepia\(([0-9]{1,2}|100)%\)$`) + Skew = regexp.MustCompile(`skew(x|y)?\(`) + Span = regexp.MustCompile(`^span [0-9]+$`) + Steps = regexp.MustCompile(`^steps\([ ]*[0-9]+([ ]*,[ ]*(start|end)?)\)$`) + Time = regexp.MustCompile(`^[0-9]+[\.]?[0-9]*(s|ms)?$`) + TransitionProp = regexp.MustCompile(`^([a-zA-Z]+,[ ]?)*[a-zA-Z]+$`) + TranslateScale = regexp.MustCompile(`(translate|translate3d|translatex|translatey|translatez|scale|scale3d|scalex|scaley|scalez)\(`) + URL = regexp.MustCompile(`^url\([\"\']?((https|http)[a-z0-9\.\\/_:]+[\"\']?)\)$`) + ZIndex = regexp.MustCompile(`^[\-]?[0-9]+$`) ) func multiSplit(value string, seps ...string) []string { @@ -388,9 +431,7 @@ func AnimationHandler(value string) bool { } func AnimationDelayHandler(value string) bool { - reg := regexp.MustCompile(`[\-]?[0-9]+[\.]?[0-9]*[s|ms]?`) - reg.Longest() - if reg.FindString(value) == value && value != "" { + if NegTime.MatchString(value) { return true } values := []string{"initial", "inherit"} @@ -405,9 +446,7 @@ func AnimationDirectionHandler(value string) bool { } func AnimationDurationHandler(value string) bool { - reg := regexp.MustCompile(`[0-9]+[\.]?[0-9]*[s|ms]?`) - reg.Longest() - if reg.FindString(value) == value && value != "" { + if Time.MatchString(value) { return true } values := []string{"initial", "inherit"} @@ -422,9 +461,7 @@ func AnimationFillModeHandler(value string) bool { } func AnimationIterationCountHandler(value string) bool { - reg := regexp.MustCompile(`[0-9]+[\.]?[0-9]*`) - reg.Longest() - if reg.FindString(value) == value && value != "" { + if Count.MatchString(value) { return true } values := []string{"infinite", "initial", "inherit"} @@ -433,9 +470,7 @@ func AnimationIterationCountHandler(value string) bool { } func AnimationNameHandler(value string) bool { - reg := regexp.MustCompile(`[a-z]+`) - reg.Longest() - return reg.FindString(value) == value && value != "" + return Alpha.MatchString(value) } func AnimationPlayStateHandler(value string) bool { @@ -450,14 +485,10 @@ func TimingFunctionHandler(value string) bool { if in(splitVals, values) { return true } - reg := regexp.MustCompile(`cubic-bezier\(([ ]*(0(.[0-9]+)?|1(.0)?),){3}[ ]*(0(.[0-9]+)?|1)\)`) - reg.Longest() - if reg.FindString(value) == value && value != "" { + if CubicBezier.MatchString(value) { return true } - reg = regexp.MustCompile(`steps\([ ]*[0-9]+([ ]*,[ ]*(start|end)?)\)`) - reg.Longest() - return reg.FindString(value) == value && value != "" + return Steps.MatchString(value) } func BackfaceVisibilityHandler(value string) bool { @@ -518,9 +549,7 @@ func ImageHandler(value string) bool { if in(splitVals, values) { return true } - reg := regexp.MustCompile(`url\([\"\']?((https|http)[a-z0-9\.\\/_:]+[\"\']?)\)`) - reg.Longest() - return reg.FindString(value) == value && value != "" + return URL.MatchString(value) } func BackgroundOriginHandler(value string) bool { @@ -535,12 +564,7 @@ func BackgroundPositionHandler(value string) bool { if in(splitVals, values) { return true } - reg := regexp.MustCompile(`[\-]*[0-9]+[cm|mm|in|px|pt|pc\%]* [[\-]*[0-9]+[cm|mm|in|px|pt|pc\%]*]*`) - reg.Longest() - if reg.FindString(value) == value && value != "" { - return true - } - return false + return Position.MatchString(value) } func BackgroundRepeatHandler(value string) bool { @@ -816,31 +840,19 @@ func CaretColorHandler(value string) bool { if in(splitVals, colorValues) { return true } - reg := regexp.MustCompile(`#[0-9abcdef]{6}`) - reg.Longest() - if reg.FindString(value) == value && value != "" { + if HexRGB.MatchString(value) { return true } - reg = regexp.MustCompile(`rgb\(([ ]*((([0-9]{1,2}|100)\%)|(([01]?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5]))),){2}([ ]*((([0-9]{1,2}|100)\%)|(([01]?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5]))))\)`) - reg.Longest() - if reg.FindString(value) == value && value != "" { + if RGB.MatchString(value) { return true } - reg = regexp.MustCompile(`rgba\(([ ]*((([0-9]{1,2}|100)\%)|(([01]?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5]))),){3}[ ]*(1(\.0)?|0|(0\.[0-9]+))\)`) - reg.Longest() - if reg.FindString(value) == value && value != "" { + if RGBA.MatchString(value) { return true } - reg = regexp.MustCompile(`hsl\([ ]*([012]?[0-9]{1,2}|3[0-5][0-9]|360),[ ]*([0-9]{0,2}|100)\%,[ ]*([0-9]{0,2}|100)\%\)`) - if reg.FindString(value) == value && value != "" { + if HSL.MatchString(value) { return true } - reg = regexp.MustCompile(`hsla\(([ ]*[012]?[0-9]{1,2}|3[0-5][0-9]|360),[ ]*([0-9]{0,2}|100)\%,[ ]*([0-9]{0,2}|100)\%,[ ]*(1|1\.0|0|(0\.[0-9]+))\)`) - reg.Longest() - if reg.FindString(value) == value && value != "" { - return true - } - return false + return HSLA.MatchString(value) } func ClearHandler(value string) bool { @@ -850,9 +862,7 @@ func ClearHandler(value string) bool { } func ClipHandler(value string) bool { - reg := regexp.MustCompile(`rect\([0-9]+px,[ ]*[0-9]+px,[ ]*[0-9]+px,[ ]*[0-9]+px\)`) - reg.Longest() - if reg.FindString(value) == value && value != "" { + if Rect.MatchString(value) { return true } values := []string{"auto", "initial", "inherit"} @@ -865,38 +875,23 @@ func ColorHandler(value string) bool { if in(splitVals, colorValues) { return true } - reg := regexp.MustCompile(`#[0-9abcdef]{6}`) - reg.Longest() - if reg.FindString(value) == value && value != "" { + if HexRGB.MatchString(value) { return true } - reg = regexp.MustCompile(`rgb\(([ ]*((([0-9]{1,2}|100)\%)|(([01]?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5]))),){2}([ ]*((([0-9]{1,2}|100)\%)|(([01]?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5]))))\)`) - reg.Longest() - if reg.FindString(value) == value && value != "" { + if RGB.MatchString(value) { return true } - reg = regexp.MustCompile(`rgba\(([ ]*((([0-9]{1,2}|100)\%)|(([01]?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5]))),){3}[ ]*(1(\.0)?|0|(0\.[0-9]+))\)`) - reg.Longest() - if reg.FindString(value) == value && value != "" { + if RGBA.MatchString(value) { return true } - reg = regexp.MustCompile(`hsl\([ ]*([012]?[0-9]{1,2}|3[0-5][0-9]|360),[ ]*([0-9]{0,2}|100)\%,[ ]*([0-9]{0,2}|100)\%\)`) - reg.Longest() - if reg.FindString(value) == value && value != "" { + if HSL.MatchString(value) { return true } - reg = regexp.MustCompile(`hsla\(([ ]*[012]?[0-9]{1,2}|3[0-5][0-9]|360),[ ]*([0-9]{0,2}|100)\%,[ ]*([0-9]{0,2}|100)\%,[ ]*(1|1\.0|0|(0\.[0-9]+))\)`) - reg.Longest() - if reg.FindString(value) == value && value != "" { - return true - } - return false + return HSLA.MatchString(value) } func ColumnCountHandler(value string) bool { - reg := regexp.MustCompile(`[0-9]+`) - reg.Longest() - if reg.FindString(value) == value && value != "" { + if Numeric.MatchString(value) { return true } values := []string{"auto", "initial", "inherit"} @@ -1000,54 +995,35 @@ func FilterHandler(value string) bool { if in(splitVals, values) { return true } - reg := regexp.MustCompile(`blur\([0-9]+px\)`) - reg.Longest() - if reg.FindString(value) == value && value != "" { + if Blur.MatchString(value) { return true } - reg = regexp.MustCompile(`(brightness|contrast)\([0-9]+\%\)`) - reg.Longest() - if reg.FindString(value) == value && value != "" { + if BrightnessCont.MatchString(value) { return true } - reg = regexp.MustCompile(`drop-shadow\(([-]?[0-9]+px) ([-]?[0-9]+px)( [-]?[0-9]+px)?( ([-]?[0-9]+px))?`) - reg.Longest() - colorValue := strings.TrimSuffix(string(reg.ReplaceAll([]byte(value), []byte{})), ")") - if ColorHandler(colorValue) { + if DropShadow.MatchString(value) { return true } - reg = regexp.MustCompile(`grayscale\(([0-9]{1,2}|100)%\)`) - reg.Longest() - if reg.FindString(value) == value && value != "" { + colorValue := strings.TrimSuffix(string(DropShadow.ReplaceAll([]byte(value), []byte{})), ")") + if ColorHandler(colorValue) { return true } - reg = regexp.MustCompile(`hue-rotate\(([12]?[0-9]{1,2}|3[0-5][0-9]|360)?\)`) - reg.Longest() - if reg.FindString(value) == value && value != "" { + if Grayscale.MatchString(value) { return true } - reg = regexp.MustCompile(`invert\(([0-9]{1,2}|100)%\)`) - reg.Longest() - if reg.FindString(value) == value && value != "" { + if HueRotate.MatchString(value) { return true } - reg = regexp.MustCompile(`opacity\(([0-9]{1,2}|100)%\)`) - reg.Longest() - if reg.FindString(value) == value && value != "" { + if Invert.MatchString(value) { return true } - reg = regexp.MustCompile(`saturate\([0-9]+%\)`) - reg.Longest() - if reg.FindString(value) == value && value != "" { + if Opacity.MatchString(value) { return true } - reg = regexp.MustCompile(`sepia\(([0-9]{1,2}|100)%\)`) - reg.Longest() - if reg.FindString(value) == value && value != "" { + if Saturate.MatchString(value) { return true } - //Not allowing URLs - return false + return Sepia.MatchString(value) } func FlexHandler(value string) bool { @@ -1092,9 +1068,7 @@ func FlexFlowHandler(value string) bool { } func FlexGrowHandler(value string) bool { - reg := regexp.MustCompile(`[0-9\.]+`) - reg.Longest() - if reg.FindString(value) == value && value != "" { + if NumericDecimal.MatchString(value) { return true } splitVals := strings.Split(value, ";") @@ -1144,11 +1118,9 @@ func FontFamilyHandler(value string) bool { if in(splitVals, values) { return true } - reg := regexp.MustCompile(`('[a-z \-]+'|[a-z \-]+)`) - reg.Longest() for _, i := range splitVals { i = strings.TrimSpace(i) - if reg.FindString(i) != i { + if Font.FindString(i) != i { return false } } @@ -1162,9 +1134,7 @@ func FontKerningHandler(value string) bool { } func FontLanguageOverrideHandler(value string) bool { - reg := regexp.MustCompile(`[a-z]+`) - reg.Longest() - return reg.FindString(value) == value && value != "" + return Alpha.MatchString(value) } func FontSizeHandler(value string) bool { @@ -1177,9 +1147,7 @@ func FontSizeHandler(value string) bool { } func FontSizeAdjustHandler(value string) bool { - reg := regexp.MustCompile(`[0-9]+[\.]?[0-9]*`) - reg.Longest() - if reg.FindString(value) == value && value != "" { + if Count.MatchString(value) { return true } values := []string{"auto", "initial", "inherit"} @@ -1298,9 +1266,7 @@ func GridColumnGapHandler(value string) bool { } func LengthHandler(value string) bool { - reg := regexp.MustCompile(`[\-]?[0-9]+[\.]?[0-9]*(%|cm|mm|in|px|pt|pc|em|ex|ch|rem|vw|vh|vmin|vmax|deg|rad|turn)?`) - reg.Longest() - return reg.FindString(value) == value && value != "" + return Length.MatchString(value) } func LineBreakHandler(value string) bool { @@ -1310,12 +1276,10 @@ func LineBreakHandler(value string) bool { } func GridAxisStartEndHandler(value string) bool { - reg := regexp.MustCompile(`[0-9]+`) - if reg.FindString(value) == value && value != "" { + if Numeric.MatchString(value) { return true } - reg = regexp.MustCompile(`span [0-9]+`) - if reg.FindString(value) == value && value != "" { + if Span.MatchString(value) { return true } values := []string{"auto"} @@ -1366,9 +1330,7 @@ func GridTemplateAreasHandler(value string) bool { if in([]string{value}, values) { return true } - reg := regexp.MustCompile(`['"]?[a-z ]+['"]?`) - reg.Longest() - return reg.FindString(value) == value && value != "" + return GridTemplateAreas.MatchString(value) } func GridTemplateColumnsHandler(value string) bool { @@ -1551,9 +1513,7 @@ func ObjectPositionHandler(value string) bool { } func OpacityHandler(value string) bool { - reg := regexp.MustCompile("(0[.]?[0-9]*)|(1.0)") - reg.Longest() - if reg.FindString(value) == value && value != "" { + if Opacity.MatchString(value) { return true } values := []string{"initial", "inherit"} @@ -1562,9 +1522,7 @@ func OpacityHandler(value string) bool { } func OrderHandler(value string) bool { - reg := regexp.MustCompile("[0-9]+") - reg.Longest() - if reg.FindString(value) == value && value != "" { + if Numeric.MatchString(value) { return true } values := []string{"initial", "inherit"} @@ -1629,9 +1587,7 @@ func OverflowWrapHandler(value string) bool { } func OrphansHandler(value string) bool { - reg := regexp.MustCompile(`[0-9]+`) - reg.Longest() - return reg.FindString(value) == value && value != "" + return Numeric.MatchString(value) } func PaddingHandler(value string) bool { @@ -1716,9 +1672,7 @@ func QuotesHandler(value string) bool { if in(splitVals, values) { return true } - reg := regexp.MustCompile(`([ ]*["'][\x{0022}\x{0027}\x{2039}\x{2039}\x{203A}\x{00AB}\x{00BB}\x{2018}\x{2019}\x{201C}-\x{201E}]["'] ["'][\x{0022}\x{0027}\x{2039}\x{2039}\x{203A}\x{00AB}\x{00BB}\x{2018}\x{2019}\x{201C}-\x{201E}]["'])+`) - reg.Longest() - return reg.FindString(value) == value && value != "" + return Quotes.MatchString(value) } func ResizeHandler(value string) bool { @@ -1766,9 +1720,7 @@ func TextCombineUprightHandler(value string) bool { if in(splitVals, values) { return true } - reg := regexp.MustCompile(`digits [2-4]`) - reg.Longest() - return reg.FindString(value) == value && value != "" + return Digits.MatchString(value) } func TextDecorationHandler(value string) bool { @@ -1813,9 +1765,7 @@ func TextJustifyHandler(value string) bool { } func TextOverflowHandler(value string) bool { - reg := regexp.MustCompile("[\"'][a-z]+[\"']") - reg.Longest() - if reg.FindString(value) == value && value != "" { + if QuotedAlpha.MatchString(value) { return true } values := []string{"clip", "ellipsis", "initial", "inherit"} @@ -1868,18 +1818,13 @@ func TransformHandler(value string) bool { if in([]string{value}, values) { return true } - reg := regexp.MustCompile(`matrix\(([ ]*[0-9]+[\.]?[0-9]*,){5}([ ]*[0-9]+[\.]?[0-9]*)\)`) - reg.Longest() - if reg.FindString(value) == value && value != "" { + if Matrix.MatchString(value) { return true } - reg = regexp.MustCompile(`matrix3d\(([ ]*[0-9]+[\.]?[0-9]*,){15}([ ]*[0-9]+[\.]?[0-9]*)\)`) - if reg.FindString(value) == value && value != "" { + if Matrix3D.MatchString(value) { return true } - reg = regexp.MustCompile(`(translate|translate3d|translatex|translatey|translatez|scale|scale3d|scalex|scaley|scalez)\(`) - reg.Longest() - subValue := string(reg.ReplaceAll([]byte(value), []byte{})) + subValue := string(TranslateScale.ReplaceAll([]byte(value), []byte{})) trimValue := strings.Split(strings.TrimSuffix(subValue, ")"), ",") valid := true for _, i := range trimValue { @@ -1891,19 +1836,13 @@ func TransformHandler(value string) bool { if valid && trimValue != nil { return true } - reg = regexp.MustCompile(`rotate(x|y|z)?\(([12]?|3[0-5][0-9]|360)\)`) - reg.Longest() - if reg.FindString(value) == value && value != "" { + if Rotate.MatchString(value) { return true } - reg = regexp.MustCompile(`rotate3d\(([ ]?(1(\.0)?|0\.[0-9]+),){3}([12]?|3[0-5][0-9]|360)\)`) - reg.Longest() - if reg.FindString(value) == value && value != "" { + if Rotate3D.MatchString(value) { return true } - reg = regexp.MustCompile(`skew(x|y)?\(`) - reg.Longest() - subValue = string(reg.ReplaceAll([]byte(value), []byte{})) + subValue = string(Skew.ReplaceAll([]byte(value), []byte{})) subValue = strings.TrimSuffix(subValue, ")") trimValue = strings.Split(subValue, ",") valid = true @@ -1916,9 +1855,7 @@ func TransformHandler(value string) bool { if valid { return true } - reg = regexp.MustCompile(`perspective\(`) - reg.Longest() - subValue = string(reg.ReplaceAll([]byte(value), []byte{})) + subValue = string(Perspective.ReplaceAll([]byte(value), []byte{})) subValue = strings.TrimSuffix(subValue, ")") return LengthHandler(subValue) } @@ -1973,9 +1910,7 @@ func TransitionHandler(value string) bool { } func TransitionDelayHandler(value string) bool { - reg := regexp.MustCompile("[0-9]+[.]?[0-9]*(s|ms)?") - reg.Longest() - if reg.FindString(value) == value && value != "" { + if Time.MatchString(value) { return true } values := []string{"initial", "inherit"} @@ -1984,9 +1919,7 @@ func TransitionDelayHandler(value string) bool { } func TransitionDurationHandler(value string) bool { - reg := regexp.MustCompile("[0-9]+[.]?[0-9]*(s|ms)?") - reg.Longest() - if reg.FindString(value) == value && value != "" { + if Time.MatchString(value) { return true } values := []string{"initial", "inherit"} @@ -1995,9 +1928,7 @@ func TransitionDurationHandler(value string) bool { } func TransitionPropertyHandler(value string) bool { - reg := regexp.MustCompile("([a-zA-Z]+,[ ]?)*[a-zA-Z]+") - reg.Longest() - if reg.FindString(value) == value && value != "" { + if TransitionProp.MatchString(value) { return true } values := []string{"none", "all", "initial", "inherit"} @@ -2075,9 +2006,7 @@ func WritingModeHandler(value string) bool { } func ZIndexHandler(value string) bool { - reg := regexp.MustCompile(`[\-]?[0-9]+`) - reg.Longest() - if reg.FindString(value) == value && value != "" { + if ZIndex.MatchString(value) { return true } values := []string{"auto", "initial", "inherit"} diff --git a/backend/vendor/github.com/microcosm-cc/bluemonday/helpers.go b/backend/vendor/github.com/microcosm-cc/bluemonday/helpers.go index 4e31fb6fba..d4039492ea 100644 --- a/backend/vendor/github.com/microcosm-cc/bluemonday/helpers.go +++ b/backend/vendor/github.com/microcosm-cc/bluemonday/helpers.go @@ -117,7 +117,7 @@ var ( // This is not exported as it's not useful by itself, and only has value // within the AllowDataURIImages func dataURIImagePrefix = regexp.MustCompile( - `^image/(gif|jpeg|png|webp);base64,`, + `^image/(gif|jpeg|png|svg\+xml|webp);base64,`, ) ) diff --git a/backend/vendor/github.com/microcosm-cc/bluemonday/sanitize.go b/backend/vendor/github.com/microcosm-cc/bluemonday/sanitize.go index 9bd91ab379..904ee82e14 100644 --- a/backend/vendor/github.com/microcosm-cc/bluemonday/sanitize.go +++ b/backend/vendor/github.com/microcosm-cc/bluemonday/sanitize.go @@ -322,9 +322,7 @@ func (p *Policy) sanitize(r io.Reader, w io.Writer) error { aps = aa } if len(token.Attr) != 0 { - token.Attr = escapeAttributes( - p.sanitizeAttrs(token.Data, token.Attr, aps), - ) + token.Attr = p.sanitizeAttrs(token.Data, token.Attr, aps) } if len(token.Attr) == 0 { @@ -434,7 +432,7 @@ func (p *Policy) sanitize(r io.Reader, w io.Writer) error { } if len(token.Attr) != 0 { - token.Attr = escapeAttributes(p.sanitizeAttrs(token.Data, token.Attr, aps)) + token.Attr = p.sanitizeAttrs(token.Data, token.Attr, aps) } if len(token.Attr) == 0 && !p.allowNoAttrs(token.Data) { @@ -565,11 +563,9 @@ attrsLoop: for _, ap := range apl { if ap.regexp != nil { if ap.regexp.MatchString(htmlAttr.Val) { - htmlAttr.Val = escapeAttribute(htmlAttr.Val) cleanAttrs = append(cleanAttrs, htmlAttr) } } else { - htmlAttr.Val = escapeAttribute(htmlAttr.Val) cleanAttrs = append(cleanAttrs, htmlAttr) } } @@ -1112,18 +1108,3 @@ func normaliseElementName(str string) string { `"`, ) } - -func escapeAttributes(attrs []html.Attribute) []html.Attribute { - escapedAttrs := []html.Attribute{} - for _, attr := range attrs { - attr.Val = escapeAttribute(attr.Val) - escapedAttrs = append(escapedAttrs, attr) - } - return escapedAttrs -} - -func escapeAttribute(val string) string { - val = strings.Replace(val, string([]rune{'\u00A0'}), ` `, -1) - val = strings.Replace(val, `"`, `"`, -1) - return val -} diff --git a/backend/vendor/modules.txt b/backend/vendor/modules.txt index 4bf67da676..37805af96b 100644 --- a/backend/vendor/modules.txt +++ b/backend/vendor/modules.txt @@ -86,7 +86,7 @@ github.com/go-chi/render github.com/go-oauth2/oauth2/v4 github.com/go-oauth2/oauth2/v4/errors github.com/go-oauth2/oauth2/v4/server -# github.com/go-pkgz/auth v1.19.1-0.20220605174438-06e72788bcbb +# github.com/go-pkgz/auth v1.19.1-0.20220624002401-8b91a0eee69a ## explicit; go 1.17 github.com/go-pkgz/auth github.com/go-pkgz/auth/avatar @@ -185,8 +185,8 @@ github.com/klauspost/compress/zstd/internal/xxhash # github.com/kyokomi/emoji/v2 v2.2.9 ## explicit; go 1.14 github.com/kyokomi/emoji/v2 -# github.com/microcosm-cc/bluemonday v1.0.18 -## explicit; go 1.16 +# github.com/microcosm-cc/bluemonday v1.0.19 +## explicit; go 1.18 github.com/microcosm-cc/bluemonday github.com/microcosm-cc/bluemonday/css # github.com/nullrocks/identicon v0.0.0-20180626043057-7875f45b0022