KeeperHub / KeeperHub/keeperhub
Expression-mode condition validation reads a regex pattern's own punctuation as operators
- Dominant language
- TypeScript
- Stars
- 24
- Forks
- 93
- Avg merge
- 1d 8h
- Merged PRs (30d)
- 266
Description
### Before filing
- [x] Searched open and closed issues and PRs: the only related thread is #2407 (the operator itself) and its PR #2445, which is where this was found.
- [x] Checked current behaviour on `staging` at `6c8eb1b0ebe1f7ece3f7c32b324a095fee41ab39`.
- [x] This is one change: the expression-mode validator's token scan, not the evaluator.
### Reason: what you cannot do today
`validateConditionExpressionUI` tokenises the expression with the pattern literal still in it, so the pattern's own punctuation is read as expression syntax. The pattern the docs, the MCP schema string and two runtime error messages recommend is refused in expression mode:
```
^0x[0-9a-fA-F]{40}$ -> Operator "-" must have exactly one space before it
^[a-c]+$ -> same
(?:0x)?[0-9a-f]+ -> same
```
So an author who builds the condition in the visual builder gets a clean save, and the same condition typed in expression mode is rejected with a message about an operator that is not in their condition. The other four scanners in `validateConditionExpression` were fixed for this by scanning a masked copy (`validateConditionExpression` masks once and passes it down); the expression-mode tokeniser was not part of that change.
### Scope
One function and its tokeniser, and it ships on its own: the fix does not depend on the evaluator change and the evaluator change does not depend on it. Only expression mode reaches this code path, which is why the visual builder is unaffected and why it is not a blocker on #2445.
### Plan
Mask quoted literals before the expression-mode tokeniser reads the string, the way `maskStringLiterals` already does for the five scanners in `validateConditionExpression`, and keep the masked copy's length so error positions still line up with what the author typed. A test through `validateConditionExpressionUI` for `^0x[0-9a-fA-F]{40}$` and `^[a-c]+$` is the check that it landed, since expression mode is the only surface that reaches it.
Contributor guide
Research direction
Start at validateConditionExpressionUI and compare its token scan with the masked-copy approach already used by validateConditionExpression and maskStringLiterals. Add coverage through validateConditionExpressionUI for ^0x[0-9a-fA-F]{40}$ and ^[a-c]+$, preserving the masked copy's length so error positions remain aligned.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- backend, testing
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 78/100