css: reject `*` local names in attribute selectors instead of panicking (#31304)
### What
Fuzzer-found panic: a 3-byte CSS input `[|*` crashes the selector parser
with `panic: internal error: entered unreachable code`.
```sh
BUN_FEATURE_FLAG_INTERNAL_FOR_TESTING=1 bun -e 'require("bun:internal-for-testing").cssInternals.minifyTest("[|*", "")'
```
Any attribute selector whose qualified name ends in `*` reproduces it:
`[|*]`, `[*|*]`, `[ns|*]` (with `@namespace ns`), regardless of how the
stylesheet is fed to the CSS parser.
### Cause
`parse_qualified_name_eplicit_namespace_helper` in
`src/css/selectors/parser.rs` accepted `*` as a local name
unconditionally:
```rust
Token::Delim(c) if *c == b'*' as u32 => {
return Ok(OptionalQName::Some(namespace, None));
}
```
The upstream `selectors` crate guards this arm with `!in_attr_selector`,
because attribute selectors must have an identifier local name — there
is no universal attribute selector. Without the guard,
`parse_attribute_selector` receives a qualified name with no local name
and hits `unreachable!()` (`ln.unwrap_or_else(|| unreachable!())`).
### Fix
Restore the `!in_attr_selector` guard on the `*` arm, so `[|*`, `[*|*]`,
`[ns|*]` fall through to the existing `InvalidQualNameInAttr` parse
error ("Invalid qualified name in attribute selector: *") instead of
panicking. Element selectors like `*|*` and valid attribute selectors
like `[*|attr]` / `[|attr]` are unaffected.
### Verification
- New test `test/js/bun/css/attr-selector-namespace-star.test.ts` panics
the test runner without the fix and passes with it (covers the exact
fuzzer input in a child process, the bracketed/namespaced variants, and
the still-valid forms).
- `test/js/bun/css/css.test.ts`, `doesnt_crash.test.ts`,
`nth-anplusb-ident.test.ts` pass with the change. R
robobun committed
5bf4941b9f4f0e7f8e77a0978a9bf34350742f34
Parent: a8aec30
Committed by GitHub <noreply@github.com>
on 5/24/2026, 3:07:15 AM