Enable portable user data on all platforms
Portable mode now works on Windows, macOS, and Linux, storing user data in a 'user-data' folder within the app directory by default. The mode is enabled by default and can be disabled via NEBULA_PORTABLE=0. All file operations for history and search data now respect portable mode, using secure file writes where appropriate. Updated .gitignore and package.json to exclude portable data from versioning and packaging. Also fixed Bing icon filename case in home.html.
This commit is contained in:
@@ -86,6 +86,10 @@ typings/
|
|||||||
site-history.json
|
site-history.json
|
||||||
bookmarks.json
|
bookmarks.json
|
||||||
bookmarks.backup.json
|
bookmarks.backup.json
|
||||||
|
search-history.json
|
||||||
|
|
||||||
|
# Portable user data folder
|
||||||
|
user-data/
|
||||||
|
|
||||||
# AppImage / SteamOS
|
# AppImage / SteamOS
|
||||||
squashfs-root/
|
squashfs-root/
|
||||||
|
|||||||
@@ -158,12 +158,13 @@ const gpuConfig = new GPUConfig();
|
|||||||
const pluginManager = new PluginManager();
|
const pluginManager = new PluginManager();
|
||||||
|
|
||||||
// Initialize portable data paths BEFORE app.ready (must be done early)
|
// Initialize portable data paths BEFORE app.ready (must be done early)
|
||||||
// This only affects Linux when NEBULA_PORTABLE=1 is set
|
// This enables portable mode on all platforms (Windows, macOS, Linux)
|
||||||
|
// Data is stored in 'user-data' folder within the application directory
|
||||||
portableData.initialize();
|
portableData.initialize();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the path for a user data file (bookmarks, history, etc.)
|
* Get the path for a user data file (bookmarks, history, etc.)
|
||||||
* Uses portable path on Linux when in portable mode, otherwise uses __dirname
|
* Uses portable path when in portable mode, otherwise uses __dirname
|
||||||
* @param {string} filename - The filename (e.g., 'bookmarks.json')
|
* @param {string} filename - The filename (e.g., 'bookmarks.json')
|
||||||
* @returns {string} The full path to the file
|
* @returns {string} The full path to the file
|
||||||
*/
|
*/
|
||||||
@@ -177,7 +178,7 @@ function getDataFilePath(filename) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the directory path for user data files
|
* Get the directory path for user data files
|
||||||
* Uses portable path on Linux when in portable mode, otherwise uses __dirname
|
* Uses portable path when in portable mode, otherwise uses __dirname
|
||||||
* @returns {string} The directory path
|
* @returns {string} The directory path
|
||||||
*/
|
*/
|
||||||
function getDataDirPath() {
|
function getDataDirPath() {
|
||||||
@@ -968,7 +969,11 @@ ipcMain.handle('save-site-history', async (event, history) => {
|
|||||||
ipcMain.handle('clear-site-history', async () => {
|
ipcMain.handle('clear-site-history', async () => {
|
||||||
const filePath = getDataFilePath('site-history.json');
|
const filePath = getDataFilePath('site-history.json');
|
||||||
try {
|
try {
|
||||||
await fs.promises.writeFile(filePath, JSON.stringify([], null, 2));
|
if (portableData.isPortableMode()) {
|
||||||
|
await portableData.writeSecureFileAsync(filePath, JSON.stringify([], null, 2));
|
||||||
|
} else {
|
||||||
|
await fs.promises.writeFile(filePath, JSON.stringify([], null, 2));
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return false;
|
return false;
|
||||||
@@ -1110,8 +1115,20 @@ ipcMain.handle('clear-browser-data', async () => {
|
|||||||
// Also reset on-disk history JSON files managed by the app
|
// Also reset on-disk history JSON files managed by the app
|
||||||
const siteHistoryPath = getDataFilePath('site-history.json');
|
const siteHistoryPath = getDataFilePath('site-history.json');
|
||||||
const searchHistoryPath = getDataFilePath('search-history.json');
|
const searchHistoryPath = getDataFilePath('search-history.json');
|
||||||
try { await fs.promises.writeFile(siteHistoryPath, JSON.stringify([], null, 2)); } catch {}
|
try {
|
||||||
try { await fs.promises.writeFile(searchHistoryPath, JSON.stringify([], null, 2)); } catch {}
|
if (portableData.isPortableMode()) {
|
||||||
|
await portableData.writeSecureFileAsync(siteHistoryPath, JSON.stringify([], null, 2));
|
||||||
|
} else {
|
||||||
|
await fs.promises.writeFile(siteHistoryPath, JSON.stringify([], null, 2));
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
|
try {
|
||||||
|
if (portableData.isPortableMode()) {
|
||||||
|
await portableData.writeSecureFileAsync(searchHistoryPath, JSON.stringify([], null, 2));
|
||||||
|
} else {
|
||||||
|
await fs.promises.writeFile(searchHistoryPath, JSON.stringify([], null, 2));
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
|
|
||||||
return true; // Indicate success
|
return true; // Indicate success
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -1124,7 +1141,11 @@ ipcMain.handle('clear-browser-data', async () => {
|
|||||||
ipcMain.handle('clear-search-history', async () => {
|
ipcMain.handle('clear-search-history', async () => {
|
||||||
const filePath = getDataFilePath('search-history.json');
|
const filePath = getDataFilePath('search-history.json');
|
||||||
try {
|
try {
|
||||||
await fs.promises.writeFile(filePath, JSON.stringify([], null, 2));
|
if (portableData.isPortableMode()) {
|
||||||
|
await portableData.writeSecureFileAsync(filePath, JSON.stringify([], null, 2));
|
||||||
|
} else {
|
||||||
|
await fs.promises.writeFile(filePath, JSON.stringify([], null, 2));
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return false;
|
return false;
|
||||||
@@ -1183,7 +1204,7 @@ ipcMain.handle('open-tab-in-new-window', (event, url) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle('save-site-history-entry', async (event, url) => {
|
ipcMain.handle('save-site-history-entry', async (event, url) => {
|
||||||
const filePath = path.join(__dirname, 'site-history.json');
|
const filePath = getDataFilePath('site-history.json');
|
||||||
try {
|
try {
|
||||||
let data = [];
|
let data = [];
|
||||||
try {
|
try {
|
||||||
@@ -1195,7 +1216,11 @@ ipcMain.handle('save-site-history-entry', async (event, url) => {
|
|||||||
// Add to beginning and clamp size
|
// Add to beginning and clamp size
|
||||||
data.unshift(url);
|
data.unshift(url);
|
||||||
if (data.length > 100) data = data.slice(0, 100);
|
if (data.length > 100) data = data.slice(0, 100);
|
||||||
await fs.promises.writeFile(filePath, JSON.stringify(data, null, 2));
|
if (portableData.isPortableMode()) {
|
||||||
|
await portableData.writeSecureFileAsync(filePath, JSON.stringify(data, null, 2));
|
||||||
|
} else {
|
||||||
|
await fs.promises.writeFile(filePath, JSON.stringify(data, null, 2));
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('[MAIN] Error saving site history entry:', err);
|
console.error('[MAIN] Error saving site history entry:', err);
|
||||||
|
|||||||
@@ -35,6 +35,12 @@
|
|||||||
"repo": "NebulaBrowser"
|
"repo": "NebulaBrowser"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"files": [
|
||||||
|
"**/*",
|
||||||
|
"!user-data/**",
|
||||||
|
"!*.backup.json"
|
||||||
|
],
|
||||||
|
"extraResources": [],
|
||||||
"mac": {
|
"mac": {
|
||||||
"category": "public.app-category.productivity",
|
"category": "public.app-category.productivity",
|
||||||
"icon": "assets/images/Logos/Nebula-Favicon.icns"
|
"icon": "assets/images/Logos/Nebula-Favicon.icns"
|
||||||
|
|||||||
+153
-58
@@ -1,13 +1,15 @@
|
|||||||
/**
|
/**
|
||||||
* Portable Data Manager for Nebula Browser
|
* Portable Data Manager for Nebula Browser
|
||||||
*
|
*
|
||||||
* Handles portable user data storage on Linux when running from extracted AppImage
|
* Handles portable user data storage for all platforms (Windows, macOS, Linux).
|
||||||
* or portable installations. Does not affect Windows or macOS behavior.
|
* Data is stored in a 'user-data' folder within the application directory,
|
||||||
|
* keeping all user data local to the compiled project.
|
||||||
*
|
*
|
||||||
* Security considerations:
|
* Security considerations:
|
||||||
* - Data is stored with restricted permissions (0700 for directories, 0600 for files)
|
* - Data is stored with restricted permissions (0700 for directories, 0600 for files on Unix)
|
||||||
* - Path validation prevents directory traversal attacks
|
* - Path validation prevents directory traversal attacks
|
||||||
* - Only enabled when explicitly set via NEBULA_PORTABLE environment variable
|
* - Portable mode is enabled by default on all platforms
|
||||||
|
* - Can be disabled via NEBULA_PORTABLE=0 environment variable
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const { app } = require('electron');
|
const { app } = require('electron');
|
||||||
@@ -23,37 +25,67 @@ class PortableDataManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if we're running in portable mode on Linux
|
* Get the application's root directory
|
||||||
* Portable mode is determined by:
|
* Works for both development and packaged builds
|
||||||
* 1. NEBULA_PORTABLE environment variable is set and truthy
|
*/
|
||||||
* 2. NEBULA_PORTABLE_PATH environment variable provides the data path
|
_getAppRootDir() {
|
||||||
* 3. Platform must be Linux (does not affect Windows or macOS)
|
// In packaged app, process.resourcesPath points to resources folder
|
||||||
|
// We want the parent directory (the app folder itself)
|
||||||
|
if (app.isPackaged) {
|
||||||
|
// For packaged apps:
|
||||||
|
// - Windows: path\to\app\resources -> path\to\app
|
||||||
|
// - macOS: path/to/App.app/Contents/Resources -> path/to/App.app/Contents
|
||||||
|
// - Linux: path/to/app/resources -> path/to/app
|
||||||
|
const resourcesPath = process.resourcesPath;
|
||||||
|
|
||||||
|
if (process.platform === 'darwin') {
|
||||||
|
// On macOS, go up two levels from Resources to get to App.app parent folder
|
||||||
|
// But we want to store data inside the .app bundle's Contents folder for portability
|
||||||
|
return path.dirname(resourcesPath); // Contents folder
|
||||||
|
} else {
|
||||||
|
// Windows and Linux: go up one level from resources
|
||||||
|
return path.dirname(resourcesPath);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Development mode: use __dirname (the directory containing main.js)
|
||||||
|
return __dirname;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if we're running in portable mode
|
||||||
|
* Portable mode is enabled by default on all platforms.
|
||||||
|
*
|
||||||
|
* Can be disabled by setting NEBULA_PORTABLE=0 or NEBULA_PORTABLE=false
|
||||||
|
* Can specify custom path via NEBULA_PORTABLE_PATH environment variable
|
||||||
*/
|
*/
|
||||||
isPortableMode() {
|
isPortableMode() {
|
||||||
if (this._isPortable !== null) {
|
if (this._isPortable !== null) {
|
||||||
return this._isPortable;
|
return this._isPortable;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only portable mode on Linux
|
// Check if NEBULA_PORTABLE is explicitly set to disable
|
||||||
if (process.platform !== 'linux') {
|
const portableEnv = process.env.NEBULA_PORTABLE;
|
||||||
this._isPortable = false;
|
if (portableEnv !== undefined) {
|
||||||
return false;
|
const isDisabled = portableEnv === '0' ||
|
||||||
|
portableEnv.toLowerCase() === 'false' ||
|
||||||
|
portableEnv.toLowerCase() === 'no';
|
||||||
|
|
||||||
|
if (isDisabled) {
|
||||||
|
this._isPortable = false;
|
||||||
|
console.log('[Portable] Portable mode disabled via NEBULA_PORTABLE environment variable');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if NEBULA_PORTABLE is set and truthy
|
// Portable mode is enabled by default on all platforms
|
||||||
const portableEnv = process.env.NEBULA_PORTABLE;
|
this._isPortable = true;
|
||||||
const isTruthy = portableEnv &&
|
|
||||||
(portableEnv === '1' ||
|
|
||||||
portableEnv.toLowerCase() === 'true' ||
|
|
||||||
portableEnv.toLowerCase() === 'yes');
|
|
||||||
|
|
||||||
this._isPortable = isTruthy;
|
|
||||||
return this._isPortable;
|
return this._isPortable;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the portable data directory path
|
* Get the portable data directory path
|
||||||
* Uses NEBULA_PORTABLE_PATH if set, otherwise returns null
|
* Uses NEBULA_PORTABLE_PATH if set, otherwise creates 'user-data' folder in app directory
|
||||||
*/
|
*/
|
||||||
getPortableDataPath() {
|
getPortableDataPath() {
|
||||||
if (this._portableDataPath !== null) {
|
if (this._portableDataPath !== null) {
|
||||||
@@ -65,21 +97,29 @@ class PortableDataManager {
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
const portablePath = process.env.NEBULA_PORTABLE_PATH;
|
// First, check if custom path is provided via environment variable
|
||||||
if (!portablePath) {
|
const customPath = process.env.NEBULA_PORTABLE_PATH;
|
||||||
console.warn('[Portable] NEBULA_PORTABLE is set but NEBULA_PORTABLE_PATH is missing');
|
if (customPath) {
|
||||||
this._portableDataPath = '';
|
const resolvedPath = path.resolve(customPath);
|
||||||
return '';
|
if (this._isPathSafe(resolvedPath)) {
|
||||||
|
this._portableDataPath = resolvedPath;
|
||||||
|
console.log(`[Portable] Using custom portable path: ${resolvedPath}`);
|
||||||
|
return this._portableDataPath;
|
||||||
|
} else {
|
||||||
|
console.warn('[Portable] Custom path is unsafe, using default location');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate and resolve the path
|
// Default: create 'user-data' folder in the application directory
|
||||||
const resolvedPath = path.resolve(portablePath);
|
const appRoot = this._getAppRootDir();
|
||||||
|
const dataPath = path.join(appRoot, 'user-data');
|
||||||
|
|
||||||
// Security: ensure path doesn't contain suspicious patterns
|
// Validate the path
|
||||||
if (this._isPathSafe(resolvedPath)) {
|
if (this._isPathSafe(dataPath)) {
|
||||||
this._portableDataPath = resolvedPath;
|
this._portableDataPath = dataPath;
|
||||||
|
console.log(`[Portable] Using portable data path: ${dataPath}`);
|
||||||
} else {
|
} else {
|
||||||
console.error('[Portable] Unsafe path detected, falling back to default');
|
console.error('[Portable] Default path is unsafe, falling back to system default');
|
||||||
this._portableDataPath = '';
|
this._portableDataPath = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,7 +153,8 @@ class PortableDataManager {
|
|||||||
this._ensureSecureDirectory(dataPath);
|
this._ensureSecureDirectory(dataPath);
|
||||||
|
|
||||||
// Create subdirectories for organized storage
|
// Create subdirectories for organized storage
|
||||||
const subdirs = ['Cache', 'Cookies', 'Local Storage', 'Session Storage', 'IndexedDB'];
|
// Note: Don't create 'Cache', 'Cookies', 'Network' - Electron manages these internally
|
||||||
|
const subdirs = ['Local Storage', 'Session Storage', 'IndexedDB'];
|
||||||
for (const subdir of subdirs) {
|
for (const subdir of subdirs) {
|
||||||
this._ensureSecureDirectory(path.join(dataPath, subdir));
|
this._ensureSecureDirectory(path.join(dataPath, subdir));
|
||||||
}
|
}
|
||||||
@@ -163,28 +204,40 @@ class PortableDataManager {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Ensure a directory exists with secure permissions
|
* Ensure a directory exists with secure permissions
|
||||||
|
* On Unix systems (macOS, Linux), applies restricted permissions (0700)
|
||||||
|
* On Windows, creates directory with default permissions (ACLs handle security)
|
||||||
*/
|
*/
|
||||||
_ensureSecureDirectory(dirPath) {
|
_ensureSecureDirectory(dirPath) {
|
||||||
if (!fs.existsSync(dirPath)) {
|
if (!fs.existsSync(dirPath)) {
|
||||||
// Create with restricted permissions (owner only: rwx------)
|
if (process.platform === 'win32') {
|
||||||
fs.mkdirSync(dirPath, { recursive: true, mode: 0o700 });
|
// Windows: create directory with default permissions
|
||||||
|
// Windows ACLs handle security through inheritance
|
||||||
|
fs.mkdirSync(dirPath, { recursive: true });
|
||||||
|
} else {
|
||||||
|
// Unix (macOS, Linux): create with restricted permissions (owner only: rwx------)
|
||||||
|
fs.mkdirSync(dirPath, { recursive: true, mode: 0o700 });
|
||||||
|
}
|
||||||
console.log(`[Portable] Created secure directory: ${dirPath}`);
|
console.log(`[Portable] Created secure directory: ${dirPath}`);
|
||||||
} else {
|
} else {
|
||||||
// Verify and fix permissions on existing directory
|
// Verify and fix permissions on existing directory (Unix only)
|
||||||
try {
|
if (process.platform !== 'win32') {
|
||||||
const stats = fs.statSync(dirPath);
|
try {
|
||||||
if (stats.isDirectory()) {
|
const stats = fs.statSync(dirPath);
|
||||||
// Set secure permissions
|
if (stats.isDirectory()) {
|
||||||
fs.chmodSync(dirPath, 0o700);
|
// Set secure permissions
|
||||||
|
fs.chmodSync(dirPath, 0o700);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.warn(`[Portable] Could not verify permissions for ${dirPath}:`, err.message);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
|
||||||
console.warn(`[Portable] Could not verify permissions for ${dirPath}:`, err.message);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write a file with secure permissions
|
* Write a file with secure permissions
|
||||||
|
* On Unix systems, applies restricted permissions (0600)
|
||||||
|
* On Windows, writes with default permissions
|
||||||
*/
|
*/
|
||||||
writeSecureFile(filePath, data) {
|
writeSecureFile(filePath, data) {
|
||||||
// Ensure parent directory exists with secure permissions
|
// Ensure parent directory exists with secure permissions
|
||||||
@@ -192,41 +245,81 @@ class PortableDataManager {
|
|||||||
this._ensureSecureDirectory(dir);
|
this._ensureSecureDirectory(dir);
|
||||||
|
|
||||||
// Write file
|
// Write file
|
||||||
fs.writeFileSync(filePath, data, { mode: 0o600 });
|
if (process.platform === 'win32') {
|
||||||
|
fs.writeFileSync(filePath, data);
|
||||||
|
} else {
|
||||||
|
fs.writeFileSync(filePath, data, { mode: 0o600 });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Async version of secure file write
|
* Async version of secure file write
|
||||||
|
* On Unix systems, applies restricted permissions (0600)
|
||||||
|
* On Windows, writes with default permissions
|
||||||
*/
|
*/
|
||||||
async writeSecureFileAsync(filePath, data) {
|
async writeSecureFileAsync(filePath, data) {
|
||||||
// Ensure parent directory exists with secure permissions
|
// Ensure parent directory exists with secure permissions
|
||||||
const dir = path.dirname(filePath);
|
const dir = path.dirname(filePath);
|
||||||
this._ensureSecureDirectory(dir);
|
this._ensureSecureDirectory(dir);
|
||||||
|
|
||||||
// Write file with restricted permissions (owner only: rw-------)
|
// Write file with restricted permissions (owner only: rw------- on Unix)
|
||||||
await fs.promises.writeFile(filePath, data, { mode: 0o600 });
|
if (process.platform === 'win32') {
|
||||||
|
await fs.promises.writeFile(filePath, data);
|
||||||
|
} else {
|
||||||
|
await fs.promises.writeFile(filePath, data, { mode: 0o600 });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate path safety (prevent directory traversal)
|
* Validate path safety (prevent directory traversal)
|
||||||
|
* Works across Windows, macOS, and Linux
|
||||||
*/
|
*/
|
||||||
_isPathSafe(testPath) {
|
_isPathSafe(testPath) {
|
||||||
// Resolve to absolute path
|
// Resolve to absolute path
|
||||||
const resolved = path.resolve(testPath);
|
const resolved = path.resolve(testPath);
|
||||||
|
|
||||||
// Check for suspicious patterns
|
// Check for directory traversal patterns
|
||||||
const dangerous = ['..', '~root', '/etc', '/var/run', '/proc', '/sys', '/dev'];
|
if (resolved.includes('..')) {
|
||||||
for (const pattern of dangerous) {
|
return false;
|
||||||
if (resolved.includes(pattern)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure it's not trying to write to system directories
|
// Platform-specific system path checks
|
||||||
const systemPaths = ['/bin', '/sbin', '/usr/bin', '/usr/sbin', '/boot', '/lib', '/lib64'];
|
if (process.platform === 'win32') {
|
||||||
for (const sysPath of systemPaths) {
|
// Windows: block system directories
|
||||||
if (resolved.startsWith(sysPath)) {
|
const dangerousWin = [
|
||||||
return false;
|
'C:\\Windows',
|
||||||
|
'C:\\Program Files',
|
||||||
|
'C:\\Program Files (x86)',
|
||||||
|
'C:\\ProgramData'
|
||||||
|
];
|
||||||
|
const resolvedLower = resolved.toLowerCase();
|
||||||
|
for (const pattern of dangerousWin) {
|
||||||
|
if (resolvedLower.startsWith(pattern.toLowerCase())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (process.platform === 'darwin') {
|
||||||
|
// macOS: block system directories
|
||||||
|
const dangerousMac = ['/System', '/Library', '/usr', '/bin', '/sbin', '/etc', '/var'];
|
||||||
|
for (const pattern of dangerousMac) {
|
||||||
|
if (resolved.startsWith(pattern) && !resolved.includes('.app')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Linux: block system directories
|
||||||
|
const dangerousLinux = ['~root', '/etc', '/var/run', '/proc', '/sys', '/dev'];
|
||||||
|
for (const pattern of dangerousLinux) {
|
||||||
|
if (resolved.includes(pattern)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const systemPaths = ['/bin', '/sbin', '/usr/bin', '/usr/sbin', '/boot', '/lib', '/lib64'];
|
||||||
|
for (const sysPath of systemPaths) {
|
||||||
|
if (resolved.startsWith(sysPath)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,8 +350,10 @@ class PortableDataManager {
|
|||||||
return {
|
return {
|
||||||
isPortable: this.isPortableMode(),
|
isPortable: this.isPortableMode(),
|
||||||
portablePath: this.getPortableDataPath(),
|
portablePath: this.getPortableDataPath(),
|
||||||
|
appRootDir: this._getAppRootDir(),
|
||||||
initialized: this._initialized,
|
initialized: this._initialized,
|
||||||
platform: process.platform,
|
platform: process.platform,
|
||||||
|
isPackaged: app.isPackaged,
|
||||||
envPortable: process.env.NEBULA_PORTABLE,
|
envPortable: process.env.NEBULA_PORTABLE,
|
||||||
envPath: process.env.NEBULA_PORTABLE_PATH
|
envPath: process.env.NEBULA_PORTABLE_PATH
|
||||||
};
|
};
|
||||||
|
|||||||
+1
-1
@@ -32,7 +32,7 @@
|
|||||||
<img src="../assets/images/icons/google.svg" alt="Google">
|
<img src="../assets/images/icons/google.svg" alt="Google">
|
||||||
</div>
|
</div>
|
||||||
<div class="search-engine-option" data-engine="bing">
|
<div class="search-engine-option" data-engine="bing">
|
||||||
<img src="../assets/images/icons/bing.svg" alt="Bing">
|
<img src="../assets/images/icons/Bing.svg" alt="Bing">
|
||||||
</div>
|
</div>
|
||||||
<div class="search-engine-option" data-engine="duckduckgo">
|
<div class="search-engine-option" data-engine="duckduckgo">
|
||||||
<img src="../assets/images/icons/duckduckgo.svg" alt="DuckDuckGo">
|
<img src="../assets/images/icons/duckduckgo.svg" alt="DuckDuckGo">
|
||||||
|
|||||||
Reference in New Issue
Block a user