fix: escape regex metacharacters in WildcardMatch - #2916
Conversation
WildcardMatch built a regex from the task name translating only "*", then
called regexp.MustCompile on it. A task name containing a regex metacharacter
either panicked (e.g. "c++" -> "invalid nested repetition operator") or
matched too loosely ("a.b" matched "axb"). Since FindMatchingTasks calls
WildcardMatch on every task, one such name breaks matching for the whole
Taskfile. Escape the name with regexp.QuoteMeta, keeping "*" as the only
wildcard.
|
Did you read our contribution guide? https://taskfile.dev/docs/contributing#ai-usage-policy |
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
left a comment
There was a problem hiding this comment.
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
}
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).WildcardMatchbuilds a regex from the task name, translating only*, and callsregexp.MustCompileon it:The raw task name is injected into the pattern, so any other metacharacter is interpreted as regex syntax:
c++yields^c++$, andregexp.MustCompilepanics withinvalid nested repetition operator: ++.a.bmatches the callaxb(the.acts as a wildcard). A realistic footgun:deploy.prodgets run by a mistypedtask deploy-prod.FindMatchingTaskscallsWildcardMatch(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.QuoteMetabefore building the pattern, then turn the (now escaped)\*back into the wildcard group:*remains the only wildcard; everything else is matched literally.Testing
Added
TestTaskWildcardMatchcovering the existingbuild-*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 fulltaskfile/astpackage suite passes and the module builds clean.