chore: Enable "curly" rule to avoid single-statement if confusion/errors.

This commit is contained in:
cpojer
2026-01-31 16:19:20 +09:00
parent 009b16fab8
commit 5ceff756e1
1266 changed files with 27871 additions and 9393 deletions

View File

@@ -73,9 +73,15 @@ function hashExecApprovalsRaw(raw: string | null): string {
}
function expandHome(value: string): string {
if (!value) return value;
if (value === "~") return os.homedir();
if (value.startsWith("~/")) return path.join(os.homedir(), value.slice(2));
if (!value) {
return value;
}
if (value === "~") {
return os.homedir();
}
if (value.startsWith("~/")) {
return path.join(os.homedir(), value.slice(2));
}
return value;
}
@@ -100,12 +106,18 @@ function mergeLegacyAgent(
const seen = new Set<string>();
const pushEntry = (entry: ExecAllowlistEntry) => {
const key = normalizeAllowlistPattern(entry.pattern);
if (!key || seen.has(key)) return;
if (!key || seen.has(key)) {
return;
}
seen.add(key);
allowlist.push(entry);
};
for (const entry of current.allowlist ?? []) pushEntry(entry);
for (const entry of legacy.allowlist ?? []) pushEntry(entry);
for (const entry of current.allowlist ?? []) {
pushEntry(entry);
}
for (const entry of legacy.allowlist ?? []) {
pushEntry(entry);
}
return {
security: current.security ?? legacy.security,
@@ -124,10 +136,14 @@ function ensureDir(filePath: string) {
function ensureAllowlistIds(
allowlist: ExecAllowlistEntry[] | undefined,
): ExecAllowlistEntry[] | undefined {
if (!Array.isArray(allowlist) || allowlist.length === 0) return allowlist;
if (!Array.isArray(allowlist) || allowlist.length === 0) {
return allowlist;
}
let changed = false;
const next = allowlist.map((entry) => {
if (entry.id) return entry;
if (entry.id) {
return entry;
}
changed = true;
return { ...entry, id: crypto.randomUUID() };
});
@@ -248,12 +264,16 @@ export function ensureExecApprovals(): ExecApprovalsFile {
}
function normalizeSecurity(value: ExecSecurity | undefined, fallback: ExecSecurity): ExecSecurity {
if (value === "allowlist" || value === "full" || value === "deny") return value;
if (value === "allowlist" || value === "full" || value === "deny") {
return value;
}
return fallback;
}
function normalizeAsk(value: ExecAsk | undefined, fallback: ExecAsk): ExecAsk {
if (value === "always" || value === "off" || value === "on-miss") return value;
if (value === "always" || value === "off" || value === "on-miss") {
return value;
}
return fallback;
}
@@ -345,7 +365,9 @@ type CommandResolution = {
function isExecutableFile(filePath: string): boolean {
try {
const stat = fs.statSync(filePath);
if (!stat.isFile()) return false;
if (!stat.isFile()) {
return false;
}
if (process.platform !== "win32") {
fs.accessSync(filePath, fs.constants.X_OK);
}
@@ -357,11 +379,15 @@ function isExecutableFile(filePath: string): boolean {
function parseFirstToken(command: string): string | null {
const trimmed = command.trim();
if (!trimmed) return null;
if (!trimmed) {
return null;
}
const first = trimmed[0];
if (first === '"' || first === "'") {
const end = trimmed.indexOf(first, 1);
if (end > 1) return trimmed.slice(1, end);
if (end > 1) {
return trimmed.slice(1, end);
}
return trimmed.slice(1);
}
const match = /^[^\s]+/.exec(trimmed);
@@ -398,7 +424,9 @@ function resolveExecutablePath(rawExecutable: string, cwd?: string, env?: NodeJS
for (const entry of entries) {
for (const ext of extensions) {
const candidate = path.join(entry, expanded + ext);
if (isExecutableFile(candidate)) return candidate;
if (isExecutableFile(candidate)) {
return candidate;
}
}
}
return undefined;
@@ -410,7 +438,9 @@ export function resolveCommandResolution(
env?: NodeJS.ProcessEnv,
): CommandResolution | null {
const rawExecutable = parseFirstToken(command);
if (!rawExecutable) return null;
if (!rawExecutable) {
return null;
}
const resolvedPath = resolveExecutablePath(rawExecutable, cwd, env);
const executableName = resolvedPath ? path.basename(resolvedPath) : rawExecutable;
return { rawExecutable, resolvedPath, executableName };
@@ -422,7 +452,9 @@ export function resolveCommandResolutionFromArgv(
env?: NodeJS.ProcessEnv,
): CommandResolution | null {
const rawExecutable = argv[0]?.trim();
if (!rawExecutable) return null;
if (!rawExecutable) {
return null;
}
const resolvedPath = resolveExecutablePath(rawExecutable, cwd, env);
const executableName = resolvedPath ? path.basename(resolvedPath) : rawExecutable;
return { rawExecutable, resolvedPath, executableName };
@@ -474,7 +506,9 @@ function globToRegExp(pattern: string): RegExp {
function matchesPattern(pattern: string, target: string): boolean {
const trimmed = pattern.trim();
if (!trimmed) return false;
if (!trimmed) {
return false;
}
const expanded = trimmed.startsWith("~") ? expandHome(trimmed) : trimmed;
const hasWildcard = /[*?]/.test(expanded);
let normalizedPattern = expanded;
@@ -493,13 +527,23 @@ function resolveAllowlistCandidatePath(
resolution: CommandResolution | null,
cwd?: string,
): string | undefined {
if (!resolution) return undefined;
if (resolution.resolvedPath) return resolution.resolvedPath;
if (!resolution) {
return undefined;
}
if (resolution.resolvedPath) {
return resolution.resolvedPath;
}
const raw = resolution.rawExecutable?.trim();
if (!raw) return undefined;
if (!raw) {
return undefined;
}
const expanded = raw.startsWith("~") ? expandHome(raw) : raw;
if (!expanded.includes("/") && !expanded.includes("\\")) return undefined;
if (path.isAbsolute(expanded)) return expanded;
if (!expanded.includes("/") && !expanded.includes("\\")) {
return undefined;
}
if (path.isAbsolute(expanded)) {
return expanded;
}
const base = cwd && cwd.trim() ? cwd.trim() : process.cwd();
return path.resolve(base, expanded);
}
@@ -508,14 +552,22 @@ export function matchAllowlist(
entries: ExecAllowlistEntry[],
resolution: CommandResolution | null,
): ExecAllowlistEntry | null {
if (!entries.length || !resolution?.resolvedPath) return null;
if (!entries.length || !resolution?.resolvedPath) {
return null;
}
const resolvedPath = resolution.resolvedPath;
for (const entry of entries) {
const pattern = entry.pattern?.trim();
if (!pattern) continue;
if (!pattern) {
continue;
}
const hasPath = pattern.includes("/") || pattern.includes("\\") || pattern.includes("~");
if (!hasPath) continue;
if (matchesPattern(pattern, resolvedPath)) return entry;
if (!hasPath) {
continue;
}
if (matchesPattern(pattern, resolvedPath)) {
return entry;
}
}
return null;
}
@@ -579,12 +631,16 @@ function iterateQuoteAware(
continue;
}
if (inSingle) {
if (ch === "'") inSingle = false;
if (ch === "'") {
inSingle = false;
}
buf += ch;
continue;
}
if (inDouble) {
if (ch === '"') inDouble = false;
if (ch === '"') {
inDouble = false;
}
buf += ch;
continue;
}
@@ -805,10 +861,18 @@ export function analyzeArgvCommand(params: {
function isPathLikeToken(value: string): boolean {
const trimmed = value.trim();
if (!trimmed) return false;
if (trimmed === "-") return false;
if (trimmed.startsWith("./") || trimmed.startsWith("../") || trimmed.startsWith("~")) return true;
if (trimmed.startsWith("/")) return true;
if (!trimmed) {
return false;
}
if (trimmed === "-") {
return false;
}
if (trimmed.startsWith("./") || trimmed.startsWith("../") || trimmed.startsWith("~")) {
return true;
}
if (trimmed.startsWith("/")) {
return true;
}
return /^[A-Za-z]:[\\/]/.test(trimmed);
}
@@ -821,7 +885,9 @@ function defaultFileExists(filePath: string): boolean {
}
export function normalizeSafeBins(entries?: string[]): Set<string> {
if (!Array.isArray(entries)) return new Set();
if (!Array.isArray(entries)) {
return new Set();
}
const normalized = entries
.map((entry) => entry.trim().toLowerCase())
.filter((entry) => entry.length > 0);
@@ -829,7 +895,9 @@ export function normalizeSafeBins(entries?: string[]): Set<string> {
}
export function resolveSafeBins(entries?: string[] | null): Set<string> {
if (entries === undefined) return normalizeSafeBins(DEFAULT_SAFE_BINS);
if (entries === undefined) {
return normalizeSafeBins(DEFAULT_SAFE_BINS);
}
return normalizeSafeBins(entries ?? []);
}
@@ -840,22 +908,34 @@ export function isSafeBinUsage(params: {
cwd?: string;
fileExists?: (filePath: string) => boolean;
}): boolean {
if (params.safeBins.size === 0) return false;
if (params.safeBins.size === 0) {
return false;
}
const resolution = params.resolution;
const execName = resolution?.executableName?.toLowerCase();
if (!execName) return false;
if (!execName) {
return false;
}
const matchesSafeBin =
params.safeBins.has(execName) ||
(process.platform === "win32" && params.safeBins.has(path.parse(execName).name));
if (!matchesSafeBin) return false;
if (!resolution?.resolvedPath) return false;
if (!matchesSafeBin) {
return false;
}
if (!resolution?.resolvedPath) {
return false;
}
const cwd = params.cwd ?? process.cwd();
const exists = params.fileExists ?? defaultFileExists;
const argv = params.argv.slice(1);
for (let i = 0; i < argv.length; i += 1) {
const token = argv[i];
if (!token) continue;
if (token === "-") continue;
if (!token) {
continue;
}
if (token === "-") {
continue;
}
if (token.startsWith("-")) {
const eqIndex = token.indexOf("=");
if (eqIndex > 0) {
@@ -866,8 +946,12 @@ export function isSafeBinUsage(params: {
}
continue;
}
if (isPathLikeToken(token)) return false;
if (exists(path.resolve(cwd, token))) return false;
if (isPathLikeToken(token)) {
return false;
}
if (exists(path.resolve(cwd, token))) {
return false;
}
}
return true;
}
@@ -897,7 +981,9 @@ function evaluateSegments(
? { ...segment.resolution, resolvedPath: candidatePath }
: segment.resolution;
const match = matchAllowlist(params.allowlist, candidateResolution);
if (match) matches.push(match);
if (match) {
matches.push(match);
}
const safe = isSafeBinUsage({
argv: segment.argv,
resolution: segment.resolution,
@@ -993,12 +1079,16 @@ function splitCommandChain(command: string): string[] | null {
continue;
}
if (inSingle) {
if (ch === "'") inSingle = false;
if (ch === "'") {
inSingle = false;
}
buf += ch;
continue;
}
if (inDouble) {
if (ch === '"') inDouble = false;
if (ch === '"') {
inDouble = false;
}
buf += ch;
continue;
}
@@ -1014,19 +1104,25 @@ function splitCommandChain(command: string): string[] | null {
}
if (ch === "&" && command[i + 1] === "&") {
if (!pushPart()) invalidChain = true;
if (!pushPart()) {
invalidChain = true;
}
i += 1;
foundChain = true;
continue;
}
if (ch === "|" && command[i + 1] === "|") {
if (!pushPart()) invalidChain = true;
if (!pushPart()) {
invalidChain = true;
}
i += 1;
foundChain = true;
continue;
}
if (ch === ";") {
if (!pushPart()) invalidChain = true;
if (!pushPart()) {
invalidChain = true;
}
foundChain = true;
continue;
}
@@ -1035,8 +1131,12 @@ function splitCommandChain(command: string): string[] | null {
}
const pushedFinal = pushPart();
if (!foundChain) return null;
if (invalidChain || !pushedFinal) return null;
if (!foundChain) {
return null;
}
if (invalidChain || !pushedFinal) {
return null;
}
return parts.length > 0 ? parts : null;
}
@@ -1187,8 +1287,12 @@ export function addAllowlistEntry(
const existing = agents[target] ?? {};
const allowlist = Array.isArray(existing.allowlist) ? existing.allowlist : [];
const trimmed = pattern.trim();
if (!trimmed) return;
if (allowlist.some((entry) => entry.pattern === trimmed)) return;
if (!trimmed) {
return;
}
if (allowlist.some((entry) => entry.pattern === trimmed)) {
return;
}
allowlist.push({ id: crypto.randomUUID(), pattern: trimmed, lastUsedAt: Date.now() });
agents[target] = { ...existing, allowlist };
approvals.agents = agents;
@@ -1214,14 +1318,18 @@ export async function requestExecApprovalViaSocket(params: {
timeoutMs?: number;
}): Promise<ExecApprovalDecision | null> {
const { socketPath, token, request } = params;
if (!socketPath || !token) return null;
if (!socketPath || !token) {
return null;
}
const timeoutMs = params.timeoutMs ?? 15_000;
return await new Promise((resolve) => {
const client = new net.Socket();
let settled = false;
let buffer = "";
const finish = (value: ExecApprovalDecision | null) => {
if (settled) return;
if (settled) {
return;
}
settled = true;
try {
client.destroy();
@@ -1250,7 +1358,9 @@ export async function requestExecApprovalViaSocket(params: {
const line = buffer.slice(0, idx).trim();
buffer = buffer.slice(idx + 1);
idx = buffer.indexOf("\n");
if (!line) continue;
if (!line) {
continue;
}
try {
const msg = JSON.parse(line) as { type?: string; decision?: ExecApprovalDecision };
if (msg?.type === "decision" && msg.decision) {