[Init] Initial commit - NetMesh terminal manager
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
This commit is contained in:
171
electron/bridges/ai/commandSafety.cjs
Normal file
171
electron/bridges/ai/commandSafety.cjs
Normal file
@@ -0,0 +1,171 @@
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Shell-aware command blocklist helpers shared by the main-process AI exec
|
||||
* paths (in-app bridge handlers, MCP TCP bridge handlers, terminal worker).
|
||||
*
|
||||
* The default table in lib/commandBlocklist.json is grouped into
|
||||
* common / posix / powershell patterns. Callers pass the best shell kind they
|
||||
* can resolve for the target session:
|
||||
* - live session objects: resolveSessionBlocklistShellKind(session) mirrors
|
||||
* the inputs the AI PTY wrapper uses (confirmed kind, live idle prompt,
|
||||
* remote login-shell hint)
|
||||
* - metadata-only paths: meta.shellType (often empty; callers that know a
|
||||
* downstream authoritative check re-runs the defaults should fall back to
|
||||
* checkBlocklistCommonOnly instead of the strict full table, so
|
||||
* POSIX-only patterns never block PowerShell-native commands)
|
||||
*
|
||||
* User-added patterns (settings list entries that are not part of the default
|
||||
* table) always apply, on every shell.
|
||||
*/
|
||||
|
||||
const {
|
||||
DEFAULT_COMMAND_BLOCKLIST,
|
||||
COMMON_PATTERNS,
|
||||
isDefaultBlocklistPattern,
|
||||
selectDefaultBlocklistPatterns,
|
||||
} = require("../../../lib/commandBlocklist.cjs");
|
||||
const { resolveEffectiveShellKind } = require("./ptyExecHelpers.cjs");
|
||||
const {
|
||||
getFreshIdlePrompt,
|
||||
isDefaultPowerShellPromptLine,
|
||||
isDefaultCmdPromptLine,
|
||||
isDefaultPosixPromptLine,
|
||||
stripAnsi,
|
||||
} = require("./shellUtils.cjs");
|
||||
|
||||
function compilePatterns(patterns) {
|
||||
return patterns
|
||||
.map((pattern) => {
|
||||
try {
|
||||
return { pattern, regex: new RegExp(pattern, "i") };
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
const compiledDefaultCache = new Map();
|
||||
|
||||
function compiledDefaultPatternsFor(shellKind) {
|
||||
const key = String(shellKind || "").toLowerCase();
|
||||
let compiled = compiledDefaultCache.get(key);
|
||||
if (!compiled) {
|
||||
compiled = compilePatterns(selectDefaultBlocklistPatterns(key));
|
||||
compiledDefaultCache.set(key, compiled);
|
||||
}
|
||||
return compiled;
|
||||
}
|
||||
|
||||
const compiledCommonPatterns = compilePatterns(COMMON_PATTERNS);
|
||||
// User blocklists are stable between settings updates; compile each list once.
|
||||
const compiledUserCache = new WeakMap();
|
||||
|
||||
function compiledUserPatterns(blocklist) {
|
||||
let compiled = compiledUserCache.get(blocklist);
|
||||
if (!compiled) {
|
||||
const userPatterns = blocklist.filter(
|
||||
(pattern) => !isDefaultBlocklistPattern(pattern),
|
||||
);
|
||||
compiled = compilePatterns(userPatterns);
|
||||
compiledUserCache.set(blocklist, compiled);
|
||||
}
|
||||
return compiled;
|
||||
}
|
||||
|
||||
function firstMatch(command, compiled) {
|
||||
for (const { pattern, regex } of compiled) {
|
||||
if (regex.test(command)) {
|
||||
return { blocked: true, matchedPattern: pattern };
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function firstEnabledDefaultMatch(command, compiled, enabledPatterns) {
|
||||
for (const { pattern, regex } of compiled) {
|
||||
if (enabledPatterns.has(pattern) && regex.test(command)) {
|
||||
return { blocked: true, matchedPattern: pattern };
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function normalizeConfiguredBlocklist(blocklist) {
|
||||
return Array.isArray(blocklist) ? blocklist : DEFAULT_COMMAND_BLOCKLIST;
|
||||
}
|
||||
|
||||
/**
|
||||
* User additions + default patterns selected for shellKind.
|
||||
* Unknown / empty shell kinds keep the strict full default table.
|
||||
*/
|
||||
function checkBlocklistForShell(command, shellKind, configuredBlocklist = DEFAULT_COMMAND_BLOCKLIST) {
|
||||
const blocklist = normalizeConfiguredBlocklist(configuredBlocklist);
|
||||
const userMatch = firstMatch(command, compiledUserPatterns(blocklist));
|
||||
if (userMatch) return userMatch;
|
||||
return firstEnabledDefaultMatch(
|
||||
command,
|
||||
compiledDefaultPatternsFor(shellKind),
|
||||
new Set(blocklist),
|
||||
) || { blocked: false };
|
||||
}
|
||||
|
||||
/**
|
||||
* User additions + shell-independent (common) default patterns only.
|
||||
* For metadata-only call sites that know a downstream authoritative check
|
||||
* re-runs the full shell-selected defaults on the live session.
|
||||
*/
|
||||
function checkBlocklistCommonOnly(command, configuredBlocklist = DEFAULT_COMMAND_BLOCKLIST) {
|
||||
const blocklist = normalizeConfiguredBlocklist(configuredBlocklist);
|
||||
const userMatch = firstMatch(command, compiledUserPatterns(blocklist));
|
||||
if (userMatch) return userMatch;
|
||||
return firstEnabledDefaultMatch(command, compiledCommonPatterns, new Set(blocklist)) || { blocked: false };
|
||||
}
|
||||
|
||||
/**
|
||||
* Best-effort shell kind for a live session, mirroring the inputs the AI PTY
|
||||
* wrapper uses (ptyExecHelpers.resolveEffectiveShellKind): confirmed shell
|
||||
* kind, live idle prompt, and the remote login-shell probe hint.
|
||||
*/
|
||||
function resolveSessionBlocklistShellKind(session) {
|
||||
if (!session || typeof session !== "object") return "";
|
||||
let prompt = null;
|
||||
try {
|
||||
prompt = getFreshIdlePrompt(session);
|
||||
} catch {
|
||||
prompt = null;
|
||||
}
|
||||
try {
|
||||
const baseKind = session.shellKind === "unknown" ? "" : (session.shellKind || "");
|
||||
const loginShellHint = session._loginShellKind || "";
|
||||
const resolved = resolveEffectiveShellKind(baseKind, prompt, { loginShellHint }) || "";
|
||||
if (baseKind || loginShellHint) return resolved;
|
||||
|
||||
const lastPromptLine = stripAnsi(String(prompt || ""))
|
||||
.replace(/\r/g, "\n")
|
||||
.split("\n")
|
||||
.pop()
|
||||
.replace(/\s+$/, "");
|
||||
if (
|
||||
isDefaultPowerShellPromptLine(lastPromptLine)
|
||||
|| isDefaultCmdPromptLine(lastPromptLine)
|
||||
|| isDefaultPosixPromptLine(lastPromptLine)
|
||||
) {
|
||||
return resolved;
|
||||
}
|
||||
|
||||
// Wrapper selection defaults an unclassified remote session to POSIX.
|
||||
// Safety keeps it unknown so failed probes retain the strict all-groups
|
||||
// fallback instead of silently omitting PowerShell rules.
|
||||
return "";
|
||||
} catch {
|
||||
return session.shellKind || session._loginShellKind || "";
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
checkBlocklistForShell,
|
||||
checkBlocklistCommonOnly,
|
||||
resolveSessionBlocklistShellKind,
|
||||
};
|
||||
Reference in New Issue
Block a user