From 13d5c0bf449266b33cb60b1267542d22b00070fc Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 4 Nov 2025 15:08:31 +0000
Subject: [PATCH 1/4] Initial plan
From f49694d0917de06aa616ab613fc02d99df2086df Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 4 Nov 2025 15:19:58 +0000
Subject: [PATCH 2/4] Extract sanitizeLabelContent to dedicated helper file
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
pkg/workflow/js/add_labels.cjs | 15 +-
pkg/workflow/js/create_issue.cjs | 15 +-
pkg/workflow/js/sanitize_label_content.cjs | 32 ++++
.../js/sanitize_label_content.test.cjs | 142 ++++++++++++++++++
4 files changed, 176 insertions(+), 28 deletions(-)
create mode 100644 pkg/workflow/js/sanitize_label_content.cjs
create mode 100644 pkg/workflow/js/sanitize_label_content.test.cjs
diff --git a/pkg/workflow/js/add_labels.cjs b/pkg/workflow/js/add_labels.cjs
index b371354ab0..ce3cfdfcef 100644
--- a/pkg/workflow/js/add_labels.cjs
+++ b/pkg/workflow/js/add_labels.cjs
@@ -1,20 +1,7 @@
// @ts-check
///
-function sanitizeLabelContent(content) {
- if (!content || typeof content !== "string") {
- return "";
- }
- let sanitized = content.trim();
- sanitized = sanitized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
- sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, "");
- sanitized = sanitized.replace(
- /(^|[^\w`])@([A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?(?:\/[A-Za-z0-9._-]+)?)/g,
- (_m, p1, p2) => `${p1}\`@${p2}\``
- );
- sanitized = sanitized.replace(/[<>&'"]/g, "");
- return sanitized.trim();
-}
+const { sanitizeLabelContent } = require("./sanitize_label_content.cjs");
async function main() {
const agentOutputFile = process.env.GH_AW_AGENT_OUTPUT;
if (!agentOutputFile) {
diff --git a/pkg/workflow/js/create_issue.cjs b/pkg/workflow/js/create_issue.cjs
index bd832624e2..51d55dcbb1 100644
--- a/pkg/workflow/js/create_issue.cjs
+++ b/pkg/workflow/js/create_issue.cjs
@@ -1,20 +1,7 @@
// @ts-check
///
-function sanitizeLabelContent(content) {
- if (!content || typeof content !== "string") {
- return "";
- }
- let sanitized = content.trim();
- sanitized = sanitized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
- sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, "");
- sanitized = sanitized.replace(
- /(^|[^\w`])@([A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?(?:\/[A-Za-z0-9._-]+)?)/g,
- (_m, p1, p2) => `${p1}\`@${p2}\``
- );
- sanitized = sanitized.replace(/[<>&'"]/g, "");
- return sanitized.trim();
-}
+const { sanitizeLabelContent } = require("./sanitize_label_content.cjs");
/**
* Generate footer with AI attribution and workflow installation instructions
diff --git a/pkg/workflow/js/sanitize_label_content.cjs b/pkg/workflow/js/sanitize_label_content.cjs
new file mode 100644
index 0000000000..04041069b3
--- /dev/null
+++ b/pkg/workflow/js/sanitize_label_content.cjs
@@ -0,0 +1,32 @@
+// @ts-check
+/**
+ * Sanitize label content for GitHub API
+ * Removes control characters, ANSI codes, and neutralizes @mentions
+ * @module sanitize_label_content
+ */
+
+/**
+ * Sanitizes label content by removing control characters, ANSI escape codes,
+ * and neutralizing @mentions to prevent unintended notifications.
+ *
+ * @param {string} content - The label content to sanitize
+ * @returns {string} The sanitized label content
+ */
+function sanitizeLabelContent(content) {
+ if (!content || typeof content !== "string") {
+ return "";
+ }
+ let sanitized = content.trim();
+ // Remove ANSI escape sequences FIRST (before removing control chars)
+ sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, "");
+ // Then remove control characters (except newlines and tabs)
+ sanitized = sanitized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
+ sanitized = sanitized.replace(
+ /(^|[^\w`])@([A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?(?:\/[A-Za-z0-9._-]+)?)/g,
+ (_m, p1, p2) => `${p1}\`@${p2}\``
+ );
+ sanitized = sanitized.replace(/[<>&'"]/g, "");
+ return sanitized.trim();
+}
+
+module.exports = { sanitizeLabelContent };
diff --git a/pkg/workflow/js/sanitize_label_content.test.cjs b/pkg/workflow/js/sanitize_label_content.test.cjs
new file mode 100644
index 0000000000..57edec6ec8
--- /dev/null
+++ b/pkg/workflow/js/sanitize_label_content.test.cjs
@@ -0,0 +1,142 @@
+import { describe, it, expect } from "vitest";
+
+// Import the function to test
+const { sanitizeLabelContent } = require("./sanitize_label_content.cjs");
+
+describe("sanitize_label_content.cjs", () => {
+ describe("sanitizeLabelContent", () => {
+ it("should return empty string for null input", () => {
+ expect(sanitizeLabelContent(null)).toBe("");
+ });
+
+ it("should return empty string for undefined input", () => {
+ expect(sanitizeLabelContent(undefined)).toBe("");
+ });
+
+ it("should return empty string for non-string input", () => {
+ expect(sanitizeLabelContent(123)).toBe("");
+ expect(sanitizeLabelContent({})).toBe("");
+ expect(sanitizeLabelContent([])).toBe("");
+ });
+
+ it("should trim whitespace from input", () => {
+ expect(sanitizeLabelContent(" test ")).toBe("test");
+ expect(sanitizeLabelContent("\n\ttest\n\t")).toBe("test");
+ });
+
+ it("should remove control characters", () => {
+ const input = "test\x00\x01\x02\x03\x04\x05\x06\x07\x08label";
+ expect(sanitizeLabelContent(input)).toBe("testlabel");
+ });
+
+ it("should remove DEL character (0x7F)", () => {
+ const input = "test\x7Flabel";
+ expect(sanitizeLabelContent(input)).toBe("testlabel");
+ });
+
+ it("should preserve newline character", () => {
+ const input = "test\nlabel";
+ expect(sanitizeLabelContent(input)).toBe("test\nlabel");
+ });
+
+ it("should remove ANSI escape codes", () => {
+ const input = "\x1b[31mred text\x1b[0m";
+ expect(sanitizeLabelContent(input)).toBe("red text");
+ });
+
+ it("should remove various ANSI codes", () => {
+ const input = "\x1b[1;32mBold Green\x1b[0m\x1b[4mUnderline\x1b[0m";
+ expect(sanitizeLabelContent(input)).toBe("Bold GreenUnderline");
+ });
+
+ it("should neutralize @mentions by wrapping in backticks", () => {
+ expect(sanitizeLabelContent("Hello @user")).toBe("Hello `@user`");
+ expect(sanitizeLabelContent("@user said something")).toBe("`@user` said something");
+ });
+
+ it("should neutralize @org/team mentions", () => {
+ expect(sanitizeLabelContent("Hello @myorg/myteam")).toBe("Hello `@myorg/myteam`");
+ });
+
+ it("should not neutralize @mentions already in backticks", () => {
+ const input = "Already `@user` handled";
+ expect(sanitizeLabelContent(input)).toBe("Already `@user` handled");
+ });
+
+ it("should neutralize multiple @mentions", () => {
+ const input = "@user1 and @user2 are here";
+ expect(sanitizeLabelContent(input)).toBe("`@user1` and `@user2` are here");
+ });
+
+ it("should remove HTML special characters", () => {
+ expect(sanitizeLabelContent("test<>&'\"label")).toBe("testlabel");
+ });
+
+ it("should remove less-than signs", () => {
+ expect(sanitizeLabelContent("a < b")).toBe("a b");
+ });
+
+ it("should remove greater-than signs", () => {
+ expect(sanitizeLabelContent("a > b")).toBe("a b");
+ });
+
+ it("should remove ampersands", () => {
+ expect(sanitizeLabelContent("test & label")).toBe("test label");
+ });
+
+ it("should remove single and double quotes", () => {
+ expect(sanitizeLabelContent('test\'s "label"')).toBe("tests label");
+ });
+
+ it("should handle complex input with multiple sanitizations", () => {
+ const input = " @user \x1b[31mred\x1b[0m test&label ";
+ expect(sanitizeLabelContent(input)).toBe("`@user` red tag testlabel");
+ });
+
+ it("should handle empty string input", () => {
+ expect(sanitizeLabelContent("")).toBe("");
+ });
+
+ it("should handle whitespace-only input", () => {
+ expect(sanitizeLabelContent(" \n\t ")).toBe("");
+ });
+
+ it("should preserve normal alphanumeric characters", () => {
+ expect(sanitizeLabelContent("bug123")).toBe("bug123");
+ expect(sanitizeLabelContent("feature-request")).toBe("feature-request");
+ });
+
+ it("should preserve hyphens and underscores", () => {
+ expect(sanitizeLabelContent("test-label_123")).toBe("test-label_123");
+ });
+
+ it("should handle consecutive control characters", () => {
+ const input = "test\x00\x01\x02\x03\x04\x05label";
+ expect(sanitizeLabelContent(input)).toBe("testlabel");
+ });
+
+ it("should handle @mentions at various positions", () => {
+ expect(sanitizeLabelContent("start @user end")).toBe("start `@user` end");
+ expect(sanitizeLabelContent("@user at start")).toBe("`@user` at start");
+ expect(sanitizeLabelContent("at end @user")).toBe("at end `@user`");
+ });
+
+ it("should not treat email-like patterns as @mentions after alphanumerics", () => {
+ const input = "email@example.com";
+ // The regex has [^\w`] which requires non-word character before @
+ // so 'email@' won't match because 'l' is a word character
+ expect(sanitizeLabelContent(input)).toBe("email@example.com");
+ });
+
+ it("should handle username edge cases", () => {
+ // Valid GitHub usernames can be 1-39 chars, alphanumeric + hyphens
+ expect(sanitizeLabelContent("@a")).toBe("`@a`");
+ expect(sanitizeLabelContent("@user-name-123")).toBe("`@user-name-123`");
+ });
+
+ it("should combine all sanitization rules correctly", () => {
+ const input = ' \x1b[31m@user\x1b[0m says & "goodbye" ';
+ expect(sanitizeLabelContent(input)).toBe("`@user` says hello goodbye");
+ });
+ });
+});
From f6efff4f23510f70f5587614c3c2d1ad9bcb2ad9 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 4 Nov 2025 15:37:05 +0000
Subject: [PATCH 3/4] Recompile workflows after extracting sanitizeLabelContent
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
.github/workflows/ci-doctor.lock.yml | 15 +---------
.../workflows/cli-version-checker.lock.yml | 15 +---------
.../duplicate-code-detector.lock.yml | 15 +---------
.../workflows/go-pattern-detector.lock.yml | 15 +---------
.github/workflows/issue-classifier.lock.yml | 15 +---------
.github/workflows/plan.lock.yml | 15 +---------
.github/workflows/poem-bot.lock.yml | 30 ++-----------------
.../semantic-function-refactor.lock.yml | 15 +---------
.github/workflows/smoke-claude.lock.yml | 15 +---------
.github/workflows/smoke-codex.lock.yml | 15 +---------
.../workflows/smoke-copilot.firewall.lock.yml | 15 +---------
.github/workflows/smoke-copilot.lock.yml | 15 +---------
.github/workflows/smoke-detector.lock.yml | 15 +---------
.github/workflows/smoke-opencode.lock.yml | 15 +---------
.../test-ollama-threat-detection.lock.yml | 15 +---------
.github/workflows/video-analyzer.lock.yml | 15 +---------
16 files changed, 17 insertions(+), 238 deletions(-)
diff --git a/.github/workflows/ci-doctor.lock.yml b/.github/workflows/ci-doctor.lock.yml
index 23f4d1d93d..450a359542 100644
--- a/.github/workflows/ci-doctor.lock.yml
+++ b/.github/workflows/ci-doctor.lock.yml
@@ -4052,20 +4052,7 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- function sanitizeLabelContent(content) {
- if (!content || typeof content !== "string") {
- return "";
- }
- let sanitized = content.trim();
- sanitized = sanitized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
- sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, "");
- sanitized = sanitized.replace(
- /(^|[^\w`])@([A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?(?:\/[A-Za-z0-9._-]+)?)/g,
- (_m, p1, p2) => `${p1}\`@${p2}\``
- );
- sanitized = sanitized.replace(/[<>&'"]/g, "");
- return sanitized.trim();
- }
+ const { sanitizeLabelContent } = require("./sanitize_label_content.cjs");
function generateFooter(
workflowName,
runUrl,
diff --git a/.github/workflows/cli-version-checker.lock.yml b/.github/workflows/cli-version-checker.lock.yml
index d8347bb2a9..939b061416 100644
--- a/.github/workflows/cli-version-checker.lock.yml
+++ b/.github/workflows/cli-version-checker.lock.yml
@@ -4086,20 +4086,7 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- function sanitizeLabelContent(content) {
- if (!content || typeof content !== "string") {
- return "";
- }
- let sanitized = content.trim();
- sanitized = sanitized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
- sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, "");
- sanitized = sanitized.replace(
- /(^|[^\w`])@([A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?(?:\/[A-Za-z0-9._-]+)?)/g,
- (_m, p1, p2) => `${p1}\`@${p2}\``
- );
- sanitized = sanitized.replace(/[<>&'"]/g, "");
- return sanitized.trim();
- }
+ const { sanitizeLabelContent } = require("./sanitize_label_content.cjs");
function generateFooter(
workflowName,
runUrl,
diff --git a/.github/workflows/duplicate-code-detector.lock.yml b/.github/workflows/duplicate-code-detector.lock.yml
index d37a14c1d3..2f32258eb6 100644
--- a/.github/workflows/duplicate-code-detector.lock.yml
+++ b/.github/workflows/duplicate-code-detector.lock.yml
@@ -3057,20 +3057,7 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- function sanitizeLabelContent(content) {
- if (!content || typeof content !== "string") {
- return "";
- }
- let sanitized = content.trim();
- sanitized = sanitized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
- sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, "");
- sanitized = sanitized.replace(
- /(^|[^\w`])@([A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?(?:\/[A-Za-z0-9._-]+)?)/g,
- (_m, p1, p2) => `${p1}\`@${p2}\``
- );
- sanitized = sanitized.replace(/[<>&'"]/g, "");
- return sanitized.trim();
- }
+ const { sanitizeLabelContent } = require("./sanitize_label_content.cjs");
function generateFooter(
workflowName,
runUrl,
diff --git a/.github/workflows/go-pattern-detector.lock.yml b/.github/workflows/go-pattern-detector.lock.yml
index 13b67fa6c1..1ff880e264 100644
--- a/.github/workflows/go-pattern-detector.lock.yml
+++ b/.github/workflows/go-pattern-detector.lock.yml
@@ -3298,20 +3298,7 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- function sanitizeLabelContent(content) {
- if (!content || typeof content !== "string") {
- return "";
- }
- let sanitized = content.trim();
- sanitized = sanitized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
- sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, "");
- sanitized = sanitized.replace(
- /(^|[^\w`])@([A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?(?:\/[A-Za-z0-9._-]+)?)/g,
- (_m, p1, p2) => `${p1}\`@${p2}\``
- );
- sanitized = sanitized.replace(/[<>&'"]/g, "");
- return sanitized.trim();
- }
+ const { sanitizeLabelContent } = require("./sanitize_label_content.cjs");
function generateFooter(
workflowName,
runUrl,
diff --git a/.github/workflows/issue-classifier.lock.yml b/.github/workflows/issue-classifier.lock.yml
index a0785e1b84..6776f1e7f2 100644
--- a/.github/workflows/issue-classifier.lock.yml
+++ b/.github/workflows/issue-classifier.lock.yml
@@ -716,20 +716,7 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- function sanitizeLabelContent(content) {
- if (!content || typeof content !== "string") {
- return "";
- }
- let sanitized = content.trim();
- sanitized = sanitized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
- sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, "");
- sanitized = sanitized.replace(
- /(^|[^\w`])@([A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?(?:\/[A-Za-z0-9._-]+)?)/g,
- (_m, p1, p2) => `${p1}\`@${p2}\``
- );
- sanitized = sanitized.replace(/[<>&'"]/g, "");
- return sanitized.trim();
- }
+ const { sanitizeLabelContent } = require("./sanitize_label_content.cjs");
async function main() {
const agentOutputFile = process.env.GH_AW_AGENT_OUTPUT;
if (!agentOutputFile) {
diff --git a/.github/workflows/plan.lock.yml b/.github/workflows/plan.lock.yml
index 64b04979a6..e9e1191905 100644
--- a/.github/workflows/plan.lock.yml
+++ b/.github/workflows/plan.lock.yml
@@ -4121,20 +4121,7 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- function sanitizeLabelContent(content) {
- if (!content || typeof content !== "string") {
- return "";
- }
- let sanitized = content.trim();
- sanitized = sanitized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
- sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, "");
- sanitized = sanitized.replace(
- /(^|[^\w`])@([A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?(?:\/[A-Za-z0-9._-]+)?)/g,
- (_m, p1, p2) => `${p1}\`@${p2}\``
- );
- sanitized = sanitized.replace(/[<>&'"]/g, "");
- return sanitized.trim();
- }
+ const { sanitizeLabelContent } = require("./sanitize_label_content.cjs");
function generateFooter(
workflowName,
runUrl,
diff --git a/.github/workflows/poem-bot.lock.yml b/.github/workflows/poem-bot.lock.yml
index cd27361566..089f0b400e 100644
--- a/.github/workflows/poem-bot.lock.yml
+++ b/.github/workflows/poem-bot.lock.yml
@@ -1156,20 +1156,7 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- function sanitizeLabelContent(content) {
- if (!content || typeof content !== "string") {
- return "";
- }
- let sanitized = content.trim();
- sanitized = sanitized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
- sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, "");
- sanitized = sanitized.replace(
- /(^|[^\w`])@([A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?(?:\/[A-Za-z0-9._-]+)?)/g,
- (_m, p1, p2) => `${p1}\`@${p2}\``
- );
- sanitized = sanitized.replace(/[<>&'"]/g, "");
- return sanitized.trim();
- }
+ const { sanitizeLabelContent } = require("./sanitize_label_content.cjs");
async function main() {
const agentOutputFile = process.env.GH_AW_AGENT_OUTPUT;
if (!agentOutputFile) {
@@ -4995,20 +4982,7 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- function sanitizeLabelContent(content) {
- if (!content || typeof content !== "string") {
- return "";
- }
- let sanitized = content.trim();
- sanitized = sanitized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
- sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, "");
- sanitized = sanitized.replace(
- /(^|[^\w`])@([A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?(?:\/[A-Za-z0-9._-]+)?)/g,
- (_m, p1, p2) => `${p1}\`@${p2}\``
- );
- sanitized = sanitized.replace(/[<>&'"]/g, "");
- return sanitized.trim();
- }
+ const { sanitizeLabelContent } = require("./sanitize_label_content.cjs");
function generateFooter(
workflowName,
runUrl,
diff --git a/.github/workflows/semantic-function-refactor.lock.yml b/.github/workflows/semantic-function-refactor.lock.yml
index 1743b9eb76..43c0a6a9fd 100644
--- a/.github/workflows/semantic-function-refactor.lock.yml
+++ b/.github/workflows/semantic-function-refactor.lock.yml
@@ -3698,20 +3698,7 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- function sanitizeLabelContent(content) {
- if (!content || typeof content !== "string") {
- return "";
- }
- let sanitized = content.trim();
- sanitized = sanitized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
- sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, "");
- sanitized = sanitized.replace(
- /(^|[^\w`])@([A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?(?:\/[A-Za-z0-9._-]+)?)/g,
- (_m, p1, p2) => `${p1}\`@${p2}\``
- );
- sanitized = sanitized.replace(/[<>&'"]/g, "");
- return sanitized.trim();
- }
+ const { sanitizeLabelContent } = require("./sanitize_label_content.cjs");
function generateFooter(
workflowName,
runUrl,
diff --git a/.github/workflows/smoke-claude.lock.yml b/.github/workflows/smoke-claude.lock.yml
index 663850a788..30fd1f183d 100644
--- a/.github/workflows/smoke-claude.lock.yml
+++ b/.github/workflows/smoke-claude.lock.yml
@@ -3148,20 +3148,7 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- function sanitizeLabelContent(content) {
- if (!content || typeof content !== "string") {
- return "";
- }
- let sanitized = content.trim();
- sanitized = sanitized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
- sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, "");
- sanitized = sanitized.replace(
- /(^|[^\w`])@([A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?(?:\/[A-Za-z0-9._-]+)?)/g,
- (_m, p1, p2) => `${p1}\`@${p2}\``
- );
- sanitized = sanitized.replace(/[<>&'"]/g, "");
- return sanitized.trim();
- }
+ const { sanitizeLabelContent } = require("./sanitize_label_content.cjs");
function generateFooter(
workflowName,
runUrl,
diff --git a/.github/workflows/smoke-codex.lock.yml b/.github/workflows/smoke-codex.lock.yml
index fb19bbeebe..0525375782 100644
--- a/.github/workflows/smoke-codex.lock.yml
+++ b/.github/workflows/smoke-codex.lock.yml
@@ -2797,20 +2797,7 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- function sanitizeLabelContent(content) {
- if (!content || typeof content !== "string") {
- return "";
- }
- let sanitized = content.trim();
- sanitized = sanitized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
- sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, "");
- sanitized = sanitized.replace(
- /(^|[^\w`])@([A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?(?:\/[A-Za-z0-9._-]+)?)/g,
- (_m, p1, p2) => `${p1}\`@${p2}\``
- );
- sanitized = sanitized.replace(/[<>&'"]/g, "");
- return sanitized.trim();
- }
+ const { sanitizeLabelContent } = require("./sanitize_label_content.cjs");
function generateFooter(
workflowName,
runUrl,
diff --git a/.github/workflows/smoke-copilot.firewall.lock.yml b/.github/workflows/smoke-copilot.firewall.lock.yml
index 15cfe62f19..a78ff172ea 100644
--- a/.github/workflows/smoke-copilot.firewall.lock.yml
+++ b/.github/workflows/smoke-copilot.firewall.lock.yml
@@ -3851,20 +3851,7 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- function sanitizeLabelContent(content) {
- if (!content || typeof content !== "string") {
- return "";
- }
- let sanitized = content.trim();
- sanitized = sanitized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
- sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, "");
- sanitized = sanitized.replace(
- /(^|[^\w`])@([A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?(?:\/[A-Za-z0-9._-]+)?)/g,
- (_m, p1, p2) => `${p1}\`@${p2}\``
- );
- sanitized = sanitized.replace(/[<>&'"]/g, "");
- return sanitized.trim();
- }
+ const { sanitizeLabelContent } = require("./sanitize_label_content.cjs");
function generateFooter(
workflowName,
runUrl,
diff --git a/.github/workflows/smoke-copilot.lock.yml b/.github/workflows/smoke-copilot.lock.yml
index 75f239866f..5b1af0ac79 100644
--- a/.github/workflows/smoke-copilot.lock.yml
+++ b/.github/workflows/smoke-copilot.lock.yml
@@ -3851,20 +3851,7 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- function sanitizeLabelContent(content) {
- if (!content || typeof content !== "string") {
- return "";
- }
- let sanitized = content.trim();
- sanitized = sanitized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
- sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, "");
- sanitized = sanitized.replace(
- /(^|[^\w`])@([A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?(?:\/[A-Za-z0-9._-]+)?)/g,
- (_m, p1, p2) => `${p1}\`@${p2}\``
- );
- sanitized = sanitized.replace(/[<>&'"]/g, "");
- return sanitized.trim();
- }
+ const { sanitizeLabelContent } = require("./sanitize_label_content.cjs");
function generateFooter(
workflowName,
runUrl,
diff --git a/.github/workflows/smoke-detector.lock.yml b/.github/workflows/smoke-detector.lock.yml
index 1852c123da..6ae2b841ca 100644
--- a/.github/workflows/smoke-detector.lock.yml
+++ b/.github/workflows/smoke-detector.lock.yml
@@ -4289,20 +4289,7 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- function sanitizeLabelContent(content) {
- if (!content || typeof content !== "string") {
- return "";
- }
- let sanitized = content.trim();
- sanitized = sanitized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
- sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, "");
- sanitized = sanitized.replace(
- /(^|[^\w`])@([A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?(?:\/[A-Za-z0-9._-]+)?)/g,
- (_m, p1, p2) => `${p1}\`@${p2}\``
- );
- sanitized = sanitized.replace(/[<>&'"]/g, "");
- return sanitized.trim();
- }
+ const { sanitizeLabelContent } = require("./sanitize_label_content.cjs");
function generateFooter(
workflowName,
runUrl,
diff --git a/.github/workflows/smoke-opencode.lock.yml b/.github/workflows/smoke-opencode.lock.yml
index 4fc6423d7d..091ab4969d 100644
--- a/.github/workflows/smoke-opencode.lock.yml
+++ b/.github/workflows/smoke-opencode.lock.yml
@@ -2321,20 +2321,7 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- function sanitizeLabelContent(content) {
- if (!content || typeof content !== "string") {
- return "";
- }
- let sanitized = content.trim();
- sanitized = sanitized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
- sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, "");
- sanitized = sanitized.replace(
- /(^|[^\w`])@([A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?(?:\/[A-Za-z0-9._-]+)?)/g,
- (_m, p1, p2) => `${p1}\`@${p2}\``
- );
- sanitized = sanitized.replace(/[<>&'"]/g, "");
- return sanitized.trim();
- }
+ const { sanitizeLabelContent } = require("./sanitize_label_content.cjs");
function generateFooter(
workflowName,
runUrl,
diff --git a/.github/workflows/test-ollama-threat-detection.lock.yml b/.github/workflows/test-ollama-threat-detection.lock.yml
index 2e9920f7f4..7c94b0a475 100644
--- a/.github/workflows/test-ollama-threat-detection.lock.yml
+++ b/.github/workflows/test-ollama-threat-detection.lock.yml
@@ -3423,20 +3423,7 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- function sanitizeLabelContent(content) {
- if (!content || typeof content !== "string") {
- return "";
- }
- let sanitized = content.trim();
- sanitized = sanitized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
- sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, "");
- sanitized = sanitized.replace(
- /(^|[^\w`])@([A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?(?:\/[A-Za-z0-9._-]+)?)/g,
- (_m, p1, p2) => `${p1}\`@${p2}\``
- );
- sanitized = sanitized.replace(/[<>&'"]/g, "");
- return sanitized.trim();
- }
+ const { sanitizeLabelContent } = require("./sanitize_label_content.cjs");
function generateFooter(
workflowName,
runUrl,
diff --git a/.github/workflows/video-analyzer.lock.yml b/.github/workflows/video-analyzer.lock.yml
index a538258fae..5d53a85877 100644
--- a/.github/workflows/video-analyzer.lock.yml
+++ b/.github/workflows/video-analyzer.lock.yml
@@ -3714,20 +3714,7 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- function sanitizeLabelContent(content) {
- if (!content || typeof content !== "string") {
- return "";
- }
- let sanitized = content.trim();
- sanitized = sanitized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
- sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, "");
- sanitized = sanitized.replace(
- /(^|[^\w`])@([A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?(?:\/[A-Za-z0-9._-]+)?)/g,
- (_m, p1, p2) => `${p1}\`@${p2}\``
- );
- sanitized = sanitized.replace(/[<>&'"]/g, "");
- return sanitized.trim();
- }
+ const { sanitizeLabelContent } = require("./sanitize_label_content.cjs");
function generateFooter(
workflowName,
runUrl,
From 5e39bb861c31bd31d5b64b211390775fd8fdece9 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 4 Nov 2025 16:07:13 +0000
Subject: [PATCH 4/4] Configure bundler to inline sanitize_label_content helper
- Add sanitize_label_content.cjs to embedded JavaScript sources
- Convert createIssueScript and addLabelsScript to bundled scripts
- Update GetJavaScriptSources to include sanitize_label_content.cjs
- Update all usages to call getter functions for proper bundling
- Fix test file to keep ES6 import for vitest (not CommonJS)
This ensures the helper function is properly inlined in compiled workflows
instead of leaving require() statements that would fail at runtime.
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
.github/workflows/ci-doctor.lock.yml | 15 ++++-
.../workflows/cli-version-checker.lock.yml | 15 ++++-
.../duplicate-code-detector.lock.yml | 15 ++++-
.../workflows/go-pattern-detector.lock.yml | 15 ++++-
.github/workflows/issue-classifier.lock.yml | 15 ++++-
.github/workflows/plan.lock.yml | 15 ++++-
.github/workflows/poem-bot.lock.yml | 30 +++++++++-
.../semantic-function-refactor.lock.yml | 15 ++++-
.github/workflows/smoke-claude.lock.yml | 15 ++++-
.github/workflows/smoke-codex.lock.yml | 15 ++++-
.../workflows/smoke-copilot.firewall.lock.yml | 15 ++++-
.github/workflows/smoke-copilot.lock.yml | 15 ++++-
.github/workflows/smoke-detector.lock.yml | 15 ++++-
.github/workflows/smoke-opencode.lock.yml | 15 ++++-
.../test-ollama-threat-detection.lock.yml | 15 ++++-
.github/workflows/video-analyzer.lock.yml | 15 ++++-
pkg/workflow/add_labels.go | 2 +-
pkg/workflow/create_issue.go | 2 +-
pkg/workflow/create_issue_subissue_test.go | 23 ++++----
pkg/workflow/js.go | 56 ++++++++++++++++---
pkg/workflow/js_test.go | 4 +-
21 files changed, 303 insertions(+), 39 deletions(-)
diff --git a/.github/workflows/ci-doctor.lock.yml b/.github/workflows/ci-doctor.lock.yml
index 450a359542..8d74a8ed15 100644
--- a/.github/workflows/ci-doctor.lock.yml
+++ b/.github/workflows/ci-doctor.lock.yml
@@ -4052,7 +4052,20 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { sanitizeLabelContent } = require("./sanitize_label_content.cjs");
+ function sanitizeLabelContent(content) {
+ if (!content || typeof content !== "string") {
+ return "";
+ }
+ let sanitized = content.trim();
+ sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, "");
+ sanitized = sanitized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
+ sanitized = sanitized.replace(
+ /(^|[^\w`])@([A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?(?:\/[A-Za-z0-9._-]+)?)/g,
+ (_m, p1, p2) => `${p1}\`@${p2}\``
+ );
+ sanitized = sanitized.replace(/[<>&'"]/g, "");
+ return sanitized.trim();
+ }
function generateFooter(
workflowName,
runUrl,
diff --git a/.github/workflows/cli-version-checker.lock.yml b/.github/workflows/cli-version-checker.lock.yml
index 939b061416..c5847c8600 100644
--- a/.github/workflows/cli-version-checker.lock.yml
+++ b/.github/workflows/cli-version-checker.lock.yml
@@ -4086,7 +4086,20 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { sanitizeLabelContent } = require("./sanitize_label_content.cjs");
+ function sanitizeLabelContent(content) {
+ if (!content || typeof content !== "string") {
+ return "";
+ }
+ let sanitized = content.trim();
+ sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, "");
+ sanitized = sanitized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
+ sanitized = sanitized.replace(
+ /(^|[^\w`])@([A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?(?:\/[A-Za-z0-9._-]+)?)/g,
+ (_m, p1, p2) => `${p1}\`@${p2}\``
+ );
+ sanitized = sanitized.replace(/[<>&'"]/g, "");
+ return sanitized.trim();
+ }
function generateFooter(
workflowName,
runUrl,
diff --git a/.github/workflows/duplicate-code-detector.lock.yml b/.github/workflows/duplicate-code-detector.lock.yml
index 2f32258eb6..ce2328dc82 100644
--- a/.github/workflows/duplicate-code-detector.lock.yml
+++ b/.github/workflows/duplicate-code-detector.lock.yml
@@ -3057,7 +3057,20 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { sanitizeLabelContent } = require("./sanitize_label_content.cjs");
+ function sanitizeLabelContent(content) {
+ if (!content || typeof content !== "string") {
+ return "";
+ }
+ let sanitized = content.trim();
+ sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, "");
+ sanitized = sanitized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
+ sanitized = sanitized.replace(
+ /(^|[^\w`])@([A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?(?:\/[A-Za-z0-9._-]+)?)/g,
+ (_m, p1, p2) => `${p1}\`@${p2}\``
+ );
+ sanitized = sanitized.replace(/[<>&'"]/g, "");
+ return sanitized.trim();
+ }
function generateFooter(
workflowName,
runUrl,
diff --git a/.github/workflows/go-pattern-detector.lock.yml b/.github/workflows/go-pattern-detector.lock.yml
index 1ff880e264..5fc8654b86 100644
--- a/.github/workflows/go-pattern-detector.lock.yml
+++ b/.github/workflows/go-pattern-detector.lock.yml
@@ -3298,7 +3298,20 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { sanitizeLabelContent } = require("./sanitize_label_content.cjs");
+ function sanitizeLabelContent(content) {
+ if (!content || typeof content !== "string") {
+ return "";
+ }
+ let sanitized = content.trim();
+ sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, "");
+ sanitized = sanitized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
+ sanitized = sanitized.replace(
+ /(^|[^\w`])@([A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?(?:\/[A-Za-z0-9._-]+)?)/g,
+ (_m, p1, p2) => `${p1}\`@${p2}\``
+ );
+ sanitized = sanitized.replace(/[<>&'"]/g, "");
+ return sanitized.trim();
+ }
function generateFooter(
workflowName,
runUrl,
diff --git a/.github/workflows/issue-classifier.lock.yml b/.github/workflows/issue-classifier.lock.yml
index 6776f1e7f2..09a15711bc 100644
--- a/.github/workflows/issue-classifier.lock.yml
+++ b/.github/workflows/issue-classifier.lock.yml
@@ -716,7 +716,20 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { sanitizeLabelContent } = require("./sanitize_label_content.cjs");
+ function sanitizeLabelContent(content) {
+ if (!content || typeof content !== "string") {
+ return "";
+ }
+ let sanitized = content.trim();
+ sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, "");
+ sanitized = sanitized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
+ sanitized = sanitized.replace(
+ /(^|[^\w`])@([A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?(?:\/[A-Za-z0-9._-]+)?)/g,
+ (_m, p1, p2) => `${p1}\`@${p2}\``
+ );
+ sanitized = sanitized.replace(/[<>&'"]/g, "");
+ return sanitized.trim();
+ }
async function main() {
const agentOutputFile = process.env.GH_AW_AGENT_OUTPUT;
if (!agentOutputFile) {
diff --git a/.github/workflows/plan.lock.yml b/.github/workflows/plan.lock.yml
index e9e1191905..f1a335e4af 100644
--- a/.github/workflows/plan.lock.yml
+++ b/.github/workflows/plan.lock.yml
@@ -4121,7 +4121,20 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { sanitizeLabelContent } = require("./sanitize_label_content.cjs");
+ function sanitizeLabelContent(content) {
+ if (!content || typeof content !== "string") {
+ return "";
+ }
+ let sanitized = content.trim();
+ sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, "");
+ sanitized = sanitized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
+ sanitized = sanitized.replace(
+ /(^|[^\w`])@([A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?(?:\/[A-Za-z0-9._-]+)?)/g,
+ (_m, p1, p2) => `${p1}\`@${p2}\``
+ );
+ sanitized = sanitized.replace(/[<>&'"]/g, "");
+ return sanitized.trim();
+ }
function generateFooter(
workflowName,
runUrl,
diff --git a/.github/workflows/poem-bot.lock.yml b/.github/workflows/poem-bot.lock.yml
index 089f0b400e..9885e40254 100644
--- a/.github/workflows/poem-bot.lock.yml
+++ b/.github/workflows/poem-bot.lock.yml
@@ -1156,7 +1156,20 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { sanitizeLabelContent } = require("./sanitize_label_content.cjs");
+ function sanitizeLabelContent(content) {
+ if (!content || typeof content !== "string") {
+ return "";
+ }
+ let sanitized = content.trim();
+ sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, "");
+ sanitized = sanitized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
+ sanitized = sanitized.replace(
+ /(^|[^\w`])@([A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?(?:\/[A-Za-z0-9._-]+)?)/g,
+ (_m, p1, p2) => `${p1}\`@${p2}\``
+ );
+ sanitized = sanitized.replace(/[<>&'"]/g, "");
+ return sanitized.trim();
+ }
async function main() {
const agentOutputFile = process.env.GH_AW_AGENT_OUTPUT;
if (!agentOutputFile) {
@@ -4982,7 +4995,20 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { sanitizeLabelContent } = require("./sanitize_label_content.cjs");
+ function sanitizeLabelContent(content) {
+ if (!content || typeof content !== "string") {
+ return "";
+ }
+ let sanitized = content.trim();
+ sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, "");
+ sanitized = sanitized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
+ sanitized = sanitized.replace(
+ /(^|[^\w`])@([A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?(?:\/[A-Za-z0-9._-]+)?)/g,
+ (_m, p1, p2) => `${p1}\`@${p2}\``
+ );
+ sanitized = sanitized.replace(/[<>&'"]/g, "");
+ return sanitized.trim();
+ }
function generateFooter(
workflowName,
runUrl,
diff --git a/.github/workflows/semantic-function-refactor.lock.yml b/.github/workflows/semantic-function-refactor.lock.yml
index 43c0a6a9fd..e6a6002743 100644
--- a/.github/workflows/semantic-function-refactor.lock.yml
+++ b/.github/workflows/semantic-function-refactor.lock.yml
@@ -3698,7 +3698,20 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { sanitizeLabelContent } = require("./sanitize_label_content.cjs");
+ function sanitizeLabelContent(content) {
+ if (!content || typeof content !== "string") {
+ return "";
+ }
+ let sanitized = content.trim();
+ sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, "");
+ sanitized = sanitized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
+ sanitized = sanitized.replace(
+ /(^|[^\w`])@([A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?(?:\/[A-Za-z0-9._-]+)?)/g,
+ (_m, p1, p2) => `${p1}\`@${p2}\``
+ );
+ sanitized = sanitized.replace(/[<>&'"]/g, "");
+ return sanitized.trim();
+ }
function generateFooter(
workflowName,
runUrl,
diff --git a/.github/workflows/smoke-claude.lock.yml b/.github/workflows/smoke-claude.lock.yml
index 30fd1f183d..73b49dae1e 100644
--- a/.github/workflows/smoke-claude.lock.yml
+++ b/.github/workflows/smoke-claude.lock.yml
@@ -3148,7 +3148,20 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { sanitizeLabelContent } = require("./sanitize_label_content.cjs");
+ function sanitizeLabelContent(content) {
+ if (!content || typeof content !== "string") {
+ return "";
+ }
+ let sanitized = content.trim();
+ sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, "");
+ sanitized = sanitized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
+ sanitized = sanitized.replace(
+ /(^|[^\w`])@([A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?(?:\/[A-Za-z0-9._-]+)?)/g,
+ (_m, p1, p2) => `${p1}\`@${p2}\``
+ );
+ sanitized = sanitized.replace(/[<>&'"]/g, "");
+ return sanitized.trim();
+ }
function generateFooter(
workflowName,
runUrl,
diff --git a/.github/workflows/smoke-codex.lock.yml b/.github/workflows/smoke-codex.lock.yml
index 0525375782..cf1a3d75d9 100644
--- a/.github/workflows/smoke-codex.lock.yml
+++ b/.github/workflows/smoke-codex.lock.yml
@@ -2797,7 +2797,20 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { sanitizeLabelContent } = require("./sanitize_label_content.cjs");
+ function sanitizeLabelContent(content) {
+ if (!content || typeof content !== "string") {
+ return "";
+ }
+ let sanitized = content.trim();
+ sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, "");
+ sanitized = sanitized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
+ sanitized = sanitized.replace(
+ /(^|[^\w`])@([A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?(?:\/[A-Za-z0-9._-]+)?)/g,
+ (_m, p1, p2) => `${p1}\`@${p2}\``
+ );
+ sanitized = sanitized.replace(/[<>&'"]/g, "");
+ return sanitized.trim();
+ }
function generateFooter(
workflowName,
runUrl,
diff --git a/.github/workflows/smoke-copilot.firewall.lock.yml b/.github/workflows/smoke-copilot.firewall.lock.yml
index a78ff172ea..609d3e021d 100644
--- a/.github/workflows/smoke-copilot.firewall.lock.yml
+++ b/.github/workflows/smoke-copilot.firewall.lock.yml
@@ -3851,7 +3851,20 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { sanitizeLabelContent } = require("./sanitize_label_content.cjs");
+ function sanitizeLabelContent(content) {
+ if (!content || typeof content !== "string") {
+ return "";
+ }
+ let sanitized = content.trim();
+ sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, "");
+ sanitized = sanitized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
+ sanitized = sanitized.replace(
+ /(^|[^\w`])@([A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?(?:\/[A-Za-z0-9._-]+)?)/g,
+ (_m, p1, p2) => `${p1}\`@${p2}\``
+ );
+ sanitized = sanitized.replace(/[<>&'"]/g, "");
+ return sanitized.trim();
+ }
function generateFooter(
workflowName,
runUrl,
diff --git a/.github/workflows/smoke-copilot.lock.yml b/.github/workflows/smoke-copilot.lock.yml
index 5b1af0ac79..62c2a60631 100644
--- a/.github/workflows/smoke-copilot.lock.yml
+++ b/.github/workflows/smoke-copilot.lock.yml
@@ -3851,7 +3851,20 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { sanitizeLabelContent } = require("./sanitize_label_content.cjs");
+ function sanitizeLabelContent(content) {
+ if (!content || typeof content !== "string") {
+ return "";
+ }
+ let sanitized = content.trim();
+ sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, "");
+ sanitized = sanitized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
+ sanitized = sanitized.replace(
+ /(^|[^\w`])@([A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?(?:\/[A-Za-z0-9._-]+)?)/g,
+ (_m, p1, p2) => `${p1}\`@${p2}\``
+ );
+ sanitized = sanitized.replace(/[<>&'"]/g, "");
+ return sanitized.trim();
+ }
function generateFooter(
workflowName,
runUrl,
diff --git a/.github/workflows/smoke-detector.lock.yml b/.github/workflows/smoke-detector.lock.yml
index 6ae2b841ca..de8716b49d 100644
--- a/.github/workflows/smoke-detector.lock.yml
+++ b/.github/workflows/smoke-detector.lock.yml
@@ -4289,7 +4289,20 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { sanitizeLabelContent } = require("./sanitize_label_content.cjs");
+ function sanitizeLabelContent(content) {
+ if (!content || typeof content !== "string") {
+ return "";
+ }
+ let sanitized = content.trim();
+ sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, "");
+ sanitized = sanitized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
+ sanitized = sanitized.replace(
+ /(^|[^\w`])@([A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?(?:\/[A-Za-z0-9._-]+)?)/g,
+ (_m, p1, p2) => `${p1}\`@${p2}\``
+ );
+ sanitized = sanitized.replace(/[<>&'"]/g, "");
+ return sanitized.trim();
+ }
function generateFooter(
workflowName,
runUrl,
diff --git a/.github/workflows/smoke-opencode.lock.yml b/.github/workflows/smoke-opencode.lock.yml
index 091ab4969d..41a898e42b 100644
--- a/.github/workflows/smoke-opencode.lock.yml
+++ b/.github/workflows/smoke-opencode.lock.yml
@@ -2321,7 +2321,20 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { sanitizeLabelContent } = require("./sanitize_label_content.cjs");
+ function sanitizeLabelContent(content) {
+ if (!content || typeof content !== "string") {
+ return "";
+ }
+ let sanitized = content.trim();
+ sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, "");
+ sanitized = sanitized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
+ sanitized = sanitized.replace(
+ /(^|[^\w`])@([A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?(?:\/[A-Za-z0-9._-]+)?)/g,
+ (_m, p1, p2) => `${p1}\`@${p2}\``
+ );
+ sanitized = sanitized.replace(/[<>&'"]/g, "");
+ return sanitized.trim();
+ }
function generateFooter(
workflowName,
runUrl,
diff --git a/.github/workflows/test-ollama-threat-detection.lock.yml b/.github/workflows/test-ollama-threat-detection.lock.yml
index 7c94b0a475..b1fc182e8a 100644
--- a/.github/workflows/test-ollama-threat-detection.lock.yml
+++ b/.github/workflows/test-ollama-threat-detection.lock.yml
@@ -3423,7 +3423,20 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { sanitizeLabelContent } = require("./sanitize_label_content.cjs");
+ function sanitizeLabelContent(content) {
+ if (!content || typeof content !== "string") {
+ return "";
+ }
+ let sanitized = content.trim();
+ sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, "");
+ sanitized = sanitized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
+ sanitized = sanitized.replace(
+ /(^|[^\w`])@([A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?(?:\/[A-Za-z0-9._-]+)?)/g,
+ (_m, p1, p2) => `${p1}\`@${p2}\``
+ );
+ sanitized = sanitized.replace(/[<>&'"]/g, "");
+ return sanitized.trim();
+ }
function generateFooter(
workflowName,
runUrl,
diff --git a/.github/workflows/video-analyzer.lock.yml b/.github/workflows/video-analyzer.lock.yml
index 5d53a85877..f1151b50d8 100644
--- a/.github/workflows/video-analyzer.lock.yml
+++ b/.github/workflows/video-analyzer.lock.yml
@@ -3714,7 +3714,20 @@ jobs:
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
- const { sanitizeLabelContent } = require("./sanitize_label_content.cjs");
+ function sanitizeLabelContent(content) {
+ if (!content || typeof content !== "string") {
+ return "";
+ }
+ let sanitized = content.trim();
+ sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, "");
+ sanitized = sanitized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
+ sanitized = sanitized.replace(
+ /(^|[^\w`])@([A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?(?:\/[A-Za-z0-9._-]+)?)/g,
+ (_m, p1, p2) => `${p1}\`@${p2}\``
+ );
+ sanitized = sanitized.replace(/[<>&'"]/g, "");
+ return sanitized.trim();
+ }
function generateFooter(
workflowName,
runUrl,
diff --git a/pkg/workflow/add_labels.go b/pkg/workflow/add_labels.go
index 4c00c8f2ad..7a0452d5fb 100644
--- a/pkg/workflow/add_labels.go
+++ b/pkg/workflow/add_labels.go
@@ -62,7 +62,7 @@ func (c *Compiler) buildAddLabelsJob(data *WorkflowData, mainJobName string) (*J
StepID: "add_labels",
MainJobName: mainJobName,
CustomEnvVars: customEnvVars,
- Script: addLabelsScript,
+ Script: getAddLabelsScript(),
Token: token,
})
diff --git a/pkg/workflow/create_issue.go b/pkg/workflow/create_issue.go
index 96bd3246af..243168af6b 100644
--- a/pkg/workflow/create_issue.go
+++ b/pkg/workflow/create_issue.go
@@ -112,7 +112,7 @@ func (c *Compiler) buildCreateOutputIssueJob(data *WorkflowData, mainJobName str
StepID: "create_issue",
MainJobName: mainJobName,
CustomEnvVars: customEnvVars,
- Script: createIssueScript,
+ Script: getCreateIssueScript(),
Token: token,
})
steps = append(steps, scriptSteps...)
diff --git a/pkg/workflow/create_issue_subissue_test.go b/pkg/workflow/create_issue_subissue_test.go
index 02cb18a333..0d931308dc 100644
--- a/pkg/workflow/create_issue_subissue_test.go
+++ b/pkg/workflow/create_issue_subissue_test.go
@@ -9,58 +9,59 @@ import (
// TestCreateIssueSubissueFeature tests that the create_issue.js script includes subissue functionality
func TestCreateIssueSubissueFeature(t *testing.T) {
+ script := getCreateIssueScript()
// Test that the script contains the subissue detection logic
- if !strings.Contains(createIssueScript, "context.payload?.issue?.number") {
+ if !strings.Contains(script, "context.payload?.issue?.number") {
t.Error("Expected create_issue.js to check for parent issue context")
}
// Test that the script modifies the body when in issue context
- if !strings.Contains(createIssueScript, "Related to #${effectiveParentIssueNumber}") {
+ if !strings.Contains(script, "Related to #${effectiveParentIssueNumber}") {
t.Error("Expected create_issue.js to add parent issue reference to body")
}
// Test that the script supports explicit parent field
- if !strings.Contains(createIssueScript, "createIssueItem.parent") {
+ if !strings.Contains(script, "createIssueItem.parent") {
t.Error("Expected create_issue.js to support explicit parent field")
}
// Test that the script uses effectiveParentIssueNumber
- if !strings.Contains(createIssueScript, "effectiveParentIssueNumber") {
+ if !strings.Contains(script, "effectiveParentIssueNumber") {
t.Error("Expected create_issue.js to use effectiveParentIssueNumber variable")
}
// Test that the script includes GraphQL sub-issue linking
- if !strings.Contains(createIssueScript, "addSubIssue") {
+ if !strings.Contains(script, "addSubIssue") {
t.Error("Expected create_issue.js to include addSubIssue GraphQL mutation")
}
// Test that the script calls github.graphql for sub-issue linking
- if !strings.Contains(createIssueScript, "github.graphql(addSubIssueMutation") {
+ if !strings.Contains(script, "github.graphql(addSubIssueMutation") {
t.Error("Expected create_issue.js to call github.graphql for sub-issue linking")
}
// Test that the script fetches node IDs before linking
- if !strings.Contains(createIssueScript, "getIssueNodeIdQuery") {
+ if !strings.Contains(script, "getIssueNodeIdQuery") {
t.Error("Expected create_issue.js to fetch issue node IDs before linking")
}
// Test that the script creates a comment on the parent issue
- if !strings.Contains(createIssueScript, "github.rest.issues.createComment") {
+ if !strings.Contains(script, "github.rest.issues.createComment") {
t.Error("Expected create_issue.js to create comment on parent issue")
}
// Test that the script has proper error handling for sub-issue linking
- if !strings.Contains(createIssueScript, "Warning: Could not link sub-issue to parent") {
+ if !strings.Contains(script, "Warning: Could not link sub-issue to parent") {
t.Error("Expected create_issue.js to have error handling for sub-issue linking")
}
// Test console logging for debugging
- if !strings.Contains(createIssueScript, "Detected issue context, parent issue") {
+ if !strings.Contains(script, "Detected issue context, parent issue") {
t.Error("Expected create_issue.js to log when issue context is detected")
}
// Test that it logs successful sub-issue linking
- if !strings.Contains(createIssueScript, "Successfully linked issue #") {
+ if !strings.Contains(script, "Successfully linked issue #") {
t.Error("Expected create_issue.js to log successful sub-issue linking")
}
}
diff --git a/pkg/workflow/js.go b/pkg/workflow/js.go
index 4450dbb198..9bcb7481cc 100644
--- a/pkg/workflow/js.go
+++ b/pkg/workflow/js.go
@@ -10,9 +10,6 @@ import (
//go:embed js/create_pull_request.cjs
var createPullRequestScript string
-//go:embed js/create_issue.cjs
-var createIssueScript string
-
//go:embed js/create_agent_task.cjs
var createAgentTaskScript string
@@ -28,9 +25,6 @@ var createPRReviewCommentScript string
//go:embed js/create_code_scanning_alert.cjs
var createCodeScanningAlertScript string
-//go:embed js/add_labels.cjs
-var addLabelsScript string
-
//go:embed js/assign_issue.cjs
var assignIssueScript string
@@ -94,6 +88,9 @@ var notifyCommentErrorScript string
//go:embed js/sanitize.cjs
var sanitizeLibScript string
+//go:embed js/sanitize_label_content.cjs
+var sanitizeLabelContentScript string
+
// Source scripts that may contain local requires
//
//go:embed js/collect_ndjson_output.cjs
@@ -105,6 +102,12 @@ var computeTextScriptSource string
//go:embed js/sanitize_output.cjs
var sanitizeOutputScriptSource string
+//go:embed js/create_issue.cjs
+var createIssueScriptSource string
+
+//go:embed js/add_labels.cjs
+var addLabelsScriptSource string
+
// Bundled scripts (lazily bundled on-demand and cached)
var (
collectJSONLOutputScript string
@@ -115,6 +118,12 @@ var (
sanitizeOutputScript string
sanitizeOutputScriptOnce sync.Once
+
+ createIssueScript string
+ createIssueScriptOnce sync.Once
+
+ addLabelsScript string
+ addLabelsScriptOnce sync.Once
)
// getCollectJSONLOutputScript returns the bundled collect_ndjson_output script
@@ -165,11 +174,44 @@ func getSanitizeOutputScript() string {
return sanitizeOutputScript
}
+// getCreateIssueScript returns the bundled create_issue script
+// Bundling is performed on first access and cached for subsequent calls
+func getCreateIssueScript() string {
+ createIssueScriptOnce.Do(func() {
+ sources := GetJavaScriptSources()
+ bundled, err := BundleJavaScriptFromSources(createIssueScriptSource, sources, "")
+ if err != nil {
+ // If bundling fails, use the source as-is
+ createIssueScript = createIssueScriptSource
+ } else {
+ createIssueScript = bundled
+ }
+ })
+ return createIssueScript
+}
+
+// getAddLabelsScript returns the bundled add_labels script
+// Bundling is performed on first access and cached for subsequent calls
+func getAddLabelsScript() string {
+ addLabelsScriptOnce.Do(func() {
+ sources := GetJavaScriptSources()
+ bundled, err := BundleJavaScriptFromSources(addLabelsScriptSource, sources, "")
+ if err != nil {
+ // If bundling fails, use the source as-is
+ addLabelsScript = addLabelsScriptSource
+ } else {
+ addLabelsScript = bundled
+ }
+ })
+ return addLabelsScript
+}
+
// 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_label_content.cjs": sanitizeLabelContentScript,
}
}
diff --git a/pkg/workflow/js_test.go b/pkg/workflow/js_test.go
index 42ed932467..fe0c05804f 100644
--- a/pkg/workflow/js_test.go
+++ b/pkg/workflow/js_test.go
@@ -238,11 +238,11 @@ func TestEmbeddedScriptsNotEmpty(t *testing.T) {
script string
}{
{"createPullRequestScript", createPullRequestScript},
- {"createIssueScript", createIssueScript},
+ {"createIssueScript", getCreateIssueScript()},
{"createDiscussionScript", createDiscussionScript},
{"createCommentScript", createCommentScript},
{"collectJSONLOutputScript", getCollectJSONLOutputScript()},
- {"addLabelsScript", addLabelsScript},
+ {"addLabelsScript", getAddLabelsScript()},
{"updateIssueScript", updateIssueScript},
{"addReactionAndEditCommentScript", addReactionAndEditCommentScript},
{"missingToolScript", missingToolScript},