mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-12 15:04:52 +09:00
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:
1 parent
42ae4d6897
commit
90a88f7e01
7 files changed
+19
-8
No files matched your search
@@ -113,7 +113,7 @@ export default class Controller {
|
||||
|
||||
constructor(
|
||||
clusterLogger: winston.Logger,
|
||||
pluginInfos: any[],
|
||||
pluginInfos: lib.PluginNodeEnvInfo[],
|
||||
configPath: string,
|
||||
config: lib.ControllerConfig,
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 & {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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); }}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
Reference in new issue
Block a user