diff --git a/admin/package.json b/admin/package.json index 7e44e563..91d20910 100644 --- a/admin/package.json +++ b/admin/package.json @@ -106,7 +106,7 @@ "webpack-cli": "^3.1.0", "webpack-dev-server": "3.2.1", "webpack-hot-middleware": "^2.24.0", - "xss": "1.0.3" + "xss": "^1.0.3" }, "devDependencies": { "@types/bn.js": "4.11.1", diff --git a/admin/src/components/Markdown/index.tsx b/admin/src/components/Markdown/index.tsx index bd4d3b5e..97b53ab7 100644 --- a/admin/src/components/Markdown/index.tsx +++ b/admin/src/components/Markdown/index.tsx @@ -5,12 +5,13 @@ import './index.less'; interface Props extends React.HTMLAttributes { source: string; + reduced?: boolean; } export default class Markdown extends React.PureComponent { render() { - const { source, ...rest } = this.props; - const html = mdToHtml(source); + const { source, reduced, ...rest } = this.props; + const html = mdToHtml(source, reduced); // TS types seem to be fighting over react prop defs for div const divProps = rest as any; return ( diff --git a/admin/src/components/Moderation/ModerationItem.tsx b/admin/src/components/Moderation/ModerationItem.tsx index df80e311..fffd1eb5 100644 --- a/admin/src/components/Moderation/ModerationItem.tsx +++ b/admin/src/components/Moderation/ModerationItem.tsx @@ -54,7 +54,7 @@ class ModerationItem extends React.Component { } description={ - + } /> diff --git a/admin/src/components/UserDetail/index.tsx b/admin/src/components/UserDetail/index.tsx index 6fa12059..84b9158a 100644 --- a/admin/src/components/UserDetail/index.tsx +++ b/admin/src/components/UserDetail/index.tsx @@ -270,7 +270,11 @@ class UserDetailNaked extends React.Component { {' '} at {formatDateMs(c.dateCreated)} - + } /> diff --git a/admin/src/util/md.ts b/admin/src/util/md.ts index 1ba2132b..9a508db4 100644 --- a/admin/src/util/md.ts +++ b/admin/src/util/md.ts @@ -1,4 +1,5 @@ import Showdown from 'showdown'; +import xss from 'xss'; const showdownConverter = new Showdown.Converter({ simplifiedAutoLink: true, @@ -9,6 +10,41 @@ const showdownConverter = new Showdown.Converter({ excludeTrailingPunctuationFromURLs: true, }); -export const mdToHtml = (text: string) => { - return showdownConverter.makeHtml(text); +export const mdToHtml = (text: string, reduced: boolean = false) => { + const html = showdownConverter.makeHtml(text); + return reduced ? xss(html, reducedXssOpts) : xss(html); +}; + +const reducedXssOpts = { + stripIgnoreTag: true, + whiteList: { + a: ['target', 'href', 'title'], + b: [], + blockquote: [], + br: [], + code: [], + del: [], + em: [], + h4: [], + h5: [], + h6: [], + hr: [], + i: [], + li: [], + ol: [], + p: [], + pre: [], + small: [], + sub: [], + sup: [], + strong: [], + table: ['width', 'border', 'align', 'valign'], + tbody: ['align', 'valign'], + td: ['width', 'rowspan', 'colspan', 'align', 'valign'], + tfoot: ['align', 'valign'], + th: ['width', 'rowspan', 'colspan', 'align', 'valign'], + thead: ['align', 'valign'], + tr: ['rowspan', 'align', 'valign'], + ul: [], + }, }; diff --git a/admin/yarn.lock b/admin/yarn.lock index ae35d90b..d391a490 100644 --- a/admin/yarn.lock +++ b/admin/yarn.lock @@ -8477,9 +8477,10 @@ xregexp@4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-4.0.0.tgz#e698189de49dd2a18cc5687b05e17c8e43943020" -xss@1.0.3: +xss@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/xss/-/xss-1.0.3.tgz#d04bd2558fd6c29c46113824d5e8b2a910054e23" + integrity sha512-LTpz3jXPLUphMMmyufoZRSKnqMj41OVypZ8uYGzvjkMV9C1EdACrhQl/EM8Qfh5htSAuMIQFOejmKAZGkJfaCg== dependencies: commander "^2.9.0" cssfilter "0.0.10" diff --git a/backend/grant/email/send.py b/backend/grant/email/send.py index 4598a9cc..1758509d 100644 --- a/backend/grant/email/send.py +++ b/backend/grant/email/send.py @@ -385,6 +385,8 @@ def make_envelope(to, type, email_args): def sendgrid_send(mail): + to = mail.___to + type = mail.___type try: sg = sendgrid.SendGridAPIClient(apikey=SENDGRID_API_KEY) res = sg.client.mail.send.post(request_body=mail.get())