feat: add pink-purple theme, fix image paste race condition, allow @/commands anywhere in input
- Add Pink Purple theme (hot pink/purple/magenta on dark plum background) - Fix race condition where clearPastedImages() in input-area ran before the async message handler could read the images, silently dropping them - Allow @ file picker and / command menu to trigger at any cursor position, not just when the input is empty - Update CHANGELOG and README with new changes
This commit is contained in:
@@ -22,12 +22,58 @@ export interface DangerCheckResult {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a command matches any blocked pattern
|
||||
* Split a shell command into individual sub-commands on chaining operators.
|
||||
* Handles &&, ||, ;, and | (pipe). Respects quoted strings.
|
||||
*/
|
||||
const splitChainedCommands = (command: string): string[] => {
|
||||
const parts: string[] = [];
|
||||
let current = "";
|
||||
let inSingle = false;
|
||||
let inDouble = false;
|
||||
|
||||
for (let i = 0; i < command.length; i++) {
|
||||
const ch = command[i];
|
||||
const next = command[i + 1];
|
||||
|
||||
if (ch === "'" && !inDouble) { inSingle = !inSingle; current += ch; continue; }
|
||||
if (ch === '"' && !inSingle) { inDouble = !inDouble; current += ch; continue; }
|
||||
if (inSingle || inDouble) { current += ch; continue; }
|
||||
|
||||
if (ch === "&" && next === "&") { parts.push(current); current = ""; i++; continue; }
|
||||
if (ch === "|" && next === "|") { parts.push(current); current = ""; i++; continue; }
|
||||
if (ch === ";") { parts.push(current); current = ""; continue; }
|
||||
if (ch === "|") { parts.push(current); current = ""; continue; }
|
||||
|
||||
current += ch;
|
||||
}
|
||||
|
||||
if (current.trim()) parts.push(current);
|
||||
return parts.map((p) => p.trim()).filter(Boolean);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if a command matches any blocked pattern.
|
||||
* For chained commands (&&, ||, ;, |), each sub-command is checked individually
|
||||
* to prevent dangerous commands hidden behind benign ones (e.g. cd /safe && rm -rf /).
|
||||
*/
|
||||
export const checkDangerousCommand = (command: string): DangerCheckResult => {
|
||||
// Normalize command for checking
|
||||
const normalizedCommand = command.trim();
|
||||
const subCommands = splitChainedCommands(command);
|
||||
|
||||
for (const subCmd of subCommands) {
|
||||
const normalized = subCmd.trim();
|
||||
for (const pattern of BLOCKED_PATTERNS) {
|
||||
if (pattern.pattern.test(normalized)) {
|
||||
return {
|
||||
blocked: true,
|
||||
pattern,
|
||||
message: formatBlockedMessage(pattern),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Also check the full command in case a pattern targets the chaining itself
|
||||
const normalizedCommand = command.trim();
|
||||
for (const pattern of BLOCKED_PATTERNS) {
|
||||
if (pattern.pattern.test(normalizedCommand)) {
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user