Skip to content
Merged
31 changes: 16 additions & 15 deletions electron/database/init.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -154,29 +154,30 @@ function getEncryptionKey() {
* SQLCipher databases start with different magic bytes than regular SQLite
*/
function isDatabaseEncrypted(dbPath) {
if (!fs.existsSync(dbPath)) {
return null; // Database doesn't exist
let fd;
try {
fd = fs.openSync(dbPath, 'r');
} catch (openErr) {
if (openErr.code === 'ENOENT') return null;
return null;
}

try {
// Read first 16 bytes of the file
const fd = fs.openSync(dbPath, 'r');
const buffer = Buffer.alloc(16);
fs.readSync(fd, buffer, 0, 16, 0);
fs.closeSync(fd);

// SQLite3 magic header: "SQLite format 3\0"
const sqliteMagic = Buffer.from('SQLite format 3\0');

// If file starts with SQLite magic, it's unencrypted

if (buffer.compare(sqliteMagic, 0, 16, 0, 16) === 0) {
return false; // Unencrypted
return false;
}

// Otherwise, assume encrypted (or corrupted)

return true;
} catch (e) {
return null; // Unable to determine
} catch {
try { fs.closeSync(fd); } catch { /* already closed */ }
return null;
}
}

Expand Down Expand Up @@ -689,13 +690,13 @@ async function seedDefaultData(defaultOrgId) {
console.log(' Account : admin@transtrack.local');
console.log(' Source : ' + passwordSource);
if (setupTokenFilePath) {
console.log(' Token : ' + defaultPassword);
console.log(' Token : (see file below)');
console.log(' File : ' + setupTokenFilePath);
console.log(' (mode 0o600 on POSIX; ACL inherited on Windows)');
} else if (envPassword) {
console.log(' Token : (supplied by env; not echoed)');
} else {
console.log(' Token : ' + defaultPassword);
console.log(' Token : (could not persist to file; set TRANSTRACK_INITIAL_ADMIN_PASSWORD env and restart)');
}
console.log(' Must change password on first sign-in: yes');
console.log('================================================================');
Expand Down
18 changes: 13 additions & 5 deletions electron/ipc/handlers/operations.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -288,12 +288,20 @@ function register() {
const ext = path.extname(importPath).toLowerCase();

const MAX_IMPORT_SIZE = 50 * 1024 * 1024; // 50 MB
const stat = fs.statSync(importPath);
if (stat.size > MAX_IMPORT_SIZE) {
throw new Error(`File too large (${(stat.size / 1024 / 1024).toFixed(1)} MB). Maximum import size is 50 MB.`);
const fd = fs.openSync(importPath, 'r');
let raw;
try {
const stat = fs.fstatSync(fd);
if (stat.size > MAX_IMPORT_SIZE) {
fs.closeSync(fd);
throw new Error(`File too large (${(stat.size / 1024 / 1024).toFixed(1)} MB). Maximum import size is 50 MB.`);
}
raw = fs.readFileSync(fd, 'utf8');
fs.closeSync(fd);
} catch (fdErr) {
try { fs.closeSync(fd); } catch { /* already closed */ }
throw fdErr;
}

const raw = fs.readFileSync(importPath, 'utf8');
let parsed;

if (ext === '.json') {
Expand Down
43 changes: 31 additions & 12 deletions electron/services/logger.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -89,26 +89,45 @@ const REMOTE_LOG_LEVELS = new Set(
.split(',').map((s) => s.trim().toLowerCase()).filter(Boolean)
);

const SAFE_META_KEYS = new Set(['error', 'code', 'component', 'action', 'duration']);
const MAX_REMOTE_MSG_LEN = 256;

function _buildRemotePayload(level, message, meta) {
const safeMsg = typeof message === 'string'
? message.slice(0, MAX_REMOTE_MSG_LEN)
: String(message || '').slice(0, MAX_REMOTE_MSG_LEN);

const safeMeta = {};
if (meta && typeof meta === 'object') {
for (const key of Object.keys(meta)) {
if (!SAFE_META_KEYS.has(key)) continue;
const val = meta[key];
if (typeof val === 'string') safeMeta[key] = val.slice(0, 128);
else if (typeof val === 'number' || typeof val === 'boolean') safeMeta[key] = val;
}
}

return {
timestamp: new Date().toISOString(),
level: String(level),
message: safeMsg,
meta: Object.keys(safeMeta).length > 0 ? safeMeta : undefined,
product: 'TransTrack',
platform: process.platform,
pid: process.pid,
};
}

function _shipRemote(level, message, meta) {
if (!REMOTE_LOG_URL || !REMOTE_LOG_LEVELS.has(level)) return;
if (typeof fetch !== 'function') return;
// Fire-and-forget; never throw out of the logger.
try {
const payload = _buildRemotePayload(level, message, meta);
fetch(REMOTE_LOG_URL, {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({
timestamp: new Date().toISOString(),
level,
message,
meta: meta || null,
product: 'TransTrack',
version: (() => {
try { return app.getVersion(); } catch { return null; }
})(),
platform: process.platform,
pid: process.pid,
}),
body: JSON.stringify(payload),
}).catch(() => { /* swallow */ });
} catch { /* swallow */ }
}
Expand Down
Loading
Loading