fix(admin): address review findings

- 0034's backfill set target_id to the acting admin for instance- and
  team-scoped quota policy changes, which are filed under the actor and have
  no user target. It now returns NULL for those, matching what quotas.py
  records going forward, with a test covering all three scopes.
- The CSV export wrote every cell verbatim. user_agent is an attacker's raw
  header, recorded without authenticating on a denied login, and the export
  is opened in a spreadsheet by an admin -- a cell starting "=" would be
  evaluated there. Every cell now has a leading formula trigger neutralized.
- The activity feed applied every response it received, so a slow reply for
  an old filter could overwrite the current one. Guarded by a request id,
  the same way settings/Analytics already does.
- The activity detail expander and the top-user drill-down were row onClick
  handlers, unreachable without a mouse. Both are real buttons now, the
  expander carrying aria-expanded and a label naming its event.
- FLOW_LABELS was applied to both breakdowns in the spend modal, so a model
  named "fallback" or "workflow" rendered as a flow description. Only the
  flow table maps keys now.
- Changing the range in that modal left the previous range's totals and
  chart on screen while the new request was in flight.
This commit is contained in:
arc53-machine committed 2026-09-22 11:13:11 +01:00
1 parent 64200a7f23
commit 2ea9d66789
7 files changed
+141 -25

No files matched your search

+23
View File
@@ -193,6 +193,29 @@ class TestExport:
lines = body.strip().split("\n")
assert [json.loads(line)["id"] for line in lines] == ["1", "2"]
def test_csv_cells_cannot_smuggle_a_spreadsheet_formula(self, client):
"""``user_agent`` is an unauthenticated attacker's raw header."""
repo = _repo(
rows=[_row(user_agent="=cmd|'/c calc'!A0", actor_id="+1", ip="-2")]
)
with _admin(repo):
body = client.get(
"/api/admin/activity/export?format=csv"
).get_data(as_text=True)
row = next(csv.DictReader(io.StringIO(body)))
assert row["user_agent"].startswith("'=")
assert row["actor_id"].startswith("'+")
assert row["ip"].startswith("'-")
def test_csv_leaves_ordinary_cells_alone(self, client):
with _admin(_repo()):
body = client.get(
"/api/admin/activity/export?format=csv"
).get_data(as_text=True)
row = next(csv.DictReader(io.StringIO(body)))
assert row["user_agent"] == "curl/8"
assert row["event"] == "oidc_login"
def test_empty_csv_export_still_has_its_header(self, client):
with _admin(_repo(rows=[])):
body = client.get("/api/admin/activity/export").get_data(as_text=True)