@openauditmodel/cli 0.3.0 → 0.4.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/README.md +60 -4
- package/dist/conformance/src/cli.js +113 -6
- package/dist/conformance/src/cli.js.map +1 -1
- package/dist/conformance/src/privacy/types.d.ts +19 -0
- package/dist/conformance/src/privacy/types.js +8 -0
- package/dist/conformance/src/privacy/types.js.map +1 -1
- package/dist/conformance/src/profiles/coverage.d.ts +62 -0
- package/dist/conformance/src/profiles/coverage.js +114 -0
- package/dist/conformance/src/profiles/coverage.js.map +1 -0
- package/package.json +7 -2
- package/profiles/README.md +42 -0
- package/profiles/api-and-integration-management/0.1/profile.json +258 -0
- package/profiles/backup-and-recovery/0.1/profile.json +178 -0
- package/profiles/customer-and-account-management/0.1/profile.json +237 -0
- package/profiles/deployment-and-change-management/0.1/profile.json +298 -0
- package/profiles/document-management/0.1/profile.json +170 -0
- package/profiles/financial-transaction-management/0.1/profile.json +247 -0
- package/profiles/identity-and-access-management/0.1/profile.json +120 -0
- package/profiles/incident-management/0.1/profile.json +256 -0
- package/profiles/incident-management/0.2/profile.json +287 -0
- package/profiles/incident-management/README.md +39 -21
- package/profiles/incident-management/profile.json +37 -6
- package/profiles/message-broker-management/0.1/profile.json +399 -0
- package/profiles/secrets-and-key-management/0.1/profile.json +219 -0
- package/semantic-conventions/README.md +15 -10
- package/semantic-conventions/backup-and-recovery.md +124 -0
- package/semantic-conventions/customer-and-account.md +133 -0
- package/semantic-conventions/financial-transactions.md +128 -0
- package/semantic-conventions/message-brokers.md +128 -0
- package/semantic-conventions/secrets-and-keys.md +132 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
{
|
|
2
|
+
"profileVersion": "0.1",
|
|
3
|
+
"name": "document-management",
|
|
4
|
+
"version": "0.1",
|
|
5
|
+
"status": "experimental",
|
|
6
|
+
"coreVersions": ["0.1"],
|
|
7
|
+
"title": "Document Management Profile",
|
|
8
|
+
"description": "Additional conformance requirements for document management audit events: creation, deletion, download, versioning, sharing, access policy, retention and legal hold. Every requirement adds to the OpenAuditModel Core Specification; none relaxes it. High-volume read events such as document.file.view are deliberately not governed.",
|
|
9
|
+
"rules": [
|
|
10
|
+
{
|
|
11
|
+
"id": "DOC-CORE-001",
|
|
12
|
+
"description": "Every governed document operation records the authorization decision that permitted it and the classification of the document it acted on.",
|
|
13
|
+
"rationale": "A document operation without a recorded decision cannot be reviewed: nothing distinguishes an action policy allowed from one that bypassed policy. Without a classification, a reviewer cannot tell whether the operation touched public material or the most sensitive record the organization holds, and every triage decision downstream depends on that difference.",
|
|
14
|
+
"severity": "error",
|
|
15
|
+
"events": ["document.file.upload", "document.file.delete", "document.file.download"],
|
|
16
|
+
"eventPrefixes": [
|
|
17
|
+
"document.share.",
|
|
18
|
+
"document.permission.",
|
|
19
|
+
"document.version.",
|
|
20
|
+
"document.retention.",
|
|
21
|
+
"document.legal-hold."
|
|
22
|
+
],
|
|
23
|
+
"requiredPaths": ["/authorization", "/resource/classification"]
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"id": "DOC-CORE-002",
|
|
27
|
+
"description": "A governed document operation should record why it happened, where the document lives, and how it correlates with the wider operation.",
|
|
28
|
+
"rationale": "These are the fields a reviewer reaches for first and a producer omits most often. They are recommended rather than required because a conforming system may legitimately have no container hierarchy, and because a missing justification should prompt a question rather than fail a build.",
|
|
29
|
+
"severity": "warning",
|
|
30
|
+
"events": ["document.file.upload", "document.file.delete", "document.file.download"],
|
|
31
|
+
"eventPrefixes": [
|
|
32
|
+
"document.share.",
|
|
33
|
+
"document.permission.",
|
|
34
|
+
"document.version.",
|
|
35
|
+
"document.retention.",
|
|
36
|
+
"document.legal-hold."
|
|
37
|
+
],
|
|
38
|
+
"recommendedPaths": ["/reason", "/resource/parentId", "/request/correlationId"]
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"id": "DOC-SHARE-001",
|
|
42
|
+
"description": "A share event records the kind of recipient the access was granted to or withdrawn from.",
|
|
43
|
+
"rationale": "Whether a document went to a colleague, a partner or an anonymous link is the single fact that determines the risk of the operation, and it cannot be reconstructed later from an identifier alone.",
|
|
44
|
+
"severity": "error",
|
|
45
|
+
"eventPrefixes": ["document.share."],
|
|
46
|
+
"requiredMetadata": [
|
|
47
|
+
{
|
|
48
|
+
"path": "/share/recipientType",
|
|
49
|
+
"type": "string"
|
|
50
|
+
}
|
|
51
|
+
]
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"id": "DOC-SHARE-002",
|
|
55
|
+
"description": "Creating a share records the permission granted, and should record when the access ends.",
|
|
56
|
+
"rationale": "A share that does not say what the recipient may do records that something was shared without recording what was given away. An expiry is recommended here and required for external recipients, because indefinite internal access is often a deliberate and reasonable choice.",
|
|
57
|
+
"severity": "error",
|
|
58
|
+
"events": ["document.share.create"],
|
|
59
|
+
"requiredMetadata": [
|
|
60
|
+
{
|
|
61
|
+
"path": "/share/permission",
|
|
62
|
+
"type": "string"
|
|
63
|
+
}
|
|
64
|
+
],
|
|
65
|
+
"recommendedPaths": ["/metadata/share/expiresAt"]
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"id": "DOC-SHARE-003",
|
|
69
|
+
"description": "A share to an external recipient is time-bounded and justified.",
|
|
70
|
+
"rationale": "An external share is a disclosure outside the organization's control. Without an expiry it is indefinite, and without a stated reason there is no record of who decided the disclosure was acceptable. This rule fires only when the producer has declared the recipient external, so the profile does not have to guess what external means for a given deployment.",
|
|
71
|
+
"severity": "error",
|
|
72
|
+
"events": ["document.share.create"],
|
|
73
|
+
"when": {
|
|
74
|
+
"path": "/metadata/share/recipientType",
|
|
75
|
+
"equals": "external"
|
|
76
|
+
},
|
|
77
|
+
"requiredPaths": ["/reason"],
|
|
78
|
+
"requiredMetadata": [
|
|
79
|
+
{
|
|
80
|
+
"path": "/share/expiresAt",
|
|
81
|
+
"type": "string"
|
|
82
|
+
}
|
|
83
|
+
]
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"id": "DOC-PERM-001",
|
|
87
|
+
"description": "A document permission change names the principal whose access changed and the permission involved.",
|
|
88
|
+
"rationale": "An access-policy change that does not say whose access changed is not reviewable at all. The principal is recorded in namespaced metadata rather than in `/subject`, because [actor-model.md](../../specification/actor-model.md) §5 defines `subject` as the principal *on whose behalf* the actor acted, and forbids its use as a generic target. A grantee or assignee is neither. This follows the identity profile, which records a role in `/metadata/role/id` for the same reason.",
|
|
89
|
+
"severity": "error",
|
|
90
|
+
"eventPrefixes": ["document.permission."],
|
|
91
|
+
"requiredMetadata": [
|
|
92
|
+
{
|
|
93
|
+
"path": "/permission/id",
|
|
94
|
+
"type": "string"
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
"path": "/permission/granteeId",
|
|
98
|
+
"type": "string"
|
|
99
|
+
}
|
|
100
|
+
],
|
|
101
|
+
"recommendedPaths": ["/reason"]
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
"id": "DOC-VERSION-001",
|
|
105
|
+
"description": "A version event identifies the version it produced or restored.",
|
|
106
|
+
"rationale": "Version history is the record of what a document said at a point in time. An event that does not name the version cannot be tied to the content it describes.",
|
|
107
|
+
"severity": "error",
|
|
108
|
+
"eventPrefixes": ["document.version."],
|
|
109
|
+
"requiredMetadata": [
|
|
110
|
+
{
|
|
111
|
+
"path": "/version/id",
|
|
112
|
+
"type": "string"
|
|
113
|
+
}
|
|
114
|
+
]
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
"id": "DOC-VERSION-002",
|
|
118
|
+
"description": "A rollback records the version it replaced and why the current content was discarded.",
|
|
119
|
+
"rationale": "A rollback destroys the prevailing content. Without the replaced version the trail cannot say what was undone, and without a reason it cannot say whether the change was a correction or a reversal of someone else's work.",
|
|
120
|
+
"severity": "error",
|
|
121
|
+
"events": ["document.version.rollback"],
|
|
122
|
+
"requiredPaths": ["/reason"],
|
|
123
|
+
"requiredMetadata": [
|
|
124
|
+
{
|
|
125
|
+
"path": "/version/previousId",
|
|
126
|
+
"type": "string"
|
|
127
|
+
}
|
|
128
|
+
]
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
"id": "DOC-DELETE-001",
|
|
132
|
+
"description": "Deleting a document is justified, and should be approved.",
|
|
133
|
+
"rationale": "Deletion is the operation an audit trail is least able to reconstruct after the fact, because the evidence is what was removed. Approval is recommended rather than required: many systems legitimately allow an owner to delete their own draft, and a rule that forbade it would be ignored rather than followed.",
|
|
134
|
+
"severity": "error",
|
|
135
|
+
"events": ["document.file.delete"],
|
|
136
|
+
"requiredPaths": ["/reason"],
|
|
137
|
+
"recommendedPaths": ["/approval"]
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
"id": "DOC-RETENTION-001",
|
|
141
|
+
"description": "A retention change records the resulting retention class, the transition and why it was made.",
|
|
142
|
+
"rationale": "Retention determines how long the evidence exists at all, so a change to it is a change to the audit trail's own reach. The profile requires the state and the transition to be recorded; it does not say what any retention class must mean, or how long anything must be kept, because those are organization and jurisdiction decisions this specification does not make.",
|
|
143
|
+
"severity": "error",
|
|
144
|
+
"eventPrefixes": ["document.retention."],
|
|
145
|
+
"requiredPaths": ["/change", "/reason"],
|
|
146
|
+
"requiredMetadata": [
|
|
147
|
+
{
|
|
148
|
+
"path": "/retention/class",
|
|
149
|
+
"type": "string"
|
|
150
|
+
}
|
|
151
|
+
],
|
|
152
|
+
"recommendedPaths": ["/approval"]
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
"id": "DOC-HOLD-001",
|
|
156
|
+
"description": "A legal hold event records whether the hold is in force and why it changed.",
|
|
157
|
+
"rationale": "A hold suspends normal retention, so an event that does not state the resulting condition leaves the document's disposition ambiguous. The profile records the state and the justification only; it asserts nothing about any jurisdiction's obligations, and conformance with this rule is not compliance with any legal duty.",
|
|
158
|
+
"severity": "error",
|
|
159
|
+
"eventPrefixes": ["document.legal-hold."],
|
|
160
|
+
"requiredPaths": ["/reason"],
|
|
161
|
+
"requiredMetadata": [
|
|
162
|
+
{
|
|
163
|
+
"path": "/legalHold/active",
|
|
164
|
+
"type": "boolean"
|
|
165
|
+
}
|
|
166
|
+
],
|
|
167
|
+
"recommendedPaths": ["/approval"]
|
|
168
|
+
}
|
|
169
|
+
]
|
|
170
|
+
}
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
{
|
|
2
|
+
"profileVersion": "0.1",
|
|
3
|
+
"name": "financial-transaction-management",
|
|
4
|
+
"version": "0.1",
|
|
5
|
+
"status": "experimental",
|
|
6
|
+
"coreVersions": ["0.1"],
|
|
7
|
+
"title": "Financial Transaction Management Profile",
|
|
8
|
+
"description": "Additional conformance requirements for audit events that record material financial operations: transfers, payments, withdrawals, deposits, refunds, reversals, payouts, settlements, chargebacks, reconciliation adjustments and transaction limit changes. Every requirement adds to the OpenAuditModel Core Specification; none relaxes it. Non-mutating financial reads such as balance enquiries, price quotes and routine reporting are deliberately not governed.",
|
|
9
|
+
"rules": [
|
|
10
|
+
{
|
|
11
|
+
"id": "FIN-CORE-001",
|
|
12
|
+
"description": "Every governed financial operation records the authorization decision that permitted it.",
|
|
13
|
+
"rationale": "Money movement is the operation an attacker and a mistaken operator both aim for, and after the fact the two look identical. Without a recorded decision nothing separates a transfer that policy permitted from one that bypassed policy entirely, and a reviewer is left inferring the control state of the system from the fact that the transaction completed. The profile records the decision; it does not evaluate it.",
|
|
14
|
+
"severity": "error",
|
|
15
|
+
"events": ["financial.limit.create", "financial.limit.delete", "financial.limit.update"],
|
|
16
|
+
"eventPrefixes": [
|
|
17
|
+
"financial.transfer.",
|
|
18
|
+
"financial.payment.",
|
|
19
|
+
"financial.withdrawal.",
|
|
20
|
+
"financial.deposit.",
|
|
21
|
+
"financial.refund.",
|
|
22
|
+
"financial.reversal.",
|
|
23
|
+
"financial.payout.",
|
|
24
|
+
"financial.settlement.",
|
|
25
|
+
"financial.chargeback.",
|
|
26
|
+
"financial.reconciliation."
|
|
27
|
+
],
|
|
28
|
+
"requiredPaths": ["/authorization"]
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"id": "FIN-CORE-002",
|
|
32
|
+
"description": "A governed financial operation should record why it happened, how the actor was authenticated, and the other financial resources the same operation touched.",
|
|
33
|
+
"rationale": "These are the three fields a financial reviewer reaches for first and a producer omits most often. They are recommended rather than required because each has a legitimate absence: a scheduled settlement has no per-event business justification, an internal batch process has no interactive authentication context, and a single-sided deposit has no counterparty resource to name. A missing one should prompt a question rather than fail a pipeline.",
|
|
34
|
+
"severity": "warning",
|
|
35
|
+
"events": ["financial.limit.create", "financial.limit.delete", "financial.limit.update"],
|
|
36
|
+
"eventPrefixes": [
|
|
37
|
+
"financial.transfer.",
|
|
38
|
+
"financial.payment.",
|
|
39
|
+
"financial.withdrawal.",
|
|
40
|
+
"financial.deposit.",
|
|
41
|
+
"financial.refund.",
|
|
42
|
+
"financial.reversal.",
|
|
43
|
+
"financial.payout.",
|
|
44
|
+
"financial.settlement.",
|
|
45
|
+
"financial.chargeback.",
|
|
46
|
+
"financial.reconciliation."
|
|
47
|
+
],
|
|
48
|
+
"recommendedPaths": ["/reason", "/authentication", "/relatedResources"]
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"id": "FIN-TXN-001",
|
|
52
|
+
"description": "A monetary movement records the transaction it belongs to, the identifier that correlates the whole transaction lifecycle, and the amount and currency that moved.",
|
|
53
|
+
"rationale": "A financial transaction is almost never one event. An authorization, a capture, a settlement, a refund and a reversal are separate operations against the same money, often minutes or months apart and often produced by different services. Without a transaction reference the events cannot be assembled into a transaction, and without a correlation identifier the transaction cannot be assembled into a case. Amount and currency are what make the trail answerable at all: an event that records that value moved without recording how much and in what unit cannot be reconciled against any ledger, and a bare number without its currency is not an amount.",
|
|
54
|
+
"severity": "error",
|
|
55
|
+
"eventPrefixes": [
|
|
56
|
+
"financial.transfer.",
|
|
57
|
+
"financial.payment.",
|
|
58
|
+
"financial.withdrawal.",
|
|
59
|
+
"financial.deposit.",
|
|
60
|
+
"financial.refund.",
|
|
61
|
+
"financial.reversal.",
|
|
62
|
+
"financial.payout.",
|
|
63
|
+
"financial.settlement.",
|
|
64
|
+
"financial.chargeback."
|
|
65
|
+
],
|
|
66
|
+
"requiredPaths": ["/request/correlationId"],
|
|
67
|
+
"requiredMetadata": [
|
|
68
|
+
{
|
|
69
|
+
"path": "/financial/transactionId",
|
|
70
|
+
"type": "string"
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"path": "/financial/amount",
|
|
74
|
+
"type": "number"
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"path": "/financial/currency",
|
|
78
|
+
"type": "string"
|
|
79
|
+
}
|
|
80
|
+
]
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"id": "FIN-TXN-002",
|
|
84
|
+
"description": "A monetary movement records which way value moved and the state the transaction was left in.",
|
|
85
|
+
"rationale": "The same amount against the same account is a credit or a debit, and the difference is the whole meaning of the event. Direction cannot be reconstructed from the event name because one deployment models a refund as an outbound payment and another as a negative capture. Business status is equally irreplaceable: `event.outcome` says whether the operation the application attempted succeeded, which is not the same question as whether the money has actually moved. A capture can succeed while the transaction remains pending settlement, and a reviewer who conflates the two will report money as final that a counterparty can still recall.",
|
|
86
|
+
"severity": "error",
|
|
87
|
+
"eventPrefixes": [
|
|
88
|
+
"financial.transfer.",
|
|
89
|
+
"financial.payment.",
|
|
90
|
+
"financial.withdrawal.",
|
|
91
|
+
"financial.deposit.",
|
|
92
|
+
"financial.refund.",
|
|
93
|
+
"financial.reversal.",
|
|
94
|
+
"financial.payout.",
|
|
95
|
+
"financial.settlement.",
|
|
96
|
+
"financial.chargeback."
|
|
97
|
+
],
|
|
98
|
+
"requiredMetadata": [
|
|
99
|
+
{
|
|
100
|
+
"path": "/financial/direction",
|
|
101
|
+
"type": "string"
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
"path": "/financial/status",
|
|
105
|
+
"type": "string"
|
|
106
|
+
}
|
|
107
|
+
]
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"id": "FIN-LINK-001",
|
|
111
|
+
"description": "A transfer or a settlement names the other financial resources it moved value between.",
|
|
112
|
+
"rationale": "A transfer and a settlement are two-sided by definition. The core model gives one resource the primary position, so an event that stops there records exactly half the operation: the money left somewhere and the trail does not say where it went. `relatedResources` is the core field for this and needs no new vocabulary, and a counterparty outside the producer's control can still be named by an opaque reference. This is a strengthening of the general `FIN-CORE-002` recommendation for the two families where a single-sided record is not merely thin but wrong.",
|
|
113
|
+
"severity": "error",
|
|
114
|
+
"eventPrefixes": ["financial.transfer.", "financial.settlement."],
|
|
115
|
+
"requiredPaths": ["/relatedResources"]
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"id": "FIN-REASON-001",
|
|
119
|
+
"description": "An operation that refuses, withdraws or unwinds value records why.",
|
|
120
|
+
"rationale": "A completed payment explains itself; a rejected, cancelled, refunded or reversed one does not. These are the operations where the customer disputes the outcome, where the loss is booked, and where the difference between a policy decision, an operational error and a deliberate write-off decides who is accountable. That difference exists only in the justification, and it is knowable only at the moment of the decision. A stated reason also separates a refund issued because goods were returned from one issued to move money to a chosen destination, which is the shape financial abuse takes when it is committed from inside.",
|
|
121
|
+
"severity": "error",
|
|
122
|
+
"events": [
|
|
123
|
+
"financial.payment.reject",
|
|
124
|
+
"financial.payment.cancel",
|
|
125
|
+
"financial.transfer.cancel",
|
|
126
|
+
"financial.payout.cancel",
|
|
127
|
+
"financial.settlement.cancel",
|
|
128
|
+
"financial.withdrawal.reject",
|
|
129
|
+
"financial.refund.create"
|
|
130
|
+
],
|
|
131
|
+
"eventPrefixes": ["financial.reversal.", "financial.chargeback."],
|
|
132
|
+
"requiredPaths": ["/reason"]
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
"id": "FIN-REVERSAL-001",
|
|
136
|
+
"description": "A reversal or a chargeback names the transaction it acts against.",
|
|
137
|
+
"rationale": "A reversal has no independent existence: it is defined entirely by the movement it undoes, and an event that does not name that movement records value appearing from nowhere. Matching a reversal to its original by amount and timing is guesswork that fails precisely where it matters, on the accounts with many similar transactions. The same holds for a chargeback, which is raised by a counterparty against a specific earlier payment and whose whole review turns on which one.",
|
|
138
|
+
"severity": "error",
|
|
139
|
+
"eventPrefixes": ["financial.reversal.", "financial.chargeback."],
|
|
140
|
+
"requiredMetadata": [
|
|
141
|
+
{
|
|
142
|
+
"path": "/financial/originalTransactionId",
|
|
143
|
+
"type": "string"
|
|
144
|
+
}
|
|
145
|
+
],
|
|
146
|
+
"recommendedPaths": ["/approval"]
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
"id": "FIN-APPROVAL-001",
|
|
150
|
+
"description": "When the producer declares that an operation required approval, the event records the approval decision.",
|
|
151
|
+
"rationale": "Approval thresholds are a local policy decision that this profile deliberately does not make. A marketplace pays out thousands of small balances an hour with no human involvement and is right to; a treasury team moves one amount a week under dual control and is right to. What the profile can require is consistency: once a producer has declared that this operation required approval, an event that does not carry the resulting decision has recorded the control as applying without recording whether it was satisfied, which is worse than not claiming the control at all. The status is required rather than the object alone because an approval record naming only a workflow does not say whether anyone approved anything.",
|
|
152
|
+
"severity": "error",
|
|
153
|
+
"events": ["financial.limit.create", "financial.limit.delete", "financial.limit.update"],
|
|
154
|
+
"eventPrefixes": [
|
|
155
|
+
"financial.transfer.",
|
|
156
|
+
"financial.payment.",
|
|
157
|
+
"financial.withdrawal.",
|
|
158
|
+
"financial.deposit.",
|
|
159
|
+
"financial.refund.",
|
|
160
|
+
"financial.reversal.",
|
|
161
|
+
"financial.payout.",
|
|
162
|
+
"financial.settlement.",
|
|
163
|
+
"financial.chargeback.",
|
|
164
|
+
"financial.reconciliation."
|
|
165
|
+
],
|
|
166
|
+
"when": {
|
|
167
|
+
"path": "/metadata/financial/approvalRequired",
|
|
168
|
+
"equals": true
|
|
169
|
+
},
|
|
170
|
+
"requiredPaths": ["/approval/status"],
|
|
171
|
+
"recommendedPaths": ["/approval/workflowId", "/approval/approvers"]
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
"id": "FIN-MANUAL-001",
|
|
175
|
+
"description": "When the producer declares an operation to be a manual intervention, the event records what was altered and why.",
|
|
176
|
+
"rationale": "A manual intervention is an operator substituting their own judgement for the system's, and it is the single most sensitive class of financial event because it leaves no upstream business record to reconcile against. The automated path has an order, an instruction or a schedule behind it; the manual path has a person. Recording the change makes the intervention measurable against the state it replaced, and recording the justification is the only chance to capture the operator's intent, which no later reviewer can reconstruct. The flag is set by the producer because only the producer knows which of its code paths is the manual one.",
|
|
177
|
+
"severity": "error",
|
|
178
|
+
"events": ["financial.limit.create", "financial.limit.delete", "financial.limit.update"],
|
|
179
|
+
"eventPrefixes": [
|
|
180
|
+
"financial.transfer.",
|
|
181
|
+
"financial.payment.",
|
|
182
|
+
"financial.withdrawal.",
|
|
183
|
+
"financial.deposit.",
|
|
184
|
+
"financial.refund.",
|
|
185
|
+
"financial.reversal.",
|
|
186
|
+
"financial.payout.",
|
|
187
|
+
"financial.settlement.",
|
|
188
|
+
"financial.chargeback.",
|
|
189
|
+
"financial.reconciliation."
|
|
190
|
+
],
|
|
191
|
+
"when": {
|
|
192
|
+
"path": "/metadata/financial/manual",
|
|
193
|
+
"equals": true
|
|
194
|
+
},
|
|
195
|
+
"requiredPaths": ["/reason", "/change"],
|
|
196
|
+
"recommendedPaths": ["/approval"]
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
"id": "FIN-RECON-001",
|
|
200
|
+
"description": "A reconciliation event names the reconciliation it belongs to.",
|
|
201
|
+
"rationale": "Reconciliation is the control that proves the producer's own record of money agrees with somebody else's. Its events are only meaningful as a set: a start, a set of findings and a completion together state what was compared and what the comparison concluded. An event that does not name its run cannot be placed in that set, so the trail records that reconciliation happened without recording what any of it decided, and a break found in one period becomes indistinguishable from a break found in another.",
|
|
202
|
+
"severity": "error",
|
|
203
|
+
"eventPrefixes": ["financial.reconciliation."],
|
|
204
|
+
"requiredMetadata": [
|
|
205
|
+
{
|
|
206
|
+
"path": "/financial/reconciliationId",
|
|
207
|
+
"type": "string"
|
|
208
|
+
}
|
|
209
|
+
],
|
|
210
|
+
"recommendedPaths": ["/request/correlationId"]
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
"id": "FIN-RECON-002",
|
|
214
|
+
"description": "A reconciliation adjustment records the change it made to the books, the amount and currency involved, and why the correction was accepted.",
|
|
215
|
+
"rationale": "An adjustment is the one reconciliation operation that moves recorded value without a business transaction behind it: the books are altered to agree with an external record. That makes it the natural hiding place for a loss nobody wants to explain, and the natural place for a genuine correction to be mistaken for one. The before and after state, the size of the correction and the stated justification are what let a later reviewer tell those two apart, and none of them can be recovered once the balances have moved on.",
|
|
216
|
+
"severity": "error",
|
|
217
|
+
"events": ["financial.reconciliation.adjust"],
|
|
218
|
+
"requiredPaths": ["/change", "/reason"],
|
|
219
|
+
"requiredMetadata": [
|
|
220
|
+
{
|
|
221
|
+
"path": "/financial/amount",
|
|
222
|
+
"type": "number"
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
"path": "/financial/currency",
|
|
226
|
+
"type": "string"
|
|
227
|
+
}
|
|
228
|
+
],
|
|
229
|
+
"recommendedPaths": ["/approval"]
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
"id": "FIN-LIMIT-001",
|
|
233
|
+
"description": "A change to a transaction limit records the kind of limit, its before and after state, and why it was changed.",
|
|
234
|
+
"rationale": "A limit is a preventive control, so changing one changes what every future transaction is allowed to do. Raising a ceiling shortly before a large movement and lowering it afterwards is the classic shape of a limit abused rather than administered, and it is visible only when the transition itself is recorded: an event stating the resulting value alone cannot show how far the control moved or that it moved back. The kind of limit is required because a daily aggregate ceiling and a single-transaction ceiling are different controls that deployments name differently, and a reviewer cannot infer which one changed from a number. The rule selects the three mutating operations by name rather than the whole `financial.limit.` family, because a limit check runs on every transaction: governing `financial.limit.check` would put the profile's heaviest requirements on its highest-volume event, and the requirement would be switched off rather than met.",
|
|
235
|
+
"severity": "error",
|
|
236
|
+
"events": ["financial.limit.create", "financial.limit.update", "financial.limit.delete"],
|
|
237
|
+
"requiredPaths": ["/change", "/reason"],
|
|
238
|
+
"requiredMetadata": [
|
|
239
|
+
{
|
|
240
|
+
"path": "/financial/limitType",
|
|
241
|
+
"type": "string"
|
|
242
|
+
}
|
|
243
|
+
],
|
|
244
|
+
"recommendedPaths": ["/approval"]
|
|
245
|
+
}
|
|
246
|
+
]
|
|
247
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
{
|
|
2
|
+
"profileVersion": "0.1",
|
|
3
|
+
"name": "identity-and-access-management",
|
|
4
|
+
"version": "0.1",
|
|
5
|
+
"status": "experimental",
|
|
6
|
+
"coreVersions": ["0.1"],
|
|
7
|
+
"title": "Identity and Access Management Profile",
|
|
8
|
+
"description": "Additional conformance requirements for identity and access management audit events. Every requirement adds to the OpenAuditModel Core Specification; none relaxes it.",
|
|
9
|
+
"rules": [
|
|
10
|
+
{
|
|
11
|
+
"id": "IAM-CORE-001",
|
|
12
|
+
"description": "Every identity operation records the authorization decision that permitted it.",
|
|
13
|
+
"rationale": "An access change without a recorded decision cannot be reviewed: nothing distinguishes a change a policy allowed from one that bypassed policy entirely.",
|
|
14
|
+
"severity": "error",
|
|
15
|
+
"eventPrefixes": ["identity."],
|
|
16
|
+
"requiredPaths": ["/authorization"]
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"id": "IAM-CORE-002",
|
|
20
|
+
"description": "Every identity operation records why it was performed and how it correlates with the request that caused it.",
|
|
21
|
+
"rationale": "Access changes are reviewed long after the fact, by someone who cannot ask the actor what they were doing.",
|
|
22
|
+
"severity": "error",
|
|
23
|
+
"eventPrefixes": ["identity."],
|
|
24
|
+
"recommendedPaths": ["/reason", "/request/correlationId"]
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"id": "IAM-ROLE-001",
|
|
28
|
+
"description": "Role assignment and revocation identify the role and whether it is privileged, and record a justification.",
|
|
29
|
+
"rationale": "The privileged flag is what makes a later reviewer able to separate routine access changes from the ones that matter, without knowing the operator's role catalogue.",
|
|
30
|
+
"severity": "error",
|
|
31
|
+
"events": ["identity.role.assign", "identity.role.revoke"],
|
|
32
|
+
"requiredPaths": ["/authorization", "/reason"],
|
|
33
|
+
"requiredMetadata": [
|
|
34
|
+
{ "path": "/role/id", "type": "string" },
|
|
35
|
+
{ "path": "/role/privileged", "type": "boolean" }
|
|
36
|
+
],
|
|
37
|
+
"recommendedPaths": ["/approval", "/request/correlationId"]
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"id": "IAM-ROLE-002",
|
|
41
|
+
"description": "A privileged role change is approved and performed from a multi-factor authenticated session.",
|
|
42
|
+
"rationale": "Privileged access is the control that protects every other control; granting it on the strength of a single factor and one person's judgement is the failure this rule exists to make visible.",
|
|
43
|
+
"severity": "error",
|
|
44
|
+
"events": ["identity.role.assign", "identity.role.revoke"],
|
|
45
|
+
"when": { "path": "/metadata/role/privileged", "equals": true },
|
|
46
|
+
"requiredPaths": ["/approval", "/authentication"],
|
|
47
|
+
"requiredValues": [{ "path": "/authentication/mfa", "equals": true }]
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"id": "IAM-PERM-001",
|
|
51
|
+
"description": "Permission grants and revocations identify the permission, its scope and whether it is privileged.",
|
|
52
|
+
"rationale": "A permission without a scope cannot be assessed: the same permission over one record and over an entire tenant are different operations.",
|
|
53
|
+
"severity": "error",
|
|
54
|
+
"eventPrefixes": ["identity.permission."],
|
|
55
|
+
"requiredPaths": ["/authorization", "/reason"],
|
|
56
|
+
"requiredMetadata": [
|
|
57
|
+
{ "path": "/permission/id", "type": "string" },
|
|
58
|
+
{ "path": "/permission/scope", "type": "string" },
|
|
59
|
+
{ "path": "/permission/privileged", "type": "boolean" }
|
|
60
|
+
],
|
|
61
|
+
"recommendedPaths": ["/approval", "/request/correlationId"]
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"id": "IAM-PERM-002",
|
|
65
|
+
"description": "A privileged permission change is approved and performed from a multi-factor authenticated session.",
|
|
66
|
+
"rationale": "Direct permission grants bypass the role model, so they warrant the same scrutiny as a privileged role change.",
|
|
67
|
+
"severity": "error",
|
|
68
|
+
"eventPrefixes": ["identity.permission."],
|
|
69
|
+
"when": { "path": "/metadata/permission/privileged", "equals": true },
|
|
70
|
+
"requiredPaths": ["/approval", "/authentication"],
|
|
71
|
+
"requiredValues": [{ "path": "/authentication/mfa", "equals": true }]
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"id": "IAM-USER-001",
|
|
75
|
+
"description": "User lifecycle events record the kind of principal the account represents.",
|
|
76
|
+
"rationale": "Distinguishing an employee account from a contractor, partner or test account is what makes an access review answerable. It requires no personal data.",
|
|
77
|
+
"severity": "error",
|
|
78
|
+
"events": ["identity.user.create", "identity.user.disable", "identity.user.delete"],
|
|
79
|
+
"requiredMetadata": [{ "path": "/user/type", "type": "string" }]
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"id": "IAM-USER-002",
|
|
83
|
+
"description": "Disabling or deleting an account records why.",
|
|
84
|
+
"rationale": "Account removal is indistinguishable from account suppression without a stated reason, and the two have very different implications.",
|
|
85
|
+
"severity": "error",
|
|
86
|
+
"events": ["identity.user.disable", "identity.user.delete"],
|
|
87
|
+
"requiredPaths": ["/reason"],
|
|
88
|
+
"recommendedPaths": ["/approval"]
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"id": "IAM-SVC-001",
|
|
92
|
+
"description": "Service account lifecycle events record the account's purpose and a human owner.",
|
|
93
|
+
"rationale": "Unowned service accounts outlive the systems that needed them. Recording an owner at creation is the only point at which the answer is known.",
|
|
94
|
+
"severity": "error",
|
|
95
|
+
"events": ["identity.service-account.create", "identity.service-account.disable"],
|
|
96
|
+
"requiredMetadata": [
|
|
97
|
+
{ "path": "/serviceAccount/purpose", "type": "string" },
|
|
98
|
+
{ "path": "/serviceAccount/ownerId", "type": "string" }
|
|
99
|
+
]
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"id": "IAM-SVC-002",
|
|
103
|
+
"description": "Service account creation records when the account is expected to expire.",
|
|
104
|
+
"rationale": "A recorded expiry turns an indefinite credential into a reviewable one. It is recommended rather than required because not every service account can be time-bound.",
|
|
105
|
+
"severity": "error",
|
|
106
|
+
"events": ["identity.service-account.create"],
|
|
107
|
+
"recommendedPaths": ["/metadata/serviceAccount/expiresAt"]
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"id": "IAM-CRED-001",
|
|
111
|
+
"description": "Credential rotation records the authorization, the justification and the kind of credential rotated.",
|
|
112
|
+
"rationale": "The kind of credential determines the blast radius of the rotation. The credential itself must never appear in the event; see the profile documentation.",
|
|
113
|
+
"severity": "error",
|
|
114
|
+
"events": ["identity.credential.rotate"],
|
|
115
|
+
"requiredPaths": ["/authorization", "/reason"],
|
|
116
|
+
"requiredMetadata": [{ "path": "/credential/type", "type": "string" }],
|
|
117
|
+
"recommendedPaths": ["/request/correlationId"]
|
|
118
|
+
}
|
|
119
|
+
]
|
|
120
|
+
}
|