Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion actions/setup/js/resolve_mentions.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ function extractMentions(text) {

const { getErrorMessage } = require("./error_helpers.cjs");

const mentionRegex = /(^|[^\w`])@([A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?(?:\/[A-Za-z0-9._-]+)?)/g;
const mentionRegex = /(^|[^\w`])@([A-Za-z0-9](?:[A-Za-z0-9_-]{0,37}[A-Za-z0-9])?(?:\/[A-Za-z0-9._-]+)?)/g;
const mentions = [];
const seen = new Set();

Expand Down
23 changes: 23 additions & 0 deletions actions/setup/js/resolve_mentions.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,29 @@ describe("resolve_mentions.cjs", () => {
it("should preserve original case", () => {
const mentions = extractMentions("Hello @UserName");
expect(mentions).toEqual(["UserName"]);
}),
it("should extract mentions with underscores", () => {
const mentions = extractMentions("Hello @user_name");
expect(mentions).toEqual(["user_name"]);
}),
it("should extract mentions with multiple underscores", () => {
const mentions = extractMentions("Hello @user_name_test");
expect(mentions).toEqual(["user_name_test"]);
}),
it("should extract mentions with underscores and hyphens", () => {
const mentions = extractMentions("Hello @user-name_test");
expect(mentions).toEqual(["user-name_test"]);
}),
it("should extract mentions with underscores at various positions", () => {
const mentions = extractMentions("@test_user @_invalid @user_ @valid_user_123");
// @_invalid: starts with underscore - not extracted (starts with non-alphanumeric)
// @user_: ends with underscore - extracted as "user" (underscore not at end)
// Other mentions are valid
expect(mentions).toEqual(["test_user", "user", "valid_user_123"]);
}),
it("should handle org/team mentions with underscores", () => {
const mentions = extractMentions("Hello @my_org/my_team");
expect(mentions).toEqual(["my_org/my_team"]);
}));
}),
describe("isPayloadUserBot", () => {
Expand Down
2 changes: 1 addition & 1 deletion actions/setup/js/sanitize_content.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ function sanitizeContent(content, maxLengthOrOptions) {
* @returns {string} Processed string
*/
function neutralizeMentions(s, allowedLowercase) {
return s.replace(/(^|[^\w`])@([A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?(?:\/[A-Za-z0-9._-]+)?)/g, (_m, p1, p2) => {
return s.replace(/(^|[^\w`])@([A-Za-z0-9](?:[A-Za-z0-9_-]{0,37}[A-Za-z0-9])?(?:\/[A-Za-z0-9._-]+)?)/g, (_m, p1, p2) => {
// Check if this mention is in the allowed aliases list (case-insensitive)
const isAllowed = allowedLowercase.includes(p2.toLowerCase());
if (isAllowed) {
Expand Down
35 changes: 35 additions & 0 deletions actions/setup/js/sanitize_content.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,26 @@ describe("sanitize_content.cjs", () => {
const result = sanitizeContent("Contact email@example.com");
expect(result).toBe("Contact email@example.com");
});

it("should neutralize @mentions with underscores", () => {
const result = sanitizeContent("Hello @user_name");
expect(result).toBe("Hello `@user_name`");
});

it("should neutralize @mentions with multiple underscores", () => {
const result = sanitizeContent("Hello @user_name_test");
expect(result).toBe("Hello `@user_name_test`");
});

it("should neutralize @mentions with underscores and hyphens", () => {
const result = sanitizeContent("Hello @user-name_test");
expect(result).toBe("Hello `@user-name_test`");
});

it("should neutralize org/team mentions with underscores", () => {
const result = sanitizeContent("Hello @my_org/my_team");
expect(result).toBe("Hello `@my_org/my_team`");
});
});

describe("@mention allowedAliases", () => {
Expand Down Expand Up @@ -162,6 +182,21 @@ describe("sanitize_content.cjs", () => {
expect(result).toBe("Hello `@user`");
});

it("should not neutralize allowed mentions with underscores", () => {
const result = sanitizeContent("Hello @user_name", { allowedAliases: ["user_name"] });
expect(result).toBe("Hello @user_name");
});

it("should neutralize disallowed mentions with underscores", () => {
const result = sanitizeContent("Hello @user_name and @other_user", { allowedAliases: ["user_name"] });
expect(result).toBe("Hello @user_name and `@other_user`");
});

it("should not neutralize org/team mentions with underscores in allowedAliases", () => {
const result = sanitizeContent("Hello @my_org/my_team", { allowedAliases: ["my_org/my_team"] });
expect(result).toBe("Hello @my_org/my_team");
});

it("should log escaped mentions for debugging", () => {
const result = sanitizeContent("Hello @user1 and @user2", { allowedAliases: ["user1"] });
expect(result).toBe("Hello @user1 and `@user2`");
Expand Down
2 changes: 1 addition & 1 deletion actions/setup/js/sanitize_content_core.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ function neutralizeCommands(s) {
function neutralizeAllMentions(s) {
// Replace @name or @org/team outside code with `@name`
// No filtering - all mentions are neutralized
return s.replace(/(^|[^\w`])@([A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?(?:\/[A-Za-z0-9._-]+)?)/g, (m, p1, p2) => {
return s.replace(/(^|[^\w`])@([A-Za-z0-9](?:[A-Za-z0-9_-]{0,37}[A-Za-z0-9])?(?:\/[A-Za-z0-9._-]+)?)/g, (m, p1, p2) => {
// Log when a mention is escaped to help debug issues
if (typeof core !== "undefined" && core.info) {
core.info(`Escaped mention: @${p2} (not in allowed list)`);
Expand Down
Loading