Extension clone improvements (part 2) (#5571)
* fix: remove the cloned directory if it contains no manifest * fix: apply feature flag guard to user extension data hosting * fix: disable inactive controls when feature flag is off * fix: change response status to 404
This commit is contained in:
@@ -7957,6 +7957,11 @@ export async function getSettings(initLoaderHandle = null) {
|
||||
Object.assign(extension_settings, (settings.extension_settings ?? {}));
|
||||
$('#third_party_extension_button').addClass('disabled');
|
||||
$('#extensions_details').addClass('disabled');
|
||||
$('#extensions_connect').addClass('disabled');
|
||||
$('#extensions_notify_updates').attr('disabled', 'disabled');
|
||||
$('#extensions_autoconnect').attr('disabled', 'disabled');
|
||||
$('#extensions_url').attr('disabled', 'disabled');
|
||||
$('#extensions_api_key').attr('disabled', 'disabled');
|
||||
}
|
||||
|
||||
firstRun = !!settings.firstRun;
|
||||
|
||||
@@ -65,14 +65,20 @@ async function checkIfRepoIsUpToDate(extensionPath) {
|
||||
|
||||
export const router = express.Router();
|
||||
|
||||
// Feature flag guard: don't allow calling any of the endpoints if extensions are disabled
|
||||
router.use((_, response, next) => {
|
||||
/**
|
||||
* Feature flag guard: don't allow calling any of the endpoints if extensions are disabled
|
||||
* @type {import('express').RequestHandler}
|
||||
*/
|
||||
export const extensionsEnabledFeatureGuard = (_, response, next) => {
|
||||
const enabled = !!getConfigValue('extensions.enabled', true, 'boolean');
|
||||
if (!enabled) {
|
||||
return response.status(400).send('Bad Request: Extensions are disabled.');
|
||||
response.sendStatus(404);
|
||||
return;
|
||||
}
|
||||
next();
|
||||
});
|
||||
};
|
||||
|
||||
router.use(extensionsEnabledFeatureGuard);
|
||||
|
||||
/**
|
||||
* HTTP POST handler function to clone a git repository from a provided URL, read the extension manifest,
|
||||
@@ -119,6 +125,7 @@ router.post('/install', async (request, response) => {
|
||||
}
|
||||
|
||||
const extensionPath = path.join(basePath, extensionNameSanitized);
|
||||
const folderName = path.basename(extensionPath);
|
||||
|
||||
if (fs.existsSync(extensionPath)) {
|
||||
return response.status(409).send(`Directory already exists at ${extensionPath}`);
|
||||
@@ -131,10 +138,17 @@ router.post('/install', async (request, response) => {
|
||||
await git.clone(parsedUrl.href, extensionPath, cloneOptions);
|
||||
console.info(`Extension has been cloned to ${extensionPath} from ${parsedUrl.href} at ${branch || '(default)'} branch`);
|
||||
|
||||
const { version, author, display_name } = await getManifest(extensionPath);
|
||||
const folderName = path.basename(extensionPath);
|
||||
|
||||
return response.send({ version, author, display_name, extensionPath, folderName });
|
||||
try {
|
||||
const manifest = await getManifest(extensionPath);
|
||||
if (!manifest || typeof manifest !== 'object' || Array.isArray(manifest)) {
|
||||
throw new Error('Manifest is not a valid JSON object.');
|
||||
}
|
||||
const { version, author, display_name } = manifest;
|
||||
return response.send({ version, author, display_name, extensionPath, folderName });
|
||||
} catch (manifestError) {
|
||||
await fs.promises.rm(extensionPath, { recursive: true, force: true });
|
||||
throw manifestError;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Importing extension failed', error);
|
||||
return response.status(500).send('Internal Server Error. Check the server logs for more details.');
|
||||
|
||||
+2
-1
@@ -22,6 +22,7 @@ import { allowKeysExposure, readSecret, writeSecret, SECRETS_FILE } from './endp
|
||||
import { getContentOfType } from './endpoints/content-manager.js';
|
||||
import { serverDirectory } from './server-directory.js';
|
||||
import { filterValidIpPatterns, getIpFromRequest } from './express-common.js';
|
||||
import { extensionsEnabledFeatureGuard } from './endpoints/extensions.js';
|
||||
|
||||
export const KEY_PREFIX = 'user:';
|
||||
const AVATAR_PREFIX = 'avatar:';
|
||||
@@ -1215,4 +1216,4 @@ router.use('/User%20Avatars/*', createRouteHandler(req => req.user.directories.a
|
||||
router.use('/assets/*', createRouteHandler(req => req.user.directories.assets));
|
||||
router.use('/user/images/*', createRouteHandler(req => req.user.directories.userImages));
|
||||
router.use('/user/files/*', createRouteHandler(req => req.user.directories.files));
|
||||
router.use('/scripts/extensions/third-party/*', createExtensionsRouteHandler(req => req.user.directories.extensions));
|
||||
router.use('/scripts/extensions/third-party/*', extensionsEnabledFeatureGuard, createExtensionsRouteHandler(req => req.user.directories.extensions));
|
||||
|
||||
Reference in New Issue
Block a user