Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
fix: Disallow data: URI's for images
  • Loading branch information
tommoor committed Aug 9, 2022
1 parent 5640ec3 commit e5c5e89
Show file tree
Hide file tree
Showing 6 changed files with 29 additions and 21 deletions.
6 changes: 3 additions & 3 deletions app/editor/components/LinkEditor.tsx
Expand Up @@ -11,7 +11,7 @@ import { setTextSelection } from "prosemirror-utils";
import { EditorView } from "prosemirror-view";
import * as React from "react";
import styled from "styled-components";
import { isInternalUrl, sanitizeHref } from "@shared/utils/urls";
import { isInternalUrl, sanitizeUrl } from "@shared/utils/urls";
import Flex from "~/components/Flex";
import { Dictionary } from "~/hooks/useDictionary";
import { ToastOptions } from "~/types";
Expand Down Expand Up @@ -70,7 +70,7 @@ class LinkEditor extends React.Component<Props, State> {
};

get href(): string {
return sanitizeHref(this.props.mark?.attrs.href) ?? "";
return sanitizeUrl(this.props.mark?.attrs.href) ?? "";
}

get suggestedLinkTitle(): string {
Expand Down Expand Up @@ -113,7 +113,7 @@ class LinkEditor extends React.Component<Props, State> {

this.discardInputValue = true;
const { from, to } = this.props;
href = sanitizeHref(href) ?? "";
href = sanitizeUrl(href) ?? "";

this.props.onSelectLink({ href, title, from, to });
};
Expand Down
4 changes: 2 additions & 2 deletions shared/editor/marks/Link.tsx
Expand Up @@ -13,7 +13,7 @@ import { EditorState, Plugin } from "prosemirror-state";
import { Decoration, DecorationSet } from "prosemirror-view";
import * as React from "react";
import ReactDOM from "react-dom";
import { isExternalUrl, sanitizeHref } from "../../utils/urls";
import { isExternalUrl, sanitizeUrl } from "../../utils/urls";
import findLinkNodes from "../queries/findLinkNodes";
import { EventType, Dispatch } from "../types";
import Mark from "./Mark";
Expand Down Expand Up @@ -80,7 +80,7 @@ export default class Link extends Mark {
"a",
{
...node.attrs,
href: sanitizeHref(node.attrs.href),
href: sanitizeUrl(node.attrs.href),
rel: "noopener noreferrer nofollow",
},
0,
Expand Down
4 changes: 2 additions & 2 deletions shared/editor/nodes/Attachment.tsx
Expand Up @@ -4,7 +4,7 @@ import { NodeSpec, NodeType, Node as ProsemirrorNode } from "prosemirror-model";
import * as React from "react";
import { Trans } from "react-i18next";
import { bytesToHumanReadable } from "../../utils/files";
import { sanitizeHref } from "../../utils/urls";
import { sanitizeUrl } from "../../utils/urls";
import toggleWrap from "../commands/toggleWrap";
import FileExtension from "../components/FileExtension";
import Widget from "../components/Widget";
Expand Down Expand Up @@ -57,7 +57,7 @@ export default class Attachment extends Node {
{
class: `attachment`,
id: node.attrs.id,
href: sanitizeHref(node.attrs.href),
href: sanitizeUrl(node.attrs.href),
download: node.attrs.title,
"data-size": node.attrs.size,
},
Expand Down
4 changes: 2 additions & 2 deletions shared/editor/nodes/Embed.tsx
Expand Up @@ -2,7 +2,7 @@ import Token from "markdown-it/lib/token";
import { NodeSpec, NodeType, Node as ProsemirrorNode } from "prosemirror-model";
import { EditorState } from "prosemirror-state";
import * as React from "react";
import { sanitizeHref } from "../../utils/urls";
import { sanitizeUrl } from "../../utils/urls";
import DisabledEmbed from "../components/DisabledEmbed";
import { MarkdownSerializerState } from "../lib/markdown/serializer";
import embedsRule from "../rules/embeds";
Expand Down Expand Up @@ -50,7 +50,7 @@ export default class Embed extends Node {
"iframe",
{
class: "embed",
src: sanitizeHref(node.attrs.href),
src: sanitizeUrl(node.attrs.href),
contentEditable: "false",
},
0,
Expand Down
12 changes: 10 additions & 2 deletions shared/editor/nodes/Image.tsx
Expand Up @@ -12,6 +12,7 @@ import * as React from "react";
import ImageZoom from "react-medium-image-zoom";
import styled from "styled-components";
import { getDataTransferFiles, getEventFiles } from "../../utils/files";
import { sanitizeUrl } from "../../utils/urls";
import { AttachmentValidation } from "../../validations";
import insertFiles, { Options } from "../commands/insertFiles";
import { MarkdownSerializerState } from "../lib/markdown/serializer";
Expand Down Expand Up @@ -197,7 +198,14 @@ export default class Image extends Node {
{
class: className,
},
["img", { ...node.attrs, contentEditable: "false" }],
[
"img",
{
...node.attrs,
src: sanitizeUrl(node.attrs.src),
contentEditable: "false",
},
],
["p", { class: "caption" }, 0],
];
},
Expand Down Expand Up @@ -507,7 +515,7 @@ const ImageComponent = (
</Button>
<ImageZoom
image={{
src,
src: sanitizeUrl(src) ?? "",
alt,
// @ts-expect-error type is incorrect, allows spreading all img props
onLoad: (ev) => {
Expand Down
20 changes: 10 additions & 10 deletions shared/utils/urls.ts
Expand Up @@ -71,24 +71,24 @@ export function isExternalUrl(url: string) {
}

/**
* For use in the editor, this function will ensure that a link href is
* For use in the editor, this function will ensure that a url is
* potentially valid, and filter out unsupported and malicious protocols.
*
* @param href The href to sanitize
* @param url The url to sanitize
* @returns The sanitized href
*/
export function sanitizeHref(href: string | null | undefined) {
if (!href) {
export function sanitizeUrl(url: string | null | undefined) {
if (!url) {
return undefined;
}

if (
!isUrl(href) &&
!href.startsWith("/") &&
!href.startsWith("#") &&
!href.startsWith("mailto:")
!isUrl(url) &&
!url.startsWith("/") &&
!url.startsWith("#") &&
!url.startsWith("mailto:")
) {
return `https://${href}`;
return `https://${url}`;
}
return href;
return url;
}

0 comments on commit e5c5e89

Please sign in to comment.