mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-17 20:21:13 +00:00
refactor: dedupe extension lowercase helpers
This commit is contained in:
@@ -460,7 +460,7 @@ export function registerAnthropicPlugin(api: OpenClawPluginApi): void {
|
||||
applyConfigDefaults: ({ config, env }) => applyAnthropicConfigDefaults({ config, env }),
|
||||
resolveDynamicModel: (ctx) => resolveAnthropicForwardCompatModel(ctx),
|
||||
resolveSyntheticAuth: ({ provider }) =>
|
||||
provider.trim().toLowerCase() === CLAUDE_CLI_BACKEND_ID
|
||||
normalizeLowercaseStringOrEmpty(provider) === CLAUDE_CLI_BACKEND_ID
|
||||
? resolveClaudeCliSyntheticAuth()
|
||||
: undefined,
|
||||
buildReplayPolicy: buildAnthropicReplayPolicy,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
|
||||
import { normalizeString } from "../record-shared.js";
|
||||
import type { SnapshotAriaNode } from "./client.js";
|
||||
import {
|
||||
@@ -17,7 +18,7 @@ export type ChromeMcpSnapshotNode = {
|
||||
};
|
||||
|
||||
function normalizeRole(node: ChromeMcpSnapshotNode): string {
|
||||
const role = typeof node.role === "string" ? node.role.trim().toLowerCase() : "";
|
||||
const role = normalizeLowercaseStringOrEmpty(node.role);
|
||||
return role || "generic";
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
} from "openclaw/plugin-sdk/ssrf-runtime";
|
||||
import {
|
||||
isRecord,
|
||||
normalizeOptionalLowercaseString,
|
||||
normalizeOptionalString,
|
||||
resolveUserPath,
|
||||
} from "openclaw/plugin-sdk/text-runtime";
|
||||
@@ -263,7 +264,7 @@ async function readJsonResponse<T>(params: {
|
||||
}
|
||||
|
||||
function inferFileExtension(params: { fileName?: string; mimeType?: string }): string {
|
||||
const normalizedMime = params.mimeType?.toLowerCase().trim();
|
||||
const normalizedMime = normalizeOptionalLowercaseString(params.mimeType);
|
||||
if (normalizedMime?.includes("jpeg")) {
|
||||
return "jpg";
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { IncomingMessage, ServerResponse } from "node:http";
|
||||
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
|
||||
import type { PluginLogger } from "../api.js";
|
||||
import { resolveRequestClientIp } from "../runtime-api.js";
|
||||
import type { DiffArtifactStore } from "./store.js";
|
||||
@@ -170,7 +171,7 @@ function setSharedHeaders(res: ServerResponse, contentType: string): void {
|
||||
}
|
||||
|
||||
function normalizeRemoteClientKey(remoteAddress: string | undefined): string {
|
||||
const normalized = remoteAddress?.trim().toLowerCase();
|
||||
const normalized = normalizeLowercaseStringOrEmpty(remoteAddress);
|
||||
if (!normalized) {
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
*/
|
||||
|
||||
import type * as Lark from "@larksuiteoapi/node-sdk";
|
||||
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
|
||||
|
||||
// Feishu text_color values (1-7)
|
||||
const TEXT_COLOR: Record<string, number> = {
|
||||
@@ -86,7 +87,7 @@ export function parseColorMarkup(content: string): Segment[] {
|
||||
}
|
||||
} else {
|
||||
// Tagged segment
|
||||
const tagStr = match[1].toLowerCase().trim();
|
||||
const tagStr = normalizeLowercaseStringOrEmpty(match[1]);
|
||||
const text = match[2];
|
||||
const tags = tagStr.split(/\s+/);
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { type ChildProcessWithoutNullStreams, spawn } from "node:child_process";
|
||||
import { createInterface, type Interface } from "node:readline";
|
||||
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
||||
import type { RuntimeEnv } from "openclaw/plugin-sdk/runtime-env";
|
||||
import { resolveUserPath } from "openclaw/plugin-sdk/text-runtime";
|
||||
import { normalizeLowercaseStringOrEmpty, resolveUserPath } from "openclaw/plugin-sdk/text-runtime";
|
||||
import { DEFAULT_IMESSAGE_PROBE_TIMEOUT_MS } from "./constants.js";
|
||||
|
||||
export type IMessageRpcError = {
|
||||
@@ -42,7 +42,7 @@ function isTestEnv(): boolean {
|
||||
if (process.env.NODE_ENV === "test") {
|
||||
return true;
|
||||
}
|
||||
const vitest = process.env.VITEST?.trim().toLowerCase();
|
||||
const vitest = normalizeLowercaseStringOrEmpty(process.env.VITEST);
|
||||
return Boolean(vitest);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { resolveMemoryRemDreamingConfig } from "openclaw/plugin-sdk/memory-core-host-status";
|
||||
import { buildAgentSessionKey } from "openclaw/plugin-sdk/routing";
|
||||
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
|
||||
import {
|
||||
colorize,
|
||||
defaultRuntime,
|
||||
@@ -215,13 +216,13 @@ function matchesPromotionSelector(
|
||||
},
|
||||
selector: string,
|
||||
): boolean {
|
||||
const trimmed = selector.trim().toLowerCase();
|
||||
const trimmed = normalizeLowercaseStringOrEmpty(selector);
|
||||
if (!trimmed) {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
candidate.key.toLowerCase() === trimmed ||
|
||||
candidate.key.toLowerCase().includes(trimmed) ||
|
||||
normalizeLowercaseStringOrEmpty(candidate.key) === trimmed ||
|
||||
normalizeLowercaseStringOrEmpty(candidate.key).includes(trimmed) ||
|
||||
candidate.path.toLowerCase().includes(trimmed) ||
|
||||
candidate.snippet.toLowerCase().includes(trimmed)
|
||||
);
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
type MemoryLightDreamingConfig,
|
||||
type MemoryRemDreamingConfig,
|
||||
} from "openclaw/plugin-sdk/memory-core-host-status";
|
||||
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
|
||||
import { writeDailyDreamingPhaseBlock } from "./dreaming-markdown.js";
|
||||
import { generateAndAppendDreamNarrative, type NarrativePhaseData } from "./dreaming-narrative.js";
|
||||
import {
|
||||
@@ -1114,7 +1115,7 @@ function jaccardSimilarity(left: string, right: string): number {
|
||||
const leftTokens = tokenizeSnippet(left);
|
||||
const rightTokens = tokenizeSnippet(right);
|
||||
if (leftTokens.size === 0 || rightTokens.size === 0) {
|
||||
return left.trim().toLowerCase() === right.trim().toLowerCase() ? 1 : 0;
|
||||
return normalizeLowercaseStringOrEmpty(left) === normalizeLowercaseStringOrEmpty(right) ? 1 : 0;
|
||||
}
|
||||
let intersection = 0;
|
||||
for (const token of leftTokens) {
|
||||
|
||||
@@ -37,6 +37,7 @@ import {
|
||||
type MemorySource,
|
||||
type MemorySyncProgressUpdate,
|
||||
} from "openclaw/plugin-sdk/memory-core-host-engine-storage";
|
||||
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
|
||||
import {
|
||||
createEmbeddingProvider,
|
||||
type EmbeddingProvider,
|
||||
@@ -91,7 +92,9 @@ const log = createSubsystemLogger("memory");
|
||||
|
||||
function shouldIgnoreMemoryWatchPath(watchPath: string): boolean {
|
||||
const normalized = path.normalize(watchPath);
|
||||
const parts = normalized.split(path.sep).map((segment) => segment.trim().toLowerCase());
|
||||
const parts = normalized
|
||||
.split(path.sep)
|
||||
.map((segment) => normalizeLowercaseStringOrEmpty(segment));
|
||||
return parts.some((segment) => IGNORED_MEMORY_WATCH_DIR_NAMES.has(segment));
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ import {
|
||||
type ResolvedQmdConfig,
|
||||
type ResolvedQmdMcporterConfig,
|
||||
} from "openclaw/plugin-sdk/memory-core-host-engine-storage";
|
||||
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
|
||||
import { asRecord } from "../dreaming-shared.js";
|
||||
import { resolveQmdCollectionPatternFlags, type QmdCollectionPatternFlag } from "./qmd-compat.js";
|
||||
|
||||
@@ -140,7 +141,9 @@ function resolveQmdEmbedLockOptions(embedTimeoutMs: number) {
|
||||
|
||||
function shouldIgnoreMemoryWatchPath(watchPath: string): boolean {
|
||||
const normalized = path.normalize(watchPath);
|
||||
const parts = normalized.split(path.sep).map((segment) => segment.trim().toLowerCase());
|
||||
const parts = normalized
|
||||
.split(path.sep)
|
||||
.map((segment) => normalizeLowercaseStringOrEmpty(segment));
|
||||
return parts.some((segment) => IGNORED_MEMORY_WATCH_DIR_NAMES.has(segment));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import * as ssrf from "openclaw/plugin-sdk/ssrf-runtime";
|
||||
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
|
||||
import { vi } from "vitest";
|
||||
|
||||
export function mockPublicPinnedHostname() {
|
||||
return vi.spyOn(ssrf, "resolvePinnedHostnameWithPolicy").mockImplementation(async (hostname) => {
|
||||
const normalized = hostname.trim().toLowerCase().replace(/\.$/, "");
|
||||
const normalized = normalizeLowercaseStringOrEmpty(hostname).replace(/\.$/, "");
|
||||
const addresses = ["93.184.216.34"];
|
||||
const lookup = ((host: string, options?: unknown, callback?: unknown) => {
|
||||
const cb =
|
||||
@@ -13,7 +14,7 @@ export function mockPublicPinnedHostname() {
|
||||
if (!cb) {
|
||||
return;
|
||||
}
|
||||
if (host.trim().toLowerCase().replace(/\.$/, "") !== normalized) {
|
||||
if (normalizeLowercaseStringOrEmpty(host).replace(/\.$/, "") !== normalized) {
|
||||
cb(null, []);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import path from "node:path";
|
||||
import type { MemorySearchResult } from "openclaw/plugin-sdk/memory-core-host-runtime-files";
|
||||
import { formatMemoryDreamingDay } from "openclaw/plugin-sdk/memory-core-host-status";
|
||||
import { appendMemoryHostEvent } from "openclaw/plugin-sdk/memory-host-events";
|
||||
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
|
||||
import {
|
||||
deriveConceptTags,
|
||||
MAX_CONCEPT_TAGS,
|
||||
@@ -241,7 +242,10 @@ function buildEntryKey(result: {
|
||||
}
|
||||
|
||||
function hashQuery(query: string): string {
|
||||
return createHash("sha1").update(query.trim().toLowerCase()).digest("hex").slice(0, 12);
|
||||
return createHash("sha1")
|
||||
.update(normalizeLowercaseStringOrEmpty(query))
|
||||
.digest("hex")
|
||||
.slice(0, 12);
|
||||
}
|
||||
|
||||
function mergeQueryHashes(existing: string[], queryHash: string): string[] {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
|
||||
import type { WikiClaim, WikiPageSummary } from "./markdown.js";
|
||||
|
||||
const DAY_MS = 24 * 60 * 60 * 1000;
|
||||
@@ -120,7 +121,7 @@ function resolveLatestTimestamp(candidates: Array<string | undefined>): string |
|
||||
}
|
||||
|
||||
export function normalizeClaimStatus(status?: string): string {
|
||||
return status?.trim().toLowerCase() || "supported";
|
||||
return normalizeLowercaseStringOrEmpty(status) || "supported";
|
||||
}
|
||||
|
||||
export function isClaimContestedStatus(status?: string): boolean {
|
||||
|
||||
@@ -308,7 +308,7 @@ export function createSynologyChatPlugin(): SynologyChatPlugin {
|
||||
text: {
|
||||
idLabel: "synologyChatUserId",
|
||||
message: "OpenClaw: your access has been approved.",
|
||||
normalizeAllowEntry: (entry: string) => entry.toLowerCase().trim(),
|
||||
normalizeAllowEntry: (entry: string) => normalizeLowercaseStringOrEmpty(entry),
|
||||
notify: async ({ cfg, id, message }) => {
|
||||
const account = resolveAccount(cfg);
|
||||
if (!account.incomingUrl) {
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
type ReplyPayload,
|
||||
} from "openclaw/plugin-sdk/reply-runtime";
|
||||
import type { MockFn } from "openclaw/plugin-sdk/testing";
|
||||
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
|
||||
import { beforeEach, vi } from "vitest";
|
||||
import type { TelegramBotDeps } from "./bot-deps.js";
|
||||
|
||||
@@ -208,7 +209,7 @@ function createModelsProviderDataFromConfig(cfg: OpenClawConfig): {
|
||||
} {
|
||||
const byProvider = new Map<string, Set<string>>();
|
||||
const add = (providerRaw: string | undefined, modelRaw: string | undefined) => {
|
||||
const provider = providerRaw?.trim().toLowerCase();
|
||||
const provider = normalizeLowercaseStringOrEmpty(providerRaw);
|
||||
const model = modelRaw?.trim();
|
||||
if (!provider || !model) {
|
||||
return;
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
formatErrorMessage,
|
||||
readErrorName,
|
||||
} from "openclaw/plugin-sdk/error-runtime";
|
||||
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
|
||||
|
||||
const TELEGRAM_NETWORK_ORIGIN = Symbol("openclaw.telegram.network-origin");
|
||||
|
||||
@@ -254,7 +255,7 @@ export function isRecoverableTelegramNetworkError(
|
||||
return true;
|
||||
}
|
||||
|
||||
const message = formatErrorMessage(candidate).trim().toLowerCase();
|
||||
const message = normalizeLowercaseStringOrEmpty(formatErrorMessage(candidate));
|
||||
if (message && ALWAYS_RECOVERABLE_MESSAGES.has(message)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -230,7 +230,7 @@ export type AdminCommand =
|
||||
* - "pending" - list all pending approvals
|
||||
*/
|
||||
export function parseAdminCommand(text: string): AdminCommand | null {
|
||||
const trimmed = text.trim().toLowerCase();
|
||||
const trimmed = normalizeLowercaseStringOrEmpty(text);
|
||||
|
||||
// "blocked" - list blocked ships
|
||||
if (trimmed === "blocked") {
|
||||
|
||||
@@ -4,6 +4,7 @@ import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { resetInboundDedupe } from "openclaw/plugin-sdk/reply-runtime";
|
||||
import { resetLogger, setLoggerOverride } from "openclaw/plugin-sdk/runtime-env";
|
||||
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
|
||||
import { afterAll, afterEach, beforeAll, beforeEach, vi, type Mock } from "vitest";
|
||||
import type { WebInboundMessage, WebListenerCloseReason } from "./inbound.js";
|
||||
import {
|
||||
@@ -152,7 +153,7 @@ export function installWebAutoReplyUnitTestHooks(opts?: { pinDns?: boolean }) {
|
||||
.spyOn(ssrf, "resolvePinnedHostname")
|
||||
.mockImplementation(async (hostname) => {
|
||||
// SSRF guard pins DNS; stub resolution to avoid live lookups in unit tests.
|
||||
const normalized = hostname.trim().toLowerCase().replace(/\.$/, "");
|
||||
const normalized = normalizeLowercaseStringOrEmpty(hostname).replace(/\.$/, "");
|
||||
const addresses = [TEST_NET_IP];
|
||||
return {
|
||||
hostname: normalized,
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
|
||||
|
||||
export function isStatusCommand(body: string) {
|
||||
const trimmed = body.trim().toLowerCase();
|
||||
const trimmed = normalizeLowercaseStringOrEmpty(body);
|
||||
if (!trimmed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import fsSync from "node:fs";
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
|
||||
import { vi } from "vitest";
|
||||
import type { MockBaileysSocket } from "../../../test/mocks/baileys.js";
|
||||
import { createMockBaileys } from "../../../test/mocks/baileys.js";
|
||||
@@ -105,7 +106,7 @@ function resolveChannelContextVisibilityModeMock(params: {
|
||||
|
||||
function resolveGroupSessionKeyMock(ctx: { From?: string; ChatType?: string; Provider?: string }) {
|
||||
const from = ctx.From?.trim() ?? "";
|
||||
const chatType = ctx.ChatType?.trim().toLowerCase();
|
||||
const chatType = normalizeLowercaseStringOrEmpty(ctx.ChatType);
|
||||
if (!from) {
|
||||
return null;
|
||||
}
|
||||
@@ -119,7 +120,7 @@ function resolveGroupSessionKeyMock(ctx: { From?: string; ChatType?: string; Pro
|
||||
}
|
||||
return {
|
||||
key: `whatsapp:group:${from.toLowerCase()}`,
|
||||
channel: ctx.Provider?.trim().toLowerCase() || "whatsapp",
|
||||
channel: normalizeLowercaseStringOrEmpty(ctx.Provider) || "whatsapp",
|
||||
id: from.toLowerCase(),
|
||||
chatType: chatType === "channel" ? "channel" : "group",
|
||||
};
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
} from "openclaw/plugin-sdk/provider-model-shared";
|
||||
import { buildProviderStreamFamilyHooks } from "openclaw/plugin-sdk/provider-stream-family";
|
||||
import { fetchZaiUsage, resolveLegacyPiAgentAccessToken } from "openclaw/plugin-sdk/provider-usage";
|
||||
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
|
||||
import { detectZaiEndpoint, type ZaiEndpointId } from "./detect.js";
|
||||
import { zaiMediaUnderstandingProvider } from "./media-understanding-provider.js";
|
||||
import { buildZaiModelDefinition } from "./model-definitions.js";
|
||||
@@ -292,7 +293,7 @@ export default definePluginEntry({
|
||||
...ZAI_TOOL_STREAM_HOOKS,
|
||||
isBinaryThinking: () => true,
|
||||
isModernModelRef: ({ modelId }) => {
|
||||
const lower = modelId.trim().toLowerCase();
|
||||
const lower = normalizeLowercaseStringOrEmpty(modelId);
|
||||
return (
|
||||
lower.startsWith("glm-5") ||
|
||||
lower.startsWith("glm-4.7") ||
|
||||
|
||||
@@ -2,15 +2,10 @@ import {
|
||||
buildChannelOutboundSessionRoute,
|
||||
type ChannelOutboundSessionRouteParams,
|
||||
} from "openclaw/plugin-sdk/core";
|
||||
|
||||
function normalizeLowercaseStringOrEmpty(value: unknown): string {
|
||||
return typeof value === "string" ? value.trim().toLowerCase() : "";
|
||||
}
|
||||
|
||||
function normalizeOptionalLowercaseString(value: unknown): string | undefined {
|
||||
const normalized = normalizeLowercaseStringOrEmpty(value);
|
||||
return normalized || undefined;
|
||||
}
|
||||
import {
|
||||
normalizeLowercaseStringOrEmpty,
|
||||
normalizeOptionalLowercaseString,
|
||||
} from "openclaw/plugin-sdk/text-runtime";
|
||||
|
||||
export function stripZalouserTargetPrefix(raw: string): string {
|
||||
return raw
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { StreamFn } from "@mariozechner/pi-agent-core";
|
||||
import { streamSimple } from "@mariozechner/pi-ai";
|
||||
import type { ThinkLevel } from "../../auto-reply/thinking.js";
|
||||
import { normalizeLowercaseStringOrEmpty } from "../../shared/string-coerce.js";
|
||||
import { streamWithPayloadPatch } from "./stream-payload-utils.js";
|
||||
|
||||
function isGemini31Model(modelId: string): boolean {
|
||||
@@ -9,7 +10,7 @@ function isGemini31Model(modelId: string): boolean {
|
||||
}
|
||||
|
||||
function isGemma4Model(modelId: string): boolean {
|
||||
return modelId.trim().toLowerCase().startsWith("gemma-4");
|
||||
return normalizeLowercaseStringOrEmpty(modelId).startsWith("gemma-4");
|
||||
}
|
||||
|
||||
function mapThinkLevelToGoogleThinkingLevel(
|
||||
@@ -106,9 +107,7 @@ export function sanitizeGoogleThinkingPayload(params: {
|
||||
}
|
||||
|
||||
const mappedLevel =
|
||||
explicitMappedLevel ??
|
||||
normalizedThinkingLevel ??
|
||||
(hadThinkingBudget ? "MINIMAL" : undefined);
|
||||
explicitMappedLevel ?? normalizedThinkingLevel ?? (hadThinkingBudget ? "MINIMAL" : undefined);
|
||||
|
||||
if (mappedLevel) {
|
||||
thinkingConfigObj.thinkingLevel = mappedLevel;
|
||||
|
||||
@@ -77,7 +77,7 @@ function parseExecDirectiveArgs(raw: string): Omit<
|
||||
if (idx === -1) {
|
||||
return null;
|
||||
}
|
||||
const key = token.slice(0, idx).trim().toLowerCase();
|
||||
const key = normalizeOptionalLowercaseString(token.slice(0, idx));
|
||||
const value = token.slice(idx + 1).trim();
|
||||
if (!key) {
|
||||
return null;
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
isTrustedSafeBinPath,
|
||||
normalizeTrustedSafeBinDirs,
|
||||
} from "../../../infra/exec-safe-bin-trust.js";
|
||||
import { normalizeOptionalLowercaseString } from "../../../shared/string-coerce.js";
|
||||
import { sanitizeForLog } from "../../../terminal/ansi.js";
|
||||
import { asObjectRecord } from "./object.js";
|
||||
|
||||
@@ -42,7 +43,7 @@ function normalizeConfiguredSafeBins(entries: unknown): string[] {
|
||||
return Array.from(
|
||||
new Set(
|
||||
entries
|
||||
.map((entry) => (typeof entry === "string" ? entry.trim().toLowerCase() : ""))
|
||||
.map((entry) => normalizeOptionalLowercaseString(entry) ?? "")
|
||||
.filter((entry) => entry.length > 0),
|
||||
),
|
||||
).toSorted();
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { vi } from "vitest";
|
||||
import * as ssrf from "../../../infra/net/ssrf.js";
|
||||
import { normalizeLowercaseStringOrEmpty } from "../../../shared/string-coerce.js";
|
||||
|
||||
export function mockPublicPinnedHostname() {
|
||||
return vi.spyOn(ssrf, "resolvePinnedHostnameWithPolicy").mockImplementation(async (hostname) => {
|
||||
const normalized = hostname.trim().toLowerCase().replace(/\.$/, "");
|
||||
const normalized = normalizeLowercaseStringOrEmpty(hostname).replace(/\.$/, "");
|
||||
const addresses = ["93.184.216.34"];
|
||||
return {
|
||||
hostname: normalized,
|
||||
|
||||
@@ -20,6 +20,10 @@ import { hasNonEmptyString } from "../infra/outbound/channel-target.js";
|
||||
import { getActivePluginRegistry } from "../plugins/runtime.js";
|
||||
import { DEFAULT_AGENT_ID } from "../routing/session-key.js";
|
||||
import { asNullableRecord } from "../shared/record-coerce.js";
|
||||
import {
|
||||
normalizeLowercaseStringOrEmpty,
|
||||
normalizeOptionalLowercaseString,
|
||||
} from "../shared/string-coerce.js";
|
||||
import { collectDeepCodeSafetyFindings } from "./audit-deep-code-safety.js";
|
||||
import { collectDeepProbeFindings } from "./audit-deep-probe-findings.js";
|
||||
import {
|
||||
@@ -395,9 +399,7 @@ export function collectGatewayConfigFindings(
|
||||
? cfg.gateway?.tools?.allow
|
||||
: [];
|
||||
const gatewayToolsAllow = new Set(
|
||||
gatewayToolsAllowRaw
|
||||
.map((v) => (typeof v === "string" ? v.trim().toLowerCase() : ""))
|
||||
.filter(Boolean),
|
||||
gatewayToolsAllowRaw.map((v) => normalizeOptionalLowercaseString(v) ?? "").filter(Boolean),
|
||||
);
|
||||
const reenabledOverHttp = DEFAULT_GATEWAY_HTTP_TOOL_DENY.filter((name) =>
|
||||
gatewayToolsAllow.has(name),
|
||||
@@ -689,7 +691,7 @@ function isStrictLoopbackTrustedProxyEntry(entry: string): boolean {
|
||||
return rawIp.trim() === "127.0.0.1" && prefix === 32;
|
||||
}
|
||||
if (ipVersion === 6) {
|
||||
return prefix === 128 && rawIp.trim().toLowerCase() === "::1";
|
||||
return prefix === 128 && normalizeLowercaseStringOrEmpty(rawIp) === "::1";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -959,7 +961,7 @@ export function collectExecRuntimeFindings(cfg: OpenClawConfig): SecurityAuditFi
|
||||
return Array.from(
|
||||
new Set(
|
||||
entries
|
||||
.map((entry) => (typeof entry === "string" ? entry.trim().toLowerCase() : ""))
|
||||
.map((entry) => normalizeOptionalLowercaseString(entry) ?? "")
|
||||
.filter((entry) => entry.length > 0),
|
||||
),
|
||||
).toSorted();
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { vi } from "vitest";
|
||||
import * as ssrf from "../infra/net/ssrf.js";
|
||||
import { normalizeLowercaseStringOrEmpty } from "../shared/string-coerce.js";
|
||||
|
||||
export function mockPinnedHostnameResolution(addresses: string[] = ["93.184.216.34"]) {
|
||||
return vi.spyOn(ssrf, "resolvePinnedHostname").mockImplementation(async (hostname) => {
|
||||
const normalized = hostname.trim().toLowerCase().replace(/\.$/, "");
|
||||
const normalized = normalizeLowercaseStringOrEmpty(hostname).replace(/\.$/, "");
|
||||
const pinnedAddresses = [...addresses];
|
||||
return {
|
||||
hostname: normalized,
|
||||
|
||||
Reference in New Issue
Block a user