Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions defaultmodules/weather/node_helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ const path = require("node:path");
const NodeHelper = require("node_helper");
const Log = require("logger");

const providersDir = path.join(__dirname, "providers");

module.exports = NodeHelper.create({
providers: {},
lastData: {},
Expand All @@ -26,7 +28,7 @@ module.exports = NodeHelper.create({
* @param {object} config The configuration object
*/
async initWeatherProvider (config) {
const identifier = config.weatherProvider.toLowerCase();
const identifier = typeof config.weatherProvider === "string" ? config.weatherProvider.toLowerCase() : "";
const instanceId = config.instanceId;

Log.log(`Attempting to initialize provider ${identifier} for instance ${instanceId}`);
Expand All @@ -46,8 +48,13 @@ module.exports = NodeHelper.create({
}

try {
// Reject anything that would resolve outside providersDir (path traversal)
const providerPath = path.join(providersDir, `${identifier}.js`);
if (path.dirname(providerPath) !== providersDir) {
throw new Error(`Unsupported weather provider: ${config.weatherProvider}`);
}

// Dynamically load the provider module
const providerPath = path.join(__dirname, "providers", `${identifier}.js`);
Log.log(`Loading provider from: ${providerPath}`);
const ProviderClass = require(providerPath);

Expand Down
15 changes: 15 additions & 0 deletions tests/unit/modules/default/weather/node_helper_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,21 @@ describe("weather node_helper reconnect handling", () => {
expect(helper.sendSocketNotification).toHaveBeenCalledTimes(1);
});

it("rejects unsupported weather providers before loading a module", async () => {
const helper = await loadWeatherNodeHelper();

await helper.initWeatherProvider({
weatherProvider: "../../calendar/node_helper",
instanceId: "weather-current",
type: "current"
});

expect(helper.sendSocketNotification).toHaveBeenCalledWith("WEATHER_ERROR", {
instanceId: "weather-current",
error: "Unsupported weather provider: ../../calendar/node_helper"
});
});

it("cleans up provider and cached data when stopping an instance", async () => {
const helper = await loadWeatherNodeHelper();
const instanceId = "weather-current";
Expand Down