You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
feat(audit): declare how each mutation is recorded in its procedure meta - #121
Stacked on #90. This is the work its review comment asks for before #90 merges: every mutation declares how the audit log records it, in its procedure meta next to its policy, and a router test enforces it.
What changes
The option (src/audit/option.ts, no imports, so declaring one doesn't load the database schema):
Option
What is stored
Used for
audit.full({ fields?, target? })
actor, action, target, outcome, plus the input (only fields, when given), the reason and the states the handler adds with ctx.audit.set
web content, groups, labels, WhatsApp, Telegram groups and grants, e-signing, Azure
The option's target gets the raw input and, once the call succeeds, its result. A target the handler sets with ctx.audit.set takes precedence.
The action is still the procedure path, so event names already stored stay valid. action can override it if a procedure is ever renamed.
Router test: fails when any mutation lacks an option, or when a none option has no why. At runtime, a mutation without an option is recorded as minimal and logged as an error.
Removed:UNAUDITED and the temporary path-prefix rule for People.
Annotations
People: the base procedure of the router factory is minimal. The person comes from a valid P-NNNN id in the input (personId or id) or from the created record. The router test checks the target function, including that an invalid raw id gives no target.
E-signing:
requests.create keeps the title, document, order, signers and output. It no longer keeps the email recipients or message.
Its recorded state after the change now names the signers by subject, role and position only. It used to store the whole request, including signers' fiscal codes, certificate subjects and email settings.
requests.decline keeps the id, and the reason in its own column.
Azure:
setAssocNumber keeps the member's id and number, not the notification address.
create keeps the number and the mailbox choice, not the names or the welcome-email address. The name is still the target's label.
The state recorded after create no longer contains the new email address.
Not included
ctx.audit.fail(reason), an explicit failure outcome for handlers, was marked optional in the review comment. The outcome is still inferred from returned shapes such as { error }, { success: false } or false. It can follow separately if those shapes turn out to mislead.
Every mutation now says how the audit log records it, next to its policy, and a
router test fails when one does not, so a new area cannot store full inputs
without anyone deciding it:
- audit.full({ fields?, target? }): the input (only `fields`, when given), the
reason and the states the handler adds.
- audit.minimal({ target? }): the actor, the action, the target and the outcome.
- audit.none(why): not recorded, with the reason in the code.
The option replaces the UNAUDITED set and the temporary rule for People:
- People's base procedure is minimal, with the person taken from a valid People
id in the input or the created record; the router test checks both.
- The bot's ingestion, its moderation reports and the e-signing profile sync are
none.
- E-signing keeps the request's title, document, signers and output, not the email
recipients or message, and its state after names the signers by subject and
position, without their fiscal codes or certificates. A decline keeps its id and
reason.
- Azure keeps the member's directory id and number, not names, the notification
address or the new mailbox address.
The helpers live in src/audit/option.ts, which imports nothing, so declaring an
option does not load the database schema. A mutation without one is recorded as
minimal and logged.
Audit recording now uses per-mutation options for full, minimal, or disabled capture. The audit middleware passes these options to event creation. Multiple routers declare capture options, including selected input fields and explicit exclusions.
Audit options define full, minimal, and disabled capture. The middleware passes the option to event creation. Tests cover detail omission, field filtering, and target selection. The documentation describes the capture modes and related filtering.
Set People audit targets src/people/router.ts, tests/idp-router.test.ts
The People user policy declares minimal capture and derives targets from valid People IDs in input or result data. Tests check target selection and audit metadata on mutations.
Declare capture for application mutations src/routers/groups/labels.ts, src/routers/wa/groups.ts, src/routers/web/*, tests/group-label-platform-isolation.test.ts, tests/guides-latest.test.ts
Group-label, WhatsApp-group, and web mutations declare full capture. Test procedure mocks support .meta() chaining.
Limit Azure and e-sign audit data src/routers/azure/members.ts, src/routers/esign.ts, tests/azure-router.test.ts
Azure and e-sign mutations declare capture options. Azure member creation omits email from its after data. E-sign request creation records selected request and signer fields.
Declare Telegram capture and exclusions src/routers/tg/*, tests/tg-users-creation-history.test.ts
Telegram group and grant mutations declare full capture. Moderation-log and bot-ingestion procedures declare no capture with reasons. The test mock supports .meta() chaining.
Possibly related issues
Issue 119 distinguishes stored-data limits from reader redaction. This change limits some stored audit data, but does not describe or implement reader redaction.
Change: Feature
Merge Risk: Low · up to 7da88
A crafted failed request can put the wrong ID in an audit record. The edge case is narrow and can be followed up without blocking merge.
Pre-merge checks: 4
Check name
Status
Explanation
Title check
The title clearly describes the main change: each mutation declares its audit-recording policy in procedure metadata.
Docstring Coverage
Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentat…
Linked Issues check
Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check
Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage
Explanation
Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 2 functions across 25 files. (1 skipped: 1 unsupported.)
Finishing Touches
Generate docstrings
Commit to this branch
Create a new PR
Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
The reason will be displayed to describe this comment to others. Learn more.
Additional comments: 1
src/routers/web/reports.ts: 1
33-33: Trivial · Maintainability & Code Quality · Low value
Unverified finding
Verification ran but could not confirm this finding. It is shown for review, not as a verified issue.
Public report creation records the full input under audit.full().
create is reachable with the publicRead service scope. The input includes free text (label, details) and a URL chosen by the caller. audit.full() stores all of it. This conflicts with the PR goal of avoiding free text that is not worth keeping. Consider audit.full({ fields: [...] }) or audit.minimal. The handler already sets after: report, so the input copy duplicates data.
Reviewing files that changed from the base of the PR and between b0f81d5 and 7da88a4.
Files selected for processing: 26
README.md
src/audit/index.ts
src/audit/option.ts
src/people/router.ts
src/routers/azure/members.ts
src/routers/esign.ts
src/routers/groups/labels.ts
src/routers/tg/audit-log.ts
src/routers/tg/grants.ts
src/routers/tg/groups.ts
src/routers/tg/messages.ts
src/routers/tg/users.ts
src/routers/wa/groups.ts
src/routers/web/associations.ts
src/routers/web/faqs.ts
src/routers/web/freshman.ts
src/routers/web/guides_matricole.ts
src/routers/web/projects.ts
src/routers/web/reports.ts
src/trpc.ts
tests/audit-json.test.ts
tests/azure-router.test.ts
tests/group-label-platform-isolation.test.ts
tests/guides-latest.test.ts
tests/idp-router.test.ts
tests/tg-users-creation-history.test.ts
Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #90. This is the work its review comment asks for before #90 merges: every mutation declares how the audit log records it, in its procedure meta next to its policy, and a router test enforces it.
What changes
The option (
src/audit/option.ts, no imports, so declaring one doesn't load the database schema):audit.full({ fields?, target? })fields, when given), the reason and the states the handler adds withctx.audit.setaudit.minimal({ target? })people_activityaudit.none(why)whyis requiredtg.messages.add,tg.users.add,tg.auditLog.record,esign.profile.synctargetgets the raw input and, once the call succeeds, its result. A target the handler sets withctx.audit.settakes precedence.actioncan override it if a procedure is ever renamed.noneoption has nowhy. At runtime, a mutation without an option is recorded asminimaland logged as an error.UNAUDITEDand the temporary path-prefix rule for People.Annotations
minimal. The person comes from a validP-NNNNid in the input (personIdorid) or from the created record. The router test checks the target function, including that an invalid raw id gives no target.requests.createkeeps the title, document, order, signers and output. It no longer keeps the email recipients or message.requests.declinekeeps the id, and the reason in its own column.setAssocNumberkeeps the member's id and number, not the notification address.createkeeps the number and the mailbox choice, not the names or the welcome-email address. The name is still the target's label.createno longer contains the new email address.Not included
ctx.audit.fail(reason), an explicit failure outcome for handlers, was marked optional in the review comment. The outcome is still inferred from returned shapes such as{ error },{ success: false }orfalse. It can follow separately if those shapes turn out to mislead.audit.list: Audit log: per-area read access and views by person #119.Checks
npx tsc --noEmit,npx biome checkandnpx vitest runpass (337 tests).none,minimal, inputfields, the undeclared fallback, target precedence and People's target function.