fix(chrome): validate attribute name in unknown-prefix branch

A selector like `foo]:has(*),body[x:value` previously produced
[foo]:has(*),body[x="..."], a syntactically valid CSS selector that
escaped the attribute match and selected `body`. Reject anything that
isn't a plain HTML attribute name.
This commit is contained in:
pj committed 2026-05-03 10:59:30 +07:00
1 parent 8c7470a1a6
commit a69bac7abd
2 files changed
+24

No files matched your search

+9
View File
@@ -3,10 +3,16 @@ package chrome
import (
"errors"
"fmt"
"regexp"
"strings"
"unicode/utf8"
)
// attrNamePattern matches HTML attribute names that are safe to drop into a
// CSS attribute selector without escaping. This avoids selectors like
// `foo]:has(*),body[x="..."]` that would escape the intended match.
var attrNamePattern = regexp.MustCompile(`^[a-zA-Z][a-zA-Z0-9_-]*$`)
// TranslateStringSelector converts a legacy string selector ("id:foo",
// "descPrefix:bar") into a CSS selector or XPath expression usable from the
// chrome driver's TapSelector fallback path. The boolean return is true when
@@ -41,6 +47,9 @@ func TranslateStringSelector(selector string) (string, bool, error) {
case "placeholder", "placeholderValue", "hintText":
return `[placeholder="` + cssEscape(value) + `"]`, false, nil
default:
if !attrNamePattern.MatchString(kind) {
return "", false, fmt.Errorf("unsafe selector prefix %q", kind)
}
return `[` + kind + `="` + cssEscape(value) + `"]`, false, nil
}
}
+15
View File
@@ -64,6 +64,21 @@ func TestTranslateStringSelector_RejectsMissingPrefix(t *testing.T) {
}
}
func TestTranslateStringSelector_RejectsUnsafePrefix(t *testing.T) {
cases := []string{
`foo]:has(*),body[x:value`,
`x y:value`,
`x"y:value`,
`*:value`,
`(:value`,
}
for _, selector := range cases {
if _, _, err := TranslateStringSelector(selector); err == nil {
t.Errorf("%q: expected error for unsafe prefix", selector)
}
}
}
func TestCSSEscape_ControlCharactersAndNUL(t *testing.T) {
cases := []struct {
name string