refactor: Misc cleanup

This commit is contained in:
Sören Beye 2022-09-10 16:52:19 +02:00
parent 8e0605683a
commit 9fa98dfdac
12 changed files with 236 additions and 34 deletions

View File

@ -5,6 +5,14 @@ const Valetudo = require("./lib/Valetudo");
const valetudo = new Valetudo();
/*
This is the easiest and most complete Mitigation against CWE-1321 Prototype Pollution
We do that after new Valetudo() to not interfere with any (library) code.
This way, this should only interfere with malicious user input (if any)
*/
Object.freeze(Object.prototype);
process.on("unhandledRejection", (reason, promise) => {
Logger.error("unhandledRejection", {
reason: reason,

View File

@ -148,7 +148,6 @@ class Valetudo {
//eslint-disable-next-line no-undef
global.gc();
//@ts-ignore
const rssAfter = process.memoryUsage.rss();
const rssDiff = rss - rssAfter;

View File

@ -29,8 +29,29 @@ class ZoneCleaningCapabilityMqttHandle extends CapabilityMqttHandle {
if (Array.isArray(req?.zones)) {
await this.capability.start(req.zones.map(z => {
if (!(z.points)) {
throw new Error("Invalid Zone");
}
return new ValetudoZone({
points: z.points,
points: {
pA: {
x: z.points.pA?.x,
y: z.points.pA?.y,
},
pB: {
x: z.points.pB?.x,
y: z.points.pB?.y,
},
pC: {
x: z.points.pC?.x,
y: z.points.pC?.y,
},
pD: {
x: z.points.pD?.x,
y: z.points.pD?.y,
},
},
iterations: z.iterations
});
}));

View File

@ -29,7 +29,13 @@ class NTPClientRouter {
});
this.router.put("/config", this.validator, (req, res) => {
this.config.set("ntpClient", req.body);
this.config.set("ntpClient", {
enabled: req.body.enabled,
server: req.body.server,
port: req.body.port,
interval: req.body.interval,
timeout: req.body.timeout
});
res.sendStatus(200);
});

View File

@ -61,19 +61,26 @@ class TimerRouter {
typeof req.body.minute === "number" &&
req.body.action && typeof req.body.action.type === "string"
) {
const storedTimers = this.config.get("timers");
const newTimer = new ValetudoTimer({
enabled: req.body.enabled === true,
dow: req.body.dow,
hour: req.body.hour,
minute: req.body.minute,
action: req.body.action
});
const action = TimerRouter.MAP_ACTION_FROM_BODY(req.body);
storedTimers[newTimer.id] = newTimer;
if (!action) {
res.sendStatus(400);
} else {
const storedTimers = this.config.get("timers");
const newTimer = new ValetudoTimer({
enabled: req.body.enabled === true,
dow: req.body.dow,
hour: req.body.hour,
minute: req.body.minute,
action: action
});
storedTimers[newTimer.id] = newTimer;
this.config.set("timers", storedTimers);
res.sendStatus(200);
}
this.config.set("timers", storedTimers);
res.sendStatus(200);
} else {
res.sendStatus(400);
}
@ -90,19 +97,25 @@ class TimerRouter {
typeof req.body.minute === "number" &&
req.body.action && typeof req.body.action.type === "string"
) {
const newTimer = new ValetudoTimer({
id: req.params.id,
enabled: req.body.enabled === true,
dow: req.body.dow,
hour: req.body.hour,
minute: req.body.minute,
action: req.body.action
});
const action = TimerRouter.MAP_ACTION_FROM_BODY(req.body);
storedTimers[newTimer.id] = newTimer;
if (!action) {
res.sendStatus(400);
} else {
const newTimer = new ValetudoTimer({
id: req.params.id,
enabled: req.body.enabled === true,
dow: req.body.dow,
hour: req.body.hour,
minute: req.body.minute,
action: action
});
this.config.set("timers", storedTimers);
res.sendStatus(200);
storedTimers[newTimer.id] = newTimer;
this.config.set("timers", storedTimers);
res.sendStatus(200);
}
} else {
res.sendStatus(400);
}
@ -129,6 +142,35 @@ class TimerRouter {
getRouter() {
return this.router;
}
/**
* @private
* @param {object} body
*/
static MAP_ACTION_FROM_BODY(body) {
let action;
switch (body.action.type) {
case ValetudoTimer.ACTION_TYPE.FULL_CLEANUP:
action = {
type: ValetudoTimer.ACTION_TYPE.FULL_CLEANUP,
params: {}
};
break;
case ValetudoTimer.ACTION_TYPE.SEGMENT_CLEANUP:
action = {
type: ValetudoTimer.ACTION_TYPE.SEGMENT_CLEANUP,
params: {
segment_ids: body.action.params.segment_ids,
iterations: body.action.params.iterations,
custom_order: body.action.params.custom_order,
}
};
break;
}
return action;
}
}
module.exports = TimerRouter;

View File

@ -83,7 +83,7 @@ class ValetudoRouter {
});
this.router.put("/config/interfaces/mqtt", this.validator, (req, res) => {
let mqttConfig = req.body;
let mqttConfig = ValetudoRouter.MAP_MQTT_CONFIG(req.body);
let oldConfig = this.config.get("mqtt");
@ -177,6 +177,51 @@ class ValetudoRouter {
hub.shutdown();
});
}
static MAP_MQTT_CONFIG(obj) {
return {
enabled: obj.enabled,
connection: {
host: obj.connection.host,
port: obj.connection.port,
tls: {
enabled: obj.connection.tls.enabled,
ca: obj.connection.tls.ca
},
authentication: {
credentials: {
enabled: obj.connection.authentication.credentials.enabled,
username: obj.connection.authentication.credentials.username,
password: obj.connection.authentication.credentials.password
},
clientCertificate: {
enabled: obj.connection.authentication.clientCertificate.enabled,
certificate: obj.connection.authentication.clientCertificate.certificate,
key: obj.connection.authentication.clientCertificate.key
}
}
},
identity: {
friendlyName: obj.identity.friendlyName,
identifier: obj.identity.identifier
},
customizations: {
topicPrefix: obj.customizations.topicPrefix,
provideMapData: obj.customizations.provideMapData
},
interfaces: {
homie: {
enabled: obj.interfaces.homie.enabled,
addICBINVMapProperty: obj.interfaces.homie.addICBINVMapProperty,
cleanAttributesOnShutdown: obj.interfaces.homie.cleanAttributesOnShutdown
},
homeassistant: {
enabled: obj.interfaces.homeassistant.enabled,
cleanAutoconfOnShutdown: obj.interfaces.homeassistant.cleanAutoconfOnShutdown
}
}
};
}
}
const MAGIC_PRIVACY_STRING = "<redacted>";

View File

@ -18,12 +18,38 @@ class CombinedVirtualRestrictionsCapabilityRouter extends CapabilityRouter {
const virtualRestrictions = new ValetudoVirtualRestrictions({
virtualWalls: req.body.virtualWalls.map(requestWall => {
return new ValetudoVirtualWall({
points: requestWall.points
points: {
pA: {
x: requestWall.points.pA.x,
y: requestWall.points.pA.y
},
pB: {
x: requestWall.points.pB.x,
y: requestWall.points.pB.y
}
}
});
}),
restrictedZones: req.body.restrictedZones.map(requestZone => {
return new ValetudoRestrictedZone({
points: requestZone.points,
points: {
pA: {
x: requestZone.points.pA.x,
y: requestZone.points.pA.y
},
pB: {
x: requestZone.points.pB.x,
y: requestZone.points.pB.y
},
pC: {
x: requestZone.points.pC.x,
y: requestZone.points.pC.y
},
pD: {
x: requestZone.points.pD.x,
y: requestZone.points.pD.y
}
},
type: requestZone.type
});
})

View File

@ -14,7 +14,17 @@ class DoNotDisturbCapabilityRouter extends CapabilityRouter {
this.router.put("/", this.validator, async (req, res) => {
if (req.body.start && req.body.end) {
try {
await this.capability.setDndConfiguration(new ValetudoDNDConfiguration(req.body));
await this.capability.setDndConfiguration(new ValetudoDNDConfiguration({
enabled: req.body.enabled,
start: {
hour: req.body.start.hour,
minute: req.body.start.minute,
},
end: {
hour: req.body.end.hour,
minute: req.body.end.minute,
}
}));
res.sendStatus(200);
} catch (e) {

View File

@ -7,7 +7,10 @@ class GoToLocationCapabilityRouter extends CapabilityRouter {
if (req.body.action === "goto" && req.body.coordinates !== undefined) {
try {
await this.capability.goTo(new ValetudoGoToLocation({
coordinates: req.body.coordinates
coordinates: {
x: req.body.coordinates.x,
y: req.body.coordinates.y
}
}));
res.sendStatus(200);
} catch (e) {

View File

@ -26,8 +26,14 @@ class MapSegmentEditCapabilityRouter extends CapabilityRouter {
try {
await this.capability.splitSegment(
new ValetudoMapSegment({id: req.body.segment_id}),
req.body.pA,
req.body.pB
{
x: req.body.pA.x,
y: req.body.pA.y,
},
{
x: req.body.pB.x,
y: req.body.pB.y,
}
);
res.sendStatus(200);

View File

@ -121,7 +121,26 @@ class WifiConfigurationCapabilityRouter extends CapabilityRouter {
this.router.put("/", this.validator, async (req, res) => {
try {
await this.capability.setWifiConfiguration(new ValetudoWifiConfiguration(req.body));
let typeSpecificSettings;
switch (req.body.credentials.type) {
case ValetudoWifiConfiguration.CREDENTIALS_TYPE.WPA2_PSK:
typeSpecificSettings = {
password: req.body.credentials.typeSpecificSettings.password
};
break;
default:
typeSpecificSettings = {};
}
await this.capability.setWifiConfiguration(new ValetudoWifiConfiguration({
ssid: req.body.ssid,
credentials: {
type: req.body.credentials.type,
typeSpecificSettings: typeSpecificSettings
}
}));
res.sendStatus(200);
} catch (e) {
this.sendErrorResponse(req, res, e);

View File

@ -12,7 +12,24 @@ class ZoneCleaningCapabilityRouter extends CapabilityRouter {
}
return new ValetudoZone({
points: z.points,
points: {
pA: {
x: z.points.pA?.x,
y: z.points.pA?.y,
},
pB: {
x: z.points.pB?.x,
y: z.points.pB?.y,
},
pC: {
x: z.points.pC?.x,
y: z.points.pC?.y,
},
pD: {
x: z.points.pD?.x,
y: z.points.pD?.y,
},
},
iterations: z.iterations
});
}));