mirror of
https://github.com/NginxProxyManager/nginx-proxy-manager.git
synced 2025-06-18 02:06:25 +00:00
Fix CVE-2024-46256 and CVE-2024-46257
- Schema validate against bad domain characters - Integration test for CVE POC examples - Cypress rewrite of plugins for file upload
This commit is contained in:
@ -1,9 +1,14 @@
|
||||
const logger = require('./logger');
|
||||
const restler = require('@jc21/restler');
|
||||
const axios = require('axios').default;
|
||||
|
||||
const BackendApi = function(config, token) {
|
||||
this.config = config;
|
||||
this.token = token;
|
||||
|
||||
this.axios = axios.create({
|
||||
baseURL: config.baseUrl,
|
||||
timeout: 5000,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@ -14,129 +19,114 @@ BackendApi.prototype.setToken = function(token) {
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {bool} returnOnError
|
||||
*/
|
||||
BackendApi.prototype._prepareOptions = function(returnOnError) {
|
||||
let options = {
|
||||
headers: {
|
||||
Accept: 'application/json'
|
||||
}
|
||||
}
|
||||
if (this.token) {
|
||||
options.headers.Authorization = 'Bearer ' + this.token;
|
||||
}
|
||||
if (returnOnError) {
|
||||
options.validateStatus = function () {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return options;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {*} response
|
||||
* @param {function} resolve
|
||||
* @param {function} reject
|
||||
* @param {bool} returnOnError
|
||||
*/
|
||||
BackendApi.prototype._handleResponse = function(response, resolve, reject, returnOnError) {
|
||||
logger('Response data:', response.data);
|
||||
if (!returnOnError && typeof response.data === 'object' && typeof response.data.error === 'object') {
|
||||
if (typeof response.data === 'object' && typeof response.data.error === 'object' && typeof response.data.error.message !== 'undefined') {
|
||||
reject(new Error(response.data.error.code + ': ' + response.data.error.message));
|
||||
} else {
|
||||
reject(new Error('Error ' + response.status));
|
||||
}
|
||||
} else {
|
||||
resolve(response.data);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {*} err
|
||||
* @param {function} resolve
|
||||
* @param {function} reject
|
||||
* @param {bool} returnOnError
|
||||
*/
|
||||
BackendApi.prototype._handleError = function(err, resolve, reject, returnOnError) {
|
||||
logger('Axios Error:', err);
|
||||
if (returnOnError) {
|
||||
resolve(typeof err.response.data !== 'undefined' ? err.response.data : err);
|
||||
} else {
|
||||
reject(err);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} method
|
||||
* @param {string} path
|
||||
* @param {bool} [returnOnError]
|
||||
* @param {*} [data]
|
||||
* @returns {Promise<object>}
|
||||
*/
|
||||
BackendApi.prototype.get = function(path, returnOnError) {
|
||||
BackendApi.prototype.request = function (method, path, returnOnError, data) {
|
||||
logger(method.toUpperCase(), this.config.baseUrl + path);
|
||||
const options = this._prepareOptions(returnOnError);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let headers = {
|
||||
Accept: 'application/json'
|
||||
};
|
||||
if (this.token) {
|
||||
headers.Authorization = 'Bearer ' + this.token;
|
||||
let opts = {
|
||||
method: method,
|
||||
url: path,
|
||||
...options
|
||||
}
|
||||
if (data !== undefined && data !== null) {
|
||||
opts.data = data;
|
||||
}
|
||||
|
||||
logger('GET ', this.config.baseUrl + path);
|
||||
|
||||
restler
|
||||
.get(this.config.baseUrl + path, {
|
||||
headers: headers,
|
||||
this.axios(opts)
|
||||
.then((response) => {
|
||||
this._handleResponse(response, resolve, reject, returnOnError);
|
||||
})
|
||||
.on('complete', function(data, response) {
|
||||
logger('Response data:', data);
|
||||
if (!returnOnError && data instanceof Error) {
|
||||
reject(data);
|
||||
} else if (!returnOnError && response.statusCode != 200) {
|
||||
if (typeof data === 'object' && typeof data.error === 'object' && typeof data.error.message !== 'undefined') {
|
||||
reject(new Error(data.error.code + ': ' + data.error.message));
|
||||
} else {
|
||||
reject(new Error('Error ' + response.statusCode));
|
||||
}
|
||||
} else {
|
||||
resolve(data);
|
||||
}
|
||||
.catch((err) => {
|
||||
this._handleError(err, resolve, reject, returnOnError);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} path
|
||||
* @param {form} form
|
||||
* @param {bool} [returnOnError]
|
||||
* @returns {Promise<object>}
|
||||
*/
|
||||
BackendApi.prototype.delete = function(path, returnOnError) {
|
||||
BackendApi.prototype.postForm = function (path, form, returnOnError) {
|
||||
logger('POST', this.config.baseUrl + path);
|
||||
const options = this._prepareOptions(returnOnError);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let headers = {
|
||||
Accept: 'application/json'
|
||||
};
|
||||
if (this.token) {
|
||||
headers.Authorization = 'Bearer ' + this.token;
|
||||
const opts = {
|
||||
...options,
|
||||
...form.getHeaders(),
|
||||
}
|
||||
|
||||
logger('DELETE ', this.config.baseUrl + path);
|
||||
|
||||
restler
|
||||
.del(this.config.baseUrl + path, {
|
||||
headers: headers,
|
||||
this.axios.post(path, form, opts)
|
||||
.then((response) => {
|
||||
this._handleResponse(response, resolve, reject, returnOnError);
|
||||
})
|
||||
.on('complete', function(data, response) {
|
||||
logger('Response data:', data);
|
||||
if (!returnOnError && data instanceof Error) {
|
||||
reject(data);
|
||||
} else if (!returnOnError && response.statusCode != 200) {
|
||||
if (typeof data === 'object' && typeof data.error === 'object' && typeof data.error.message !== 'undefined') {
|
||||
reject(new Error(data.error.code + ': ' + data.error.message));
|
||||
} else {
|
||||
reject(new Error('Error ' + response.statusCode));
|
||||
}
|
||||
} else {
|
||||
resolve(data);
|
||||
}
|
||||
.catch((err) => {
|
||||
this._handleError(err, resolve, reject, returnOnError);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} path
|
||||
* @param {object} data
|
||||
* @param {bool} [returnOnError]
|
||||
* @returns {Promise<object>}
|
||||
*/
|
||||
BackendApi.prototype.postJson = function(path, data, returnOnError) {
|
||||
logger('POST ', this.config.baseUrl + path);
|
||||
return this._putPostJson('postJson', path, data, returnOnError);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} path
|
||||
* @param {object} data
|
||||
* @param {bool} [returnOnError]
|
||||
* @returns {Promise<object>}
|
||||
*/
|
||||
BackendApi.prototype.putJson = function(path, data, returnOnError) {
|
||||
logger('PUT ', this.config.baseUrl + path);
|
||||
return this._putPostJson('putJson', path, data, returnOnError);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} path
|
||||
* @param {object} data
|
||||
* @param {bool} [returnOnError]
|
||||
* @returns {Promise<object>}
|
||||
*/
|
||||
BackendApi.prototype._putPostJson = function(fn, path, data, returnOnError) {
|
||||
return new Promise((resolve, reject) => {
|
||||
restler[fn](this.config.baseUrl + path, data, {
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: 'Bearer ' + this.token,
|
||||
},
|
||||
}).on('complete', function(data, response) {
|
||||
logger('Response data:', data);
|
||||
if (!returnOnError && data instanceof Error) {
|
||||
reject(data);
|
||||
} else if (!returnOnError && (response.statusCode < 200 || response.statusCode >= 300)) {
|
||||
if (typeof data === 'object' && typeof data.error === 'object' && typeof data.error.message !== 'undefined') {
|
||||
reject(new Error(data.error.code + ': ' + data.error.message));
|
||||
} else {
|
||||
reject(new Error('Error ' + response.statusCode));
|
||||
}
|
||||
} else {
|
||||
resolve(data);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = BackendApi;
|
||||
|
@ -1,8 +1,9 @@
|
||||
const fs = require('fs');
|
||||
const FormData = require('form-data');
|
||||
const logger = require('./logger');
|
||||
const Client = require('./client');
|
||||
|
||||
module.exports = function (config) {
|
||||
|
||||
logger('Client Ready using', config.baseUrl);
|
||||
|
||||
return {
|
||||
@ -17,7 +18,7 @@ module.exports = function (config) {
|
||||
backendApiGet: (options) => {
|
||||
const api = new Client(config);
|
||||
api.setToken(options.token);
|
||||
return api.get(options.path, options.returnOnError || false);
|
||||
return api.request('get', options.path, options.returnOnError || false);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -31,7 +32,26 @@ module.exports = function (config) {
|
||||
backendApiPost: (options) => {
|
||||
const api = new Client(config);
|
||||
api.setToken(options.token);
|
||||
return api.postJson(options.path, options.data, options.returnOnError || false);
|
||||
return api.request('post', options.path, options.returnOnError || false, options.data);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {object} options
|
||||
* @param {string} options.token JWT
|
||||
* @param {string} options.path API path
|
||||
* @param {object} options.files
|
||||
* @param {bool} [options.returnOnError] If true, will return instead of throwing errors
|
||||
* @returns {string}
|
||||
*/
|
||||
backendApiPostFiles: (options) => {
|
||||
const api = new Client(config);
|
||||
api.setToken(options.token);
|
||||
|
||||
const form = new FormData();
|
||||
for (let [key, value] of Object.entries(options.files)) {
|
||||
form.append(key, fs.createReadStream(config.fixturesFolder + '/' + value));
|
||||
}
|
||||
return api.postForm(options.path, form, options.returnOnError || false);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -45,7 +65,7 @@ module.exports = function (config) {
|
||||
backendApiPut: (options) => {
|
||||
const api = new Client(config);
|
||||
api.setToken(options.token);
|
||||
return api.putJson(options.path, options.data, options.returnOnError || false);
|
||||
return api.request('put', options.path, options.returnOnError || false, options.data);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -58,7 +78,7 @@ module.exports = function (config) {
|
||||
backendApiDelete: (options) => {
|
||||
const api = new Client(config);
|
||||
api.setToken(options.token);
|
||||
return api.delete(options.path, options.returnOnError || false);
|
||||
return api.request('delete', options.path, options.returnOnError || false);
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
Reference in New Issue
Block a user