Skip to content

atlas-core: topics/projects filters are unescaped SQL LIKE patterns — % and _ act as wildcards, a quote crosses JSON element boundaries #5375

Open

Description

@dezren39

Bug: atlas-core topics/projects filters are unescaped SQL LIKE patterns — %/_ in a filter value act as wildcards and a " crosses JSON element boundaries

pkgs/atlas-search/crates/atlas-core/src/filters.rs builds the topics/projects
filter (the values stored in l.topics / l.projects are JSON list text such as
["nixos","backup"]) as a raw LIKE pattern with the user-supplied value
interpolated verbatim:

  • Filters::where_clause (filters.rs:177-182):
    clauses.push(format!("{col} LIKE ?")) with param format!("%\"{v}\"%")
    for l.topics and l.projects.
  • Filters::session_clauses (filters.rs:213-218): the identical construction
    for the Turso vector branch's session subquery.

There is no escaping and no ESCAPE clause anywhere in atlas-core. SQL LIKE
treats % as "any run of characters" and _ as "any single character", so a
filter value is a pattern, not a literal topic/project name:

Verified (laptop, origin/main a65000a)

  1. Rust probe through atlas-core itself (temporary test in a scan worktree at
    a65000a, cargo test -p atlas-core --test probe_like): Filters::new
    with topics ["my_topic", "100%", "a\",\"b"] and projects ["%"] emits
    l.topics LIKE ? / l.projects LIKE ? clauses whose params are the raw
    interpolations %"my_topic"%, %"100%"%, %"a","b"%, %"%"% in both
    where_clause and session_clauses.
  2. SQLite probe of exactly that pattern against JSON-list rows:
    • filter my_topic matches ["my-topic","other"] and ["myXtopic"]
      (the _ wildcard) — neither row has a topic named my_topic;
    • filter % matches every non-empty topics list
      (["my-topic","other"], ["myXtopic"], ["a","b"], ["100 days"]) —
      the filter silently becomes a no-op;
    • filter a","b matches ["a","b"] — the embedded " crosses the JSON
      element boundary, so two distinct topics satisfy a filter for one
      (nonexistent) topic;
    • filter 100% matches ["100 days"];
    • control filter my-topic matches only ["my-topic","other"], as intended.

Why it matters

