Compare commits
2 Commits
main
...
codex/outb
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c51af21281 | ||
|
|
92a1ba88bd |
@ -143,6 +143,24 @@ describe("resolveMessageChannelSelection", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("skips configured-channel scanning when includeConfigured is false", async () => {
|
||||
const isConfigured = vi.fn(async () => true);
|
||||
mocks.listChannelPlugins.mockReturnValue([makePlugin({ id: "whatsapp", isConfigured })]);
|
||||
|
||||
const selection = await resolveMessageChannelSelection({
|
||||
cfg: {} as never,
|
||||
channel: "telegram",
|
||||
includeConfigured: false,
|
||||
});
|
||||
|
||||
expect(selection).toEqual({
|
||||
channel: "telegram",
|
||||
configured: [],
|
||||
source: "explicit",
|
||||
});
|
||||
expect(isConfigured).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("falls back to tool context channel when explicit channel is unknown", async () => {
|
||||
const selection = await resolveMessageChannelSelection({
|
||||
cfg: {} as never,
|
||||
|
||||
@ -146,11 +146,15 @@ export async function resolveMessageChannelSelection(params: {
|
||||
cfg: OpenClawConfig;
|
||||
channel?: string | null;
|
||||
fallbackChannel?: string | null;
|
||||
includeConfigured?: boolean;
|
||||
}): Promise<{
|
||||
channel: MessageChannelId;
|
||||
configured: MessageChannelId[];
|
||||
source: MessageChannelSelectionSource;
|
||||
}> {
|
||||
const includeConfigured = params.includeConfigured !== false;
|
||||
const resolveConfigured = async () =>
|
||||
includeConfigured ? await listConfiguredMessageChannels(params.cfg) : [];
|
||||
const normalized = normalizeMessageChannel(params.channel);
|
||||
if (normalized) {
|
||||
const availableExplicit = resolveAvailableKnownChannel({
|
||||
@ -165,7 +169,7 @@ export async function resolveMessageChannelSelection(params: {
|
||||
if (fallback) {
|
||||
return {
|
||||
channel: fallback,
|
||||
configured: await listConfiguredMessageChannels(params.cfg),
|
||||
configured: await resolveConfigured(),
|
||||
source: "tool-context-fallback",
|
||||
};
|
||||
}
|
||||
@ -176,7 +180,7 @@ export async function resolveMessageChannelSelection(params: {
|
||||
}
|
||||
return {
|
||||
channel: availableExplicit,
|
||||
configured: await listConfiguredMessageChannels(params.cfg),
|
||||
configured: await resolveConfigured(),
|
||||
source: "explicit",
|
||||
};
|
||||
}
|
||||
@ -188,12 +192,12 @@ export async function resolveMessageChannelSelection(params: {
|
||||
if (fallback) {
|
||||
return {
|
||||
channel: fallback,
|
||||
configured: await listConfiguredMessageChannels(params.cfg),
|
||||
configured: await resolveConfigured(),
|
||||
source: "tool-context-fallback",
|
||||
};
|
||||
}
|
||||
|
||||
const configured = await listConfiguredMessageChannels(params.cfg);
|
||||
const configured = await resolveConfigured();
|
||||
if (configured.length === 1) {
|
||||
return { channel: configured[0], configured, source: "single-configured" };
|
||||
}
|
||||
|
||||
@ -226,6 +226,7 @@ async function resolveChannel(
|
||||
cfg,
|
||||
channel: readStringParam(params, "channel"),
|
||||
fallbackChannel: toolContext?.currentChannelProvider,
|
||||
includeConfigured: false,
|
||||
});
|
||||
if (selection.source === "tool-context-fallback") {
|
||||
params.channel = selection.channel;
|
||||
@ -318,14 +319,13 @@ async function handleBroadcastAction(
|
||||
throw new Error("Broadcast requires at least one target in --targets.");
|
||||
}
|
||||
const channelHint = readStringParam(params, "channel");
|
||||
const configured = await listConfiguredMessageChannels(input.cfg);
|
||||
if (configured.length === 0) {
|
||||
throw new Error("Broadcast requires at least one configured channel.");
|
||||
}
|
||||
const targetChannels =
|
||||
channelHint && channelHint.trim().toLowerCase() !== "all"
|
||||
? [await resolveChannel(input.cfg, { channel: channelHint }, input.toolContext)]
|
||||
: configured;
|
||||
: await listConfiguredMessageChannels(input.cfg);
|
||||
if (targetChannels.length === 0) {
|
||||
throw new Error("Broadcast requires at least one configured channel.");
|
||||
}
|
||||
const results: Array<{
|
||||
channel: ChannelId;
|
||||
to: string;
|
||||
|
||||
@ -136,6 +136,7 @@ async function resolveRequiredChannel(params: {
|
||||
await resolveMessageChannelSelection({
|
||||
cfg: params.cfg,
|
||||
channel: params.channel,
|
||||
includeConfigured: false,
|
||||
})
|
||||
).channel;
|
||||
}
|
||||
|
||||
29
src/security/scan-paths.test.ts
Normal file
29
src/security/scan-paths.test.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const originalPlatform = process.platform;
|
||||
|
||||
function setPlatform(value: NodeJS.Platform): void {
|
||||
Object.defineProperty(process, "platform", {
|
||||
configurable: true,
|
||||
value,
|
||||
});
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
setPlatform(originalPlatform);
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
describe("security scan path guards", () => {
|
||||
it("uses Windows-aware containment checks for differently normalized paths", async () => {
|
||||
setPlatform("win32");
|
||||
const { isPathInside } = await import("./scan-paths.js");
|
||||
|
||||
expect(
|
||||
isPathInside(String.raw`C:\Workspace\Root`, String.raw`c:\workspace\root\hooks\hook`),
|
||||
).toBe(true);
|
||||
expect(
|
||||
isPathInside(String.raw`\\?\C:\Workspace\Root`, String.raw`C:\workspace\root\hooks\hook`),
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
@ -1,11 +1,8 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { isPathInside as isBoundaryPathInside } from "../infra/path-guards.js";
|
||||
|
||||
export function isPathInside(basePath: string, candidatePath: string): boolean {
|
||||
const base = path.resolve(basePath);
|
||||
const candidate = path.resolve(candidatePath);
|
||||
const rel = path.relative(base, candidate);
|
||||
return rel === "" || (!rel.startsWith(`..${path.sep}`) && rel !== ".." && !path.isAbsolute(rel));
|
||||
return isBoundaryPathInside(basePath, candidatePath);
|
||||
}
|
||||
|
||||
function safeRealpathSync(filePath: string): string | null {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user