Skip to content

fix: escape regex metacharacters in WildcardMatch - #2916

Open
vsaraikin wants to merge 1 commit into
go-task:mainfrom
vsaraikin:fix/wildcard-match-escape
Open

fix: escape regex metacharacters in WildcardMatch#2916
vsaraikin wants to merge 1 commit into
go-task:mainfrom
vsaraikin:fix/wildcard-match-escape

Conversation

@vsaraikin

Copy link
Copy Markdown
Contributor

What

A task whose name contains a regex metacharacter breaks task matching for the whole Taskfile — either with a hard panic or a silent mis-match.

(*Task).WildcardMatch builds a regex from the task name, translating only *, and calls regexp.MustCompile on it:

regexStr := fmt.Sprintf("^%s$", strings.ReplaceAll(taskName, "*", "(.*)"))
regex := regexp.MustCompile(regexStr)

The raw task name is injected into the pattern, so any other metacharacter is interpreted as regex syntax:

  • Panic / crash — a task named c++ yields ^c++$, and regexp.MustCompile panics with invalid nested repetition operator: ++.
  • False positive — a task named a.b matches the call axb (the . acts as a wildcard). A realistic footgun: deploy.prod gets run by a mistyped task deploy-prod.

FindMatchingTasks calls WildcardMatch(call.Task) on every task whenever the requested name isn't a direct/alias match, so a single task with such a name breaks matching for the entire Taskfile.

Fix

Escape the task name with regexp.QuoteMeta before building the pattern, then turn the (now escaped) \* back into the wildcard group:

regexStr := fmt.Sprintf("^%s$", strings.ReplaceAll(regexp.QuoteMeta(taskName), `\*`, "(.*)"))

* remains the only wildcard; everything else is matched literally.

Testing

Added TestTaskWildcardMatch covering the existing build-* wildcard behavior plus the metacharacter cases (c++, a.b, deploy.prod). On the current code the test panics (invalid nested repetition operator); with the fix it passes. The full taskfile/ast package suite passes and the module builds clean.

@vmaerten

Copy link
Copy Markdown
Member

Did you read our contribution guide? https://taskfile.dev/docs/contributing#ai-usage-policy
Any AI usage should be disclosed.
The PR description and the PR itself both look AI-generated.
Your comment also looks AI-generated to me.
We want to communicate with humans, not bot.

@vsaraikin

Copy link
Copy Markdown
Contributor Author

Did you read our contribution guide? https://taskfile.dev/docs/contributing#ai-usage-policy Any AI usage should be disclosed. The PR description and the PR itself both look AI-generated. Your comment also looks AI-generated to me. We want to communicate with humans, not bot.

Fair point, sorry about that. Yeah I used an AI tool to clean up the writeup and I should've flagged the description – my bad. But the comments are mine.

@trulede trulede left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would request that you probably should write a normal Task test to capture the behaviour from end-2-end. Similar table driven approach, which is good. Then drop the unit test from the PR if you are happy with that.

This code (AI generated, but I suspected as much) shows a faster algorithm which I think should be considered in the PR.

func (t *Task) WildcardMatch(name string) (bool, []string) {
	names := append([]string{t.Task}, t.Aliases...)

	for _, taskName := range names {
		// First, quick check without regex if there are no wildcards
		if !strings.Contains(taskName, "*") {
			if taskName == name {
				return true, nil
			}
			continue
		}

		pattern := regexp.QuoteMeta(taskName)
		pattern = strings.ReplaceAll(pattern, `\*`, "(.*)")
		regex := regexp.MustCompile("^" + pattern + "$")
		wildcards := regex.FindStringSubmatch(name)
		if len(wildcards) > 1 {
			return true, wildcards[1:]
		}
	}

	return false, nil
}

A task whose name contains a regex metacharacter breaks task matching for
the whole Taskfile. WildcardMatch built a pattern from the raw task name,
so a task named "c++" panicked in MustCompile and a task named "a.b"
matched "axb".

Escape the name with QuoteMeta before turning "*" back into the wildcard
group, and skip the regex entirely when the name has no wildcard.
@vsaraikin
vsaraikin force-pushed the fix/wildcard-match-escape branch from a16e7f6 to f19bb31 Compare September 4, 2026 12:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants