Skip to content

fix: footer parser does not escape special chars for regex #4560

@GrayedFox

Description

@GrayedFox

Steps to Reproduce

  1. Create footer notes with special chars for example [1]

Current Behavior

The square brackets will be interpreted as special RegEx chars instead of escaped ones

Expected Behavior

String values when supplied via noteKeywords array should be escaped before being joined.

Affected packages

  • cli
  • core
  • prompt
  • config-angular

Possible Solution

I've already fixed this on our end as we were already using a custom footer parser. Here's a workaround, note that the parser regex here is our custom one (not the default one) as we have a wider range of valid footer tokens.

/**
 * Known Commitlint parser options, home grown as they are not well documented.
 *
 * Ref: https://github.com/conventional-changelog/commitlint/issues/3336
 */
export interface ParserOpts {
  headerCorrespondence?: string[];
  headerPattern?: RegExp;
  noteKeywords?: string[];
  notesPattern?: (noteKeywords: string) => RegExp;
}

/**
 * Helper that escapes special characters in a string for use in a regular expression.
 */
const escapeRegExp = (string: string): string => string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');

/**
 * Ensures links are treated as footers.
 */
const linkKeywords: string[] = ['[1]', '[2]', '[3]', '[4]', '[5]', '[6]', '[7]', '[8]', '[9]'];

/**
 * Ensures project names are treated as footers.
 */
const projectKeywords: string[] = ['PROJ1', 'PROJ2', 'PROJ3'];

/**
 * Ensures the following miscellaneous keywords are treated as footers.
 */
const miscKeywords: string[] = ['BREAKING CHANGE', 'BREAKING-CHANGE', 'JIRA'];

/**
 * Keywords (or 'notes') are how the Commitlint parser knows something is a footer or not.
 *
 * Footers must be affixed with a colon `:`, space ` `, or a hyphen `-`:
 *
 *  - `JIRA:` is a valid footer
 *  - `JIRA ` is a valid footer
 *  - `JIRA-` is a valid footer
 *  - `[1]:` is  valid footer
 *  - `[1] ` is a valid footer
 *  - `[1]-` is a valid footer
 *  - `PROJ` without a colon, hyphen, or space affix will not be recognized as a footer
 *  - `PROJ1-123` will be recognized as a footer
 *  - `PROJ2:123` will be recognized as a footer
 *  - `PROJ3-123 https://project.techops.com/TECHOPS/issue/123` is also fine since footer matching
 *     cares only about the starting sequence of the string
 */
const qantasKeywords: string[] = [...miscKeywords, ...projectKeywords, ...linkKeywords];

/**
 * This is the revised notes (footer recognition) pattern but now with properly escaped chars.
 *
 * It also now recognises footers that have a hyphen or space affix provided the footer token is
 * at the beginning of the string.
 */
const notesPatternWithEscapedChars = (noteKeywords: string): RegExp => {
  // split the string into back into individual keywords
  const keywords = noteKeywords.split('|');

  // escape each keyword individually to handle '[1]' etc.
  const escapedKeywords = keywords.map(escapeRegExp);

  // join them back into a regex-safe selection string
  const safeSelection = escapedKeywords.join('|');

  // return the new RegExp that allows footers with a dash, colon, or space after it
  return new RegExp(`^[\\s|*]*(${safeSelection})[:\\s-]+(.*)`);
};

const parserOpts: ParserOpts = {
  noteKeywords: qantasKeywords,
  notesPattern: notesPatternWithEscapedChars,
};

export default parserOpts;

Context

No response

commitlint --version

@commitlint/[email protected]

git --version

git version 2.50.1 (Apple Git-155)

node --version

v22.18.0

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions