Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optionally sanitize links rendered by the links extension #2169

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions demos/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"ts": "tsc --project tsconfig.base.json --noEmit && tsc --project tsconfig.react.json --noEmit && tsc --project tsconfig.vue-2.json --noEmit && tsc --project tsconfig.vue-3.json --noEmit"
},
"dependencies": {
"@braintree/sanitize-url": "^5.0.2",
"d3": "^7.1.1",
"fast-glob": "^3.2.7",
"remixicon": "^2.5.0",
Expand Down
6 changes: 6 additions & 0 deletions demos/src/Marks/Link/React/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Paragraph from '@tiptap/extension-paragraph'
import Text from '@tiptap/extension-text'
import Link from '@tiptap/extension-link'
import Code from '@tiptap/extension-code'
import { sanitizeUrl } from '@braintree/sanitize-url'
import './styles.scss'

export default () => {
Expand All @@ -15,6 +16,7 @@ export default () => {
Text,
Link.configure({
openOnClick: false,
sanitizeUrl,
}),
Code,
],
Expand All @@ -25,6 +27,10 @@ export default () => {
<p>
By default every link will get a <code>rel="noopener noreferrer nofollow"</code> attribute. It’s configurable though.
</p>
<p>
By default, links will not be sanitized. But for this example, we use a package and an included option to sanitize.
Without sanitization, <a href="javascript:alert(123)">this link could be malicious</a>.
</p>
`,
})

Expand Down
8 changes: 7 additions & 1 deletion demos/src/Marks/Link/Vue/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import Paragraph from '@tiptap/extension-paragraph'
import Text from '@tiptap/extension-text'
import Link from '@tiptap/extension-link'
import Code from '@tiptap/extension-code'
import { sanitizeUrl } from '@braintree/sanitize-url'

export default {
components: {
Expand All @@ -37,6 +38,7 @@ export default {
Text,
Link.configure({
openOnClick: false,
sanitizeUrl,
}),
Code,
],
Expand All @@ -45,7 +47,11 @@ export default {
Wow, this editor has support for links to the whole <a href="https://en.wikipedia.org/wiki/World_Wide_Web">world wide web</a>. We tested a lot of URLs and I think you can add *every URL* you want. Isn’t that cool? Let’s try <a href="https://statamic.com/">another one!</a> Yep, seems to work.
</p>
<p>
By default every link will get a <code>rel="noopener noreferrer nofollow"</code> attribute. It’s configurable though.
By default, every link will get a <code>rel="noopener noreferrer nofollow"</code> attribute. It’s configurable though.
</p>
<p>
By default, links will not be sanitized. But for this example, we use a package and an included option to sanitize.
Without sanitization, <a href="javascript:alert(123)">this link could be malicious</a>.
</p>
`,
})
Expand Down
15 changes: 15 additions & 0 deletions packages/extension-link/src/link.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ export interface LinkOptions {
* A list of HTML attributes to be rendered.
*/
HTMLAttributes: Record<string, any>,
/**
* Optionally provide a function to sanitize links before rendering. This should be done using,
* a popular package for url sanitization, if this editor displays any user-generated input.
* Without this, a malicious actor will be able to insert link text such as `javascript:alert(123)`
* to run arbitrary javascript onClick.
*/
sanitizeUrl?: (href: string) => string,
}

declare module '@tiptap/core' {
Expand Down Expand Up @@ -76,7 +83,15 @@ export const Link = Mark.create<LinkOptions>({
},

renderHTML({ HTMLAttributes }) {
if (this.options.sanitizeUrl != null && typeof this.options.sanitizeUrl === 'function') {
const SanitizedHTMLAttributes = {
...HTMLAttributes,
href: this.options.sanitizeUrl(HTMLAttributes.href),
}
return ['a', mergeAttributes(this.options.HTMLAttributes, SanitizedHTMLAttributes), 0]
}
return ['a', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0]

},

addCommands() {
Expand Down
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -951,6 +951,11 @@
"@babel/helper-validator-identifier" "^7.15.7"
to-fast-properties "^2.0.0"

"@braintree/sanitize-url@^5.0.2":
version "5.0.2"
resolved "https://registry.yarnpkg.com/@braintree/sanitize-url/-/sanitize-url-5.0.2.tgz#b23080fa35520e993a8a37a0f5bca26aa4650a48"
integrity sha512-NBEJlHWrhQucLhZGHtSxM2loSaNUMajC7KOYJLyfcdW/6goVoff2HoYI3bz8YCDN0wKGbxtUL0gx2dvHpvnWlw==

"@cypress/request@^2.88.6":
version "2.88.7"
resolved "https://registry.yarnpkg.com/@cypress/request/-/request-2.88.7.tgz#386d960ab845a96953723348088525d5a75aaac4"
Expand Down