mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-09 21:20:44 +00:00
fix(cli): sanitize plugin command descriptors
This commit is contained in:
@@ -71,4 +71,26 @@ describe("command-descriptor-utils", () => {
|
||||
"delta",
|
||||
]);
|
||||
});
|
||||
|
||||
it("strips terminal escapes from rendered descriptor descriptions", () => {
|
||||
const program = new Command();
|
||||
|
||||
addCommandDescriptorsToProgram(program, [
|
||||
{
|
||||
name: "safe-command",
|
||||
description: "Open \u001B]8;;https://example.test\u0007link\u001B]8;;\u0007 now\u001B[2J",
|
||||
},
|
||||
]);
|
||||
|
||||
expect(program.commands[0]?.description()).toBe("Open link now");
|
||||
});
|
||||
|
||||
it("rejects unsafe descriptor command names before rendering", () => {
|
||||
const program = new Command();
|
||||
|
||||
expect(() =>
|
||||
addCommandDescriptorsToProgram(program, [{ name: "bad\nname", description: "Bad" }]),
|
||||
).toThrow('Invalid CLI command name: "bad\\nname"');
|
||||
expect(program.commands).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import type { Command } from "commander";
|
||||
import { sanitizeForLog } from "../../terminal/ansi.js";
|
||||
import type { NamedCommandDescriptor } from "./command-group-descriptors.js";
|
||||
|
||||
export type CommandDescriptorLike = Pick<NamedCommandDescriptor, "name" | "description">;
|
||||
|
||||
const SAFE_COMMAND_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]*$/;
|
||||
|
||||
export type CommandDescriptorCatalog<TDescriptor extends NamedCommandDescriptor> = {
|
||||
descriptors: readonly TDescriptor[];
|
||||
getDescriptors: () => readonly TDescriptor[];
|
||||
@@ -10,6 +13,23 @@ export type CommandDescriptorCatalog<TDescriptor extends NamedCommandDescriptor>
|
||||
getCommandsWithSubcommands: () => string[];
|
||||
};
|
||||
|
||||
export function normalizeCommandDescriptorName(name: string): string | null {
|
||||
const normalized = name.trim();
|
||||
return SAFE_COMMAND_NAME_PATTERN.test(normalized) ? normalized : null;
|
||||
}
|
||||
|
||||
export function assertSafeCommandDescriptorName(name: string): string {
|
||||
const normalized = normalizeCommandDescriptorName(name);
|
||||
if (!normalized) {
|
||||
throw new Error(`Invalid CLI command name: ${JSON.stringify(name.trim())}`);
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
||||
export function sanitizeCommandDescriptorDescription(description: string): string {
|
||||
return sanitizeForLog(description).trim();
|
||||
}
|
||||
|
||||
export function getCommandDescriptorNames(descriptors: readonly CommandDescriptorLike[]): string[] {
|
||||
return descriptors.map((descriptor) => descriptor.name);
|
||||
}
|
||||
@@ -56,11 +76,12 @@ export function addCommandDescriptorsToProgram(
|
||||
existingCommands: Set<string> = new Set(),
|
||||
): Set<string> {
|
||||
for (const descriptor of descriptors) {
|
||||
if (existingCommands.has(descriptor.name)) {
|
||||
const name = assertSafeCommandDescriptorName(descriptor.name);
|
||||
if (existingCommands.has(name)) {
|
||||
continue;
|
||||
}
|
||||
program.command(descriptor.name).description(descriptor.description);
|
||||
existingCommands.add(descriptor.name);
|
||||
program.command(name).description(sanitizeCommandDescriptorDescription(descriptor.description));
|
||||
existingCommands.add(name);
|
||||
}
|
||||
return existingCommands;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user