strapi-cms-audit-log 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +212 -0
- package/LICENSE +21 -0
- package/README.md +1026 -0
- package/dist/admin/App-BLt4lqEM.js +1284 -0
- package/dist/admin/App-ou5hy99f.mjs +1266 -0
- package/dist/admin/en-B0rPiE2W.mjs +87 -0
- package/dist/admin/en-BMlJxq3g.js +87 -0
- package/dist/admin/index-BYi8OPTw.js +80 -0
- package/dist/admin/index-DdmY-p3Q.mjs +81 -0
- package/dist/admin/index.js +4 -0
- package/dist/admin/index.mjs +4 -0
- package/dist/admin/src/components/AuditLogFilters.d.ts +18 -0
- package/dist/admin/src/components/AuditLogTable.d.ts +19 -0
- package/dist/admin/src/components/ChangeViewer.d.ts +14 -0
- package/dist/admin/src/components/JsonViewer.d.ts +20 -0
- package/dist/admin/src/components/PluginIcon.d.ts +3 -0
- package/dist/admin/src/components/WidgetDiff.d.ts +19 -0
- package/dist/admin/src/hooks/useAuditLogs.d.ts +36 -0
- package/dist/admin/src/index.d.ts +3 -0
- package/dist/admin/src/pages/App.d.ts +11 -0
- package/dist/admin/src/pages/AuditLogDetails.d.ts +10 -0
- package/dist/admin/src/pages/AuditLogs.d.ts +3 -0
- package/dist/admin/src/permissions.d.ts +26 -0
- package/dist/admin/src/pluginId.d.ts +2 -0
- package/dist/admin/src/types.d.ts +91 -0
- package/dist/admin/src/utils/format.d.ts +41 -0
- package/dist/admin/src/utils/getTranslation.d.ts +2 -0
- package/dist/admin/src/utils/widgets.d.ts +85 -0
- package/dist/server/index.js +1856 -0
- package/dist/server/index.mjs +1856 -0
- package/dist/server/src/bootstrap.d.ts +14 -0
- package/dist/server/src/config/index.d.ts +52 -0
- package/dist/server/src/constants.d.ts +140 -0
- package/dist/server/src/content-types/audit-log/index.d.ts +86 -0
- package/dist/server/src/content-types/audit-log/schema.d.ts +141 -0
- package/dist/server/src/content-types/index.d.ts +88 -0
- package/dist/server/src/controllers/audit-log.d.ts +31 -0
- package/dist/server/src/controllers/index.d.ts +42 -0
- package/dist/server/src/destroy.d.ts +19 -0
- package/dist/server/src/index.d.ts +328 -0
- package/dist/server/src/register.d.ts +31 -0
- package/dist/server/src/routes/admin.d.ts +26 -0
- package/dist/server/src/routes/index.d.ts +19 -0
- package/dist/server/src/services/access.d.ts +51 -0
- package/dist/server/src/services/audit.d.ts +25 -0
- package/dist/server/src/services/config.d.ts +30 -0
- package/dist/server/src/services/context.d.ts +33 -0
- package/dist/server/src/services/diff.d.ts +48 -0
- package/dist/server/src/services/immutability.d.ts +30 -0
- package/dist/server/src/services/index.d.ts +137 -0
- package/dist/server/src/services/retention.d.ts +22 -0
- package/dist/server/src/services/security.d.ts +54 -0
- package/dist/server/src/services/snapshot.d.ts +46 -0
- package/dist/server/src/services/tracker.d.ts +39 -0
- package/dist/server/src/types/index.d.ts +214 -0
- package/dist/server/src/utils/json.d.ts +17 -0
- package/dist/server/src/utils/paths.d.ts +34 -0
- package/dist/server/src/utils/sanitize.d.ts +16 -0
- package/package.json +112 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here.
|
|
4
|
+
|
|
5
|
+
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
|
|
6
|
+
the project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [1.1.0] — 2026-09-03
|
|
9
|
+
|
|
10
|
+
Adds the half of an audit trail that is not about content — authentication,
|
|
11
|
+
authorisation and the admin domain — plus a per-widget before/after view of
|
|
12
|
+
dynamic zones and optional structured-log forwarding.
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
**Security events**
|
|
17
|
+
- Logins, failed logins and logouts, from Strapi's `admin.auth.success`,
|
|
18
|
+
`admin.auth.error` and `admin.logout` events. All available in **Community
|
|
19
|
+
Edition**; no Enterprise licence and no patching of Strapi's own code.
|
|
20
|
+
- A failed login records the **attempted email**, read off the live request body
|
|
21
|
+
because Strapi's event carries no identity at all. Only `email` is read; the
|
|
22
|
+
password is never touched.
|
|
23
|
+
- Denied requests (`access.denied`) for every 401 and 403, with the method, path,
|
|
24
|
+
status and the reason Strapi gave. Recorded by a Koa middleware registered from
|
|
25
|
+
the plugin's `register` lifecycle, which is the only phase that lands it ahead
|
|
26
|
+
of the router.
|
|
27
|
+
- Admin user, role and permission changes (`admin.user.*`, `admin.role.*`,
|
|
28
|
+
`admin.permission.*`) and media library operations (`media.*`,
|
|
29
|
+
`media-folder.*`).
|
|
30
|
+
- Admin-domain records keep an **allow-list** of identifying fields, so a
|
|
31
|
+
`user.update` payload cannot carry a password hash, reset token or registration
|
|
32
|
+
token into the audit table.
|
|
33
|
+
- New `securityEvents` option — `'*'`, a subset, or `[]` to register no listeners
|
|
34
|
+
and add no middleware at all.
|
|
35
|
+
- A listener can never fail the operation that emitted it. `eventHub.emit` awaits
|
|
36
|
+
its subscribers inside that operation, so every handler is wrapped and
|
|
37
|
+
`failOnAuditError` is deliberately not honoured on this side.
|
|
38
|
+
|
|
39
|
+
**Per-widget before/after**
|
|
40
|
+
- The detail page reassembles dynamic zones and renders each changed widget
|
|
41
|
+
**twice** — the whole widget as it was, then the whole widget as it now is —
|
|
42
|
+
with the differing fields highlighted in both copies.
|
|
43
|
+
- Zones are detected structurally (an array whose members carry `__component`),
|
|
44
|
+
so this covers `widgets`, `blocks` and any zone added later, on any content
|
|
45
|
+
type, with nothing to register.
|
|
46
|
+
- Slots are paired by position rather than component id, because Strapi
|
|
47
|
+
regenerates component row ids on every save; pairing by id would report every
|
|
48
|
+
widget as removed-and-re-added.
|
|
49
|
+
- Added, removed and replaced slots are distinguished; unchanged widgets collapse
|
|
50
|
+
behind a toggle.
|
|
51
|
+
- Computed in the browser from `before`/`after`, so it needs no new column, costs
|
|
52
|
+
no extra query, and works retroactively on records already in the table.
|
|
53
|
+
|
|
54
|
+
**Centralized logging / SIEM**
|
|
55
|
+
- New `forwardToLogger` and `forwardLogLevel` options mirror each record to
|
|
56
|
+
`strapi.log` as one line of structured JSON, for a collector to pick up off
|
|
57
|
+
container stdout. Snapshots are excluded; only the changed field paths are
|
|
58
|
+
emitted.
|
|
59
|
+
|
|
60
|
+
**Schema**
|
|
61
|
+
- `outcome` (`success` / `failure`) and `metadata` (JSON) columns, plus an
|
|
62
|
+
`audit_logs_outcome_idx` index, so "show me every failure" is one indexed
|
|
63
|
+
query. Both are filterable from the admin list and the API.
|
|
64
|
+
|
|
65
|
+
### Changed
|
|
66
|
+
|
|
67
|
+
- The list page's filter dropdowns now offer **everything filterable**, not only
|
|
68
|
+
the values that already appear in the table. Previously a fresh install listed
|
|
69
|
+
two or three content types and one user, so there was no way to ask 'has anyone
|
|
70
|
+
touched Insights?' until somebody already had — which is exactly when the
|
|
71
|
+
question matters. Content types come from the content-type registry (respecting
|
|
72
|
+
`contentTypes`, and excluding the plugin's own type), users from `admin::user`,
|
|
73
|
+
locales from i18n, and actions, sources and outcomes from their full value sets. Values found only in stored rows — a deleted content type, a
|
|
74
|
+
removed administrator — are merged in, since those are what an investigation
|
|
75
|
+
goes looking for and they exist nowhere else. The content type and user filters
|
|
76
|
+
became searchable comboboxes, because both lists are now long.
|
|
77
|
+
|
|
78
|
+
- `auditSystemOperations` is now actually honoured. It was declared and resolved
|
|
79
|
+
but never consulted. Events emitted outside any request — chiefly Strapi's
|
|
80
|
+
boot-time permission reconciliation — are labelled `source: "system"` rather
|
|
81
|
+
than `admin`, and `auditSystemOperations: false` drops them instead of storing
|
|
82
|
+
them.
|
|
83
|
+
- The admin list gains an Outcome column and an Outcome filter; `outcome` is also
|
|
84
|
+
a sortable column and an accepted query parameter.
|
|
85
|
+
- `AuditEntryInput.action` widens from `AuditAction` to `AuditAnyAction`.
|
|
86
|
+
`AuditFilterOptions` gains `outcomes`, and `AuditLog` gains `outcome` and
|
|
87
|
+
`metadata`. Consumers reading those types may need to widen their own.
|
|
88
|
+
|
|
89
|
+
### Fixed
|
|
90
|
+
|
|
91
|
+
- **Installing from a local path could make the plugin page unreachable.** With
|
|
92
|
+
`"strapi-plugin-audit-log": "file:../strapi-plugin-audit-log"`, yarn 1 copies
|
|
93
|
+
the directory wholesale — `files` is not honoured and `node_modules` is not
|
|
94
|
+
skipped — so the plugin's *dev* dependencies land at
|
|
95
|
+
`node_modules/strapi-plugin-audit-log/node_modules/`, including `@strapi/strapi`,
|
|
96
|
+
`@strapi/admin`, `react` and `react-router-dom`. Node and Vite resolve from the
|
|
97
|
+
importing file upward, so those nested copies win and the plugin's admin code
|
|
98
|
+
binds to a *second* instance of the admin runtime with its own React context.
|
|
99
|
+
|
|
100
|
+
The result is silent and very hard to attribute: `addMenuLink` still works
|
|
101
|
+
(it hands a plain object to the app's router, so the sidebar entry appears),
|
|
102
|
+
but `Page.Protect` calls `useAuth('Protect', s => s.permissions)` **without**
|
|
103
|
+
the `shouldThrowOnMissingContext` flag, so the missing context yields
|
|
104
|
+
`undefined` instead of an error, `(userPermissions || [])` makes it `[]`, and a
|
|
105
|
+
Super Admin who holds the permission is shown "You don't have the permissions
|
|
106
|
+
to access that content".
|
|
107
|
+
|
|
108
|
+
Nothing in the database, the RBAC registration or the API response is wrong in
|
|
109
|
+
this state, which is what makes it so misleading. See the "Installing from a
|
|
110
|
+
local path" note in the README; consuming projects should strip the nested
|
|
111
|
+
`node_modules` after install.
|
|
112
|
+
|
|
113
|
+
- The admin permission constants now declare the full permission entity
|
|
114
|
+
(`id`, `actionParameters`, `properties`, `conditions` alongside `action` and
|
|
115
|
+
`subject`), matching what `GET /admin/users/me/permissions` returns and what
|
|
116
|
+
Strapi's own first-party plugins declare. Housekeeping rather than a bug fix —
|
|
117
|
+
`{ action, subject: null }` already satisfied both of the admin's permission
|
|
118
|
+
checks.
|
|
119
|
+
- Media library and admin-domain operations are no longer invisible. Both write
|
|
120
|
+
through `strapi.db.query`, below the Document Service, so the tracker could
|
|
121
|
+
never see them; they are covered by the event listeners instead. The
|
|
122
|
+
Limitations section has been corrected accordingly.
|
|
123
|
+
|
|
124
|
+
## [1.0.0] — 2026-09-02
|
|
125
|
+
|
|
126
|
+
First release.
|
|
127
|
+
|
|
128
|
+
### Added
|
|
129
|
+
|
|
130
|
+
**Automatic tracking**
|
|
131
|
+
- A single Document Service middleware (`strapi.documents.use`) covering every
|
|
132
|
+
content type, present and future, with no per-content-type code.
|
|
133
|
+
- `create`, `update`, `delete`, `publish` and `unpublish`, across the admin
|
|
134
|
+
panel, the REST and GraphQL content APIs and custom server code.
|
|
135
|
+
- A create or update that also publishes records both actions, since Strapi's
|
|
136
|
+
repository publishes internally without re-entering the middleware.
|
|
137
|
+
- One record per affected locale, so a bulk publish is attributable per
|
|
138
|
+
translation.
|
|
139
|
+
|
|
140
|
+
**Diffs**
|
|
141
|
+
- Generic, schema-agnostic diff engine producing dotted paths with indexed array
|
|
142
|
+
members (`seo.metaTitle`, `blocks[2].heading`).
|
|
143
|
+
- Handles primitives, nested objects, components, repeatable components, dynamic
|
|
144
|
+
zones, relations, media, arrays and localized fields.
|
|
145
|
+
- `Date` and its ISO string compare equal, as do `null` and `undefined`, so a
|
|
146
|
+
re-save does not report spurious changes.
|
|
147
|
+
- Depth budget and a 500-change cap, both with explicit markers when hit.
|
|
148
|
+
|
|
149
|
+
**Redaction**
|
|
150
|
+
- Case-insensitive, wildcard-capable ignore patterns matched at any depth.
|
|
151
|
+
- Built-in defaults covering password, token, secret, apiKey, privateKey,
|
|
152
|
+
credential, accessKey, salt and otp variants.
|
|
153
|
+
- `additionalIgnoredFields` to extend the defaults, `ignoredFields` to replace
|
|
154
|
+
them, `ignoredChangeFields` to exclude from the diff only.
|
|
155
|
+
- Redacted values are removed rather than masked, and stripped before the diff
|
|
156
|
+
runs.
|
|
157
|
+
|
|
158
|
+
**Performance**
|
|
159
|
+
- Snapshot queries scoped to the attributes a write actually touched.
|
|
160
|
+
- Relations and media reduced to identifying fields.
|
|
161
|
+
- Dynamic zones populated so that only the component types present in the data
|
|
162
|
+
are queried — five widgets used out of 173 declared costs five queries.
|
|
163
|
+
- Optional data-driven second pass for nested dynamic-zone content, gated on
|
|
164
|
+
`maxPopulateDepth`.
|
|
165
|
+
- One `findMany` across all affected locales rather than one per locale.
|
|
166
|
+
- Six database indexes, declared in the schema and created by Strapi's schema
|
|
167
|
+
sync on every supported database.
|
|
168
|
+
|
|
169
|
+
**Admin UI**
|
|
170
|
+
- `Audit Logs` sidebar entry at `/admin/audit-logs`, filtered by RBAC.
|
|
171
|
+
- Server-side pagination, search, sorting and filtering by action, content type,
|
|
172
|
+
user, locale, source, document id and date range, all reflected in the URL.
|
|
173
|
+
- Detail page with full metadata, a readable before/after diff and collapsible
|
|
174
|
+
JSON viewers.
|
|
175
|
+
- Delete, shown only to roles holding `plugin::audit-log.delete`.
|
|
176
|
+
|
|
177
|
+
**Security**
|
|
178
|
+
- `plugin::audit-log.read`, `.delete` and `.settings` registered with Strapi's
|
|
179
|
+
admin RBAC; read and delete deliberately separate.
|
|
180
|
+
- Admin-only routes, each guarded by `admin::isAuthenticatedAdmin` plus an
|
|
181
|
+
explicit `admin::hasPermissions`; no content-api route exists.
|
|
182
|
+
- Records are immutable: the collection type is hidden from the Content Manager,
|
|
183
|
+
which is what withholds write permissions, plus a Document Service guard for
|
|
184
|
+
server code.
|
|
185
|
+
- Query parameters are whitelisted and object-valued filters rejected, so a
|
|
186
|
+
client cannot author query operators; `sort` accepts only known columns.
|
|
187
|
+
- Actor and request metadata come from the server-side request context, never
|
|
188
|
+
from a request body.
|
|
189
|
+
|
|
190
|
+
**Operations**
|
|
191
|
+
- Retention on `strapi.cron`, off the request path; `retentionDays: 0` registers
|
|
192
|
+
no job.
|
|
193
|
+
- `writeMode` (`sync` by default) with pending writes drained on shutdown.
|
|
194
|
+
- `failOnAuditError` (`false` by default) with the trade-off documented.
|
|
195
|
+
- `context.runAs()` for labelling migrations, cron tasks and other work with no
|
|
196
|
+
request behind it.
|
|
197
|
+
|
|
198
|
+
**Types**
|
|
199
|
+
- Strict TypeScript throughout, with `AuditAction`, `AuditLog`, `AuditChange`,
|
|
200
|
+
`AuditContext`, `AuditConfig` and others exported from
|
|
201
|
+
`strapi-plugin-audit-log/strapi-server`.
|
|
202
|
+
|
|
203
|
+
### Notes
|
|
204
|
+
|
|
205
|
+
- The audited document's id is stored as `contentDocumentId`, not `documentId`:
|
|
206
|
+
Strapi v5 reserves the latter as an attribute name and throws at boot if a
|
|
207
|
+
schema declares one. Each audit row still carries its own framework
|
|
208
|
+
`documentId`.
|
|
209
|
+
- Collection-valued configuration options default to `null` internally, because
|
|
210
|
+
Strapi merges plugin config with lodash's `defaultsDeep`, which merges arrays
|
|
211
|
+
element-wise. The real defaults are applied afterwards, so a user-supplied
|
|
212
|
+
array replaces ours cleanly.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Latheefwac
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|