fix: sanitize flow step summaries for filesystem-safe names (#8554)

* fix: sanitize flow step summaries for filesystem-safe names

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* chore: bump windmill-utils-internal to 1.3.6

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: handle Windows reserved device names in flow step sanitization

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: collapse consecutive underscores in sanitized flow step names

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* chore: bump windmill-utils-internal to 1.3.7

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* bump

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Ruben Fiszel
2026-03-27 11:38:20 +00:00
committed by GitHub
parent d06b42613f
commit e15bfbf91e
10 changed files with 41 additions and 19 deletions
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "windmill-utils-internal",
"version": "1.3.5",
"version": "1.3.6",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "windmill-utils-internal",
"version": "1.3.5",
"version": "1.3.6",
"license": "Apache 2.0",
"devDependencies": {
"@types/node": "^24.2.0",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "windmill-utils-internal",
"version": "1.3.5",
"version": "1.3.7",
"description": "Internal utility functions for Windmill",
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
@@ -1 +1 @@
export * from "./config.ts";
export * from "./config";
+5 -5
View File
@@ -8,8 +8,8 @@
* - Cross-platform path constants
*/
export * from "./inline-scripts.ts";
export * from "./path-utils.ts";
export * from "./parse.ts";
export * from "./config.ts";
export { SEP, DELIMITER } from "./constants.ts";
export * from "./inline-scripts";
export * from "./path-utils";
export * from "./parse";
export * from "./config";
export { SEP, DELIMITER } from "./constants";
@@ -1,5 +1,5 @@
import { newPathAssigner, PathAssigner } from "../path-utils/path-assigner.ts";
import { FlowModule, RawScript, ScriptLang } from "../gen/types.gen.ts";
import { newPathAssigner, PathAssigner } from "../path-utils/path-assigner";
import { FlowModule, RawScript, ScriptLang } from "../gen/types.gen";
/**
* Represents an inline script extracted from a flow module
@@ -1,2 +1,2 @@
export * from "./replacer.ts";
export * from "./extractor.ts";
export * from "./replacer";
export * from "./extractor";
@@ -1,4 +1,4 @@
import { AiAgent, FlowModule, FlowValue, RawScript } from "../gen/types.gen.ts";
import { AiAgent, FlowModule, FlowValue, RawScript } from "../gen/types.gen";
export type LocalScriptInfo = {
content: string;
@@ -1 +1 @@
export * from "./parse-schema.ts";
export * from "./parse-schema";
@@ -1 +1 @@
export * from "./path-assigner.ts";
export * from "./path-assigner";
@@ -1,4 +1,4 @@
import { RawScript } from "../gen/types.gen.ts";
import { RawScript } from "../gen/types.gen";
const INLINE_SCRIPT_PREFIX = "inline_script";
@@ -111,6 +111,28 @@ export function getLanguageFromExtension(
return undefined;
}
/**
* Sanitizes a summary string for use as a filesystem-safe name.
* Removes or replaces characters that are invalid on common filesystems.
*/
const WINDOWS_RESERVED = /^(con|prn|aux|nul|com[0-9]|lpt[0-9])$/;
export function sanitizeForFilesystem(summary: string): string {
const name = summary
.toLowerCase()
.replaceAll(" ", "_")
// Remove characters invalid on Windows/Unix/Mac: / \ : * ? " < > |
// Also remove control characters (0x00-0x1F) and DEL (0x7F)
// deno-lint-ignore no-control-regex
.replace(/[/\\:*?"<>|\x00-\x1f\x7f]/g, "")
// Collapse consecutive underscores
.replace(/_+/g, "_")
// Trim leading/trailing dots and underscores (hidden files, Windows edge cases)
.replace(/^[._]+|[._]+$/g, "");
// Prefix Windows reserved device names (CON, PRN, AUX, NUL, COM0-9, LPT0-9)
return WINDOWS_RESERVED.test(name) ? `_${name}` : name;
}
export interface PathAssigner {
assignPath(summary: string | undefined, language: SupportedLanguage): [string, string];
}
@@ -144,7 +166,7 @@ export function newPathAssigner(defaultTs: "bun" | "deno" | PathAssignerOptions,
): [string, string] {
let name;
name = summary?.toLowerCase()?.replaceAll(" ", "_") ?? "";
name = summary ? sanitizeForFilesystem(summary) : "";
let original_name = name;
@@ -185,7 +207,7 @@ export function newRawAppPathAssigner(defaultTs: "bun" | "deno"): PathAssigner {
): [string, string] {
let name;
name = summary?.toLowerCase()?.replaceAll(" ", "_") ?? "";
name = summary ? sanitizeForFilesystem(summary) : "";
let original_name = name;