Fix /api/plugins leaking filesystem paths

Replace PluginWebApi's requirePath member with npmPackage which is only
populated if the package is not marked as private and the requirePath
matches the name. This prevents the public /api/plugins endpoint from
leaking filesystem paths into the controller.
This commit is contained in:
Hornwitser committed 2023-12-02 01:43:28 +01:00
1 parent 42ae4d6897
commit 90a88f7e01
7 files changed
+19 -8

No files matched your search

+1 -1
View File
@@ -113,7 +113,7 @@ export default class Controller {
constructor(
clusterLogger: winston.Logger,
pluginInfos: any[],
pluginInfos: lib.PluginNodeEnvInfo[],
configPath: string,
config: lib.ControllerConfig,
+2 -2
View File
@@ -95,7 +95,7 @@ async function getMetrics(req: Request, res: Response, next: any) {
function getPlugins(req: Request, res: Response) {
let plugins: lib.PluginWebApi[] = [];
for (let pluginInfo of req.app.locals.controller.pluginInfos) {
for (let pluginInfo of (req.app.locals.controller as Controller).pluginInfos) {
let name = pluginInfo.name;
let loaded = req.app.locals.controller.plugins.has(name);
let enabled = loaded && req.app.locals.controller.config.group(pluginInfo.name).get("load_plugin");
@@ -115,7 +115,7 @@ function getPlugins(req: Request, res: Response) {
if (web.main === "remoteEntry.js") {
web.error = "Incompatible old remoteEntry.js entrypoint.";
}
plugins.push({ name, version: pluginInfo.version, enabled, loaded, web, requirePath: pluginInfo.requirePath });
plugins.push({ name, version: pluginInfo.version, enabled, loaded, web, npmPackage: pluginInfo.npmPackage });
}
res.send(plugins);
}
+6 -1
View File
@@ -7,5 +7,10 @@ export type PluginWebApi = {
main: string;
error?: string;
};
requirePath: string;
/**
* NPM package this plugin is published as. Not present if the package
* is private or the path used to load it does not match the name of the
* package.
*/
npmPackage?: string;
}
+6
View File
@@ -35,6 +35,12 @@ export type PluginNodeEnvInfo = PluginDeclaration & {
requirePath: string;
version: string;
manifest: any;
/**
* NPM package this plugin is published as. Not present if the package
* is private or the path used to load it does not match the name of the
* package.
*/
npmPackage?: string;
};
export type PluginWebpackEnvInfo = PluginDeclaration & {
+2 -1
View File
@@ -24,7 +24,7 @@ export async function loadPluginInfos(pluginList: Map<string, string>) {
let plugins: libPlugin.PluginNodeEnvInfo[] = [];
for (let [pluginName, pluginPath] of pluginList) {
let pluginInfo: libPlugin.PluginNodeEnvInfo;
let pluginPackage: { version: string, main?: string };
let pluginPackage: { name?: string, version: string, main?: string, private?: boolean };
try {
pluginInfo = require(pluginPath).default;
@@ -47,6 +47,7 @@ export async function loadPluginInfos(pluginList: Map<string, string>) {
pluginInfo.requirePath = pluginPath;
pluginInfo.version = pluginPackage.version;
pluginInfo.npmPackage = !pluginPackage.private && pluginPath === pluginPackage.name ? pluginPath : undefined;
plugins.push(pluginInfo);
}
return plugins;
+1 -2
View File
@@ -59,8 +59,7 @@ function GenerateHostTokenButton() {
}
}, [open]);
// Only install plugins that aren't filesystem paths. Npm modules have max 1 forward slash in their name.
const pluginString = pluginList.map(p => `"${p.requirePath}"`).filter(x => x.split("/").length <= 1).join(" ");
const pluginString = pluginList.filter(p => p.npmPackage).map(p => `"${p.npmPackage}"`).join(" ");
return <>
<Button
onClick={() => { setOpen(true); }}
+1 -1
View File
@@ -42,7 +42,7 @@ describe("lib/plugin_loader", function() {
it("should load test plugin", async function() {
assert.deepEqual(
await lib.loadPluginInfos(new Map([["test", path.resolve(testPlugin)]])),
[{ name: "test", version: "0.0.1", requirePath: path.resolve(testPlugin) }]
[{ name: "test", version: "0.0.1", npmPackage: undefined, requirePath: path.resolve(testPlugin) }]
);
});
it("should reject on broken plugin", async function() {