Skip to content

fix(queries): don't split on semicolons inside string literals - #918

Merged
ErikBjare merged 3 commits into
ActivityWatch:masterfrom
TimeToBuildBob:fix/querystr-semicolons-in-strings
Jul 25, 2026
Merged

fix(queries): don't split on semicolons inside string literals#918
ErikBjare merged 3 commits into
ActivityWatch:masterfrom
TimeToBuildBob:fix/querystr-semicolons-in-strings

Conversation

@TimeToBuildBob

@TimeToBuildBob TimeToBuildBob commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Root cause

querystr_to_array() naively splits on all ; characters. Category rules get embedded as JSON string literals inside the query — for example a rule with regex foo;bar becomes:

events = categorize(events, [["Work", {"type": "regex", "regex": "foo;bar"}]]);

A plain .split(';') shreds this into two broken fragments before the query reaches aw-server, causing a parse error. Both aw-server-python and aw-server-rust handle ; inside quoted string literals correctly — the bug is entirely in the client-side splitter.

This explains the forum report in #724 and why the UI warning (PR #724) was only a workaround.

Fix

src/queries.ts

The new querystr_to_array tracks double-quoted string context (with backslash-escape support) and only treats ; as a statement separator when it appears outside a string literal.

Also exports querystr_to_array for direct unit testing and adds 4 tests:

  • basic multi-statement split
  • semicolon inside string literal (the regression case)
  • escaped double-quote inside string
  • whitespace/empty statement filtering

View files (7 updated)

All view-level query splitting paths now use querystr_to_array instead of a naive .split(';'):

  • views/Alerts.vue, views/Search.vue, views/Graph.vue, views/Report.vue, views/QueryExplorer.vue — replace .split(';').map(s => s.trim() + ';')
  • views/Timeline.vue, views/Trends.vue — replace the longer split/trim/filter/map chain

Investigation notes (from #724 comment thread)

Checked aw-server-rust's lexer (aw-query/src/lexer.rs): the string-literal rule "([^\"]|(\\\"))*" is matched before the bare ;Token::Semi rule, so semicolons inside quoted strings are correctly captured as part of Token::String. The server is not the problem.

Fixes #724.

@codecov

codecov Bot commented Jul 25, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 38.63%. Comparing base (58173d8) to head (96d2096).

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #918      +/-   ##
==========================================
+ Coverage   38.11%   38.63%   +0.51%     
==========================================
  Files          42       42              
  Lines        2259     2278      +19     
  Branches      428      435       +7     
==========================================
+ Hits          861      880      +19     
  Misses       1377     1377              
  Partials       21       21              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@greptile-apps

greptile-apps Bot commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR introduces quote-aware AQL statement splitting and routes all previously identified view-level query paths through the shared helper.

  • Preserves semicolons inside double-quoted literals, including escaped quotes.
  • Updates Alerts, Graph, Query Explorer, Report, Search, Timeline, and Trends to pass the helper output directly to the query client.
  • Adds focused regression coverage for splitting, quoted semicolons, escapes, whitespace, and empty statements.

Confidence Score: 5/5

The query-splitting fix appears safe to merge.

No blocking failure remains in the previously reported view-level query-splitting paths.

Important Files Changed

Filename Overview
src/queries.ts Adds the shared quote-aware AQL statement splitter and regression coverage confirms the reported literal-semicolon case.
src/views/QueryExplorer.vue Replaces raw-query semicolon splitting with the shared helper before direct query execution.
src/views/Search.vue Routes generated search queries through the shared statement splitter.
src/views/Timeline.vue Uses the shared splitter for the newly added AFK-filter query path.
src/views/Trends.vue Uses the shared splitter for generated category trend queries.
src/views/Report.vue Routes report queries through the shared splitter.
src/views/Graph.vue Routes graph queries through the shared splitter.
src/views/Alerts.vue Routes alert queries through the shared splitter.

Reviews (2): Last reviewed commit: "fix(views): use querystr_to_array in all..." | Re-trigger Greptile

Comment thread src/queries.ts
@TimeToBuildBob
TimeToBuildBob force-pushed the fix/querystr-semicolons-in-strings branch from aafe7dc to e42479b Compare July 25, 2026 00:20
@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

CI flags two lint warnings (treated as errors with --max-warnings=0):

  1. src/queries.ts:12no-shadow: the inner escape variable shadows the global escape. Renaming it (e.g. isEscaped) will fix this.
  2. test/unit/queries.test.node.ts:246 — prettier: trailing newline. npx prettier --write test/unit/queries.test.node.ts should clear it.

Greptile also flagged a P1 (confidence 3/5): view-level query execution paths apparently still call split(';') directly rather than going through the new querystr_to_array. So the regression case (category regex with semicolons) would still fire for those paths. That looks like a real gap worth addressing before merge.

@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

Both issues are now fixed and pushed.

Lint (now passing):

  • Renamed escapeinEscape in queries.ts to avoid shadowing the JS global
  • Collapsed the two-line string assignment in the test to satisfy prettier

View-level scope (Greptile P1 addressed):
Extended querystr_to_array to all seven views that were still doing a naive .split(';'):

  • Alerts.vue, Search.vue, Graph.vue, Report.vue, QueryExplorer.vue — replaced query.split(';').map(s => s.trim() + ';')
  • Timeline.vue, Trends.vue — replaced the longer 4-step split/trim/filter/map chain

CI is running on the updated branch.

@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

@greptileai review

TimeToBuildBob and others added 3 commits July 25, 2026 00:27
querystr_to_array() was naively splitting on all ';' characters, which
shredded category rules whose regex contained a semicolon. For example,
a rule with regex "foo;bar" gets embedded into the query as:

  events = categorize(events, [["Work", {"type":"regex","regex":"foo;bar"}]]);

A plain .split(';') would cut this into two broken fragments before the
query ever reaches aw-server, causing a parse error on the server side.
Both aw-server-python and aw-server-rust handle semicolons correctly
inside quoted strings — the bug is purely in the client-side splitter.

The fix tracks string-literal context (double-quoted, with backslash
escape support) and only treats ';' as a statement separator when it
appears outside a string.

Also exports querystr_to_array for direct unit testing and adds 4 unit
tests covering: basic split, semicolon-in-string (the regression case),
escaped quote handling, and whitespace filtering.

Closes ActivityWatch#724 (the right fix is here, not a UI warning/block).
…tier

- `escape` is a reserved JS global; rename to `inEscape` to silence no-shadow
- collapse two-line const assignment in test to satisfy prettier line-length rule
Seven views were still doing a naive .split(';') on query strings before
sending them to aw-client. Category rules with semicolons in their regex
(e.g. "foo;bar") embed as JSON string literals inside the AQL query, so a
plain split shreds them before they reach the server.

Replace every .split(';').map(s => s.trim() + ';') chain (and the two
longer filter-variant chains in Timeline and Trends) with querystr_to_array,
which correctly skips semicolons inside double-quoted string literals.
@TimeToBuildBob
TimeToBuildBob force-pushed the fix/querystr-semicolons-in-strings branch from 557161e to 96d2096 Compare July 25, 2026 00:28
@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

CI-green and mergeable (Greptile 5/5) — waiting only on a maintainer click.

This PR is ready to merge, but the bot has pull-only access to this repo and can't self-merge — surfacing it here so it isn't lost. The monitoring loop will stop re-flagging it now that this note is posted.

@ErikBjare
ErikBjare merged commit cefc618 into ActivityWatch:master Jul 25, 2026
8 checks passed
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.

2 participants