added security settings
changed fixes and issues
This commit is contained in:
+16
-6
@@ -83,10 +83,15 @@ export function resticProcessEnv() {
|
||||
|
||||
export async function readEnvSettings() {
|
||||
const fileValues = await readEnvFile();
|
||||
return editableEnv.map((field) => ({
|
||||
...field,
|
||||
value: fileValues[field.key] ?? process.env[field.key] ?? '',
|
||||
}));
|
||||
return editableEnv.map((field) => {
|
||||
const value = fileValues[field.key] ?? process.env[field.key] ?? '';
|
||||
// Secrets are write-only: never echo their value back to callers. The UI
|
||||
// only learns whether a value is currently set via `hasValue`.
|
||||
if (field.secret) {
|
||||
return { ...field, value: '', hasValue: Boolean(value) };
|
||||
}
|
||||
return { ...field, value };
|
||||
});
|
||||
}
|
||||
|
||||
export async function writeEnvSettings(values) {
|
||||
@@ -94,14 +99,19 @@ export async function writeEnvSettings(values) {
|
||||
const currentValues = await readEnvFile();
|
||||
const nextValues = { ...currentValues };
|
||||
|
||||
const secretKeys = new Set(editableEnv.filter((field) => field.secret).map((field) => field.key));
|
||||
for (const [key, value] of Object.entries(values || {})) {
|
||||
if (!allowedKeys.has(key)) continue;
|
||||
if (key === 'API_TOKEN' && String(value || '').length < minApiTokenLength) {
|
||||
const text = String(value ?? '');
|
||||
// Secrets are write-only: an empty submission keeps the existing value so
|
||||
// the masked UI does not wipe credentials when saving unrelated fields.
|
||||
if (secretKeys.has(key) && text === '') continue;
|
||||
if (key === 'API_TOKEN' && text.length < minApiTokenLength) {
|
||||
const error = new Error(`API_TOKEN must be at least ${minApiTokenLength} characters long.`);
|
||||
error.status = 400;
|
||||
throw error;
|
||||
}
|
||||
nextValues[key] = String(value ?? '');
|
||||
nextValues[key] = text;
|
||||
}
|
||||
|
||||
const body = editableEnv
|
||||
|
||||
+9
-1
@@ -1,5 +1,6 @@
|
||||
import cors from 'cors';
|
||||
import express from 'express';
|
||||
import crypto from 'node:crypto';
|
||||
import http from 'node:http';
|
||||
import https from 'node:https';
|
||||
import { readFile } from 'node:fs/promises';
|
||||
@@ -25,7 +26,7 @@ app.use((req, res, next) => {
|
||||
return;
|
||||
}
|
||||
const header = req.get('authorization') || '';
|
||||
if (header === `Bearer ${config.apiToken}`) {
|
||||
if (config.apiToken && timingSafeEqual(header, `Bearer ${config.apiToken}`)) {
|
||||
next();
|
||||
return;
|
||||
}
|
||||
@@ -36,6 +37,13 @@ function normalizeIp(value) {
|
||||
return String(value || '').replace(/^::ffff:/, '');
|
||||
}
|
||||
|
||||
function timingSafeEqual(a, b) {
|
||||
const bufA = Buffer.from(String(a));
|
||||
const bufB = Buffer.from(String(b));
|
||||
if (bufA.length !== bufB.length) return false;
|
||||
return crypto.timingSafeEqual(bufA, bufB);
|
||||
}
|
||||
|
||||
app.use('/api/health', healthRouter);
|
||||
app.use('/api/vms', vmsRouter);
|
||||
app.use('/api/snapshots', snapshotsRouter);
|
||||
|
||||
Reference in New Issue
Block a user