fix: namespace parsing in gitlab (#84)

* fix: namespace parsing in gitlab

* test: add test for nested namespace

---------

Co-authored-by: Andrea Lamparelli <a.lamparelli95@gmail.com>
This commit is contained in:
Shyim 2023-12-05 16:08:49 +01:00 committed by GitHub
parent e7c9b4795b
commit ed32d2275b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 538 additions and 109 deletions

150
dist/cli/index.js vendored
View file

@ -1024,9 +1024,15 @@ class GitLabClient {
* @returns {{owner: string, project: string}} * @returns {{owner: string, project: string}}
*/ */
extractMergeRequestData(mrUrl) { extractMergeRequestData(mrUrl) {
const elems = mrUrl.replace("/-/", "/").split("/"); const { pathname } = new URL(mrUrl);
const elems = pathname.substring(1).replace("/-/", "/").split("/");
let namespace = "";
for (let i = 0; i < elems.length - 3; i++) {
namespace += elems[i] + "/";
}
namespace = namespace.substring(0, namespace.length - 1);
return { return {
namespace: elems[elems.length - 4], namespace: namespace,
project: elems[elems.length - 3], project: elems[elems.length - 3],
id: parseInt(mrUrl.substring(mrUrl.lastIndexOf("/") + 1, mrUrl.length)), id: parseInt(mrUrl.substring(mrUrl.lastIndexOf("/") + 1, mrUrl.length)),
}; };
@ -19253,7 +19259,7 @@ exports.suggestSimilar = suggestSimilar;
/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
"use strict"; "use strict";
// Axios v1.4.0 Copyright (c) 2023 Matt Zabriskie and contributors // Axios v1.6.0 Copyright (c) 2023 Matt Zabriskie and contributors
const FormData$1 = __nccwpck_require__(4334); const FormData$1 = __nccwpck_require__(4334);
@ -19823,8 +19829,9 @@ const reduceDescriptors = (obj, reducer) => {
const reducedDescriptors = {}; const reducedDescriptors = {};
forEach(descriptors, (descriptor, name) => { forEach(descriptors, (descriptor, name) => {
if (reducer(descriptor, name, obj) !== false) { let ret;
reducedDescriptors[name] = descriptor; if ((ret = reducer(descriptor, name, obj)) !== false) {
reducedDescriptors[name] = ret || descriptor;
} }
}); });
@ -20608,10 +20615,6 @@ function formDataToJSON(formData) {
return null; return null;
} }
const DEFAULT_CONTENT_TYPE = {
'Content-Type': undefined
};
/** /**
* It takes a string, tries to parse it, and if it fails, it returns the stringified version * It takes a string, tries to parse it, and if it fails, it returns the stringified version
* of the input * of the input
@ -20750,19 +20753,16 @@ const defaults = {
headers: { headers: {
common: { common: {
'Accept': 'application/json, text/plain, */*' 'Accept': 'application/json, text/plain, */*',
'Content-Type': undefined
} }
} }
}; };
utils.forEach(['delete', 'get', 'head'], function forEachMethodNoData(method) { utils.forEach(['delete', 'get', 'head', 'post', 'put', 'patch'], (method) => {
defaults.headers[method] = {}; defaults.headers[method] = {};
}); });
utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
defaults.headers[method] = utils.merge(DEFAULT_CONTENT_TYPE);
});
const defaults$1 = defaults; const defaults$1 = defaults;
// RawAxiosHeaders whose duplicates are ignored by node // RawAxiosHeaders whose duplicates are ignored by node
@ -21096,7 +21096,17 @@ class AxiosHeaders {
AxiosHeaders.accessor(['Content-Type', 'Content-Length', 'Accept', 'Accept-Encoding', 'User-Agent', 'Authorization']); AxiosHeaders.accessor(['Content-Type', 'Content-Length', 'Accept', 'Accept-Encoding', 'User-Agent', 'Authorization']);
utils.freezeMethods(AxiosHeaders.prototype); // reserved names hotfix
utils.reduceDescriptors(AxiosHeaders.prototype, ({value}, key) => {
let mapped = key[0].toUpperCase() + key.slice(1); // map `set` => `Set`
return {
get: () => value,
set(headerValue) {
this[mapped] = headerValue;
}
}
});
utils.freezeMethods(AxiosHeaders); utils.freezeMethods(AxiosHeaders);
const AxiosHeaders$1 = AxiosHeaders; const AxiosHeaders$1 = AxiosHeaders;
@ -21216,7 +21226,7 @@ function buildFullPath(baseURL, requestedURL) {
return requestedURL; return requestedURL;
} }
const VERSION = "1.4.0"; const VERSION = "1.6.0";
function parseProtocol(url) { function parseProtocol(url) {
const match = /^([-+\w]{1,25})(:?\/\/|:)/.exec(url); const match = /^([-+\w]{1,25})(:?\/\/|:)/.exec(url);
@ -21820,6 +21830,18 @@ const wrapAsync = (asyncExecutor) => {
}) })
}; };
const resolveFamily = ({address, family}) => {
if (!utils.isString(address)) {
throw TypeError('address must be a string');
}
return ({
address,
family: family || (address.indexOf('.') < 0 ? 6 : 4)
});
};
const buildAddressEntry = (address, family) => resolveFamily(utils.isObject(address) ? address : {address, family});
/*eslint consistent-return:0*/ /*eslint consistent-return:0*/
const httpAdapter = isHttpAdapterSupported && function httpAdapter(config) { const httpAdapter = isHttpAdapterSupported && function httpAdapter(config) {
return wrapAsync(async function dispatchHttpRequest(resolve, reject, onDone) { return wrapAsync(async function dispatchHttpRequest(resolve, reject, onDone) {
@ -21830,15 +21852,16 @@ const httpAdapter = isHttpAdapterSupported && function httpAdapter(config) {
let rejected = false; let rejected = false;
let req; let req;
if (lookup && utils.isAsyncFn(lookup)) { if (lookup) {
lookup = callbackify$1(lookup, (entry) => { const _lookup = callbackify$1(lookup, (value) => utils.isArray(value) ? value : [value]);
if(utils.isString(entry)) { // hotfix to support opt.all option which is required for node 20.x
entry = [entry, entry.indexOf('.') < 0 ? 6 : 4]; lookup = (hostname, opt, cb) => {
} else if (!utils.isArray(entry)) { _lookup(hostname, opt, (err, arg0, arg1) => {
throw new TypeError('lookup async function must return an array [ip: string, family: number]]') const addresses = utils.isArray(arg0) ? arg0.map(addr => buildAddressEntry(addr)) : [buildAddressEntry(arg0, arg1)];
}
return entry; opt.all ? cb(err, addresses) : cb(err, addresses[0].address, addresses[0].family);
}); });
};
} }
// temporary internal emitter until the AxiosRequest class will be implemented // temporary internal emitter until the AxiosRequest class will be implemented
@ -22065,11 +22088,13 @@ const httpAdapter = isHttpAdapterSupported && function httpAdapter(config) {
auth, auth,
protocol, protocol,
family, family,
lookup,
beforeRedirect: dispatchBeforeRedirect, beforeRedirect: dispatchBeforeRedirect,
beforeRedirects: {} beforeRedirects: {}
}; };
// cacheable-lookup integration hotfix
!utils.isUndefined(lookup) && (options.lookup = lookup);
if (config.socketPath) { if (config.socketPath) {
options.socketPath = config.socketPath; options.socketPath = config.socketPath;
} else { } else {
@ -22143,7 +22168,7 @@ const httpAdapter = isHttpAdapterSupported && function httpAdapter(config) {
delete res.headers['content-encoding']; delete res.headers['content-encoding'];
} }
switch (res.headers['content-encoding']) { switch ((res.headers['content-encoding'] || '').toLowerCase()) {
/*eslint default-case:0*/ /*eslint default-case:0*/
case 'gzip': case 'gzip':
case 'x-gzip': case 'x-gzip':
@ -22239,7 +22264,7 @@ const httpAdapter = isHttpAdapterSupported && function httpAdapter(config) {
} }
response.data = responseData; response.data = responseData;
} catch (err) { } catch (err) {
reject(AxiosError.from(err, null, config, response.request, response)); return reject(AxiosError.from(err, null, config, response.request, response));
} }
settle(resolve, reject, response); settle(resolve, reject, response);
}); });
@ -22276,7 +22301,7 @@ const httpAdapter = isHttpAdapterSupported && function httpAdapter(config) {
// This is forcing a int timeout to avoid problems if the `req` interface doesn't handle other types. // This is forcing a int timeout to avoid problems if the `req` interface doesn't handle other types.
const timeout = parseInt(config.timeout, 10); const timeout = parseInt(config.timeout, 10);
if (isNaN(timeout)) { if (Number.isNaN(timeout)) {
reject(new AxiosError( reject(new AxiosError(
'error trying to parse `config.timeout` to int', 'error trying to parse `config.timeout` to int',
AxiosError.ERR_BAD_OPTION_VALUE, AxiosError.ERR_BAD_OPTION_VALUE,
@ -22495,11 +22520,16 @@ const xhrAdapter = isXHRAdapterSupported && function (config) {
} }
} }
let contentType;
if (utils.isFormData(requestData)) { if (utils.isFormData(requestData)) {
if (platform.isStandardBrowserEnv || platform.isStandardBrowserWebWorkerEnv) { if (platform.isStandardBrowserEnv || platform.isStandardBrowserWebWorkerEnv) {
requestHeaders.setContentType(false); // Let the browser set it requestHeaders.setContentType(false); // Let the browser set it
} else { } else if(!requestHeaders.getContentType(/^\s*multipart\/form-data/)){
requestHeaders.setContentType('multipart/form-data;', false); // mobile/desktop app frameworks requestHeaders.setContentType('multipart/form-data'); // mobile/desktop app frameworks
} else if(utils.isString(contentType = requestHeaders.getContentType())){
// fix semicolon duplication issue for ReactNative FormData implementation
requestHeaders.setContentType(contentType.replace(/^\s*(multipart\/form-data);+/, '$1'));
} }
} }
@ -22617,8 +22647,8 @@ const xhrAdapter = isXHRAdapterSupported && function (config) {
// Specifically not if we're in a web worker, or react-native. // Specifically not if we're in a web worker, or react-native.
if (platform.isStandardBrowserEnv) { if (platform.isStandardBrowserEnv) {
// Add xsrf header // Add xsrf header
const xsrfValue = (config.withCredentials || isURLSameOrigin(fullPath)) // regarding CVE-2023-45857 config.withCredentials condition was removed temporarily
&& config.xsrfCookieName && cookies.read(config.xsrfCookieName); const xsrfValue = isURLSameOrigin(fullPath) && config.xsrfCookieName && cookies.read(config.xsrfCookieName);
if (xsrfValue) { if (xsrfValue) {
requestHeaders.set(config.xsrfHeaderName, xsrfValue); requestHeaders.set(config.xsrfHeaderName, xsrfValue);
@ -22702,6 +22732,10 @@ utils.forEach(knownAdapters, (fn, value) => {
} }
}); });
const renderReason = (reason) => `- ${reason}`;
const isResolvedHandle = (adapter) => utils.isFunction(adapter) || adapter === null || adapter === false;
const adapters = { const adapters = {
getAdapter: (adapters) => { getAdapter: (adapters) => {
adapters = utils.isArray(adapters) ? adapters : [adapters]; adapters = utils.isArray(adapters) ? adapters : [adapters];
@ -22710,32 +22744,46 @@ const adapters = {
let nameOrAdapter; let nameOrAdapter;
let adapter; let adapter;
const rejectedReasons = {};
for (let i = 0; i < length; i++) { for (let i = 0; i < length; i++) {
nameOrAdapter = adapters[i]; nameOrAdapter = adapters[i];
if((adapter = utils.isString(nameOrAdapter) ? knownAdapters[nameOrAdapter.toLowerCase()] : nameOrAdapter)) { let id;
adapter = nameOrAdapter;
if (!isResolvedHandle(nameOrAdapter)) {
adapter = knownAdapters[(id = String(nameOrAdapter)).toLowerCase()];
if (adapter === undefined) {
throw new AxiosError(`Unknown adapter '${id}'`);
}
}
if (adapter) {
break; break;
} }
rejectedReasons[id || '#' + i] = adapter;
} }
if (!adapter) { if (!adapter) {
if (adapter === false) {
const reasons = Object.entries(rejectedReasons)
.map(([id, state]) => `adapter ${id} ` +
(state === false ? 'is not supported by the environment' : 'is not available in the build')
);
let s = length ?
(reasons.length > 1 ? 'since :\n' + reasons.map(renderReason).join('\n') : ' ' + renderReason(reasons[0])) :
'as no adapter specified';
throw new AxiosError( throw new AxiosError(
`Adapter ${nameOrAdapter} is not supported by the environment`, `There is no suitable adapter to dispatch the request ` + s,
'ERR_NOT_SUPPORT' 'ERR_NOT_SUPPORT'
); );
} }
throw new Error(
utils.hasOwnProp(knownAdapters, nameOrAdapter) ?
`Adapter '${nameOrAdapter}' is not available in the build` :
`Unknown adapter '${nameOrAdapter}'`
);
}
if (!utils.isFunction(adapter)) {
throw new TypeError('adapter is not a function');
}
return adapter; return adapter;
}, },
adapters: knownAdapters adapters: knownAdapters
@ -23066,15 +23114,13 @@ class Axios {
// Set config.method // Set config.method
config.method = (config.method || this.defaults.method || 'get').toLowerCase(); config.method = (config.method || this.defaults.method || 'get').toLowerCase();
let contextHeaders;
// Flatten headers // Flatten headers
contextHeaders = headers && utils.merge( let contextHeaders = headers && utils.merge(
headers.common, headers.common,
headers[config.method] headers[config.method]
); );
contextHeaders && utils.forEach( headers && utils.forEach(
['delete', 'get', 'head', 'post', 'put', 'patch', 'common'], ['delete', 'get', 'head', 'post', 'put', 'patch', 'common'],
(method) => { (method) => {
delete headers[method]; delete headers[method];
@ -23484,6 +23530,8 @@ axios.AxiosHeaders = AxiosHeaders$1;
axios.formToJSON = thing => formDataToJSON(utils.isHTMLForm(thing) ? new FormData(thing) : thing); axios.formToJSON = thing => formDataToJSON(utils.isHTMLForm(thing) ? new FormData(thing) : thing);
axios.getAdapter = adapters.getAdapter;
axios.HttpStatusCode = HttpStatusCode$1; axios.HttpStatusCode = HttpStatusCode$1;
axios.default = axios; axios.default = axios;

150
dist/gha/index.js vendored
View file

@ -994,9 +994,15 @@ class GitLabClient {
* @returns {{owner: string, project: string}} * @returns {{owner: string, project: string}}
*/ */
extractMergeRequestData(mrUrl) { extractMergeRequestData(mrUrl) {
const elems = mrUrl.replace("/-/", "/").split("/"); const { pathname } = new URL(mrUrl);
const elems = pathname.substring(1).replace("/-/", "/").split("/");
let namespace = "";
for (let i = 0; i < elems.length - 3; i++) {
namespace += elems[i] + "/";
}
namespace = namespace.substring(0, namespace.length - 1);
return { return {
namespace: elems[elems.length - 4], namespace: namespace,
project: elems[elems.length - 3], project: elems[elems.length - 3],
id: parseInt(mrUrl.substring(mrUrl.lastIndexOf("/") + 1, mrUrl.length)), id: parseInt(mrUrl.substring(mrUrl.lastIndexOf("/") + 1, mrUrl.length)),
}; };
@ -18666,7 +18672,7 @@ module.exports = require("zlib");
/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
"use strict"; "use strict";
// Axios v1.4.0 Copyright (c) 2023 Matt Zabriskie and contributors // Axios v1.6.0 Copyright (c) 2023 Matt Zabriskie and contributors
const FormData$1 = __nccwpck_require__(4334); const FormData$1 = __nccwpck_require__(4334);
@ -19236,8 +19242,9 @@ const reduceDescriptors = (obj, reducer) => {
const reducedDescriptors = {}; const reducedDescriptors = {};
forEach(descriptors, (descriptor, name) => { forEach(descriptors, (descriptor, name) => {
if (reducer(descriptor, name, obj) !== false) { let ret;
reducedDescriptors[name] = descriptor; if ((ret = reducer(descriptor, name, obj)) !== false) {
reducedDescriptors[name] = ret || descriptor;
} }
}); });
@ -20021,10 +20028,6 @@ function formDataToJSON(formData) {
return null; return null;
} }
const DEFAULT_CONTENT_TYPE = {
'Content-Type': undefined
};
/** /**
* It takes a string, tries to parse it, and if it fails, it returns the stringified version * It takes a string, tries to parse it, and if it fails, it returns the stringified version
* of the input * of the input
@ -20163,19 +20166,16 @@ const defaults = {
headers: { headers: {
common: { common: {
'Accept': 'application/json, text/plain, */*' 'Accept': 'application/json, text/plain, */*',
'Content-Type': undefined
} }
} }
}; };
utils.forEach(['delete', 'get', 'head'], function forEachMethodNoData(method) { utils.forEach(['delete', 'get', 'head', 'post', 'put', 'patch'], (method) => {
defaults.headers[method] = {}; defaults.headers[method] = {};
}); });
utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
defaults.headers[method] = utils.merge(DEFAULT_CONTENT_TYPE);
});
const defaults$1 = defaults; const defaults$1 = defaults;
// RawAxiosHeaders whose duplicates are ignored by node // RawAxiosHeaders whose duplicates are ignored by node
@ -20509,7 +20509,17 @@ class AxiosHeaders {
AxiosHeaders.accessor(['Content-Type', 'Content-Length', 'Accept', 'Accept-Encoding', 'User-Agent', 'Authorization']); AxiosHeaders.accessor(['Content-Type', 'Content-Length', 'Accept', 'Accept-Encoding', 'User-Agent', 'Authorization']);
utils.freezeMethods(AxiosHeaders.prototype); // reserved names hotfix
utils.reduceDescriptors(AxiosHeaders.prototype, ({value}, key) => {
let mapped = key[0].toUpperCase() + key.slice(1); // map `set` => `Set`
return {
get: () => value,
set(headerValue) {
this[mapped] = headerValue;
}
}
});
utils.freezeMethods(AxiosHeaders); utils.freezeMethods(AxiosHeaders);
const AxiosHeaders$1 = AxiosHeaders; const AxiosHeaders$1 = AxiosHeaders;
@ -20629,7 +20639,7 @@ function buildFullPath(baseURL, requestedURL) {
return requestedURL; return requestedURL;
} }
const VERSION = "1.4.0"; const VERSION = "1.6.0";
function parseProtocol(url) { function parseProtocol(url) {
const match = /^([-+\w]{1,25})(:?\/\/|:)/.exec(url); const match = /^([-+\w]{1,25})(:?\/\/|:)/.exec(url);
@ -21233,6 +21243,18 @@ const wrapAsync = (asyncExecutor) => {
}) })
}; };
const resolveFamily = ({address, family}) => {
if (!utils.isString(address)) {
throw TypeError('address must be a string');
}
return ({
address,
family: family || (address.indexOf('.') < 0 ? 6 : 4)
});
};
const buildAddressEntry = (address, family) => resolveFamily(utils.isObject(address) ? address : {address, family});
/*eslint consistent-return:0*/ /*eslint consistent-return:0*/
const httpAdapter = isHttpAdapterSupported && function httpAdapter(config) { const httpAdapter = isHttpAdapterSupported && function httpAdapter(config) {
return wrapAsync(async function dispatchHttpRequest(resolve, reject, onDone) { return wrapAsync(async function dispatchHttpRequest(resolve, reject, onDone) {
@ -21243,15 +21265,16 @@ const httpAdapter = isHttpAdapterSupported && function httpAdapter(config) {
let rejected = false; let rejected = false;
let req; let req;
if (lookup && utils.isAsyncFn(lookup)) { if (lookup) {
lookup = callbackify$1(lookup, (entry) => { const _lookup = callbackify$1(lookup, (value) => utils.isArray(value) ? value : [value]);
if(utils.isString(entry)) { // hotfix to support opt.all option which is required for node 20.x
entry = [entry, entry.indexOf('.') < 0 ? 6 : 4]; lookup = (hostname, opt, cb) => {
} else if (!utils.isArray(entry)) { _lookup(hostname, opt, (err, arg0, arg1) => {
throw new TypeError('lookup async function must return an array [ip: string, family: number]]') const addresses = utils.isArray(arg0) ? arg0.map(addr => buildAddressEntry(addr)) : [buildAddressEntry(arg0, arg1)];
}
return entry; opt.all ? cb(err, addresses) : cb(err, addresses[0].address, addresses[0].family);
}); });
};
} }
// temporary internal emitter until the AxiosRequest class will be implemented // temporary internal emitter until the AxiosRequest class will be implemented
@ -21478,11 +21501,13 @@ const httpAdapter = isHttpAdapterSupported && function httpAdapter(config) {
auth, auth,
protocol, protocol,
family, family,
lookup,
beforeRedirect: dispatchBeforeRedirect, beforeRedirect: dispatchBeforeRedirect,
beforeRedirects: {} beforeRedirects: {}
}; };
// cacheable-lookup integration hotfix
!utils.isUndefined(lookup) && (options.lookup = lookup);
if (config.socketPath) { if (config.socketPath) {
options.socketPath = config.socketPath; options.socketPath = config.socketPath;
} else { } else {
@ -21556,7 +21581,7 @@ const httpAdapter = isHttpAdapterSupported && function httpAdapter(config) {
delete res.headers['content-encoding']; delete res.headers['content-encoding'];
} }
switch (res.headers['content-encoding']) { switch ((res.headers['content-encoding'] || '').toLowerCase()) {
/*eslint default-case:0*/ /*eslint default-case:0*/
case 'gzip': case 'gzip':
case 'x-gzip': case 'x-gzip':
@ -21652,7 +21677,7 @@ const httpAdapter = isHttpAdapterSupported && function httpAdapter(config) {
} }
response.data = responseData; response.data = responseData;
} catch (err) { } catch (err) {
reject(AxiosError.from(err, null, config, response.request, response)); return reject(AxiosError.from(err, null, config, response.request, response));
} }
settle(resolve, reject, response); settle(resolve, reject, response);
}); });
@ -21689,7 +21714,7 @@ const httpAdapter = isHttpAdapterSupported && function httpAdapter(config) {
// This is forcing a int timeout to avoid problems if the `req` interface doesn't handle other types. // This is forcing a int timeout to avoid problems if the `req` interface doesn't handle other types.
const timeout = parseInt(config.timeout, 10); const timeout = parseInt(config.timeout, 10);
if (isNaN(timeout)) { if (Number.isNaN(timeout)) {
reject(new AxiosError( reject(new AxiosError(
'error trying to parse `config.timeout` to int', 'error trying to parse `config.timeout` to int',
AxiosError.ERR_BAD_OPTION_VALUE, AxiosError.ERR_BAD_OPTION_VALUE,
@ -21908,11 +21933,16 @@ const xhrAdapter = isXHRAdapterSupported && function (config) {
} }
} }
let contentType;
if (utils.isFormData(requestData)) { if (utils.isFormData(requestData)) {
if (platform.isStandardBrowserEnv || platform.isStandardBrowserWebWorkerEnv) { if (platform.isStandardBrowserEnv || platform.isStandardBrowserWebWorkerEnv) {
requestHeaders.setContentType(false); // Let the browser set it requestHeaders.setContentType(false); // Let the browser set it
} else { } else if(!requestHeaders.getContentType(/^\s*multipart\/form-data/)){
requestHeaders.setContentType('multipart/form-data;', false); // mobile/desktop app frameworks requestHeaders.setContentType('multipart/form-data'); // mobile/desktop app frameworks
} else if(utils.isString(contentType = requestHeaders.getContentType())){
// fix semicolon duplication issue for ReactNative FormData implementation
requestHeaders.setContentType(contentType.replace(/^\s*(multipart\/form-data);+/, '$1'));
} }
} }
@ -22030,8 +22060,8 @@ const xhrAdapter = isXHRAdapterSupported && function (config) {
// Specifically not if we're in a web worker, or react-native. // Specifically not if we're in a web worker, or react-native.
if (platform.isStandardBrowserEnv) { if (platform.isStandardBrowserEnv) {
// Add xsrf header // Add xsrf header
const xsrfValue = (config.withCredentials || isURLSameOrigin(fullPath)) // regarding CVE-2023-45857 config.withCredentials condition was removed temporarily
&& config.xsrfCookieName && cookies.read(config.xsrfCookieName); const xsrfValue = isURLSameOrigin(fullPath) && config.xsrfCookieName && cookies.read(config.xsrfCookieName);
if (xsrfValue) { if (xsrfValue) {
requestHeaders.set(config.xsrfHeaderName, xsrfValue); requestHeaders.set(config.xsrfHeaderName, xsrfValue);
@ -22115,6 +22145,10 @@ utils.forEach(knownAdapters, (fn, value) => {
} }
}); });
const renderReason = (reason) => `- ${reason}`;
const isResolvedHandle = (adapter) => utils.isFunction(adapter) || adapter === null || adapter === false;
const adapters = { const adapters = {
getAdapter: (adapters) => { getAdapter: (adapters) => {
adapters = utils.isArray(adapters) ? adapters : [adapters]; adapters = utils.isArray(adapters) ? adapters : [adapters];
@ -22123,32 +22157,46 @@ const adapters = {
let nameOrAdapter; let nameOrAdapter;
let adapter; let adapter;
const rejectedReasons = {};
for (let i = 0; i < length; i++) { for (let i = 0; i < length; i++) {
nameOrAdapter = adapters[i]; nameOrAdapter = adapters[i];
if((adapter = utils.isString(nameOrAdapter) ? knownAdapters[nameOrAdapter.toLowerCase()] : nameOrAdapter)) { let id;
adapter = nameOrAdapter;
if (!isResolvedHandle(nameOrAdapter)) {
adapter = knownAdapters[(id = String(nameOrAdapter)).toLowerCase()];
if (adapter === undefined) {
throw new AxiosError(`Unknown adapter '${id}'`);
}
}
if (adapter) {
break; break;
} }
rejectedReasons[id || '#' + i] = adapter;
} }
if (!adapter) { if (!adapter) {
if (adapter === false) {
const reasons = Object.entries(rejectedReasons)
.map(([id, state]) => `adapter ${id} ` +
(state === false ? 'is not supported by the environment' : 'is not available in the build')
);
let s = length ?
(reasons.length > 1 ? 'since :\n' + reasons.map(renderReason).join('\n') : ' ' + renderReason(reasons[0])) :
'as no adapter specified';
throw new AxiosError( throw new AxiosError(
`Adapter ${nameOrAdapter} is not supported by the environment`, `There is no suitable adapter to dispatch the request ` + s,
'ERR_NOT_SUPPORT' 'ERR_NOT_SUPPORT'
); );
} }
throw new Error(
utils.hasOwnProp(knownAdapters, nameOrAdapter) ?
`Adapter '${nameOrAdapter}' is not available in the build` :
`Unknown adapter '${nameOrAdapter}'`
);
}
if (!utils.isFunction(adapter)) {
throw new TypeError('adapter is not a function');
}
return adapter; return adapter;
}, },
adapters: knownAdapters adapters: knownAdapters
@ -22479,15 +22527,13 @@ class Axios {
// Set config.method // Set config.method
config.method = (config.method || this.defaults.method || 'get').toLowerCase(); config.method = (config.method || this.defaults.method || 'get').toLowerCase();
let contextHeaders;
// Flatten headers // Flatten headers
contextHeaders = headers && utils.merge( let contextHeaders = headers && utils.merge(
headers.common, headers.common,
headers[config.method] headers[config.method]
); );
contextHeaders && utils.forEach( headers && utils.forEach(
['delete', 'get', 'head', 'post', 'put', 'patch', 'common'], ['delete', 'get', 'head', 'post', 'put', 'patch', 'common'],
(method) => { (method) => {
delete headers[method]; delete headers[method];
@ -22897,6 +22943,8 @@ axios.AxiosHeaders = AxiosHeaders$1;
axios.formToJSON = thing => formDataToJSON(utils.isHTMLForm(thing) ? new FormData(thing) : thing); axios.formToJSON = thing => formDataToJSON(utils.isHTMLForm(thing) ? new FormData(thing) : thing);
axios.getAdapter = adapters.getAdapter;
axios.HttpStatusCode = HttpStatusCode$1; axios.HttpStatusCode = HttpStatusCode$1;
axios.default = axios; axios.default = axios;

View file

@ -181,9 +181,18 @@ export default class GitLabClient implements GitClient {
* @returns {{owner: string, project: string}} * @returns {{owner: string, project: string}}
*/ */
private extractMergeRequestData(mrUrl: string): {namespace: string, project: string, id: number} { private extractMergeRequestData(mrUrl: string): {namespace: string, project: string, id: number} {
const elems: string[] = mrUrl.replace("/-/", "/").split("/"); const { pathname } = new URL(mrUrl);
const elems: string[] = pathname.substring(1).replace("/-/", "/").split("/");
let namespace = "";
for (let i = 0; i < elems.length - 3; i++) {
namespace += elems[i] + "/";
}
namespace = namespace.substring(0, namespace.length - 1);
return { return {
namespace: elems[elems.length - 4], namespace: namespace,
project: elems[elems.length - 3], project: elems[elems.length - 3],
id: parseInt(mrUrl.substring(mrUrl.lastIndexOf("/") + 1, mrUrl.length)), id: parseInt(mrUrl.substring(mrUrl.lastIndexOf("/") + 1, mrUrl.length)),
}; };

View file

@ -323,4 +323,29 @@ describe("github service", () => {
body: "this is second comment", body: "this is second comment",
}); });
}); });
test("get pull request for nested namespaces", async () => {
const res: GitPullRequest = await gitClient.getPullRequestFromUrl("https://my.gitlab.host.com/mysuperorg/6/mysuperproduct/mysuperunit/backporting-example/-/merge_requests/4");
// check content
expect(res.sourceRepo).toEqual({
owner: "superuser",
project: "backporting-example",
cloneUrl: "https://my.gitlab.host.com/mysuperorg/6/mysuperproduct/mysuperunit/backporting-example.git"
});
expect(res.targetRepo).toEqual({
owner: "superuser",
project: "backporting-example",
cloneUrl: "https://my.gitlab.host.com/mysuperorg/6/mysuperproduct/mysuperunit/backporting-example.git"
});
expect(res.title).toBe("Update test.txt");
expect(res.commits!.length).toBe(1);
expect(res.commits).toEqual(["ebb1eca696c42fd067658bd9b5267709f78ef38e"]);
// check axios invocation
expect(axiosInstanceSpy.get).toBeCalledTimes(3); // merge request and 2 repos
expect(axiosInstanceSpy.get).toBeCalledWith("/projects/mysuperorg%2F6%2Fmysuperproduct%2Fmysuperunit%2Fbackporting-example/merge_requests/4");
expect(axiosInstanceSpy.get).toBeCalledWith("/projects/1645");
expect(axiosInstanceSpy.get).toBeCalledWith("/projects/1645");
});
}); });

View file

@ -1,7 +1,7 @@
import LoggerServiceFactory from "@bp/service/logger/logger-service-factory"; import LoggerServiceFactory from "@bp/service/logger/logger-service-factory";
import { Moctokit } from "@kie/mock-github"; import { Moctokit } from "@kie/mock-github";
import { TARGET_OWNER, REPO, MERGED_PR_FIXTURE, OPEN_PR_FIXTURE, NOT_MERGED_PR_FIXTURE, NOT_FOUND_PR_NUMBER, MULT_COMMITS_PR_FIXTURE, MULT_COMMITS_PR_COMMITS, NEW_PR_URL, NEW_PR_NUMBER } from "./github-data"; import { TARGET_OWNER, REPO, MERGED_PR_FIXTURE, OPEN_PR_FIXTURE, NOT_MERGED_PR_FIXTURE, NOT_FOUND_PR_NUMBER, MULT_COMMITS_PR_FIXTURE, MULT_COMMITS_PR_COMMITS, NEW_PR_URL, NEW_PR_NUMBER } from "./github-data";
import { CLOSED_NOT_MERGED_MR, MERGED_SQUASHED_MR, OPEN_MR, OPEN_PR_COMMITS, PROJECT_EXAMPLE, SUPERUSER} from "./gitlab-data"; import { CLOSED_NOT_MERGED_MR, MERGED_SQUASHED_MR, NESTED_NAMESPACE_MR, OPEN_MR, OPEN_PR_COMMITS, PROJECT_EXAMPLE, NESTED_PROJECT_EXAMPLE, SUPERUSER} from "./gitlab-data";
// high number, for each test we are not expecting // high number, for each test we are not expecting
// to send more than 3 reqs per api endpoint // to send more than 3 reqs per api endpoint
@ -22,8 +22,12 @@ export const getAxiosMocked = (url: string) => {
data = OPEN_MR; data = OPEN_MR;
} else if (url.endsWith("merge_requests/3")) { } else if (url.endsWith("merge_requests/3")) {
data = CLOSED_NOT_MERGED_MR; data = CLOSED_NOT_MERGED_MR;
} else if (url.endsWith("merge_requests/4")) {
data = NESTED_NAMESPACE_MR;
} else if (url.endsWith("projects/76316")) { } else if (url.endsWith("projects/76316")) {
data = PROJECT_EXAMPLE; data = PROJECT_EXAMPLE;
} else if (url.endsWith("projects/1645")) {
data = NESTED_PROJECT_EXAMPLE;
} else if (url.endsWith("users?username=superuser")) { } else if (url.endsWith("users?username=superuser")) {
data = [SUPERUSER]; data = [SUPERUSER];
} else if (url.endsWith("merge_requests/2/commits")) { } else if (url.endsWith("merge_requests/2/commits")) {

View file

@ -161,6 +161,166 @@ export const PROJECT_EXAMPLE = {
} }
}; };
export const NESTED_PROJECT_EXAMPLE = {
"id":1645,
"description":null,
"name":"Backporting Example",
"name_with_namespace":"Super User / Backporting Example",
"path":"backporting-example",
"path_with_namespace":"mysuperorg/6/mysuperproduct/mysuperunit/backporting-example",
"created_at":"2023-06-23T13:45:15.121Z",
"default_branch":"main",
"tag_list":[
],
"topics":[
],
"ssh_url_to_repo":"git@my.gitlab.host.com:mysuperorg/6/mysuperproduct/mysuperunit/backporting-example.git",
"http_url_to_repo":"https://my.gitlab.host.com/mysuperorg/6/mysuperproduct/mysuperunit/backporting-example.git",
"web_url":"https://my.gitlab.host.com/mysuperorg/6/mysuperproduct/mysuperunit/backporting-example",
"readme_url":"https://my.gitlab.host.com/mysuperorg/6/mysuperproduct/mysuperunit/backporting-example/-/blob/main/README.md",
"forks_count":0,
"avatar_url":null,
"star_count":0,
"last_activity_at":"2023-06-28T14:05:42.596Z",
"namespace":{
"id":70747,
"name":"Super User",
"path":"superuser",
"kind":"user",
"full_path":"superuser",
"parent_id":null,
"avatar_url":"/uploads/-/system/user/avatar/14041/avatar.png",
"web_url":"https://my.gitlab.host.com/superuser"
},
"_links":{
"self":"https://my.gitlab.host.com/api/v4/projects/1645",
"issues":"https://my.gitlab.host.com/api/v4/projects/1645/issues",
"merge_requests":"https://my.gitlab.host.com/api/v4/projects/1645/merge_requests",
"repo_branches":"https://my.gitlab.host.com/api/v4/projects/1645/repository/branches",
"labels":"https://my.gitlab.host.com/api/v4/projects/1645/labels",
"events":"https://my.gitlab.host.com/api/v4/projects/1645/events",
"members":"https://my.gitlab.host.com/api/v4/projects/1645/members",
"cluster_agents":"https://my.gitlab.host.com/api/v4/projects/1645/cluster_agents"
},
"packages_enabled":true,
"empty_repo":false,
"archived":false,
"visibility":"private",
"owner":{
"id":14041,
"username":"superuser",
"name":"Super User",
"state":"active",
"avatar_url":"https://my.gitlab.host.com/uploads/-/system/user/avatar/14041/avatar.png",
"web_url":"https://my.gitlab.host.com/superuser"
},
"resolve_outdated_diff_discussions":false,
"container_expiration_policy":{
"cadence":"1d",
"enabled":false,
"keep_n":10,
"older_than":"90d",
"name_regex":".*",
"name_regex_keep":null,
"next_run_at":"2023-06-24T13:45:15.167Z"
},
"issues_enabled":true,
"merge_requests_enabled":true,
"wiki_enabled":true,
"jobs_enabled":true,
"snippets_enabled":true,
"container_registry_enabled":true,
"service_desk_enabled":false,
"service_desk_address":null,
"can_create_merge_request_in":true,
"issues_access_level":"enabled",
"repository_access_level":"enabled",
"merge_requests_access_level":"enabled",
"forking_access_level":"enabled",
"wiki_access_level":"enabled",
"builds_access_level":"enabled",
"snippets_access_level":"enabled",
"pages_access_level":"private",
"analytics_access_level":"enabled",
"container_registry_access_level":"enabled",
"security_and_compliance_access_level":"private",
"releases_access_level":"enabled",
"environments_access_level":"enabled",
"feature_flags_access_level":"enabled",
"infrastructure_access_level":"enabled",
"monitor_access_level":"enabled",
"emails_disabled":null,
"shared_runners_enabled":true,
"lfs_enabled":true,
"creator_id":14041,
"import_url":null,
"import_type":null,
"import_status":"none",
"import_error":null,
"open_issues_count":0,
"description_html":"",
"updated_at":"2023-06-28T14:05:42.596Z",
"ci_default_git_depth":20,
"ci_forward_deployment_enabled":true,
"ci_job_token_scope_enabled":false,
"ci_separated_caches":true,
"ci_allow_fork_pipelines_to_run_in_parent_project":true,
"build_git_strategy":"fetch",
"keep_latest_artifact":true,
"restrict_user_defined_variables":false,
"runners_token":"TOKEN",
"runner_token_expiration_interval":null,
"group_runners_enabled":true,
"auto_cancel_pending_pipelines":"enabled",
"build_timeout":3600,
"auto_devops_enabled":false,
"auto_devops_deploy_strategy":"continuous",
"ci_config_path":"",
"public_jobs":true,
"shared_with_groups":[
],
"only_allow_merge_if_pipeline_succeeds":false,
"allow_merge_on_skipped_pipeline":null,
"request_access_enabled":true,
"only_allow_merge_if_all_discussions_are_resolved":false,
"remove_source_branch_after_merge":true,
"printing_merge_request_link_enabled":true,
"merge_method":"merge",
"squash_option":"default_off",
"enforce_auth_checks_on_uploads":true,
"suggestion_commit_message":null,
"merge_commit_template":null,
"squash_commit_template":null,
"issue_branch_template":null,
"autoclose_referenced_issues":true,
"approvals_before_merge":0,
"mirror":false,
"external_authorization_classification_label":null,
"marked_for_deletion_at":null,
"marked_for_deletion_on":null,
"requirements_enabled":false,
"requirements_access_level":"enabled",
"security_and_compliance_enabled":true,
"compliance_frameworks":[
],
"issues_template":null,
"merge_requests_template":null,
"merge_pipelines_enabled":false,
"merge_trains_enabled":false,
"allow_pipeline_trigger_approve_deployment":false,
"permissions":{
"project_access":{
"access_level":50,
"notification_level":3
},
"group_access":null
}
};
export const MERGED_SQUASHED_MR = { export const MERGED_SQUASHED_MR = {
"id":807106, "id":807106,
"iid":1, "iid":1,
@ -580,3 +740,138 @@ export const SUPERUSER = {
"avatar_url":"https://my.gitlab.host.com/uploads/-/system/user/avatar/14041/avatar.png", "avatar_url":"https://my.gitlab.host.com/uploads/-/system/user/avatar/14041/avatar.png",
"web_url":"https://my.gitlab.host.com/superuser" "web_url":"https://my.gitlab.host.com/superuser"
}; };
export const NESTED_NAMESPACE_MR = {
"id":807106,
"iid":1,
"project_id":1645,
"title":"Update test.txt",
"description":"This is the body",
"state":"merged",
"created_at":"2023-06-28T14:32:40.943Z",
"updated_at":"2023-06-28T14:37:12.108Z",
"merged_by":{
"id":14041,
"username":"superuser",
"name":"Super User",
"state":"active",
"avatar_url":"https://my.gitlab.host.com/uploads/-/system/user/avatar/14041/avatar.png",
"web_url":"https://my.gitlab.host.com/superuser"
},
"merge_user":{
"id":14041,
"username":"superuser",
"name":"Super User",
"state":"active",
"avatar_url":"https://my.gitlab.host.com/uploads/-/system/user/avatar/14041/avatar.png",
"web_url":"https://my.gitlab.host.com/superuser"
},
"merged_at":"2023-06-28T14:37:11.667Z",
"closed_by":null,
"closed_at":null,
"target_branch":"main",
"source_branch":"feature",
"user_notes_count":0,
"upvotes":0,
"downvotes":0,
"author":{
"id":14041,
"username":"superuser",
"name":"Super User",
"state":"active",
"avatar_url":"https://my.gitlab.host.com/uploads/-/system/user/avatar/14041/avatar.png",
"web_url":"https://my.gitlab.host.com/superuser"
},
"assignees":[
{
"id":14041,
"username":"superuser",
"name":"Super User",
"state":"active",
"avatar_url":"https://my.gitlab.host.com/uploads/-/system/user/avatar/14041/avatar.png",
"web_url":"https://my.gitlab.host.com/superuser"
}
],
"assignee":{
"id":14041,
"username":"superuser",
"name":"Super User",
"state":"active",
"avatar_url":"https://my.gitlab.host.com/uploads/-/system/user/avatar/14041/avatar.png",
"web_url":"https://my.gitlab.host.com/superuser"
},
"reviewers":[
{
"id":1404188,
"username":"superuser1",
"name":"Super User",
"state":"active",
"avatar_url":"https://my.gitlab.host.com/uploads/-/system/user/avatar/14041/avatar.png",
"web_url":"https://my.gitlab.host.com/superuser"
},
{
"id":1404199,
"username":"superuser2",
"name":"Super User",
"state":"active",
"avatar_url":"https://my.gitlab.host.com/uploads/-/system/user/avatar/14041/avatar.png",
"web_url":"https://my.gitlab.host.com/superuser"
}
],
"source_project_id":1645,
"target_project_id":1645,
"labels":[
"gitlab-original-label"
],
"draft":false,
"work_in_progress":false,
"milestone":null,
"merge_when_pipeline_succeeds":false,
"merge_status":"can_be_merged",
"detailed_merge_status":"not_open",
"sha":"9e15674ebd48e05c6e428a1fa31dbb60a778d644",
"merge_commit_sha":"4d369c3e9a8d1d5b7e56c892a8ab2a7666583ac3",
"squash_commit_sha":"ebb1eca696c42fd067658bd9b5267709f78ef38e",
"discussion_locked":null,
"should_remove_source_branch":true,
"force_remove_source_branch":true,
"reference":"!2",
"references":{
"short":"!2",
"relative":"!2",
"full":"superuser/backporting-example!2"
},
"web_url":"https://my.gitlab.host.com/mysuperorg/6/mysuperproduct/mysuperunit/backporting-example/-/merge_requests/4",
"time_stats":{
"time_estimate":0,
"total_time_spent":0,
"human_time_estimate":null,
"human_total_time_spent":null
},
"squash":true,
"squash_on_merge":true,
"task_completion_status":{
"count":0,
"completed_count":0
},
"has_conflicts":false,
"blocking_discussions_resolved":true,
"approvals_before_merge":null,
"subscribed":true,
"changes_count":"1",
"latest_build_started_at":null,
"latest_build_finished_at":null,
"first_deployed_to_production_at":null,
"pipeline":null,
"head_pipeline":null,
"diff_refs":{
"base_sha":"2c553a0c4c133a51806badce5fa4842b7253cb3b",
"head_sha":"9e15674ebd48e05c6e428a1fa31dbb60a778d644",
"start_sha":"2c553a0c4c133a51806badce5fa4842b7253cb3b"
},
"merge_error":null,
"first_contribution":false,
"user":{
"can_merge":true
}
};