mirror of
https://github.com/amitwh/markdown-converter.git
synced 2026-08-02 18:10:18 +05:30
108 lines
3.1 KiB
JavaScript
108 lines
3.1 KiB
JavaScript
// src/main/utils/paths.js
|
|
// Path helpers — extracted from src/main.js lines 109-207
|
|
const { app } = require('electron');
|
|
const path = require('path');
|
|
const fs = require('fs');
|
|
|
|
function getAllowedDirectories() {
|
|
const dirs = [
|
|
app.getPath('documents'),
|
|
app.getPath('desktop'),
|
|
app.getPath('downloads'),
|
|
app.getPath('home'),
|
|
process.cwd() // Current working directory
|
|
].filter(Boolean); // Remove any undefined paths
|
|
return dirs;
|
|
}
|
|
|
|
/**
|
|
* Validates that a file path is safe and doesn't attempt path traversal
|
|
* @param {string} filePath - The path to validate
|
|
* @returns {{ valid: boolean, resolved: string, error?: string }}
|
|
*/
|
|
function validatePath(filePath) {
|
|
if (!filePath || typeof filePath !== 'string') {
|
|
return { valid: false, resolved: '', error: 'Invalid path' };
|
|
}
|
|
|
|
// Resolve to absolute path (handles .., ., symlinks)
|
|
let resolved;
|
|
try {
|
|
resolved = path.resolve(filePath);
|
|
} catch (err) {
|
|
return { valid: false, resolved: '', error: 'Invalid path format' };
|
|
}
|
|
|
|
// Normalize path separators
|
|
resolved = path.normalize(resolved);
|
|
|
|
// Check for null bytes (path injection)
|
|
if (resolved.includes('\0')) {
|
|
return { valid: false, resolved: '', error: 'Null byte in path' };
|
|
}
|
|
|
|
// Check if path exists
|
|
if (!fs.existsSync(resolved)) {
|
|
return { valid: false, resolved, error: 'Path does not exist' };
|
|
}
|
|
|
|
return { valid: true, resolved };
|
|
}
|
|
|
|
/**
|
|
* Resolves a path for operations where the target may not exist yet.
|
|
* Validates string shape and blocks obviously sensitive locations.
|
|
* @param {string} filePath
|
|
* @returns {{ valid: boolean, resolved: string, error?: string }}
|
|
*/
|
|
function resolveWritablePath(filePath) {
|
|
if (!filePath || typeof filePath !== 'string') {
|
|
return { valid: false, resolved: '', error: 'Invalid path' };
|
|
}
|
|
|
|
let resolved;
|
|
try {
|
|
resolved = path.normalize(path.resolve(filePath));
|
|
} catch (err) {
|
|
return { valid: false, resolved: '', error: 'Invalid path format' };
|
|
}
|
|
|
|
if (resolved.includes('\0')) {
|
|
return { valid: false, resolved: '', error: 'Null byte in path' };
|
|
}
|
|
|
|
if (!isPathAccessible(resolved)) {
|
|
return { valid: false, resolved, error: 'Path is not accessible' };
|
|
}
|
|
|
|
return { valid: true, resolved };
|
|
}
|
|
|
|
/**
|
|
* Checks if a resolved path is within allowed directories
|
|
* For an editor app, we allow access to all user-accessible paths
|
|
* but log any suspicious access attempts
|
|
* @param {string} resolvedPath - The resolved absolute path
|
|
* @returns {boolean}
|
|
*/
|
|
function isPathAccessible(resolvedPath) {
|
|
// Block access to sensitive system directories
|
|
const blockedPaths = [
|
|
'/etc/passwd', '/etc/shadow', '/root',
|
|
'C:\\Windows\\System32', 'C:\\Windows\\System',
|
|
'/System', '/private/etc'
|
|
];
|
|
|
|
const normalizedPath = resolvedPath.toLowerCase();
|
|
for (const blocked of blockedPaths) {
|
|
if (normalizedPath.startsWith(blocked.toLowerCase())) {
|
|
console.warn('[SECURITY] Blocked access to sensitive path:', resolvedPath);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
module.exports = { getAllowedDirectories, validatePath, resolveWritablePath, isPathAccessible };
|