Finding
Strix exact-head run 34746057545 on #1055 head 50c4935eef1029467595f7004818643598b737c9 completed SUCCESS and uploaded strix-reports artifact 10318318320 (digest sha256:45c82c7f1f044185a4795b4b4db37d2ce30346e44e516ce027f2bf53fe89581e). The report contains one Medium finding, CWE-862: the post_read-gated GET /api/posts/{post_id}/summary can persistently create/update the shared corporate_entity catalog when no stored summary exists.
This is independently reproducible on protected main@83eba56149eb802cd63642c507c324c9976ec78e: read_post_summary authorizes visibility through _load_visible_post, derives a missing summary, then calls persist_post_summary(...) with live _corporate_hierarchy_inference_client() and _relation_verification_client(). persist_post_summary can reach canonical get_or_create_corporate_entity, whose mutation is global catalog state rather than a read-only projection.
The same codebase already treats catalog-enriching LLM work as a write boundary on extract-keymen / customer-hint resolution (post_admin). The mismatch is therefore an authorization/integrity defect, not merely an LLM-cost concern.
Realistic RED
Add a real-service API regression using an authenticated account with post_read but no post_admin and a visible post with no persisted summary:
- configure summary generation so the returned role contains an organization actor not already uniquely bound in the corporate catalog;
- call
GET /api/posts/{post_id}/summary as the post_read-only account;
- assert the summary read may still succeed/persist its summary projection, but the global
corporate_entity table and hierarchy remain unchanged and live hierarchy/relation-verification clients are not admitted for catalog mutation;
- call the same path as a
post_admin account and prove catalog enrichment remains available where the write permission is explicit;
- repeat for stale-summary fallback/provider failure to prove no privilege-dependent cleanup regression.
Do not turn the GET itself into post_admin-only unless product authority explicitly changes the read contract. Do not hide the finding by disabling summary persistence or by introducing a tenant-local duplicate catalog.
Minimal causal repair
At the read-model/application boundary, separate summary read/persistence from optional global catalog enrichment. A post_read account may consume/derive a summary, but only an account with post_admin may provide live hierarchy-inference/relation-verification capabilities that can create or modify global catalog entries. Preserve existing exact-match consumption of already-known catalog identities where it is read-only.
Prefer an explicit capability/policy argument over implicit provider availability if that makes the invariant enforceable in domain tests. The canonical catalog implementation stays in corporate_entity_ingestion; no matching SQL/provider routing is copied into main.py.
Acceptance
post_read-only summary GET cannot create/update/delete corporate_entity or its hierarchy;
post_admin summary derivation may still perform the currently intended catalog enrichment;
- existing ABAC visibility and
post_read ability to read/derive a summary are preserved;
- summary projection persistence remains deterministic/idempotent and does not gain broader authorization;
- executable tests cover both permission classes and prove global catalog row-count/content invariance for
post_read;
- no provider/network call capable of catalog mutation is admitted from the low-privilege path;
- Strix finding is re-run on the repaired exact head and becomes absent/closed by code-path evidence;
- no provider/model override, local catalog clone, broad
post_admin substitution for all summary reads, warning suppression, force push, destructive rebase, or gate weakening.
Coordinate with #1077: the permission fix and the catalog connection-lease repair are separate invariants. One controls who may mutate the shared catalog; the other controls how long DB resources are retained around provider I/O.
Finding
Strix exact-head run
34746057545on #1055 head50c4935eef1029467595f7004818643598b737c9completed SUCCESS and uploadedstrix-reportsartifact10318318320(digestsha256:45c82c7f1f044185a4795b4b4db37d2ce30346e44e516ce027f2bf53fe89581e). The report contains one Medium finding, CWE-862: thepost_read-gatedGET /api/posts/{post_id}/summarycan persistently create/update the sharedcorporate_entitycatalog when no stored summary exists.This is independently reproducible on protected
main@83eba56149eb802cd63642c507c324c9976ec78e:read_post_summaryauthorizes visibility through_load_visible_post, derives a missing summary, then callspersist_post_summary(...)with live_corporate_hierarchy_inference_client()and_relation_verification_client().persist_post_summarycan reach canonicalget_or_create_corporate_entity, whose mutation is global catalog state rather than a read-only projection.The same codebase already treats catalog-enriching LLM work as a write boundary on
extract-keymen/ customer-hint resolution (post_admin). The mismatch is therefore an authorization/integrity defect, not merely an LLM-cost concern.Realistic RED
Add a real-service API regression using an authenticated account with
post_readbut nopost_adminand a visible post with no persisted summary:GET /api/posts/{post_id}/summaryas thepost_read-only account;corporate_entitytable and hierarchy remain unchanged and live hierarchy/relation-verification clients are not admitted for catalog mutation;post_adminaccount and prove catalog enrichment remains available where the write permission is explicit;Do not turn the GET itself into
post_admin-only unless product authority explicitly changes the read contract. Do not hide the finding by disabling summary persistence or by introducing a tenant-local duplicate catalog.Minimal causal repair
At the read-model/application boundary, separate summary read/persistence from optional global catalog enrichment. A
post_readaccount may consume/derive a summary, but only an account withpost_adminmay provide live hierarchy-inference/relation-verification capabilities that can create or modify global catalog entries. Preserve existing exact-match consumption of already-known catalog identities where it is read-only.Prefer an explicit capability/policy argument over implicit provider availability if that makes the invariant enforceable in domain tests. The canonical catalog implementation stays in
corporate_entity_ingestion; no matching SQL/provider routing is copied intomain.py.Acceptance
post_read-only summary GET cannot create/update/deletecorporate_entityor its hierarchy;post_adminsummary derivation may still perform the currently intended catalog enrichment;post_readability to read/derive a summary are preserved;post_read;post_adminsubstitution for all summary reads, warning suppression, force push, destructive rebase, or gate weakening.Coordinate with #1077: the permission fix and the catalog connection-lease repair are separate invariants. One controls who may mutate the shared catalog; the other controls how long DB resources are retained around provider I/O.