chore(lint): tighten lint exception coverage · openclaw/openclaw@3950605
steipete
·
2026-05-31
·
via Recent Commits to openclaw:main
File tree
embedded-agent-runner/run
| Original file line number | Diff line number | Diff line change |
|---|
@@ -218,13 +218,6 @@
|
218 | 218 | "**/node_modules/**" |
219 | 219 | ], |
220 | 220 | "overrides": [ |
221 | | - { |
222 | | -"files": ["src/security/**"], |
223 | | -"rules": { |
224 | | -"eslint/no-warning-comments": "off", |
225 | | -"oxc/no-map-spread": "off" |
226 | | - } |
227 | | - }, |
228 | 221 | { |
229 | 222 | "files": [ |
230 | 223 | "**/*.test.ts", |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -14,7 +14,17 @@ export function resolveWhatsAppDocumentFileName(params: {
|
14 | 14 | mimetype?: string; |
15 | 15 | }): string { |
16 | 16 | const fallbackName = resolveWhatsAppDefaultDocumentFileName(params.mimetype); |
17 | | -// eslint-disable-next-line no-control-regex |
18 | | -const stripped = params.fileName?.replace(/[\x00-\x1f\x7f]/g, "").trim(); |
| 17 | +const stripped = stripAsciiControlCharacters(params.fileName ?? "").trim(); |
19 | 18 | return stripped || fallbackName; |
20 | 19 | } |
| 20 | + |
| 21 | +function stripAsciiControlCharacters(value: string): string { |
| 22 | +let stripped = ""; |
| 23 | +for (const char of value) { |
| 24 | +const code = char.charCodeAt(0); |
| 25 | +if (code > 0x1f && code !== 0x7f) { |
| 26 | +stripped += char; |
| 27 | +} |
| 28 | +} |
| 29 | +return stripped; |
| 30 | +} |
| Original file line number | Diff line number | Diff line change |
|---|
@@ -340,12 +340,9 @@ export async function connectMcpClient(params: {
|
340 | 340 | process.stderr.write(`[openclaw mcp] ${String(chunk)}`); |
341 | 341 | }); |
342 | 342 | const rawMessages: unknown[] = []; |
343 | | -// The MCP stdio transport here exposes a writable onmessage callback at |
344 | | -// runtime, not an EventTarget-style addEventListener API. |
345 | | -// oxlint-disable-next-line unicorn/prefer-add-event-listener |
346 | | -transport.onmessage = (message) => { |
| 343 | +Reflect.set(transport, "onmessage", (message: unknown) => { |
347 | 344 | pushBounded(rawMessages, message, MCP_RAW_MESSAGE_RETAIN_LIMIT); |
348 | | -}; |
| 345 | +}); |
349 | 346 | |
350 | 347 | const client = new Client({ name: "docker-mcp-channels", version: "1.0.0" }); |
351 | 348 | await connectMcpWithTimeout(client, transport, MCP_CONNECT_TIMEOUT_MS); |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -43,8 +43,7 @@ export {
|
43 | 43 | |
44 | 44 | /** Strip null bytes from paths to prevent ENOTDIR errors. */ |
45 | 45 | function stripNullBytes(s: string): string { |
46 | | -// eslint-disable-next-line no-control-regex |
47 | | -return s.replace(/\0/g, ""); |
| 46 | +return s.split("\0").join(""); |
48 | 47 | } |
49 | 48 | |
50 | 49 | const AUTO_FALLBACK_PRIMARY_PROBE_INTERVAL_MS = 5 * 60 * 1000; |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -75,8 +75,7 @@ const PATH_PATTERN = new RegExp(PATH_REGEX_SOURCE, "gi");
|
75 | 75 | * "photo---1c77ce17-20b9-4546-be64-6e36a9adcb2c.png" |
76 | 76 | * "图片---1c77ce17-20b9-4546-be64-6e36a9adcb2c.png" |
77 | 77 | */ |
78 | | -// eslint-disable-next-line no-control-regex |
79 | | -const MEDIA_URI_REGEX = /\bmedia:\/\/inbound\/([^\]\s/\\\x00]+)/; |
| 78 | +const MEDIA_URI_REGEX = /\bmedia:\/\/inbound\/([^\]\s/\\]+)/; |
80 | 79 | |
81 | 80 | /** |
82 | 81 | * Result of detecting an image reference in text. |
@@ -361,7 +360,7 @@ export function detectImageReferences(prompt: string): DetectedImageRef[] {
|
361 | 360 | // This must be tested before the extension-based path regex because the |
362 | 361 | // URI has no file extension suffix in its base form. |
363 | 362 | const mediaUriMatch = content.match(MEDIA_URI_REGEX); |
364 | | -if (mediaUriMatch) { |
| 363 | +if (mediaUriMatch && !mediaUriMatch[1].includes("\0")) { |
365 | 364 | const uri = `media://inbound/${mediaUriMatch[1]}`; |
366 | 365 | const dedupeKey = normalizeRefForDedupe(uri); |
367 | 366 | if (!seen.has(dedupeKey)) { |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -548,9 +548,7 @@ function sanitizeMountPathHint(value?: string): string | undefined {
|
548 | 548 | if (!trimmed) { |
549 | 549 | return undefined; |
550 | 550 | } |
551 | | -// Prevent prompt injection via control/newline characters in system prompt hints. |
552 | | -// eslint-disable-next-line no-control-regex |
553 | | -if (/[\r\n\u0000-\u001F\u007F\u0085\u2028\u2029]/.test(trimmed)) { |
| 551 | +if (hasPromptUnsafeControlCharacter(trimmed)) { |
554 | 552 | return undefined; |
555 | 553 | } |
556 | 554 | if (!/^[A-Za-z0-9._\-/:]+$/.test(trimmed)) { |
@@ -559,6 +557,16 @@ function sanitizeMountPathHint(value?: string): string | undefined {
|
559 | 557 | return trimmed; |
560 | 558 | } |
561 | 559 | |
| 560 | +function hasPromptUnsafeControlCharacter(value: string): boolean { |
| 561 | +for (const char of value) { |
| 562 | +const code = char.charCodeAt(0); |
| 563 | +if (code <= 0x1f || code === 0x7f || code === 0x85 || code === 0x2028 || code === 0x2029) { |
| 564 | +return true; |
| 565 | +} |
| 566 | +} |
| 567 | +return false; |
| 568 | +} |
| 569 | + |
562 | 570 | async function cleanupProvisionalSession( |
563 | 571 | childSessionKey: string, |
564 | 572 | options?: { |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -55,8 +55,8 @@ export type ChannelGatewayMethodDescriptor = {
|
55 | 55 | description?: string; |
56 | 56 | }; |
57 | 57 | |
58 | | -// Omitted generic means "plugin with some account shape", not "plugin whose |
59 | | -// account is literally Record<string, unknown>". |
| 58 | +// Omitted generic means "plugin with some account shape"; using unknown makes |
| 59 | +// callback parameters contravariant and rejects concrete plugin implementations. |
60 | 60 | // oxlint-disable-next-line typescript/no-explicit-any |
61 | 61 | export type ChannelPlugin<ResolvedAccount = any, Probe = unknown, Audit = unknown> = { |
62 | 62 | id: ChannelId; |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -95,6 +95,8 @@ export interface ChannelsConfig {
|
95 | 95 | * Channel sections are plugin-owned and keyed by arbitrary channel ids. |
96 | 96 | * Keep the lookup permissive so augmented channel configs remain ergonomic at call sites. |
97 | 97 | */ |
98 | | -// eslint-disable-next-line @typescript-eslint/no-explicit-any |
| 98 | +// Plugin-owned channel sections are open-world config; narrowing this breaks |
| 99 | +// SDK config-write helpers that accept account-shaped channel records. |
| 100 | +// oxlint-disable-next-line typescript/no-explicit-any |
99 | 101 | [key: string]: any; |
100 | 102 | } |
| Original file line number | Diff line number | Diff line change |
|---|
@@ -25,11 +25,10 @@ function rawWsDataToString(data: RawData): string {
|
25 | 25 | function activeClientSocketsToPort(port: number): Socket[] { |
26 | 26 | // Node has no public active-handle API; this regression must prove the probe |
27 | 27 | // promise does not resolve while the client-side socket handle is still live. |
28 | | -// oxlint-disable no-underscore-dangle |
29 | | -const handles = |
30 | | -(process as typeof process & { _getActiveHandles?: () => unknown[] })._getActiveHandles?.() ?? |
31 | | -[]; |
32 | | -// oxlint-enable no-underscore-dangle |
| 28 | +const getActiveHandles = Reflect.get(process, "_getActiveHandles") as |
| 29 | +| (() => unknown[]) |
| 30 | +| undefined; |
| 31 | +const handles = getActiveHandles?.() ?? []; |
33 | 32 | return handles.filter( |
34 | 33 | (handle): handle is Socket => handle instanceof Socket && handle.remotePort === port, |
35 | 34 | ); |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -180,15 +180,13 @@ export function collectSmallModelRiskFindings(params: {
|
180 | 180 | return findings; |
181 | 181 | } |
182 | 182 | |
183 | | -const smallModels = models |
184 | | -.map((entry) => { |
185 | | -const paramB = inferParamBFromIdOrName(entry.id); |
186 | | -if (!paramB || paramB > SMALL_MODEL_PARAM_B_MAX) { |
187 | | -return null; |
188 | | -} |
189 | | -return { ...entry, paramB }; |
190 | | -}) |
191 | | -.filter((entry): entry is { id: string; source: string; paramB: number } => Boolean(entry)); |
| 183 | +const smallModels: Array<{ id: string; source: string; paramB: number }> = []; |
| 184 | +for (const entry of models) { |
| 185 | +const paramB = inferParamBFromIdOrName(entry.id); |
| 186 | +if (paramB && paramB <= SMALL_MODEL_PARAM_B_MAX) { |
| 187 | +smallModels.push({ id: entry.id, source: entry.source, paramB }); |
| 188 | +} |
| 189 | +} |
192 | 190 | |
193 | 191 | if (smallModels.length === 0) { |
194 | 192 | return findings; |
|
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。