diff --git a/.github/workflows/artifacts-summary.lock.yml b/.github/workflows/artifacts-summary.lock.yml index a812e655b2..4a8cacb0b3 100644 --- a/.github/workflows/artifacts-summary.lock.yml +++ b/.github/workflows/artifacts-summary.lock.yml @@ -3342,6 +3342,18 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd with: script: | + function sanitizeWorkflowName(name) { + + return name + + .toLowerCase() + + .replace(/[:\\/\s]/g, "-") + + .replace(/[^a-z0-9._-]/g, "-"); + + } + function main() { const fs = require("fs"); @@ -3648,18 +3660,6 @@ jobs: } - function sanitizeWorkflowName(name) { - - return name - - .toLowerCase() - - .replace(/[:\\/\s]/g, "-") - - .replace(/[^a-z0-9._-]/g, "-"); - - } - if (typeof module !== "undefined" && module.exports) { module.exports = { @@ -3670,8 +3670,6 @@ jobs: generateFirewallSummary, - sanitizeWorkflowName, - main, }; diff --git a/.github/workflows/changeset.lock.yml b/.github/workflows/changeset.lock.yml index d332a18874..ac3546573d 100644 --- a/.github/workflows/changeset.lock.yml +++ b/.github/workflows/changeset.lock.yml @@ -3948,6 +3948,18 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd with: script: | + function sanitizeWorkflowName(name) { + + return name + + .toLowerCase() + + .replace(/[:\\/\s]/g, "-") + + .replace(/[^a-z0-9._-]/g, "-"); + + } + function main() { const fs = require("fs"); @@ -4254,18 +4266,6 @@ jobs: } - function sanitizeWorkflowName(name) { - - return name - - .toLowerCase() - - .replace(/[:\\/\s]/g, "-") - - .replace(/[^a-z0-9._-]/g, "-"); - - } - if (typeof module !== "undefined" && module.exports) { module.exports = { @@ -4276,8 +4276,6 @@ jobs: generateFirewallSummary, - sanitizeWorkflowName, - main, }; diff --git a/.github/workflows/cli-version-checker.lock.yml b/.github/workflows/cli-version-checker.lock.yml index dc5a87411c..ec81be4759 100644 --- a/.github/workflows/cli-version-checker.lock.yml +++ b/.github/workflows/cli-version-checker.lock.yml @@ -3464,6 +3464,18 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd with: script: | + function sanitizeWorkflowName(name) { + + return name + + .toLowerCase() + + .replace(/[:\\/\s]/g, "-") + + .replace(/[^a-z0-9._-]/g, "-"); + + } + function main() { const fs = require("fs"); @@ -3770,18 +3782,6 @@ jobs: } - function sanitizeWorkflowName(name) { - - return name - - .toLowerCase() - - .replace(/[:\\/\s]/g, "-") - - .replace(/[^a-z0-9._-]/g, "-"); - - } - if (typeof module !== "undefined" && module.exports) { module.exports = { @@ -3792,8 +3792,6 @@ jobs: generateFirewallSummary, - sanitizeWorkflowName, - main, }; diff --git a/.github/workflows/copilot-pr-prompt-analysis.lock.yml b/.github/workflows/copilot-pr-prompt-analysis.lock.yml index 9129c3e219..f86ba766e2 100644 --- a/.github/workflows/copilot-pr-prompt-analysis.lock.yml +++ b/.github/workflows/copilot-pr-prompt-analysis.lock.yml @@ -3684,6 +3684,18 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd with: script: | + function sanitizeWorkflowName(name) { + + return name + + .toLowerCase() + + .replace(/[:\\/\s]/g, "-") + + .replace(/[^a-z0-9._-]/g, "-"); + + } + function main() { const fs = require("fs"); @@ -3990,18 +4002,6 @@ jobs: } - function sanitizeWorkflowName(name) { - - return name - - .toLowerCase() - - .replace(/[:\\/\s]/g, "-") - - .replace(/[^a-z0-9._-]/g, "-"); - - } - if (typeof module !== "undefined" && module.exports) { module.exports = { @@ -4012,8 +4012,6 @@ jobs: generateFirewallSummary, - sanitizeWorkflowName, - main, }; diff --git a/.github/workflows/daily-firewall-report.lock.yml b/.github/workflows/daily-firewall-report.lock.yml index 519e646f5d..d6877db889 100644 --- a/.github/workflows/daily-firewall-report.lock.yml +++ b/.github/workflows/daily-firewall-report.lock.yml @@ -4095,6 +4095,18 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd with: script: | + function sanitizeWorkflowName(name) { + + return name + + .toLowerCase() + + .replace(/[:\\/\s]/g, "-") + + .replace(/[^a-z0-9._-]/g, "-"); + + } + function main() { const fs = require("fs"); @@ -4401,18 +4413,6 @@ jobs: } - function sanitizeWorkflowName(name) { - - return name - - .toLowerCase() - - .replace(/[:\\/\s]/g, "-") - - .replace(/[^a-z0-9._-]/g, "-"); - - } - if (typeof module !== "undefined" && module.exports) { module.exports = { @@ -4423,8 +4423,6 @@ jobs: generateFirewallSummary, - sanitizeWorkflowName, - main, }; diff --git a/.github/workflows/daily-news.lock.yml b/.github/workflows/daily-news.lock.yml index 40b1e447f3..3d79e8f9d0 100644 --- a/.github/workflows/daily-news.lock.yml +++ b/.github/workflows/daily-news.lock.yml @@ -4057,6 +4057,18 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd with: script: | + function sanitizeWorkflowName(name) { + + return name + + .toLowerCase() + + .replace(/[:\\/\s]/g, "-") + + .replace(/[^a-z0-9._-]/g, "-"); + + } + function main() { const fs = require("fs"); @@ -4363,18 +4375,6 @@ jobs: } - function sanitizeWorkflowName(name) { - - return name - - .toLowerCase() - - .replace(/[:\\/\s]/g, "-") - - .replace(/[^a-z0-9._-]/g, "-"); - - } - if (typeof module !== "undefined" && module.exports) { module.exports = { @@ -4385,8 +4385,6 @@ jobs: generateFirewallSummary, - sanitizeWorkflowName, - main, }; diff --git a/.github/workflows/daily-repo-chronicle.lock.yml b/.github/workflows/daily-repo-chronicle.lock.yml index 4dac9ca6c1..9de368b9fa 100644 --- a/.github/workflows/daily-repo-chronicle.lock.yml +++ b/.github/workflows/daily-repo-chronicle.lock.yml @@ -3948,6 +3948,18 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd with: script: | + function sanitizeWorkflowName(name) { + + return name + + .toLowerCase() + + .replace(/[:\\/\s]/g, "-") + + .replace(/[^a-z0-9._-]/g, "-"); + + } + function main() { const fs = require("fs"); @@ -4254,18 +4266,6 @@ jobs: } - function sanitizeWorkflowName(name) { - - return name - - .toLowerCase() - - .replace(/[:\\/\s]/g, "-") - - .replace(/[^a-z0-9._-]/g, "-"); - - } - if (typeof module !== "undefined" && module.exports) { module.exports = { @@ -4276,8 +4276,6 @@ jobs: generateFirewallSummary, - sanitizeWorkflowName, - main, }; diff --git a/.github/workflows/dev.firewall.lock.yml b/.github/workflows/dev.firewall.lock.yml index 5a014fd1db..cde838bffa 100644 --- a/.github/workflows/dev.firewall.lock.yml +++ b/.github/workflows/dev.firewall.lock.yml @@ -1546,6 +1546,18 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd with: script: | + function sanitizeWorkflowName(name) { + + return name + + .toLowerCase() + + .replace(/[:\\/\s]/g, "-") + + .replace(/[^a-z0-9._-]/g, "-"); + + } + function main() { const fs = require("fs"); @@ -1852,18 +1864,6 @@ jobs: } - function sanitizeWorkflowName(name) { - - return name - - .toLowerCase() - - .replace(/[:\\/\s]/g, "-") - - .replace(/[^a-z0-9._-]/g, "-"); - - } - if (typeof module !== "undefined" && module.exports) { module.exports = { @@ -1874,8 +1874,6 @@ jobs: generateFirewallSummary, - sanitizeWorkflowName, - main, }; diff --git a/.github/workflows/firewall.lock.yml b/.github/workflows/firewall.lock.yml index 8540897bfa..01840559e1 100644 --- a/.github/workflows/firewall.lock.yml +++ b/.github/workflows/firewall.lock.yml @@ -1541,6 +1541,18 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd with: script: | + function sanitizeWorkflowName(name) { + + return name + + .toLowerCase() + + .replace(/[:\\/\s]/g, "-") + + .replace(/[^a-z0-9._-]/g, "-"); + + } + function main() { const fs = require("fs"); @@ -1847,18 +1859,6 @@ jobs: } - function sanitizeWorkflowName(name) { - - return name - - .toLowerCase() - - .replace(/[:\\/\s]/g, "-") - - .replace(/[^a-z0-9._-]/g, "-"); - - } - if (typeof module !== "undefined" && module.exports) { module.exports = { @@ -1869,8 +1869,6 @@ jobs: generateFirewallSummary, - sanitizeWorkflowName, - main, }; diff --git a/.github/workflows/mcp-inspector.lock.yml b/.github/workflows/mcp-inspector.lock.yml index 466cffb57c..db091ecca6 100644 --- a/.github/workflows/mcp-inspector.lock.yml +++ b/.github/workflows/mcp-inspector.lock.yml @@ -3914,6 +3914,18 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd with: script: | + function sanitizeWorkflowName(name) { + + return name + + .toLowerCase() + + .replace(/[:\\/\s]/g, "-") + + .replace(/[^a-z0-9._-]/g, "-"); + + } + function main() { const fs = require("fs"); @@ -4220,18 +4232,6 @@ jobs: } - function sanitizeWorkflowName(name) { - - return name - - .toLowerCase() - - .replace(/[:\\/\s]/g, "-") - - .replace(/[^a-z0-9._-]/g, "-"); - - } - if (typeof module !== "undefined" && module.exports) { module.exports = { @@ -4242,8 +4242,6 @@ jobs: generateFirewallSummary, - sanitizeWorkflowName, - main, }; diff --git a/.github/workflows/python-data-charts.lock.yml b/.github/workflows/python-data-charts.lock.yml index f389f68793..722a60ac8d 100644 --- a/.github/workflows/python-data-charts.lock.yml +++ b/.github/workflows/python-data-charts.lock.yml @@ -3658,6 +3658,18 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd with: script: | + function sanitizeWorkflowName(name) { + + return name + + .toLowerCase() + + .replace(/[:\\/\s]/g, "-") + + .replace(/[^a-z0-9._-]/g, "-"); + + } + function main() { const fs = require("fs"); @@ -3964,18 +3976,6 @@ jobs: } - function sanitizeWorkflowName(name) { - - return name - - .toLowerCase() - - .replace(/[:\\/\s]/g, "-") - - .replace(/[^a-z0-9._-]/g, "-"); - - } - if (typeof module !== "undefined" && module.exports) { module.exports = { @@ -3986,8 +3986,6 @@ jobs: generateFirewallSummary, - sanitizeWorkflowName, - main, }; diff --git a/.github/workflows/research.lock.yml b/.github/workflows/research.lock.yml index e8c3a7010a..9c84ce832d 100644 --- a/.github/workflows/research.lock.yml +++ b/.github/workflows/research.lock.yml @@ -3307,6 +3307,18 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd with: script: | + function sanitizeWorkflowName(name) { + + return name + + .toLowerCase() + + .replace(/[:\\/\s]/g, "-") + + .replace(/[^a-z0-9._-]/g, "-"); + + } + function main() { const fs = require("fs"); @@ -3613,18 +3625,6 @@ jobs: } - function sanitizeWorkflowName(name) { - - return name - - .toLowerCase() - - .replace(/[:\\/\s]/g, "-") - - .replace(/[^a-z0-9._-]/g, "-"); - - } - if (typeof module !== "undefined" && module.exports) { module.exports = { @@ -3635,8 +3635,6 @@ jobs: generateFirewallSummary, - sanitizeWorkflowName, - main, }; diff --git a/.github/workflows/smoke-copilot.lock.yml b/.github/workflows/smoke-copilot.lock.yml index 2360e6dcc8..f0b108d138 100644 --- a/.github/workflows/smoke-copilot.lock.yml +++ b/.github/workflows/smoke-copilot.lock.yml @@ -3230,6 +3230,18 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd with: script: | + function sanitizeWorkflowName(name) { + + return name + + .toLowerCase() + + .replace(/[:\\/\s]/g, "-") + + .replace(/[^a-z0-9._-]/g, "-"); + + } + function main() { const fs = require("fs"); @@ -3536,18 +3548,6 @@ jobs: } - function sanitizeWorkflowName(name) { - - return name - - .toLowerCase() - - .replace(/[:\\/\s]/g, "-") - - .replace(/[^a-z0-9._-]/g, "-"); - - } - if (typeof module !== "undefined" && module.exports) { module.exports = { @@ -3558,8 +3558,6 @@ jobs: generateFirewallSummary, - sanitizeWorkflowName, - main, }; diff --git a/.github/workflows/technical-doc-writer.lock.yml b/.github/workflows/technical-doc-writer.lock.yml index df00249d95..9f5515622f 100644 --- a/.github/workflows/technical-doc-writer.lock.yml +++ b/.github/workflows/technical-doc-writer.lock.yml @@ -4099,6 +4099,18 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd with: script: | + function sanitizeWorkflowName(name) { + + return name + + .toLowerCase() + + .replace(/[:\\/\s]/g, "-") + + .replace(/[^a-z0-9._-]/g, "-"); + + } + function main() { const fs = require("fs"); @@ -4405,18 +4417,6 @@ jobs: } - function sanitizeWorkflowName(name) { - - return name - - .toLowerCase() - - .replace(/[:\\/\s]/g, "-") - - .replace(/[^a-z0-9._-]/g, "-"); - - } - if (typeof module !== "undefined" && module.exports) { module.exports = { @@ -4427,8 +4427,6 @@ jobs: generateFirewallSummary, - sanitizeWorkflowName, - main, }; diff --git a/.github/workflows/weekly-issue-summary.lock.yml b/.github/workflows/weekly-issue-summary.lock.yml index 2d3a035bdb..695a3b5d41 100644 --- a/.github/workflows/weekly-issue-summary.lock.yml +++ b/.github/workflows/weekly-issue-summary.lock.yml @@ -3859,6 +3859,18 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd with: script: | + function sanitizeWorkflowName(name) { + + return name + + .toLowerCase() + + .replace(/[:\\/\s]/g, "-") + + .replace(/[^a-z0-9._-]/g, "-"); + + } + function main() { const fs = require("fs"); @@ -4165,18 +4177,6 @@ jobs: } - function sanitizeWorkflowName(name) { - - return name - - .toLowerCase() - - .replace(/[:\\/\s]/g, "-") - - .replace(/[^a-z0-9._-]/g, "-"); - - } - if (typeof module !== "undefined" && module.exports) { module.exports = { @@ -4187,8 +4187,6 @@ jobs: generateFirewallSummary, - sanitizeWorkflowName, - main, }; diff --git a/pkg/workflow/js.go b/pkg/workflow/js.go index 4450dbb198..15ee957754 100644 --- a/pkg/workflow/js.go +++ b/pkg/workflow/js.go @@ -67,9 +67,6 @@ var parseCodexLogScript string //go:embed js/parse_copilot_log.cjs var parseCopilotLogScript string -//go:embed js/parse_firewall_logs.cjs -var parseFirewallLogsScript string - //go:embed js/validate_errors.cjs var validateErrorsScript string @@ -94,6 +91,9 @@ var notifyCommentErrorScript string //go:embed js/sanitize.cjs var sanitizeLibScript string +//go:embed js/sanitize_workflow_name.cjs +var sanitizeWorkflowNameScript string + // Source scripts that may contain local requires // //go:embed js/collect_ndjson_output.cjs @@ -105,6 +105,9 @@ var computeTextScriptSource string //go:embed js/sanitize_output.cjs var sanitizeOutputScriptSource string +//go:embed js/parse_firewall_logs.cjs +var parseFirewallLogsScriptSource string + // Bundled scripts (lazily bundled on-demand and cached) var ( collectJSONLOutputScript string @@ -115,6 +118,9 @@ var ( sanitizeOutputScript string sanitizeOutputScriptOnce sync.Once + + parseFirewallLogsScript string + parseFirewallLogsScriptOnce sync.Once ) // getCollectJSONLOutputScript returns the bundled collect_ndjson_output script @@ -165,11 +171,28 @@ func getSanitizeOutputScript() string { return sanitizeOutputScript } +// getParseFirewallLogsScript returns the bundled parse_firewall_logs script +// Bundling is performed on first access and cached for subsequent calls +func getParseFirewallLogsScript() string { + parseFirewallLogsScriptOnce.Do(func() { + sources := GetJavaScriptSources() + bundled, err := BundleJavaScriptFromSources(parseFirewallLogsScriptSource, sources, "") + if err != nil { + // If bundling fails, use the source as-is + parseFirewallLogsScript = parseFirewallLogsScriptSource + } else { + parseFirewallLogsScript = bundled + } + }) + return parseFirewallLogsScript +} + // GetJavaScriptSources returns a map of all embedded JavaScript sources // The keys are the relative paths from the js directory func GetJavaScriptSources() map[string]string { return map[string]string{ - "sanitize.cjs": sanitizeLibScript, + "sanitize.cjs": sanitizeLibScript, + "sanitize_workflow_name.cjs": sanitizeWorkflowNameScript, } } @@ -587,7 +610,7 @@ func GetLogParserScript(name string) string { case "parse_copilot_log": return parseCopilotLogScript case "parse_firewall_logs": - return parseFirewallLogsScript + return getParseFirewallLogsScript() case "validate_errors": return validateErrorsScript default: diff --git a/pkg/workflow/js/parse_firewall_logs.cjs b/pkg/workflow/js/parse_firewall_logs.cjs index ba0b3726fe..0498f456fa 100644 --- a/pkg/workflow/js/parse_firewall_logs.cjs +++ b/pkg/workflow/js/parse_firewall_logs.cjs @@ -6,6 +6,8 @@ * Firewall log format: timestamp client_ip:port domain dest_ip:port proto method status decision url user_agent */ +const { sanitizeWorkflowName } = require("./sanitize_workflow_name.cjs"); + function main() { const fs = require("fs"); const path = require("path"); @@ -234,25 +236,12 @@ function generateFirewallSummary(analysis) { return summary; } -/** - * Sanitizes a workflow name for use in file paths - * @param {string} name - Workflow name to sanitize - * @returns {string} Sanitized name - */ -function sanitizeWorkflowName(name) { - return name - .toLowerCase() - .replace(/[:\\/\s]/g, "-") - .replace(/[^a-z0-9._-]/g, "-"); -} - // Export for testing if (typeof module !== "undefined" && module.exports) { module.exports = { parseFirewallLogLine, isRequestAllowed, generateFirewallSummary, - sanitizeWorkflowName, main, }; } diff --git a/pkg/workflow/js/parse_firewall_logs.test.cjs b/pkg/workflow/js/parse_firewall_logs.test.cjs index a60dc554ff..409e517006 100644 --- a/pkg/workflow/js/parse_firewall_logs.test.cjs +++ b/pkg/workflow/js/parse_firewall_logs.test.cjs @@ -17,7 +17,6 @@ describe("parse_firewall_logs.cjs", () => { let parseFirewallLogLine; let isRequestAllowed; let generateFirewallSummary; - let sanitizeWorkflowName; beforeEach(() => { vi.clearAllMocks(); @@ -32,7 +31,6 @@ describe("parse_firewall_logs.cjs", () => { `global.testParseFirewallLogLine = parseFirewallLogLine; global.testIsRequestAllowed = isRequestAllowed; global.testGenerateFirewallSummary = generateFirewallSummary; - global.testSanitizeWorkflowName = sanitizeWorkflowName; // Export for testing` ); @@ -41,7 +39,6 @@ describe("parse_firewall_logs.cjs", () => { parseFirewallLogLine = global.testParseFirewallLogLine; isRequestAllowed = global.testIsRequestAllowed; generateFirewallSummary = global.testGenerateFirewallSummary; - sanitizeWorkflowName = global.testSanitizeWorkflowName; }); describe("parseFirewallLogLine", () => { @@ -139,12 +136,6 @@ describe("parse_firewall_logs.cjs", () => { }); }); - describe("sanitizeWorkflowName", () => { - test("should convert to lowercase", () => { - expect(sanitizeWorkflowName("MyWorkflow")).toBe("myworkflow"); - }); - }); - describe("generateFirewallSummary", () => { test("should generate summary with blocked requests only", () => { const analysis = { diff --git a/pkg/workflow/js/sanitize_workflow_name.cjs b/pkg/workflow/js/sanitize_workflow_name.cjs new file mode 100644 index 0000000000..adb5571634 --- /dev/null +++ b/pkg/workflow/js/sanitize_workflow_name.cjs @@ -0,0 +1,14 @@ +// @ts-check +/** + * Sanitizes a workflow name for use in file paths + * @param {string} name - Workflow name to sanitize + * @returns {string} Sanitized name + */ +function sanitizeWorkflowName(name) { + return name + .toLowerCase() + .replace(/[:\\/\s]/g, "-") + .replace(/[^a-z0-9._-]/g, "-"); +} + +module.exports = { sanitizeWorkflowName }; diff --git a/pkg/workflow/js/sanitize_workflow_name.test.cjs b/pkg/workflow/js/sanitize_workflow_name.test.cjs new file mode 100644 index 0000000000..5a0eff3b00 --- /dev/null +++ b/pkg/workflow/js/sanitize_workflow_name.test.cjs @@ -0,0 +1,46 @@ +import { describe, it as test, expect } from "vitest"; +const { sanitizeWorkflowName } = require("./sanitize_workflow_name.cjs"); + +describe("sanitize_workflow_name.cjs", () => { + describe("sanitizeWorkflowName", () => { + test("should convert to lowercase", () => { + expect(sanitizeWorkflowName("MyWorkflow")).toBe("myworkflow"); + }); + + test("should replace colons with hyphens", () => { + expect(sanitizeWorkflowName("workflow:name")).toBe("workflow-name"); + }); + + test("should replace backslashes with hyphens", () => { + expect(sanitizeWorkflowName("workflow\\name")).toBe("workflow-name"); + }); + + test("should replace forward slashes with hyphens", () => { + expect(sanitizeWorkflowName("workflow/name")).toBe("workflow-name"); + }); + + test("should replace spaces with hyphens", () => { + expect(sanitizeWorkflowName("workflow name")).toBe("workflow-name"); + }); + + test("should replace invalid characters with hyphens", () => { + expect(sanitizeWorkflowName("workflow@name!")).toBe("workflow-name-"); + }); + + test("should preserve dots, underscores, and hyphens", () => { + expect(sanitizeWorkflowName("my-workflow_v1.0")).toBe("my-workflow_v1.0"); + }); + + test("should handle complex workflow names", () => { + expect(sanitizeWorkflowName("My Workflow: v1.0 (test)")).toBe("my-workflow--v1.0--test-"); + }); + + test("should handle empty string", () => { + expect(sanitizeWorkflowName("")).toBe(""); + }); + + test("should handle already sanitized names", () => { + expect(sanitizeWorkflowName("my-workflow")).toBe("my-workflow"); + }); + }); +});