Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/lib')
-rw-r--r--app/assets/javascripts/lib/dompurify.js53
-rw-r--r--app/assets/javascripts/lib/graphql.js1
-rw-r--r--app/assets/javascripts/lib/utils/axios_startup_calls.js2
-rw-r--r--app/assets/javascripts/lib/utils/common_utils.js1
-rw-r--r--app/assets/javascripts/lib/utils/csrf.js8
-rw-r--r--app/assets/javascripts/lib/utils/datetime_utility.js19
-rw-r--r--app/assets/javascripts/lib/utils/experimentation.js3
-rw-r--r--app/assets/javascripts/lib/utils/highlight.js2
-rw-r--r--app/assets/javascripts/lib/utils/rails_ujs.js20
-rw-r--r--app/assets/javascripts/lib/utils/url_utility.js33
10 files changed, 131 insertions, 11 deletions
diff --git a/app/assets/javascripts/lib/dompurify.js b/app/assets/javascripts/lib/dompurify.js
new file mode 100644
index 00000000000..d9ea57fbbce
--- /dev/null
+++ b/app/assets/javascripts/lib/dompurify.js
@@ -0,0 +1,53 @@
+import { sanitize as dompurifySanitize, addHook } from 'dompurify';
+import { getBaseURL, relativePathToAbsolute } from '~/lib/utils/url_utility';
+
+// Safely allow SVG <use> tags
+
+const defaultConfig = {
+ ADD_TAGS: ['use'],
+};
+
+// Only icons urls from `gon` are allowed
+const getAllowedIconUrls = (gon = window.gon) =>
+ [gon.sprite_file_icons, gon.sprite_icons].filter(Boolean);
+
+const isUrlAllowed = url => getAllowedIconUrls().some(allowedUrl => url.startsWith(allowedUrl));
+
+const isHrefSafe = url =>
+ isUrlAllowed(url) || isUrlAllowed(relativePathToAbsolute(url, getBaseURL()));
+
+const removeUnsafeHref = (node, attr) => {
+ if (!node.hasAttribute(attr)) {
+ return;
+ }
+
+ if (!isHrefSafe(node.getAttribute(attr))) {
+ node.removeAttribute(attr);
+ }
+};
+
+/**
+ * Sanitize icons' <use> tag attributes, to safely include
+ * svgs such as in:
+ *
+ * <svg viewBox="0 0 100 100">
+ * <use href="/assets/icons-xxx.svg#icon_name"></use>
+ * </svg>
+ *
+ * @param {Object} node - Node to sanitize
+ */
+const sanitizeSvgIcon = node => {
+ removeUnsafeHref(node, 'href');
+
+ // Note: `xlink:href` is deprecated, but still in use
+ // https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/xlink:href
+ removeUnsafeHref(node, 'xlink:href');
+};
+
+addHook('afterSanitizeAttributes', node => {
+ if (node.tagName.toLowerCase() === 'use') {
+ sanitizeSvgIcon(node);
+ }
+});
+
+export const sanitize = (val, config = defaultConfig) => dompurifySanitize(val, config);
diff --git a/app/assets/javascripts/lib/graphql.js b/app/assets/javascripts/lib/graphql.js
index d2907f401c0..0e07f7d8e44 100644
--- a/app/assets/javascripts/lib/graphql.js
+++ b/app/assets/javascripts/lib/graphql.js
@@ -31,6 +31,7 @@ export default (resolvers = {}, config = {}) => {
// We set to `same-origin` which is default value in modern browsers.
// See https://github.com/whatwg/fetch/pull/585 for more information.
credentials: 'same-origin',
+ batchMax: config.batchMax || 10,
};
const uploadsLink = ApolloLink.split(
diff --git a/app/assets/javascripts/lib/utils/axios_startup_calls.js b/app/assets/javascripts/lib/utils/axios_startup_calls.js
index 7e2665b910c..7bb1da5aed5 100644
--- a/app/assets/javascripts/lib/utils/axios_startup_calls.js
+++ b/app/assets/javascripts/lib/utils/axios_startup_calls.js
@@ -7,7 +7,7 @@ const removeGitLabUrl = url => url.replace(gon.gitlab_url, '');
const getFullUrl = req => {
const url = removeGitLabUrl(req.url);
- return mergeUrlParams(req.params || {}, url);
+ return mergeUrlParams(req.params || {}, url, { sort: true });
};
const handleStartupCall = async ({ fetchCall }, req) => {
diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js
index bcf302cc262..28b624168d5 100644
--- a/app/assets/javascripts/lib/utils/common_utils.js
+++ b/app/assets/javascripts/lib/utils/common_utils.js
@@ -44,6 +44,7 @@ export const checkPageAndAction = (page, action) => {
return pagePath === page && actionPath === action;
};
+export const isInIncidentPage = () => checkPageAndAction('issues', 'incident');
export const isInIssuePage = () => checkPageAndAction('issues', 'show');
export const isInMRPage = () => checkPageAndAction('merge_requests', 'show');
export const isInEpicPage = () => checkPageAndAction('epics', 'show');
diff --git a/app/assets/javascripts/lib/utils/csrf.js b/app/assets/javascripts/lib/utils/csrf.js
index ca9828c4682..3114a2a0dfb 100644
--- a/app/assets/javascripts/lib/utils/csrf.js
+++ b/app/assets/javascripts/lib/utils/csrf.js
@@ -1,5 +1,3 @@
-import $ from 'jquery';
-
/*
This module provides easy access to the CSRF token and caches
it for re-use. It also exposes some values commonly used in relation
@@ -20,7 +18,6 @@ If you need to compose a headers object, use the spread operator:
see also http://guides.rubyonrails.org/security.html#cross-site-request-forgery-csrf
and https://github.com/rails/jquery-rails/blob/v4.3.1/vendor/assets/javascripts/jquery_ujs.js#L59-L62
*/
-
const csrf = {
init() {
const tokenEl = document.querySelector('meta[name=csrf-token]');
@@ -52,9 +49,4 @@ const csrf = {
csrf.init();
-// use our cached token for any $.rails-generated AJAX requests
-if ($.rails) {
- $.rails.csrfToken = () => csrf.token;
-}
-
export default csrf;
diff --git a/app/assets/javascripts/lib/utils/datetime_utility.js b/app/assets/javascripts/lib/utils/datetime_utility.js
index b193a8b2c9a..261f76a0f2d 100644
--- a/app/assets/javascripts/lib/utils/datetime_utility.js
+++ b/app/assets/javascripts/lib/utils/datetime_utility.js
@@ -743,3 +743,22 @@ export const differenceInMilliseconds = (startDate, endDate = Date.now()) => {
const endDateInMS = endDate instanceof Date ? endDate.getTime() : endDate;
return endDateInMS - startDateInMS;
};
+
+/**
+ * A utility which returns a new date at the first day of the month for any given date.
+ *
+ * @param {Date} date
+ *
+ * @return {Date} the date at the first day of the month
+ */
+export const dateAtFirstDayOfMonth = date => new Date(newDate(date).setDate(1));
+
+/**
+ * A utility function which checks if two dates match.
+ *
+ * @param {Date|Int} date1 Can be either a date object or a unix timestamp.
+ * @param {Date|Int} date2 Can be either a date object or a unix timestamp.
+ *
+ * @return {Boolean} true if the dates match
+ */
+export const datesMatch = (date1, date2) => differenceInMilliseconds(date1, date2) === 0;
diff --git a/app/assets/javascripts/lib/utils/experimentation.js b/app/assets/javascripts/lib/utils/experimentation.js
new file mode 100644
index 00000000000..555e76055e0
--- /dev/null
+++ b/app/assets/javascripts/lib/utils/experimentation.js
@@ -0,0 +1,3 @@
+export function isExperimentEnabled(experimentKey) {
+ return Boolean(window.gon?.experiments?.[experimentKey]);
+}
diff --git a/app/assets/javascripts/lib/utils/highlight.js b/app/assets/javascripts/lib/utils/highlight.js
index 32553af9af3..8fa8af670b3 100644
--- a/app/assets/javascripts/lib/utils/highlight.js
+++ b/app/assets/javascripts/lib/utils/highlight.js
@@ -1,5 +1,5 @@
import fuzzaldrinPlus from 'fuzzaldrin-plus';
-import { sanitize } from 'dompurify';
+import { sanitize } from '~/lib/dompurify';
/**
* Wraps substring matches with HTML `<span>` elements.
diff --git a/app/assets/javascripts/lib/utils/rails_ujs.js b/app/assets/javascripts/lib/utils/rails_ujs.js
new file mode 100644
index 00000000000..8b40cc7bd11
--- /dev/null
+++ b/app/assets/javascripts/lib/utils/rails_ujs.js
@@ -0,0 +1,20 @@
+import Rails from '@rails/ujs';
+
+export const initRails = () => {
+ // eslint-disable-next-line no-underscore-dangle
+ if (!window._rails_loaded) {
+ Rails.start();
+
+ // Count XHR requests for tests. See spec/support/helpers/wait_for_requests.rb
+ window.pendingRailsUJSRequests = 0;
+ document.body.addEventListener('ajax:complete', () => {
+ window.pendingRailsUJSRequests -= 1;
+ });
+
+ document.body.addEventListener('ajax:beforeSend', () => {
+ window.pendingRailsUJSRequests += 1;
+ });
+ }
+};
+
+export { Rails };
diff --git a/app/assets/javascripts/lib/utils/url_utility.js b/app/assets/javascripts/lib/utils/url_utility.js
index e9c3fe0a406..7f6b212b5fc 100644
--- a/app/assets/javascripts/lib/utils/url_utility.js
+++ b/app/assets/javascripts/lib/utils/url_utility.js
@@ -16,7 +16,7 @@ function decodeUrlParameter(val) {
return decodeURIComponent(val.replace(/\+/g, '%20'));
}
-function cleanLeadingSeparator(path) {
+export function cleanLeadingSeparator(path) {
return path.replace(PATH_SEPARATOR_LEADING_REGEX, '');
}
@@ -73,6 +73,7 @@ export function getParameterValues(sParam, url = window.location) {
* @param {String} url
* @param {Object} options
* @param {Boolean} options.spreadArrays - split array values into separate key/value-pairs
+ * @param {Boolean} options.sort - alphabetically sort params in the returned url (in asc order, i.e., a-z)
*/
export function mergeUrlParams(params, url, options = {}) {
const { spreadArrays = false, sort = false } = options;
@@ -255,6 +256,15 @@ export function getBaseURL() {
}
/**
+ * Takes a URL and returns content from the start until the final '/'
+ *
+ * @param {String} url - full url, including protocol and host
+ */
+export function stripFinalUrlSegment(url) {
+ return new URL('.', url).href;
+}
+
+/**
* Returns true if url is an absolute URL
*
* @param {String} url
@@ -434,3 +444,24 @@ export function getHTTPProtocol(url) {
const protocol = url.split(':');
return protocol.length > 1 ? protocol[0] : undefined;
}
+
+/**
+ * Strips the filename from the given path by removing every non-slash character from the end of the
+ * passed parameter.
+ * @param {string} path
+ */
+export function stripPathTail(path = '') {
+ return path.replace(/[^/]+$/, '');
+}
+
+export function getURLOrigin(url) {
+ if (!url) {
+ return window.location.origin;
+ }
+
+ try {
+ return new URL(url).origin;
+ } catch (e) {
+ return null;
+ }
+}