@openauditmodel/cli 0.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/LICENSE +215 -0
- package/README.md +647 -0
- package/dist/conformance/src/cli.d.ts +20 -0
- package/dist/conformance/src/cli.js +560 -0
- package/dist/conformance/src/cli.js.map +1 -0
- package/dist/conformance/src/format-errors.d.ts +20 -0
- package/dist/conformance/src/format-errors.js +116 -0
- package/dist/conformance/src/format-errors.js.map +1 -0
- package/dist/conformance/src/integrity/canonicalize.d.ts +25 -0
- package/dist/conformance/src/integrity/canonicalize.js +100 -0
- package/dist/conformance/src/integrity/canonicalize.js.map +1 -0
- package/dist/conformance/src/integrity/digest.d.ts +54 -0
- package/dist/conformance/src/integrity/digest.js +126 -0
- package/dist/conformance/src/integrity/digest.js.map +1 -0
- package/dist/conformance/src/integrity/types.d.ts +86 -0
- package/dist/conformance/src/integrity/types.js +36 -0
- package/dist/conformance/src/integrity/types.js.map +1 -0
- package/dist/conformance/src/integrity/verify-chain.d.ts +23 -0
- package/dist/conformance/src/integrity/verify-chain.js +235 -0
- package/dist/conformance/src/integrity/verify-chain.js.map +1 -0
- package/dist/conformance/src/integrity/verify-event.d.ts +29 -0
- package/dist/conformance/src/integrity/verify-event.js +140 -0
- package/dist/conformance/src/integrity/verify-event.js.map +1 -0
- package/dist/conformance/src/privacy/entropy.d.ts +14 -0
- package/dist/conformance/src/privacy/entropy.js +86 -0
- package/dist/conformance/src/privacy/entropy.js.map +1 -0
- package/dist/conformance/src/privacy/field-names.d.ts +36 -0
- package/dist/conformance/src/privacy/field-names.js +121 -0
- package/dist/conformance/src/privacy/field-names.js.map +1 -0
- package/dist/conformance/src/privacy/lint-event.d.ts +18 -0
- package/dist/conformance/src/privacy/lint-event.js +272 -0
- package/dist/conformance/src/privacy/lint-event.js.map +1 -0
- package/dist/conformance/src/privacy/rules.d.ts +44 -0
- package/dist/conformance/src/privacy/rules.js +161 -0
- package/dist/conformance/src/privacy/rules.js.map +1 -0
- package/dist/conformance/src/privacy/safe-formats.d.ts +32 -0
- package/dist/conformance/src/privacy/safe-formats.js +118 -0
- package/dist/conformance/src/privacy/safe-formats.js.map +1 -0
- package/dist/conformance/src/privacy/size-analysis.d.ts +36 -0
- package/dist/conformance/src/privacy/size-analysis.js +87 -0
- package/dist/conformance/src/privacy/size-analysis.js.map +1 -0
- package/dist/conformance/src/privacy/token-patterns.d.ts +24 -0
- package/dist/conformance/src/privacy/token-patterns.js +126 -0
- package/dist/conformance/src/privacy/token-patterns.js.map +1 -0
- package/dist/conformance/src/privacy/traverse.d.ts +33 -0
- package/dist/conformance/src/privacy/traverse.js +61 -0
- package/dist/conformance/src/privacy/traverse.js.map +1 -0
- package/dist/conformance/src/privacy/types.d.ts +58 -0
- package/dist/conformance/src/privacy/types.js +32 -0
- package/dist/conformance/src/privacy/types.js.map +1 -0
- package/dist/conformance/src/privacy/url-analysis.d.ts +40 -0
- package/dist/conformance/src/privacy/url-analysis.js +148 -0
- package/dist/conformance/src/privacy/url-analysis.js.map +1 -0
- package/dist/conformance/src/profiles/check-profile.d.ts +22 -0
- package/dist/conformance/src/profiles/check-profile.js +72 -0
- package/dist/conformance/src/profiles/check-profile.js.map +1 -0
- package/dist/conformance/src/profiles/evaluate-rule.d.ts +27 -0
- package/dist/conformance/src/profiles/evaluate-rule.js +87 -0
- package/dist/conformance/src/profiles/evaluate-rule.js.map +1 -0
- package/dist/conformance/src/profiles/load-profile.d.ts +30 -0
- package/dist/conformance/src/profiles/load-profile.js +95 -0
- package/dist/conformance/src/profiles/load-profile.js.map +1 -0
- package/dist/conformance/src/profiles/resolve-pointer.d.ts +44 -0
- package/dist/conformance/src/profiles/resolve-pointer.js +123 -0
- package/dist/conformance/src/profiles/resolve-pointer.js.map +1 -0
- package/dist/conformance/src/profiles/select-rules.d.ts +13 -0
- package/dist/conformance/src/profiles/select-rules.js +42 -0
- package/dist/conformance/src/profiles/select-rules.js.map +1 -0
- package/dist/conformance/src/profiles/types.d.ts +101 -0
- package/dist/conformance/src/profiles/types.js +31 -0
- package/dist/conformance/src/profiles/types.js.map +1 -0
- package/dist/conformance/src/profiles/validate-profile-definition.d.ts +12 -0
- package/dist/conformance/src/profiles/validate-profile-definition.js +47 -0
- package/dist/conformance/src/profiles/validate-profile-definition.js.map +1 -0
- package/dist/conformance/src/sources.d.ts +53 -0
- package/dist/conformance/src/sources.js +153 -0
- package/dist/conformance/src/sources.js.map +1 -0
- package/dist/conformance/src/validate-core.d.ts +30 -0
- package/dist/conformance/src/validate-core.js +62 -0
- package/dist/conformance/src/validate-core.js.map +1 -0
- package/dist/conformance/src/validate.d.ts +34 -0
- package/dist/conformance/src/validate.js +62 -0
- package/dist/conformance/src/validate.js.map +1 -0
- package/dist/conformance/src/validator-interface.d.ts +35 -0
- package/dist/conformance/src/validator-interface.js +19 -0
- package/dist/conformance/src/validator-interface.js.map +1 -0
- package/package.json +85 -0
- package/profiles/README.md +201 -0
- package/profiles/api-and-integration-management/README.md +271 -0
- package/profiles/api-and-integration-management/profile.json +258 -0
- package/profiles/backup-and-recovery/README.md +318 -0
- package/profiles/backup-and-recovery/profile.json +178 -0
- package/profiles/customer-and-account-management/README.md +340 -0
- package/profiles/customer-and-account-management/profile.json +237 -0
- package/profiles/deployment-and-change-management/README.md +359 -0
- package/profiles/deployment-and-change-management/profile.json +298 -0
- package/profiles/document-management/README.md +120 -0
- package/profiles/document-management/profile.json +170 -0
- package/profiles/financial-transaction-management/README.md +339 -0
- package/profiles/financial-transaction-management/profile.json +247 -0
- package/profiles/identity-and-access-management/README.md +112 -0
- package/profiles/identity-and-access-management/profile.json +120 -0
- package/profiles/incident-management/README.md +338 -0
- package/profiles/incident-management/profile.json +256 -0
- package/profiles/message-broker-management/README.md +344 -0
- package/profiles/message-broker-management/profile.json +399 -0
- package/profiles/profile-definition.schema.json +213 -0
- package/profiles/secrets-and-key-management/README.md +331 -0
- package/profiles/secrets-and-key-management/profile.json +219 -0
- package/schemas/v0.1/audit-event.schema.json +911 -0
- package/semantic-conventions/README.md +52 -0
- package/semantic-conventions/authentication.md +111 -0
- package/semantic-conventions/configuration-and-change.md +122 -0
- package/semantic-conventions/correlation-and-tracing.md +387 -0
- package/semantic-conventions/data-access.md +115 -0
- package/semantic-conventions/event-naming.md +114 -0
- package/semantic-conventions/identity-and-access.md +112 -0
- package/semantic-conventions/privileged-operations.md +110 -0
- package/semantic-conventions/workflow-and-approval.md +109 -0
- package/specification/actor-model.md +135 -0
- package/specification/approval-and-delegation.md +150 -0
- package/specification/authentication.md +116 -0
- package/specification/authorization.md +117 -0
- package/specification/change-model.md +134 -0
- package/specification/delivery.md +136 -0
- package/specification/design-principles.md +137 -0
- package/specification/event-model.md +387 -0
- package/specification/evidence-model.md +109 -0
- package/specification/extension-model.md +159 -0
- package/specification/integrity.md +314 -0
- package/specification/overview.md +172 -0
- package/specification/privacy.md +386 -0
- package/specification/resource-model.md +116 -0
- package/specification/terminology.md +135 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# OpenAuditModel Core Specification — Overview
|
|
2
|
+
|
|
3
|
+
**Specification version: 0.1 · Status: Experimental · This document: Normative**
|
|
4
|
+
|
|
5
|
+
> OpenAuditModel defines a common, verifiable and backend-independent audit event model for business
|
|
6
|
+
> applications.
|
|
7
|
+
|
|
8
|
+
## 1. Purpose
|
|
9
|
+
|
|
10
|
+
Business applications record auditable operations. Almost every application invents its own shape for
|
|
11
|
+
those records, so audit data cannot be validated, correlated, reviewed or exported without bespoke
|
|
12
|
+
work in every system. OpenAuditModel defines one shape for that data.
|
|
13
|
+
|
|
14
|
+
The specification describes **what an audit event is**, not where it is stored, how it is
|
|
15
|
+
transported, or which regulation it satisfies.
|
|
16
|
+
|
|
17
|
+
## 2. Scope
|
|
18
|
+
|
|
19
|
+
This specification defines:
|
|
20
|
+
|
|
21
|
+
1. The structure of an audit event.
|
|
22
|
+
2. The meaning of each field in that structure.
|
|
23
|
+
3. The rules a conforming event MUST satisfy.
|
|
24
|
+
4. The rules a conforming producer and consumer MUST satisfy.
|
|
25
|
+
5. The extension mechanism by which vendors and domains add their own data.
|
|
26
|
+
|
|
27
|
+
This specification does not define:
|
|
28
|
+
|
|
29
|
+
- A storage format, database, index or retention system.
|
|
30
|
+
- A transport protocol, wire encoding or delivery guarantee.
|
|
31
|
+
- A query language or reporting model.
|
|
32
|
+
- An authorization or policy evaluation engine.
|
|
33
|
+
- A mapping to any regulation, standard or control framework.
|
|
34
|
+
|
|
35
|
+
See [design-principles.md](design-principles.md) for why these exclusions exist, and the repository
|
|
36
|
+
[README](../README.md) for the full list of non-goals.
|
|
37
|
+
|
|
38
|
+
## 3. Conformance
|
|
39
|
+
|
|
40
|
+
### 3.1 Conforming event
|
|
41
|
+
|
|
42
|
+
A JSON document is a **conforming OpenAuditModel v0.1 event** if and only if it validates against the
|
|
43
|
+
[OpenAuditModel Audit Event Schema](../schemas/v0.1/audit-event.schema.json), identified by:
|
|
44
|
+
|
|
45
|
+
```text
|
|
46
|
+
https://openauditmodel.org/schemas/audit-event/0.1/schema.json
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Schema validation is necessary but **not sufficient** for a good audit event. Rules that a schema
|
|
50
|
+
cannot express — a `subject` used as a target resource, a secret placed in `metadata`, an event name
|
|
51
|
+
that changed meaning between releases — are stated normatively in this specification and MUST be
|
|
52
|
+
satisfied by conforming producers even though a validator cannot detect their violation.
|
|
53
|
+
|
|
54
|
+
### 3.2 Conforming producer
|
|
55
|
+
|
|
56
|
+
A conforming producer:
|
|
57
|
+
|
|
58
|
+
- MUST emit events that validate against the canonical schema.
|
|
59
|
+
- MUST set `specVersion` to the version of the specification the event conforms to.
|
|
60
|
+
- MUST NOT record the values listed as prohibited in [privacy.md](privacy.md).
|
|
61
|
+
- MUST keep the meaning of an event name stable across releases, as required by
|
|
62
|
+
[event-model.md](event-model.md).
|
|
63
|
+
- SHOULD populate only the optional fields its audit purpose requires.
|
|
64
|
+
|
|
65
|
+
### 3.3 Conforming consumer
|
|
66
|
+
|
|
67
|
+
A conforming consumer:
|
|
68
|
+
|
|
69
|
+
- MUST accept any event that validates against the canonical schema, including events that use only
|
|
70
|
+
the required fields.
|
|
71
|
+
- MUST NOT reject an event because it carries `metadata` or `extensions` the consumer does not
|
|
72
|
+
recognise.
|
|
73
|
+
- MUST treat `event.summary` as human-readable text and MUST NOT parse it for machine decisions.
|
|
74
|
+
- SHOULD deduplicate events by `id`, as described in [delivery.md](delivery.md).
|
|
75
|
+
|
|
76
|
+
### 3.4 Conformance is not compliance
|
|
77
|
+
|
|
78
|
+
Conformance to this specification is a statement about the **shape and semantics of data**. It is not
|
|
79
|
+
a statement about regulatory, legal or contractual compliance, and it MUST NOT be presented as one.
|
|
80
|
+
|
|
81
|
+
## 4. Document status labels
|
|
82
|
+
|
|
83
|
+
Every document in this repository is labelled with one of:
|
|
84
|
+
|
|
85
|
+
| Label | Meaning |
|
|
86
|
+
| ---------------- | ----------------------------------------------------------------------------------------------------------- |
|
|
87
|
+
| **Normative** | Defines requirements. Conforming implementations MUST satisfy them. |
|
|
88
|
+
| **Informative** | Explains, illustrates or maps. Carries no requirements, even where it uses examples that look prescriptive. |
|
|
89
|
+
| **Experimental** | Expected to change, possibly incompatibly, before version 1.0. All of version 0.1 is experimental. |
|
|
90
|
+
|
|
91
|
+
Where a document is normative, only the capitalized keywords defined in
|
|
92
|
+
[terminology.md](terminology.md) express requirements.
|
|
93
|
+
|
|
94
|
+
Everything under [examples/](../examples/), [mappings/](../mappings/) and [profiles/](../profiles/)
|
|
95
|
+
is informative in v0.1. [semantic-conventions/](../semantic-conventions/) is normative only where it
|
|
96
|
+
says so explicitly; its recommended vocabularies are otherwise SHOULD-level guidance.
|
|
97
|
+
|
|
98
|
+
## 5. Specification documents
|
|
99
|
+
|
|
100
|
+
| Document | Covers |
|
|
101
|
+
| -------------------------------------------------------- | --------------------------------------------------------------- |
|
|
102
|
+
| [terminology.md](terminology.md) | Normative keywords and the vocabulary used throughout |
|
|
103
|
+
| [design-principles.md](design-principles.md) | The constraints that shape every decision in the model |
|
|
104
|
+
| [event-model.md](event-model.md) | Top-level structure, identity, time, event descriptor, outcomes |
|
|
105
|
+
| [actor-model.md](actor-model.md) | Who acted, and on whose behalf |
|
|
106
|
+
| [resource-model.md](resource-model.md) | What was acted upon |
|
|
107
|
+
| [authentication.md](authentication.md) | How the actor was authenticated |
|
|
108
|
+
| [authorization.md](authorization.md) | Which decision permitted or denied the operation |
|
|
109
|
+
| [approval-and-delegation.md](approval-and-delegation.md) | Human approval and transferred authority |
|
|
110
|
+
| [change-model.md](change-model.md) | What changed, without leaking the data that changed |
|
|
111
|
+
| [evidence-model.md](evidence-model.md) | Referencing supporting material |
|
|
112
|
+
| [privacy.md](privacy.md) | What MUST NOT be recorded, and how personal data is described |
|
|
113
|
+
| [integrity.md](integrity.md) | Tamper-evidence, and what it does and does not guarantee |
|
|
114
|
+
| [delivery.md](delivery.md) | Producing, transporting and receiving events |
|
|
115
|
+
| [extension-model.md](extension-model.md) | `metadata`, `extensions` and profile boundaries |
|
|
116
|
+
|
|
117
|
+
## 6. Versioning and compatibility
|
|
118
|
+
|
|
119
|
+
### 6.1 Version 0.1
|
|
120
|
+
|
|
121
|
+
`specVersion` is a constant in v0.1:
|
|
122
|
+
|
|
123
|
+
```json
|
|
124
|
+
{ "specVersion": "0.1" }
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
The canonical schema enforces this with `const`. An event that declares any other value is not a
|
|
128
|
+
conforming v0.1 event.
|
|
129
|
+
|
|
130
|
+
Fixing the value keeps the experimental phase unambiguous: there is exactly one version of the model
|
|
131
|
+
in existence, and no producer can claim conformance to a version that has not been published.
|
|
132
|
+
|
|
133
|
+
### 6.2 Future versions
|
|
134
|
+
|
|
135
|
+
The compatibility strategy **is expected to change** after v0.1. Later versions may:
|
|
136
|
+
|
|
137
|
+
- Replace the `const` constraint with a range or a set of accepted versions.
|
|
138
|
+
- Define which changes are compatible and which require a version increment.
|
|
139
|
+
- Define how a consumer should behave when it receives a newer minor version.
|
|
140
|
+
|
|
141
|
+
None of that is decided in v0.1, and implementations MUST NOT assume any particular future strategy.
|
|
142
|
+
The decision will be recorded as an architecture decision record in [decisions/](../decisions/).
|
|
143
|
+
|
|
144
|
+
### 6.3 What is already committed to
|
|
145
|
+
|
|
146
|
+
Even in the experimental phase, two rules hold:
|
|
147
|
+
|
|
148
|
+
1. **Event names do not silently change meaning.** If the meaning of an event name changes, the name
|
|
149
|
+
changes. See [event-model.md](event-model.md).
|
|
150
|
+
2. **Extensions never weaken the core.** An extension MUST NOT remove a required field, relax a
|
|
151
|
+
constraint, or redefine the meaning of a core field. See
|
|
152
|
+
[extension-model.md](extension-model.md).
|
|
153
|
+
|
|
154
|
+
## 7. Relationship to other standards
|
|
155
|
+
|
|
156
|
+
OpenAuditModel is an **event model**, not a transport, a telemetry pipeline or a security taxonomy.
|
|
157
|
+
It is designed to be carried by, and mapped to, the standards that already exist:
|
|
158
|
+
|
|
159
|
+
- **CloudEvents** MAY carry an OpenAuditModel event as its `data` payload.
|
|
160
|
+
- **OpenTelemetry** MAY transport, collect and correlate audit events, and `request.traceId` /
|
|
161
|
+
`request.spanId` are defined to be compatible with W3C Trace Context.
|
|
162
|
+
- **ECS** and **OCSF** MAY be produced from OpenAuditModel events by export mappings.
|
|
163
|
+
- **CADF** is a conceptual reference for audit semantics.
|
|
164
|
+
- **OSCAL** may later be used for control and assessment mappings.
|
|
165
|
+
|
|
166
|
+
None of these are required. An OpenAuditModel event is valid on its own, with no envelope and no
|
|
167
|
+
pipeline. See [mappings/](../mappings/), which is informative.
|
|
168
|
+
|
|
169
|
+
## 8. Stability warning
|
|
170
|
+
|
|
171
|
+
Version 0.1 is **experimental and not production-ready**. Field names, constraints, vocabularies and
|
|
172
|
+
the compatibility strategy may all change. It carries **no compliance guarantee** of any kind.
|
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
# Privacy
|
|
2
|
+
|
|
3
|
+
**Specification version: 0.1 · Status: Experimental · This document: Normative**
|
|
4
|
+
|
|
5
|
+
Audit data concentrates the most sensitive relationship in a system: who did what to whom, and when.
|
|
6
|
+
It is frequently retained longer than production data, replicated to more systems, and read by more
|
|
7
|
+
people. This document defines what MUST NOT be recorded, and how the personal-data character of an
|
|
8
|
+
event is described.
|
|
9
|
+
|
|
10
|
+
None of this constitutes legal advice or a compliance guarantee. See §9.
|
|
11
|
+
|
|
12
|
+
## 1. Values that MUST NEVER be recorded
|
|
13
|
+
|
|
14
|
+
The following MUST NOT appear in any field of an OpenAuditModel event — not in `metadata`, not in
|
|
15
|
+
`extensions`, not in `attributes`, not in `before` or `after`, not in an error message, not in a
|
|
16
|
+
summary, and not inside a reference:
|
|
17
|
+
|
|
18
|
+
1. Passwords, in any form, including password hashes and password history.
|
|
19
|
+
2. Access tokens.
|
|
20
|
+
3. Refresh tokens.
|
|
21
|
+
4. API keys.
|
|
22
|
+
5. Connection strings.
|
|
23
|
+
6. Private keys and other secret key material.
|
|
24
|
+
7. Authorization headers.
|
|
25
|
+
8. Session cookies and any other value that grants a session by possession.
|
|
26
|
+
|
|
27
|
+
This list is absolute. There is no audit purpose that requires the value of a credential; the audit
|
|
28
|
+
purpose is served by recording the **fact** of its creation, use, rotation or revocation.
|
|
29
|
+
|
|
30
|
+
Where a system's session identifier is itself a bearer credential, producers MUST record a derived,
|
|
31
|
+
non-reversible correlation value or omit the field. See
|
|
32
|
+
[authentication.md](authentication.md).
|
|
33
|
+
|
|
34
|
+
## 2. Data that MUST NOT be captured automatically
|
|
35
|
+
|
|
36
|
+
The following MUST NOT be captured automatically — that is, without an explicit, per-field decision
|
|
37
|
+
by the producer:
|
|
38
|
+
|
|
39
|
+
1. Full request bodies.
|
|
40
|
+
2. Full response bodies.
|
|
41
|
+
3. Message broker payloads.
|
|
42
|
+
4. Database query parameters.
|
|
43
|
+
5. Query strings. The canonical schema enforces this for `request.route`.
|
|
44
|
+
6. Complete database records in `change.before` or `change.after`.
|
|
45
|
+
7. Raw forwarding and client headers copied verbatim.
|
|
46
|
+
|
|
47
|
+
A producer MAY record a **selected, named subset** of any of these where the audit purpose requires
|
|
48
|
+
it and the values have been reviewed. The prohibition is on capturing them wholesale because they
|
|
49
|
+
happened to be available.
|
|
50
|
+
|
|
51
|
+
## 3. The allowlist model
|
|
52
|
+
|
|
53
|
+
Audit event fields MUST follow an **allowlist** model.
|
|
54
|
+
|
|
55
|
+
A producer decides, per field, what it records. It MUST NOT populate audit fields by serializing
|
|
56
|
+
whatever object was at hand and removing known-bad keys afterwards. Denylists fail silently: the
|
|
57
|
+
first time a new field appears in an upstream model, it is captured, and nobody finds out until it is
|
|
58
|
+
in seven years of retained records.
|
|
59
|
+
|
|
60
|
+
Practically:
|
|
61
|
+
|
|
62
|
+
- Map application data to audit fields explicitly, field by field.
|
|
63
|
+
- Prefer `changedFields` over `before`/`after` when the field names alone answer the question.
|
|
64
|
+
- Prefer identifiers over values, and stable identifiers over personal identifiers.
|
|
65
|
+
- Prefer references over content.
|
|
66
|
+
- Prefer route templates over resolved paths.
|
|
67
|
+
- Treat every new audit field as a change that needs review, in the same way a new database column
|
|
68
|
+
containing personal data would.
|
|
69
|
+
|
|
70
|
+
## 4. Data minimization
|
|
71
|
+
|
|
72
|
+
Audit data MUST be minimized to what the audit purpose requires.
|
|
73
|
+
|
|
74
|
+
- A field that no reviewer, control or investigation would use SHOULD NOT be recorded.
|
|
75
|
+
- An optional object SHOULD be omitted when the producer has no specific need for it. Completeness is
|
|
76
|
+
not a goal; see [design-principles.md](design-principles.md).
|
|
77
|
+
- `actor.displayName`, `resource.name` and `request.ipAddress` are the fields most often recorded out
|
|
78
|
+
of habit. Each is personal data in common circumstances, and each SHOULD be justified rather than
|
|
79
|
+
defaulted.
|
|
80
|
+
- Minimization applies to retention as well as capture. The model provides `privacy.retentionClass`
|
|
81
|
+
so that events can be governed differently; enforcing it is the operator's responsibility.
|
|
82
|
+
|
|
83
|
+
## 5. The privacy object
|
|
84
|
+
|
|
85
|
+
`privacy` is OPTIONAL and describes how this event relates to personal data.
|
|
86
|
+
|
|
87
|
+
| Field | Meaning |
|
|
88
|
+
| ---------------------- | ------------------------------------------------------------- |
|
|
89
|
+
| `containsPersonalData` | Whether the event as serialized contains personal data. |
|
|
90
|
+
| `dataCategories` | Categories of personal data present. Open vocabulary. |
|
|
91
|
+
| `processing` | How personal data was handled when the event was produced. |
|
|
92
|
+
| `minimized` | Whether the producer applied data minimization to this event. |
|
|
93
|
+
| `retentionClass` | Producer-defined retention class governing this event. |
|
|
94
|
+
| `purpose` | Purpose for which the event is recorded. |
|
|
95
|
+
|
|
96
|
+
### 5.1 Data categories
|
|
97
|
+
|
|
98
|
+
RECOMMENDED values:
|
|
99
|
+
|
|
100
|
+
```text
|
|
101
|
+
identifier contact network financial health
|
|
102
|
+
biometric location employment behavioral special-category
|
|
103
|
+
other
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
`special-category` marks data that the operator's own policy treats as requiring heightened
|
|
107
|
+
protection. The specification deliberately does not define which categories are special, because that
|
|
108
|
+
determination is jurisdictional.
|
|
109
|
+
|
|
110
|
+
Producers SHOULD populate `dataCategories` whenever `containsPersonalData` is `true`. The schema does
|
|
111
|
+
not enforce this, because a producer may know that personal data is present before it has classified
|
|
112
|
+
it.
|
|
113
|
+
|
|
114
|
+
### 5.2 Processing
|
|
115
|
+
|
|
116
|
+
`processing` MUST be one of:
|
|
117
|
+
|
|
118
|
+
| Value | Meaning |
|
|
119
|
+
| ----------- | -------------------------------------------------------------- |
|
|
120
|
+
| `include` | Personal data is present in the event as recorded. |
|
|
121
|
+
| `mask` | Personal data was partially obscured before recording. |
|
|
122
|
+
| `hash` | Personal data was replaced with a digest. |
|
|
123
|
+
| `drop` | Personal data was removed entirely. |
|
|
124
|
+
| `encrypt` | Personal data was encrypted before recording. |
|
|
125
|
+
| `reference` | Personal data was replaced with a reference to another system. |
|
|
126
|
+
|
|
127
|
+
A single value describes the treatment applied to the event as a whole. Per-field processing
|
|
128
|
+
descriptions are an open question for v0.2.
|
|
129
|
+
|
|
130
|
+
Note that `hash` is not anonymization. Hashing a low-entropy identifier such as an email address or a
|
|
131
|
+
national identification number is reversible by enumeration and MUST NOT be presented as removing the
|
|
132
|
+
personal-data character of the field.
|
|
133
|
+
|
|
134
|
+
### 5.3 Regulation neutrality
|
|
135
|
+
|
|
136
|
+
The privacy object MUST NOT be extended with regulation-specific fields: no lawful basis
|
|
137
|
+
enumerations, no article references, no jurisdiction-specific consent flags, no framework identifiers.
|
|
138
|
+
|
|
139
|
+
Those are interpretations of audit data, produced by mapping artifacts maintained outside this
|
|
140
|
+
specification. Encoding one framework's vocabulary into the core model would make the model wrong
|
|
141
|
+
everywhere else, and stale as soon as the framework is revised.
|
|
142
|
+
|
|
143
|
+
## 6. Validation cannot detect secrets
|
|
144
|
+
|
|
145
|
+
Schema validation alone **cannot** detect a leaked secret.
|
|
146
|
+
|
|
147
|
+
A password placed in `metadata.oldValue` is a valid string in a valid free-form object. The event will
|
|
148
|
+
pass every conformance test in this repository. This is a limitation of machine validation, not a gap
|
|
149
|
+
that a future schema version will close.
|
|
150
|
+
|
|
151
|
+
Implementations SHOULD therefore:
|
|
152
|
+
|
|
153
|
+
- Run secret scanning over audit events before they leave the producer, and again in the pipeline.
|
|
154
|
+
- Review audit instrumentation in code review with the same seriousness as data model changes.
|
|
155
|
+
- Fail closed: when a producer cannot determine whether a value is safe, it SHOULD omit the value.
|
|
156
|
+
- Treat the audit store as a system holding personal data, with corresponding access control,
|
|
157
|
+
retention and disclosure controls.
|
|
158
|
+
|
|
159
|
+
`auditmodel lint-privacy` makes **part** of this checkable. The rest of this section describes what
|
|
160
|
+
that part is, and — more importantly — what it is not.
|
|
161
|
+
|
|
162
|
+
### 6.1 Four kinds of rule
|
|
163
|
+
|
|
164
|
+
The requirements in this document fall into four groups. Conflating them is how a linter comes to be
|
|
165
|
+
trusted for things it cannot do.
|
|
166
|
+
|
|
167
|
+
| Kind | Enforced by | Examples |
|
|
168
|
+
| --------------------------- | ---------------------------- | --------------------------------------------------------- |
|
|
169
|
+
| **Normative requirement** | This specification. Binding. | §1 "passwords MUST NEVER be recorded" |
|
|
170
|
+
| **Deterministic lint rule** | Exact matching. No guessing. | A property named `password`; a PEM private key marker |
|
|
171
|
+
| **Heuristic lint rule** | Measurement. Guesses. | High character entropy; an oversized object |
|
|
172
|
+
| **Not automatable** | Review. Nothing else. | Whether a value is personal data; whether §3 was followed |
|
|
173
|
+
|
|
174
|
+
A deterministic rule can still be wrong about **meaning**: a property named `password` might hold a
|
|
175
|
+
policy description. It is deterministic because the same input always produces the same finding, not
|
|
176
|
+
because the finding is always correct.
|
|
177
|
+
|
|
178
|
+
### 6.2 What the linter checks
|
|
179
|
+
|
|
180
|
+
`auditmodel lint-privacy` is deterministic local static analysis. It reads events, reports
|
|
181
|
+
suspicions, and does nothing else. It MUST NOT send event content anywhere, resolve or fetch any
|
|
182
|
+
reference, open any file an event names, modify or redact an event, or consult any remote service or
|
|
183
|
+
model.
|
|
184
|
+
|
|
185
|
+
Deterministic rules:
|
|
186
|
+
|
|
187
|
+
| Rule | Detects |
|
|
188
|
+
| -------------------------------- | ------------------------------------------------------------------------------------- |
|
|
189
|
+
| `OAM-PRIV-001` | A populated property whose name is a credential name after normalization |
|
|
190
|
+
| `OAM-PRIV-002` | A value shaped as `Bearer`, `Basic`, `Digest`, `ApiKey` or `Token` plus a credential |
|
|
191
|
+
| `OAM-PRIV-003` | A PEM marker introducing private key material |
|
|
192
|
+
| `OAM-PRIV-010` | A structurally valid JSON Web Token |
|
|
193
|
+
| `OAM-PRIV-011` | A published access key identifier shape |
|
|
194
|
+
| `OAM-PRIV-012` to `OAM-PRIV-016` | Published credential prefixes for source forge, messaging, payment and cloud API keys |
|
|
195
|
+
| `OAM-PRIV-030` | A URL with embedded user information |
|
|
196
|
+
| `OAM-PRIV-031` | An evidence reference carrying a query string or fragment |
|
|
197
|
+
| `OAM-PRIV-040` | A connection string carrying a password |
|
|
198
|
+
| `OAM-PRIV-041` | A connection string without a credential, which still discloses infrastructure |
|
|
199
|
+
| `OAM-PRIV-061` | A populated property whose name denotes a raw request, response or message body |
|
|
200
|
+
|
|
201
|
+
Heuristic rules:
|
|
202
|
+
|
|
203
|
+
| Rule | Measures |
|
|
204
|
+
| -------------- | --------------------------------------- |
|
|
205
|
+
| `OAM-PRIV-050` | Character entropy, as a token candidate |
|
|
206
|
+
| `OAM-PRIV-060` | Value size, as a minimization signal |
|
|
207
|
+
|
|
208
|
+
Recognising a published credential prefix is a property of the **tooling**, not of the model. The
|
|
209
|
+
specification, the canonical schema and the semantic conventions name no vendor, and no lint rule
|
|
210
|
+
introduces a field, vocabulary or concept into the model.
|
|
211
|
+
|
|
212
|
+
### 6.3 What the linter does not check
|
|
213
|
+
|
|
214
|
+
It does not, and largely cannot, determine:
|
|
215
|
+
|
|
216
|
+
- Whether a value **is** personal data. `privacy.dataCategories` is a producer declaration; nothing
|
|
217
|
+
verifies it.
|
|
218
|
+
- Whether `privacy.minimized` is truthful.
|
|
219
|
+
- Whether the audit purpose required a field to be recorded at all.
|
|
220
|
+
- Whether a hash of a low-entropy identifier is reversible by enumeration.
|
|
221
|
+
- Whether `actor.displayName`, `resource.name` or `request.ipAddress` were justified.
|
|
222
|
+
- Whether §3's allowlist model was followed. That is a property of the producer's **code**; an event
|
|
223
|
+
built by copying a request object and one built field by field can be byte-identical.
|
|
224
|
+
- Whether the audit store has access control or retention (§7).
|
|
225
|
+
- Anything about regulatory classification (§9).
|
|
226
|
+
- Whether a detected credential is real, current, revoked or usable. No rule validates a signature,
|
|
227
|
+
decodes a token for its claims, or contacts an issuer.
|
|
228
|
+
|
|
229
|
+
### 6.4 A finding is a suspicion; a clean result is not a clearance
|
|
230
|
+
|
|
231
|
+
A finding means a value **matched a rule**. It does not establish a data breach, a regulatory
|
|
232
|
+
violation, a confirmed credential or confirmed personal data. Tooling and documentation MUST NOT
|
|
233
|
+
present findings in those terms.
|
|
234
|
+
|
|
235
|
+
**A clean result does not mean an event is safe or compliant.** A password that happens to be a
|
|
236
|
+
dictionary word, stored under a field named `note`, matches nothing here. Neither does a national
|
|
237
|
+
identification number, a home address or a medical detail: the linter looks for values shaped like
|
|
238
|
+
secrets, and most personal data is not shaped like anything.
|
|
239
|
+
|
|
240
|
+
### 6.5 Findings never contain the offending value
|
|
241
|
+
|
|
242
|
+
No finding — in any output format — contains the value that produced it, any part of it, a preview, a
|
|
243
|
+
prefix, a suffix, or a decoded claim. A finding carries a rule identifier, a severity, a confidence,
|
|
244
|
+
a JSON Pointer, a message and a recommendation.
|
|
245
|
+
|
|
246
|
+
The reason is direct: linter output goes into CI logs, pull request comments and issue trackers,
|
|
247
|
+
which are usually less protected than the audit store. A tool that echoed what it found would move
|
|
248
|
+
secrets from a controlled system into an uncontrolled one, and would do it precisely when a secret
|
|
249
|
+
was present.
|
|
250
|
+
|
|
251
|
+
### 6.6 Inspected locations
|
|
252
|
+
|
|
253
|
+
Values are inspected under:
|
|
254
|
+
|
|
255
|
+
```text
|
|
256
|
+
/metadata /change/before
|
|
257
|
+
/extensions /change/after
|
|
258
|
+
/actor/attributes /event/error/message
|
|
259
|
+
/subject/attributes /event/summary
|
|
260
|
+
/resource/attributes /reason/text
|
|
261
|
+
/relatedResources/*/attributes /reason/reference
|
|
262
|
+
/evidence/*/reference /request/route
|
|
263
|
+
/authorization/reason /delegation/reason
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
Property **names** are inspected recursively within `metadata`, `extensions`, every `attributes`
|
|
267
|
+
object, `change.before` and `change.after`. Extension keys are also tested on their final
|
|
268
|
+
dot-separated segment, because a reverse-domain key names its field there.
|
|
269
|
+
|
|
270
|
+
Everything else is excluded, including `integrity` digests and signatures, `request.traceId` and
|
|
271
|
+
`request.spanId`, and the event `id`. Those fields hold high-entropy values by design; scanning them
|
|
272
|
+
would produce a finding on every well-formed event.
|
|
273
|
+
|
|
274
|
+
### 6.7 Known-safe exclusions from the entropy heuristic
|
|
275
|
+
|
|
276
|
+
The entropy rule — and **only** the entropy rule — ignores values recognised as: UUIDs, ULIDs, W3C
|
|
277
|
+
Trace Context trace and span identifiers, lower-case hexadecimal digests of SHA-256, SHA-384 and
|
|
278
|
+
SHA-512 length, RFC 3339 timestamps, purely numeric identifiers, lower-case separated identifiers and
|
|
279
|
+
reference paths, anything containing `://`, and redaction placeholders.
|
|
280
|
+
|
|
281
|
+
A value under a property named `password` is reported whatever it looks like. A value matching a
|
|
282
|
+
published credential format is reported even if it also looks like an identifier.
|
|
283
|
+
|
|
284
|
+
### 6.8 Fixed thresholds
|
|
285
|
+
|
|
286
|
+
Version 0.1 hard-codes these. They are not configurable; see
|
|
287
|
+
[ADR 0007](../decisions/0007-deterministic-privacy-linting.md).
|
|
288
|
+
|
|
289
|
+
| Threshold | Value |
|
|
290
|
+
| ------------------------------- | ------------------------------------ |
|
|
291
|
+
| Minimum length for entropy | 24 characters |
|
|
292
|
+
| Maximum length for entropy | 4096 characters |
|
|
293
|
+
| Entropy threshold | 4.0 bits per character |
|
|
294
|
+
| Minimum character classes | 3 of 4 (lower, upper, digit, symbol) |
|
|
295
|
+
| Oversized: serialized size | 4096 bytes |
|
|
296
|
+
| Oversized: property count | 50 |
|
|
297
|
+
| Oversized: nesting depth | 6 |
|
|
298
|
+
| Oversized: array length | 100 |
|
|
299
|
+
| Oversized: single string length | 2048 characters |
|
|
300
|
+
| Maximum traversal depth | 64 |
|
|
301
|
+
|
|
302
|
+
### 6.9 False positives and false negatives
|
|
303
|
+
|
|
304
|
+
Both are expected, and the design prefers false negatives in the heuristic rules and false positives
|
|
305
|
+
in nothing.
|
|
306
|
+
|
|
307
|
+
**False positives** are most likely from `OAM-PRIV-050` on legitimate opaque identifiers, from
|
|
308
|
+
`OAM-PRIV-060` on genuinely detailed metadata, and from `OAM-PRIV-001` where a credential-named field
|
|
309
|
+
holds something else. `OAM-PRIV-050` reports at low confidence for exactly this reason.
|
|
310
|
+
|
|
311
|
+
**False negatives** are certain. A secret in an unpublished format, under a harmless name, below the
|
|
312
|
+
entropy threshold, or in all lower case with a separator, is invisible. So is any personal data that
|
|
313
|
+
is not shaped like a secret. A recognised redaction placeholder under a credential name is
|
|
314
|
+
deliberately not reported, because a tool that flags `"password": "[REDACTED]"` teaches people to
|
|
315
|
+
ignore it.
|
|
316
|
+
|
|
317
|
+
`OAM-PRIV-001` reports a credential-named property when it holds a **scalar**. A **container** under
|
|
318
|
+
such a name is treated as a descriptor and is not itself reported; its members are inspected
|
|
319
|
+
individually. `credential: { "type": "api-key" }` describes a credential without carrying one, and is
|
|
320
|
+
the shape the identity-and-access-management profile requires for credential rotation events —
|
|
321
|
+
flagging it would fire on every conforming rotation event, which is how a linter earns the reputation
|
|
322
|
+
that gets it switched off. The accepted cost is a secret stored under a harmless member name inside
|
|
323
|
+
such a container.
|
|
324
|
+
|
|
325
|
+
### 6.10 Schema validation is not secret scanning
|
|
326
|
+
|
|
327
|
+
These are different questions and neither substitutes for the other.
|
|
328
|
+
|
|
329
|
+
Schema validation asks whether an event is **well formed**. It passes an event containing a password
|
|
330
|
+
in `metadata`, because a password is a valid string. Privacy linting asks whether an event **looks
|
|
331
|
+
like it contains something it should not**. It passes an event that is structurally broken, which is
|
|
332
|
+
why the linter validates first and refuses to deep-lint an invalid event: traversing an arbitrary
|
|
333
|
+
structure yields findings whose paths mean nothing.
|
|
334
|
+
|
|
335
|
+
### 6.11 Exit codes
|
|
336
|
+
|
|
337
|
+
```text
|
|
338
|
+
0 no findings
|
|
339
|
+
1 one or more privacy findings, or a schema-invalid event
|
|
340
|
+
2 usage error, or a file could not be read or parsed
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
A schema-invalid event is reported and **not** deep linted, and exits non-zero: a clean privacy
|
|
344
|
+
result for an event that was never linted would be misleading.
|
|
345
|
+
|
|
346
|
+
### 6.12 Using it
|
|
347
|
+
|
|
348
|
+
```bash
|
|
349
|
+
auditmodel lint-privacy examples/privacy/clean/minimal-clean-event.json
|
|
350
|
+
auditmodel lint-privacy examples/privacy/findings/access-token-field.json
|
|
351
|
+
auditmodel lint-privacy examples/privacy --format json
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
Producers SHOULD run it in CI over fixture events and over samples of real output, and SHOULD treat
|
|
355
|
+
it as one control among several. It is not a substitute for reviewing audit instrumentation, and
|
|
356
|
+
running it does not make an event safe.
|
|
357
|
+
|
|
358
|
+
## 7. Access to audit data
|
|
359
|
+
|
|
360
|
+
Audit data MUST be access-controlled. It describes individuals' behaviour, and in many systems it is
|
|
361
|
+
readable by more people than the data it describes.
|
|
362
|
+
|
|
363
|
+
The specification defines no access control model. It notes only that an audit trail readable by
|
|
364
|
+
everyone in an organization is a privacy exposure that no amount of field-level minimization
|
|
365
|
+
compensates for.
|
|
366
|
+
|
|
367
|
+
## 8. Example
|
|
368
|
+
|
|
369
|
+
```json
|
|
370
|
+
{
|
|
371
|
+
"privacy": {
|
|
372
|
+
"containsPersonalData": true,
|
|
373
|
+
"dataCategories": ["identifier", "contact", "behavioral"],
|
|
374
|
+
"processing": "reference",
|
|
375
|
+
"minimized": true,
|
|
376
|
+
"retentionClass": "standard",
|
|
377
|
+
"purpose": "operational-audit"
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
## 9. No legal or compliance guarantee
|
|
383
|
+
|
|
384
|
+
This document describes engineering practice for reducing the risk that audit data creates. It is not
|
|
385
|
+
legal advice, it does not establish a lawful basis for processing, and conformance to it does not
|
|
386
|
+
constitute compliance with any law, regulation, standard or contract.
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# Resource Model
|
|
2
|
+
|
|
3
|
+
**Specification version: 0.1 · Status: Experimental · This document: Normative**
|
|
4
|
+
|
|
5
|
+
## 1. Purpose
|
|
6
|
+
|
|
7
|
+
`resource` answers "what was acted upon". It is REQUIRED, because an audit event that records an
|
|
8
|
+
action without a target cannot be reviewed, correlated or scoped to a data subject.
|
|
9
|
+
|
|
10
|
+
## 2. Structure
|
|
11
|
+
|
|
12
|
+
### 2.1 Required
|
|
13
|
+
|
|
14
|
+
| Field | Meaning |
|
|
15
|
+
| ------ | -------------------------------------------------------------- |
|
|
16
|
+
| `type` | Kind of resource. Open vocabulary. |
|
|
17
|
+
| `id` | Stable identifier of the resource within the producing system. |
|
|
18
|
+
|
|
19
|
+
### 2.2 Optional
|
|
20
|
+
|
|
21
|
+
| Field | Meaning |
|
|
22
|
+
| ---------------- | ------------------------------------------------------------------ |
|
|
23
|
+
| `name` | Human-readable resource name. |
|
|
24
|
+
| `parentId` | Containing resource: folder, workspace, project, cluster, account. |
|
|
25
|
+
| `classification` | Data classification of the resource. Open vocabulary. |
|
|
26
|
+
| `ownerId` | Owning principal or team. |
|
|
27
|
+
| `tenantId` | Tenant the resource belongs to. |
|
|
28
|
+
| `attributes` | Producer-defined additional attributes. |
|
|
29
|
+
|
|
30
|
+
## 3. Resource types are open-ended
|
|
31
|
+
|
|
32
|
+
`resource.type` is an **open vocabulary**. The core schema MUST NOT enumerate resource types, and a
|
|
33
|
+
consumer MUST NOT reject a resource type it does not recognise.
|
|
34
|
+
|
|
35
|
+
Types observed across the domains this model targets include:
|
|
36
|
+
|
|
37
|
+
```text
|
|
38
|
+
user role document incident
|
|
39
|
+
configuration deployment database-record api-key
|
|
40
|
+
consumer-group topic queue workflow
|
|
41
|
+
tenant report session share-link
|
|
42
|
+
policy secret cluster service
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
This list is illustrative, not exhaustive and not normative. A hard-coded list of resource types is
|
|
46
|
+
the fastest way to make a general model unusable in a domain nobody thought of.
|
|
47
|
+
|
|
48
|
+
Resource types MUST be lower-case, hyphen-separated tokens, so that independently produced events
|
|
49
|
+
remain comparable in form even when the vocabularies differ.
|
|
50
|
+
|
|
51
|
+
## 4. Identifiers and names
|
|
52
|
+
|
|
53
|
+
- `id` MUST be stable for the lifetime of the resource. A path or display name that changes when the
|
|
54
|
+
resource is moved or renamed is not an identifier.
|
|
55
|
+
- `id` MUST be recorded even when the operation failed, when the producer knows what was attempted.
|
|
56
|
+
- `name` is OPTIONAL, and MAY itself be sensitive. Document titles, incident summaries and file names
|
|
57
|
+
routinely contain personal data, customer names and unreleased business information. Producers
|
|
58
|
+
SHOULD consider whether recording `name` is necessary for the audit purpose. See
|
|
59
|
+
[privacy.md](privacy.md).
|
|
60
|
+
- `resource.attributes` MUST NOT contain the **contents** of the resource. An audit event records
|
|
61
|
+
that a document was downloaded; it does not carry the document.
|
|
62
|
+
|
|
63
|
+
## 5. Classification
|
|
64
|
+
|
|
65
|
+
`classification` describes the sensitivity of the resource. It is an **open vocabulary**, because
|
|
66
|
+
classification schemes are defined by organizations and differ legitimately in the number of levels
|
|
67
|
+
and their names.
|
|
68
|
+
|
|
69
|
+
RECOMMENDED values:
|
|
70
|
+
|
|
71
|
+
```text
|
|
72
|
+
public internal confidential restricted secret unknown
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Organizations that use a different scheme MAY use their own tokens. Where a mapping to the
|
|
76
|
+
recommended values is possible, producers SHOULD publish it so that cross-system reporting remains
|
|
77
|
+
meaningful.
|
|
78
|
+
|
|
79
|
+
`classification` describes the **resource**, not the event. The sensitivity of the audit event itself
|
|
80
|
+
is described by [privacy.md](privacy.md).
|
|
81
|
+
|
|
82
|
+
## 6. Related resources
|
|
83
|
+
|
|
84
|
+
`relatedResources` is an OPTIONAL array of additional resources affected by, or required to
|
|
85
|
+
interpret, the same operation. Each entry has the same shape as `resource`.
|
|
86
|
+
|
|
87
|
+
Rules:
|
|
88
|
+
|
|
89
|
+
1. `resource` MUST hold the **primary** target. `relatedResources` MUST NOT be used as an alternative
|
|
90
|
+
to choosing one.
|
|
91
|
+
2. `relatedResources`, when present, MUST contain at least one entry.
|
|
92
|
+
3. `relatedResources` SHOULD be used for genuinely affected resources, not for arbitrary context. A
|
|
93
|
+
consumer group reset affects the consumer group primarily and the topic secondarily; both belong.
|
|
94
|
+
The cluster's monitoring dashboard does not.
|
|
95
|
+
4. A bulk operation over many resources SHOULD be recorded as one event per resource where the
|
|
96
|
+
resources are individually significant, and as a single event with a representative primary
|
|
97
|
+
resource and a count in `metadata` where they are not. Producers SHOULD document which approach
|
|
98
|
+
they use, because the two are not interchangeable for review purposes.
|
|
99
|
+
|
|
100
|
+
Example:
|
|
101
|
+
|
|
102
|
+
```json
|
|
103
|
+
{
|
|
104
|
+
"resource": { "type": "consumer-group", "id": "payments-settlement-consumer" },
|
|
105
|
+
"relatedResources": [
|
|
106
|
+
{ "type": "topic", "id": "payments.settlement.completed" },
|
|
107
|
+
{ "type": "cluster", "id": "cluster-streaming-production" }
|
|
108
|
+
]
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## 7. Resource is not subject
|
|
113
|
+
|
|
114
|
+
`resource` is what the operation acted **on**. `subject` is the principal the operation was performed
|
|
115
|
+
**for**. When an administrator disables a user account, the user is a `resource` — nobody delegated
|
|
116
|
+
authority to the administrator. See [actor-model.md](actor-model.md).
|