Filter-before-rank with exact classification filters is a headline contract of
atlas-core (filters.rs module doc; step-2 PR #5352). A topic or project whose
name legitimately contains _ (common: my_topic, pm_stack) over-matches
unrelated sessions, and a % value disables the filter entirely — wrong
results are returned as if the filter had been applied, with no error or note.
Because both branch constructions share the flaw, FTS and vector results are
affected identically, so fusion cannot mask it. The step-3 CLI (#5353) will
surface these filters directly to users.

Note on parity: the construction is inherited from the production Python's
Filters (the port mirrors its SQL byte-for-byte and the parity fixtures pin
it). This is a deliberate-deviation candidate: the fix changes the recorded
SQL/params for values containing %, _, \, or ", so the parity fixtures
should gain these cases as documented deviations rather than silently
diverging — the Python side carries the same bug and should get the same fix
or an explicit waiver.

Acceptance criteria

  • A topics/projects filter value matches only a JSON element exactly equal
    to it: %, _, and \ in the value are treated as literal characters
    (e.g. escaped with an explicit ESCAPE clause), and a value containing
    " cannot match across element boundaries.
  • Both construction sites are fixed consistently: where_clause
    (filters.rs, FTS/local) and session_clauses (filters.rs, Turso vector
    branch subquery).
  • New tests pin at least: filter my_topic does not match my-topic or
    myXtopic rows but does match a my_topic row; filter % matches only
    a literal % element (not every list); filter a","b does not match
    ["a","b"]; ordinary values (nixos) behave exactly as before.
  • The parity fixtures / step-6 Gate B (Atlas step 6: golden-parity gate (Gate B) + cutover #5356) record the changed params
    for wildcard-bearing values as an intentional deviation from the Python,
    or the Python construction is fixed in step with a note on this issue.
  • Existing atlas-core unit + parity tests pass (cargo test -p atlas-core),
    excepting fixture expectations updated per the previous criterion.

Activity

  1. added
    kind/bugSomething isn't working (prefixed form of the classic bug label)
    task/small-fixQueue packet kind: bounded fix with reproduction and targeted tests
    queue/queuedHas a queued task in the Supabase agent queue
    source/creatorFiled by the new-issue-creator job
    priority/lowNice to have; do when convenient
    app/atlas-searchpkgs/atlas-search (Atlas search service + atlas-embed)
    on Nov 4, 2026
  2. dezren39 commented on Nov 4, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · filed

    • Agent: new-issue-creator (lane A, new-issue-creator-code-a)
    • Session / ID: new-issue-creator-code-a cron run 2026-11-04 03:35 CST
    • Found via: filed this run
    • Task: Filed this verified bug: atlas-core topics/projects filters build unescaped SQL LIKE patterns in both where_clause and session_clauses (filters.rs), so %/_ act as wildcards and a quote crosses JSON element boundaries. Verified by a Rust probe through atlas-core itself plus a SQLite probe of the exact pattern at origin/main a65000a.
    • Expectation: effort s — escape LIKE metacharacters (explicit ESCAPE) at both construction sites, pin the wildcard/quote cases with tests, and record the changed params as an intentional parity deviation for Gate B (Atlas step 6: golden-parity gate (Gate B) + cutover #5356).
    • Links: evidence body in this issue; queue packet packet-5375-fix.json (repo-health-code-pilot)
  3. added
    work/assignedQueue worker lifecycle state
    queue/claimedQueue worker lifecycle state
    and removed
    queue/queuedHas a queued task in the Supabase agent queue
    on Nov 4, 2026
  4. dezren39 commented on Nov 4, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · handed off

  5. 4 remaining items

  6. added
    effort/sSmall: expected under 2 hours of work
    and removed
    queue/claimedQueue worker lifecycle state
    work/assignedQueue worker lifecycle state
    on Nov 5, 2026
  7. dezren39 commented on Nov 5, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · triaged

  8. dezren39 commented on Nov 5, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · triaged (correction)

  9. dezren39 commented on Nov 5, 2026

    @dezren39
    MemberAuthor

    Bound and launched OpenCode session ses_e5e56110d2fffevRYx4fXqxZ1t for queue task 9838 (issue #5375). The task was already claimed and handed off by repo-worker-code.

  10. dezren39 commented on Nov 5, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · picked up

  11. dezren39 commented on Nov 5, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · blocked

    • Agent: queue worker (repo-worker-code), lane opencode
    • Session / ID: OpenCode session ses_e5e56110d2fffevRYx4fXqxZ1t; task 9838
    • Found via: laptop execution access check
    • Task: task 9838 is blocked: laptop-agent returned HTTP 502 from its broker, and the documented SSH fallback could not initialize because the runtime proxy environment is unavailable. No laptop worktree or branch was created; no edits or tests were run in the sandbox.
    • Expectation: resume task 9838 when laptop execution access is restored; create the task worktree on the laptop and continue with implementation and targeted tests.
    • Links: task 9838; issue atlas-core: topics/projects filters are unescaped SQL LIKE patterns — % and _ act as wildcards, a quote crosses JSON element boundaries #5375
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

app/atlas-searchpkgs/atlas-search (Atlas search service + atlas-embed)effort/sSmall: expected under 2 hours of workkind/bugSomething isn't working (prefixed form of the classic bug label)priority/lowNice to have; do when convenientsource/creatorFiled by the new-issue-creator jobtask/small-fixQueue packet kind: bounded fix with reproduction and targeted tests

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions