Skip to content

audit: running a report writes no sys_audit_log row #31797

Open

Description

@objectstack-fleet

repo:cloud#3 · os-warren · session_01JQwxbZqfEd7pcckjz3pGqn

Filing gate: ② a finding from a dispatched card. cloud#3665 (Enterprise P2 item 4) lists "report run" among the events an enterprise SIEM must receive. Its dev measured at pin 6ed533f that no framework writer records it (report cloud#3665 comment 6731925007). The cloud lane does not dispatch it; triage routes it.

维护者速读

  • 问题:运行报表不留审计记录,安全审查问"谁导出/查看了哪张报表"时答不上来。
  • 要做:报表执行时写一行审计(谁、哪张报表、过滤条件的字段名、行数、时间),不记数据值。
  • 需要你做的:无,分诊定车道。

Evidence (pin 6ed533f)

  • read-audit.ts records record views only; the sys_audit_log.action enum has no report value; no writer on the report execution path. Seam: spec sys_audit_log.action → runtime consumer none.

Work

  1. Add an action value for a report run and write one row per run on the report execution path: principal, report name, filter field names (not values), row count, outcome.
  2. Keep it to one row per run, as the export door's run-level row does (feat(audit): every data export writes one run-level sys_audit_log row, and the export action comes back with its writer #31000).

Done when

  • Running a report leaves exactly one sys_audit_log row with the report name, filter fields and row count; a refused run writes none or an outcome row, as decided; tests pin it.

Dedupe: repo:objectstack-ai/objectstack is:issue "report run audit" → none.

Related: cloud#3665, cloud#4018, objectstack#31000.

Activity

  1. objectstack-fleet commented on Nov 9, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: domain:server · p3 · pm:queue · 2026-11-09T04:56Z

    ⛔ Not a claim.


    Generated by Claude Code

  2. added
    domain:serverDomain lane — seat card indexed by label:pm:seat
    pm:queueReady for the PM dispatch loop
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Nov 9, 2026
  3. objectstack-fleet commented on Nov 9, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · os-steve · session_01ADHkmQKZzp4DLtqiCkZZRM

    Claim: PM loop round 15 · 2026-11-09T06:29Z
    Session: session_01ADHkmSnFAgPUKAz3xraGoJ (card thread, dev) · seat session_01ADHkmQKZzp4DLtqiCkZZRM
    Account: os-steve
    Branch: claude/project-thread-r06jvv
    Worktree: objectstack-issue-31797
    Domain: domain:server
    Seat: domain:server#2
    File surface: packages/plugins/plugin-audit/src/** and its tests, the report door's file only to call the writer, i18n bundles if needed, root .changeset/. ⛔ No packages/spec/** without the seat re-scoping, no ui/**
    Container & model: S, mode:thread, model: default tier, effort: medium
    Clause-②: yes (widening: a new sys_audit_log.action value)
    Metadata-protocol: none
    Responsibility: finding of cloud#3665 (report cloud#3665 comment 6731925007), filed by repo:cloud#3
    Thread-read: none
    Premise: holds as filed, read at claim on origin/main ad622d30: the sys_audit_log action options have no report value, and the export door's run-level row (data-export-audit.ts) is the only run-level writer
    Serial constraints cleared: none; no other in-flight card on this seat touches plugin-audit

    Scope: Fixes #31797. One run of a report writes exactly one sys_audit_log row: the principal, the report name, the filter field names (never values), the row count and the outcome, following the export door's run-level precedent. Order: /mnt/project-files/server-seat2/standard-order.md + /mnt/project-files/server-seat2/31797-delta.md. Summons: the maintainer's 「server 任务很多,你应该跟进并发5」 (project chat, 2026-11-09T02:28Z).

  4. objectstack-fleet commented on Nov 9, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · os-steve · session_01ADHkmQKZzp4DLtqiCkZZRM

    待裁 · 「运行报表写审计」在服务端没有可挂的入口 · 2026-11-09T06:31Z

    一句话问题: 卡要求每次运行报表写一行审计,并记下报表名。但 main 上报表是在浏览器里拼出来的,服务端只看到一次次数据集查询,既拿不到报表名,也分不清这次查询是报表发的还是仪表盘发的。

    背景

    cloud#3665(企业版 SIEM)要求收到「报表运行」事件,cloud 车道据此开了本卡。卡线程第 0 步实测(树 ad622d30,读数 /mnt/project-files/server-seat2/31797-step0.md):

    所以卡面的证据(「报表执行路径上没有写入方」)属实,原因是这条路径根本不存在。

    Governing text

    协议声明:Clause-②: yes (widening)(新增一个 action 取值),Metadata-protocol: none。

    前提复核: git grep -n "_retiredReportsServiceProvider" origin/main -- packages/rest/src/rest-server.ts 有命中;git grep -n "queryDataset" origin/main -- ui/packages/plugin-report/src/DatasetReportRenderer.tsx 有命中。

    选项

    选项 做什么 客户可感知后果 工作量
    A 新建服务端「运行报表」接口:服务端读取报表定义、编出查询,并写一行带权威报表名的 report_run 审计;前端渲染器改走这个接口 SIEM 能看到「谁在何时运行了哪张报表」,名字由服务端给出,可信 新 REST 路由 + ui 改动 + 等于部分撤回 #20102 的退役方向,需要 ADR · 2–3 PR;无先例
    B 数据集查询请求体加一个可选的 report 字段,带了就记审计 报表名由客户端自报,可以不带,不能当合规控制;打开一张报表会写 N 行 REST 请求体加宽 + ui 改动 · 1–2 PR;不推荐
    C 审计服务端实际执行的读取:每次 POST /analytics/dataset/query 写一行 dataset_query,记录调用者、数据集名、所选维度和指标名、过滤字段名(不记值)、行数和结果。按导出接口的先例与写入方式实现,并像记录读取审计一样按数据集在安装时开启 SIEM 能看到「谁在什么过滤条件下读了哪个数据集、读了多少行」,覆盖报表和仪表盘;看不到报表名。只有开启了的数据集才写,不开启时日志量不变 plugin-audit + analytics-routes.ts · 1 PR;先例 #31000(导出)、read-audit(按对象开启)
    D 判定卡面前提不成立,关卡,把发现交回 cloud#3665:报表是客户端组合出来的,要审计报表需等 A 今天 SIEM 已能收到导出审计和记录读取审计,报表维度暂缺 0 PR

    业务含义直译:

    • A:像 Salesforce 的报表运行日志,记到「哪张报表」这一层。
    • C:像数据库的查询审计,记到「读了什么数据」这一层,更接近安全审查真正想知道的事;报表名要等 A 才有。
    • D:先不做,等产品决定报表要不要回到服务端执行。

    os-decision-facets
    ① 项目长远合理性:C 记录服务端实际执行的读取,每一行都是权威事实;A 要把报表执行搬回服务端,等于部分撤回 #20102 的退役方向;B 把客户端自报的名字写进审计。
    ② 实际业务拉动:cloud#3665 的企业 SIEM 要求收到报表事件;本席没有读到它是否必须精确到报表名(置信缺口)。
    ③ 防 AI 犯错:C 的 action 按记录内容命名,不会被误读成「报表运行」;B 的名字可以被省略或伪造。
    ④ 创业阶段不扩散:C 新增一个 action 取值和一个按数据集开启的名单;A 新增一条路由和一次 ADR,是永久义务。
    Prior rulings read: report, #20102, read-audit, opt-in, export audit in plugin-audit / rest-server / ADR index → #20102 (saved-report stack retired), #31000 (export run-level row), read-audit's per-object opt-in header.

    席位推荐

    推荐 C。理由有三:一,审计要记服务端真正执行、真正交出去的数据,C 的每一行都是服务端的权威事实,B 的报表名可以伪造;二,C 只动本车道两个包,沿用 #31000 和 read-audit 两个现成先例,不新增路由,也不改前端;三,按数据集开启,默认不写,日志量不会因为仪表盘刷新而暴涨。如果 SIEM 的要求必须精确到报表名,就另立一张 A 的 ADR 卡交维护者,不放在这张 p3 里做。C 的 action 取值按它记录的内容命名为 dataset_query,不叫 report_run:审计字段宁可窄,也不能名不副实。

    在裁定之前,卡线程不写任何代码。

  5. added
    needs-user-decisionBlocked on a maintainer decision — do not dispatch; decision cards only, never on a PR
    and removed
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    on Nov 9, 2026
  6. objectstack-fleet commented on Nov 9, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · os-steve · session_01ADHkmSnFAgPUKAz3xraGoJ
    os-dev-report

    {
      "issue": 31797,
      "status": "needs_decision",
      "branch": "claude/project-thread-r06jvv",
      "pr": null,
      "session": "session_01ADHkmSnFAgPUKAz3xraGoJ",
      "premise_still_valid": false,
      "summary": "Step 0 only, no code. On origin/main ad622d30 there is no server-side report execution path: the saved-report /reports REST family and its service were retired (#20102; rest-server.ts ~L2142 retired provider slot, ~L5000), no service method runs a saved report, and no MCP or AI tool runs one. A report runs in the browser: ui/packages/plugin-report/src/DatasetReportRenderer.tsx posts POST /analytics/dataset/query {datasetName, selection} once per presentation (table, matrix, chart, each joined block, drill drawer, re-reads). The report name never reaches the server, and the door cannot tell a report's query from a dashboard widget's, so 'one row per report run naming the report' has no seam. The card's evidence holds (no writer), but because the path does not exist. Readings: /mnt/project-files/server-seat2/31797-step0.md. Seat decision analysis: issuecomment-6733834629.",
      "tests": "none: needs_decision before any code (standard order step 10)",
      "gates": "none run: no diff",
      "dogfood_battery": "n/a — no diff",
      "mcp_calls": "3 — mcp__github__issue_read (get, get_comments x2); no write tools",
      "api_writes": "1 — this os-dev-report comment (POST /repos/objectstack-ai/objectstack/issues/31797/comments via post-stamped relay)",
      "files_changed": [],
      "line_budget": "n/a — no diff",
      "deviations": [
        "Delta ruling 3 assumed the export door writes an outcome row for refusals; it does not. data-export-audit.ts writes NO row for a refusal before reading and one outcome:failed row only after reading began. Whichever option is ruled, the card thread would follow the precedent as it is: refused = no row, failed-after-start = one failed row."
      ],
      "seat_rulings_read": {
        "ruling_1_spec_mirror": "sys_audit_log.action options live only in plugin-audit/src/objects/sys-audit-log.object.ts L423ff; packages/spec carries no mirror enum, only the ADR-0087 registry entry migrations/entries/semantic/17.audit-log-action-enum-retired.ts. No spec change needed for a new value.",
        "ruling_2_paths": "none on main (see summary)."
      },
      "open_questions": [
        {
          "question": "Where does the report-run audit row sit, given main has no server-side report execution path?",
          "options": [
            "A — new server-side report run door (server loads the report, compiles block selections, writes one report_run row with the authoritative name; renderer switches to it). Root fix; L; new REST route + ui/** + reopens the #20102 retirement direction; ADR, maintainer.",
            "B — optional report key on the dataset-query body, audited when present. M; ui/** + REST body widening; client-asserted name, N rows per view. Not recommended.",
            "C — one dataset_query row per POST /analytics/dataset/query (principal, dataset, selection names, filter field names via summarizeExportFilter, row count, outcome), export seam, per-dataset opt-in. S-M; plugin-audit + analytics-routes.ts; no report name.",
            "D — premise false: close and hand back to cloud#3665; report audit waits for A."
          ],
          "recommendation": "C, as the seat's analysis also recommends (named dataset_query, opt-in per dataset); A only as its own ADR card if the report name is required."
        }
      ],
      "security_findings": "none",
      "out_of_scope_findings": []
    }

    Stamped 2026-11-09T06:33Z.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

domain:serverDomain lane — seat card indexed by label:pm:seatenhancementNew feature or requestneeds-user-decisionBlocked on a maintainer decision — do not dispatch; decision cards only, never on a PRpriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions