Symptom
The __search companion column — declared invisible to clients — is echoed in every record body: query results, GET by id, /search hits, and the 201 create body. Still present after restarting with OS_SEARCH_PINYIN_ENABLED=false.
The $searchFields-override half of the contract IS correctly enforced (supplying __search as an override → 400 "is hidden"); only the default-projection strip is missing.
Root cause
packages/objectql/src/search-companion.ts provisions the column hidden + system + readonly + searchable:false. Those flags only keep it out of auto-views, the $search auto-default, and $searchFields overrides — no read path strips it from the default projection, so hidden system columns come back (as organization_id does).
Confirmed still present on origin/main (76d74ecb): the provisioning (lines 151–154) still sets hidden / readonly / system / searchable:false, with no default-projection strip on any read path.
Impact
Disclosure risk is low — the value derives from the display field only, and secret/FLS-restricted sources are excluded. But the declared client-invisibility contract is broken and every payload carries a private index column.
Reproduction
POST /api/v1/data/showcase_contact/query {"search":"zhangwei","limit":1}
→ the returned record contains "__search":"zhangwei zw"
GET /api/v1/data/showcase_account/
→ "__search":"huaningkeji hnkj"
GET /api/v1/search?q=INV-1003
→ each hit.record carries a __search key
The 201 body of a create carries it too. Still echoed after restarting with OS_SEARCH_PINYIN_ENABLED=false.
Source
Extracted from the QA run #7629 (framework 92f26f7, console 6314e87f).
Symptom
The
__searchcompanion column — declared invisible to clients — is echoed in every record body: query results, GET by id,/searchhits, and the 201 create body. Still present after restarting withOS_SEARCH_PINYIN_ENABLED=false.The
$searchFields-override half of the contract IS correctly enforced (supplying__searchas an override → 400 "is hidden"); only the default-projection strip is missing.Root cause
packages/objectql/src/search-companion.tsprovisions the columnhidden+system+readonly+searchable:false. Those flags only keep it out of auto-views, the$searchauto-default, and$searchFieldsoverrides — no read path strips it from the default projection, so hidden system columns come back (asorganization_iddoes).Confirmed still present on
origin/main(76d74ecb): the provisioning (lines 151–154) still setshidden/readonly/system/searchable:false, with no default-projection strip on any read path.Impact
Disclosure risk is low — the value derives from the display field only, and secret/FLS-restricted sources are excluded. But the declared client-invisibility contract is broken and every payload carries a private index column.
Reproduction
The 201 body of a create carries it too. Still echoed after restarting with
OS_SEARCH_PINYIN_ENABLED=false.Source
Extracted from the QA run #7629 (framework 92f26f7, console 6314e87f).