TidGi-Desktop/src/services/libs/fixPath.ts
lin onetwo ea78df0ab3
Fix: security errors (#648)
* refactor: outdated usage

* fix: use JSON.stringify to prevent '`' break the string to execute any script

* fix: use json5 parse LLM tool calling

* fix  Potential file system race condition

* fix  Indirect uncontrolled command line

* Update callImageGenerationAPI.ts

* While the shell path is now validated, arguments_.join(' ') could still contain shell metacharacters that enable command injection. The arguments array should be validated or passed in a way that prevents shell interpretation, such as using the array form of execSync instead of string interpolation.

* directly Remove TiddlyWiki special characters that could cause parsing issues

* prevent race condition

* fix: subscribe to latest pauseNotification

* Return a lightweight mock notification object to avoid creating real browser notifications

* Update responsePatternUtility.ts
2025-10-24 22:16:03 +08:00

105 lines
2.9 KiB
TypeScript

/**
* This fixes https://github.com/google/zx/issues/230
*/
import { isWin } from '@/helpers/system';
import { execFileSync } from 'child_process';
import { userInfo } from 'os';
import process from 'process';
import stripAnsi from 'strip-ansi';
const defaultShell = (() => {
const { env, platform } = process;
if (platform === 'win32') {
return env.COMSPEC ?? 'pwsh.exe'; // 'cmd.exe';
}
try {
const { shell } = userInfo();
if (shell) {
return shell;
}
} catch (_error: unknown) {
// userInfo may throw in some environments; ignore and fallback to defaults
void _error;
}
if (platform === 'darwin') {
return env.SHELL ?? '/bin/zsh';
}
return env.SHELL ?? '/bin/sh';
})();
const arguments_ = ['-ilc', 'echo -n "_SHELL_ENV_DELIMITER_"; env; echo -n "_SHELL_ENV_DELIMITER_"; exit'];
const environment = {
// Disables Oh My Zsh auto-update thing that can block the process.
DISABLE_AUTO_UPDATE: 'true',
};
const parseEnvironment = (environment_: string): Record<string, string> => {
environment_ = environment_.split('_SHELL_ENV_DELIMITER_')[1];
const returnValue: Record<string, string> = {};
for (const line of stripAnsi(environment_).split('\n').filter(Boolean)) {
const [key, ...values] = line.split('=');
returnValue[key] = values.join('=');
}
return returnValue;
};
/**
* Validates that a shell path is safe to execute
* Only allows absolute paths without shell metacharacters
*/
function validateShellPath(shellPath: string): boolean {
// Must be an absolute path
if (!shellPath.startsWith('/')) {
return false;
}
// Must not contain shell metacharacters that could enable injection
const dangerousChars = /[;&|`$(){}[\]<>'"\\]/;
if (dangerousChars.test(shellPath)) {
return false;
}
return true;
}
export function shellEnvironmentSync(shell?: string): NodeJS.ProcessEnv {
if (isWin) {
return process.env;
}
const shellToUse = shell ?? defaultShell;
// Validate shell path to prevent command injection
if (!validateShellPath(shellToUse)) {
console.warn(`[fixPath] Invalid shell path rejected: ${shellToUse}`);
return process.env;
}
try {
// Use execFileSync to prevent argument injection - arguments are passed as array, not shell-interpolated string
// This prevents shell metacharacters in arguments from being interpreted
const stdout = execFileSync(shellToUse, arguments_, {
env: environment,
});
return parseEnvironment(String(stdout));
} catch (error) {
if (shell === undefined) {
return process.env;
} else {
throw error;
}
}
}
export function shellPathSync(): string | undefined {
const { PATH } = shellEnvironmentSync();
return PATH;
}
export function fixPath(): void {
if (isWin) {
return;
}
process.env.PATH = shellPathSync() ?? ['./node_modules/.bin', '/.nodebrew/current/bin', '/usr/local/bin', process.env.PATH].join(':');
}