Skip to content
Snippets Groups Projects
Commit 810afe1a authored by Tom Robinson's avatar Tom Robinson Committed by GitHub
Browse files

Merge pull request #4503 from q-m/feature/urlify-strings

Detect URL and Email in unspecified strings
parents f8519dda 86e85870
No related branches found
No related tags found
No related merge requests found
......@@ -103,18 +103,19 @@ export function formatTimeWithUnit(value, unit, options = {}) {
}
}
const EMAIL_WHITELIST_REGEX = /.+@.+/;
// https://github.com/angular/angular.js/blob/v1.6.3/src/ng/directive/input.js#L27
const EMAIL_WHITELIST_REGEX = /^(?=.{1,254}$)(?=.{1,64}@)[-!#$%&'*+/0-9=?A-Z^_`a-z{|}~]+(\.[-!#$%&'*+/0-9=?A-Z^_`a-z{|}~]+)*@[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?(\.[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?)*$/;
export function formatEmail(value, { jsx } = {}) {
if (jsx && EMAIL_WHITELIST_REGEX.test(value)) {
return <ExternalLink href={"mailto:" + value}>{value}</ExternalLink>;
} else {
value;
return value;
}
}
// prevent `javascript:` etc URLs
const URL_WHITELIST_REGEX = /^(https?|mailto):/;
// based on https://github.com/angular/angular.js/blob/v1.6.3/src/ng/directive/input.js#L25
const URL_WHITELIST_REGEX = /^(https?|mailto):\/*(?:[^:@]+(?::[^@]+)?@)?(?:[^\s:/?#]+|\[[a-f\d:]+])(?::\d+)?(?:\/[^?#]*)?(?:\?[^#]*)?(?:#.*)?$/i;
export function formatUrl(value, { jsx } = {}) {
if (jsx && URL_WHITELIST_REGEX.test(value)) {
......@@ -124,6 +125,15 @@ export function formatUrl(value, { jsx } = {}) {
}
}
// fallback for formatting a string without a column special_type
function formatStringFallback(value, options = {}) {
value = formatUrl(value, options);
if (typeof value === 'string') {
value = formatEmail(value, options);
}
return value;
}
export function formatValue(value, options = {}) {
let column = options.column;
options = {
......@@ -142,7 +152,7 @@ export function formatValue(value, options = {}) {
} else if (isDate(column) || moment.isDate(value) || moment.isMoment(value) || moment(value, ["YYYY-MM-DD'T'HH:mm:ss.SSSZ"], true).isValid()) {
return parseTimestamp(value, column && column.unit).format("LLLL");
} else if (typeof value === "string") {
return value;
return formatStringFallback(value, options);
} else if (typeof value === "number") {
if (isCoordinate(column)) {
return DECIMAL_DEGREES_FORMATTER(value);
......
......@@ -57,6 +57,12 @@ describe('formatting', () => {
expect(formatValue(37.7749, { column: { base_type: TYPE.Number, special_type: TYPE.Latitude }})).toEqual("37.77490000");
expect(formatValue(-122.4194, { column: { base_type: TYPE.Number, special_type: TYPE.Longitude }})).toEqual("-122.41940000");
});
it("should return a component for links in jsx mode", () => {
expect(isElementOfType(formatValue("http://metabase.com/", { jsx: true }), ExternalLink)).toEqual(true);
});
it("should return a component for email addresses in jsx mode", () => {
expect(isElementOfType(formatValue("tom@metabase.com", { jsx: true }), ExternalLink)).toEqual(true);
});
});
describe("formatUrl", () => {
......@@ -68,6 +74,10 @@ describe('formatting', () => {
expect(isElementOfType(formatUrl("https://metabase.com/", { jsx: true }), ExternalLink)).toEqual(true);
expect(isElementOfType(formatUrl("mailto:tom@metabase.com", { jsx: true }), ExternalLink)).toEqual(true);
});
it("should not return a link component for unrecognized links in jsx mode", () => {
expect(isElementOfType(formatUrl("nonexistent://metabase.com/", { jsx: true }), ExternalLink)).toEqual(false);
expect(isElementOfType(formatUrl("metabase.com", { jsx: true }), ExternalLink)).toEqual(false);
});
it("should return a string for javascript:, data:, and other links in jsx mode", () => {
expect(formatUrl("javascript:alert('pwnd')", { jsx: true })).toEqual("javascript:alert('pwnd')");
expect(formatUrl("data:text/plain;charset=utf-8,hello%20world", { jsx: true })).toEqual("data:text/plain;charset=utf-8,hello%20world");
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment