@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,112 @@
|
|
|
1
|
+
# Identity and Access Management Profile
|
|
2
|
+
|
|
3
|
+
**Profile version: 0.1 · Core versions: 0.1 · Status: Experimental · Implemented**
|
|
4
|
+
|
|
5
|
+
Additional conformance requirements for identity and access management audit events: accounts, roles,
|
|
6
|
+
permissions, service accounts and credential rotation.
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
auditmodel check-profile examples/profiles/identity-and-access-management/valid \
|
|
10
|
+
--profile identity-and-access-management
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
The rules live in [profile.json](profile.json) and are enforced by the declarative engine described in
|
|
14
|
+
[profiles/README.md](../README.md). Nothing in this profile is implemented in code.
|
|
15
|
+
|
|
16
|
+
## Why this profile first
|
|
17
|
+
|
|
18
|
+
Access changes are the operations every organization audits, and the fields involved are the same
|
|
19
|
+
everywhere: which role, which permission, at what scope, was it privileged, who approved it, was the
|
|
20
|
+
session multi-factor authenticated. Unlike document sharing or incident priority, none of that
|
|
21
|
+
depends on a product's data model or on an organization's scales. See
|
|
22
|
+
[ADR 0008](../../decisions/0008-declarative-profile-conformance.md) §9.
|
|
23
|
+
|
|
24
|
+
## Rules
|
|
25
|
+
|
|
26
|
+
| Rule | Applies to | Requires |
|
|
27
|
+
| -------------- | -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
28
|
+
| `IAM-CORE-001` | `identity.` prefix | `/authorization` |
|
|
29
|
+
| `IAM-CORE-002` | `identity.` prefix | _Recommends_ `/reason`, `/request/correlationId` |
|
|
30
|
+
| `IAM-ROLE-001` | `identity.role.assign`, `identity.role.revoke` | `/authorization`, `/reason`, `/metadata/role/id` string, `/metadata/role/privileged` boolean. _Recommends_ `/approval`, `/request/correlationId` |
|
|
31
|
+
| `IAM-ROLE-002` | the same, **when** `/metadata/role/privileged` is `true` | `/approval`, `/authentication`, and `/authentication/mfa` equal to `true` |
|
|
32
|
+
| `IAM-PERM-001` | `identity.permission.` prefix | `/authorization`, `/reason`, `/metadata/permission/id` string, `/metadata/permission/scope` string, `/metadata/permission/privileged` boolean. _Recommends_ `/approval`, `/request/correlationId` |
|
|
33
|
+
| `IAM-PERM-002` | the same, **when** `/metadata/permission/privileged` is `true` | `/approval`, `/authentication`, and `/authentication/mfa` equal to `true` |
|
|
34
|
+
| `IAM-USER-001` | `identity.user.create`, `.disable`, `.delete` | `/metadata/user/type` string |
|
|
35
|
+
| `IAM-USER-002` | `identity.user.disable`, `identity.user.delete` | `/reason`. _Recommends_ `/approval` |
|
|
36
|
+
| `IAM-SVC-001` | `identity.service-account.create`, `.disable` | `/metadata/serviceAccount/purpose` string, `/metadata/serviceAccount/ownerId` string |
|
|
37
|
+
| `IAM-SVC-002` | `identity.service-account.create` | _Recommends_ `/metadata/serviceAccount/expiresAt` |
|
|
38
|
+
| `IAM-CRED-001` | `identity.credential.rotate` | `/authorization`, `/reason`, `/metadata/credential/type` string. _Recommends_ `/request/correlationId` |
|
|
39
|
+
|
|
40
|
+
Recommendations produce warnings and never fail conformance.
|
|
41
|
+
|
|
42
|
+
### Why the privileged flag is required rather than inferred
|
|
43
|
+
|
|
44
|
+
`role.privileged` and `permission.privileged` are required booleans because no external reader can
|
|
45
|
+
determine whether `role-4471` is privileged. Recording the answer at the moment of the change is the
|
|
46
|
+
only point at which it is known, and it is what makes `IAM-ROLE-002` and `IAM-PERM-002` able to demand
|
|
47
|
+
approval and multi-factor authentication exactly where they matter.
|
|
48
|
+
|
|
49
|
+
An event that omits the flag fails `IAM-ROLE-001` rather than silently escaping the conditional rule.
|
|
50
|
+
The conditional itself does not fire on a missing flag; the presence requirement is what closes that
|
|
51
|
+
gap.
|
|
52
|
+
|
|
53
|
+
### Multi-factor authentication
|
|
54
|
+
|
|
55
|
+
`IAM-ROLE-002` and `IAM-PERM-002` require `/authentication/mfa` to **equal `true`**, not merely to be
|
|
56
|
+
present. `mfa: false` is a conforming core event and a profile violation: it records, accurately, that
|
|
57
|
+
a privileged access change was made from a single-factor session.
|
|
58
|
+
|
|
59
|
+
## Normative requirements this profile cannot check
|
|
60
|
+
|
|
61
|
+
These are requirements of the profile. No tool verifies them, and that is stated rather than
|
|
62
|
+
approximated by a rule that would be wrong.
|
|
63
|
+
|
|
64
|
+
1. **The primary resource MUST identify the target** — the principal, role, permission or service
|
|
65
|
+
account the operation acted upon. A checker can confirm `/resource/id` is present; it cannot
|
|
66
|
+
confirm it names the right thing. See [actor-model.md](../../specification/actor-model.md): the
|
|
67
|
+
target of an identity operation is a `resource`, and `subject` appears only when someone's
|
|
68
|
+
authority was borrowed.
|
|
69
|
+
2. **The actor and the target MUST NOT be represented ambiguously.** An administrator disabling an
|
|
70
|
+
account is the `actor`; the account is the `resource`. Recording the account as both is
|
|
71
|
+
structurally valid and semantically wrong.
|
|
72
|
+
3. **Organization and tenant context is not required globally.** Not every application is
|
|
73
|
+
multi-tenant, and the profile does not assume one.
|
|
74
|
+
4. **Personally identifiable attributes are never required.** No rule requires a display name, an
|
|
75
|
+
email address or any other direct personal identifier. `/metadata/user/type` records the kind of
|
|
76
|
+
principal — employee, contractor, partner, test — which is what an access review needs, and needs
|
|
77
|
+
no personal data at all.
|
|
78
|
+
|
|
79
|
+
## Credentials and secrets
|
|
80
|
+
|
|
81
|
+
**Service account secrets, keys and tokens MUST NOT be placed in an audit event.** Neither must the
|
|
82
|
+
old or new value of a rotated credential, private key material, or any token.
|
|
83
|
+
|
|
84
|
+
`identity.credential.rotate` records the _kind_ of credential in `/metadata/credential/type` and
|
|
85
|
+
nothing about its value. `IAM-SVC-001` requires a purpose and an owner, and requires no credential at
|
|
86
|
+
all.
|
|
87
|
+
|
|
88
|
+
This profile does **not** check for secrets. That is
|
|
89
|
+
[`auditmodel lint-privacy`](../../specification/privacy.md), which is a separate, complementary
|
|
90
|
+
command: a profile says which fields must be present, the linter says which values must not. Every
|
|
91
|
+
published fixture in this profile is required by test to pass both, and the credential and service
|
|
92
|
+
account fixtures are additionally checked field by field for secret-shaped members.
|
|
93
|
+
|
|
94
|
+
## Fixtures
|
|
95
|
+
|
|
96
|
+
[examples/profiles/identity-and-access-management/](../../examples/profiles/identity-and-access-management/)
|
|
97
|
+
holds seven conforming events, seven that violate exactly one rule, and one event the profile does not
|
|
98
|
+
govern.
|
|
99
|
+
|
|
100
|
+
## Compatibility
|
|
101
|
+
|
|
102
|
+
The profile version is independent of the core specification version. `coreVersions` declares which
|
|
103
|
+
core versions the profile applies to; an event declaring any other `specVersion` is **not applicable**
|
|
104
|
+
rather than in violation.
|
|
105
|
+
|
|
106
|
+
Adding a rule is a breaking change for producers, in the same sense as adding a required core field.
|
|
107
|
+
|
|
108
|
+
## Not a compliance statement
|
|
109
|
+
|
|
110
|
+
Conformance to this profile means an event carries the fields this profile requires. It is not
|
|
111
|
+
compliance with any law, regulation, standard or contract, and MUST NOT be presented as evidence of
|
|
112
|
+
one.
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
# Incident Management Profile
|
|
2
|
+
|
|
3
|
+
**Status: Experimental. Implemented in v0.1, 15 rules — 12 enforceable, 3 advisory.**
|
|
4
|
+
|
|
5
|
+
Scope: applications that manage the lifecycle of incidents, problems and corrective actions —
|
|
6
|
+
operations tooling, IT service management, quality management, safety reporting, regulatory event
|
|
7
|
+
handling.
|
|
8
|
+
|
|
9
|
+
The profile is vendor-neutral. It describes the lifecycle any incident system implements, not the
|
|
10
|
+
data model of any product. It assumes no priority scale, no escalation matrix, no approval process
|
|
11
|
+
and no service commitment framework.
|
|
12
|
+
|
|
13
|
+
The enforceable rules are in [profile.json](profile.json).
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
auditmodel check-profile examples/profiles/incident-management/valid --profile incident-management
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Purpose
|
|
20
|
+
|
|
21
|
+
An incident trail is not one event. Raising, triaging, escalating, resolving, analysing, remediating
|
|
22
|
+
and closing are separate operations performed hours or weeks apart, often by different applications
|
|
23
|
+
and different principals, and the questions asked afterwards are always about the sequence rather
|
|
24
|
+
than about any single step: who decided this was minor, when did we know, who was accountable, was
|
|
25
|
+
the fix ever verified, why was it closed, and why was it reopened.
|
|
26
|
+
|
|
27
|
+
The core model can carry all of that. This profile requires the parts without which the sequence
|
|
28
|
+
cannot be reconstructed at all.
|
|
29
|
+
|
|
30
|
+
## Scope
|
|
31
|
+
|
|
32
|
+
### Event families
|
|
33
|
+
|
|
34
|
+
| Family | Governed | Notes |
|
|
35
|
+
| --------------------------------------------- | -------- | ------------------------------------------------- |
|
|
36
|
+
| `incident.case.create` | yes | Raising a case |
|
|
37
|
+
| `incident.priority.change` | yes | Reassessment of priority |
|
|
38
|
+
| `incident.assignment.change` | yes | Change of accountable owner |
|
|
39
|
+
| `incident.major.declare` | yes | Escalation to major |
|
|
40
|
+
| `incident.case.resolve` | yes | Service restored |
|
|
41
|
+
| `incident.case.close`, `incident.case.cancel` | yes | Terminal transitions |
|
|
42
|
+
| `incident.case.reopen` | yes | A new lifecycle transition, governed on its own |
|
|
43
|
+
| `incident.rca.*` | yes | Root cause analysis, including approval |
|
|
44
|
+
| `incident.sla.breach` | yes | A missed commitment; excluded from `INC-CORE-001` |
|
|
45
|
+
| `problem.case.create`, `problem.case.close` | yes | Problem management, where it is a separate record |
|
|
46
|
+
| `corrective-action.*` | yes | Opening, verifying and closing an action |
|
|
47
|
+
| `monitoring.alert.*` | **no** | Alert observations |
|
|
48
|
+
| `incident.note.*`, `incident.timeline.*` | **no** | Working notes and timeline chatter |
|
|
49
|
+
| `incident.case.view` and other reads | **no** | Reading a case record |
|
|
50
|
+
|
|
51
|
+
The names come from
|
|
52
|
+
[workflow-and-approval.md](../../semantic-conventions/workflow-and-approval.md), which already
|
|
53
|
+
defines the `incident.*` case lifecycle. Two families are added because that document stops at the
|
|
54
|
+
incident: `problem.case.*` for organizations that keep a problem record separate from the incidents
|
|
55
|
+
that revealed it, and `corrective-action.*` for the remediation that outlives both. Both follow the
|
|
56
|
+
core naming rules; `corrective-action.open` uses the two-segment form permitted where a resource
|
|
57
|
+
segment would be artificial, exactly as `authentication.login` does.
|
|
58
|
+
|
|
59
|
+
### Explicit exclusions
|
|
60
|
+
|
|
61
|
+
**Alert and monitoring observations.** A monitoring system emits alerts continuously, the
|
|
62
|
+
overwhelming majority of which clear on their own and never become a managed case. Requiring an
|
|
63
|
+
authorization decision, a lifecycle state and a priority on each of them would put the profile's
|
|
64
|
+
heaviest requirements on the highest-volume event in the estate, and the requirement would be
|
|
65
|
+
switched off rather than met. An alert that _does_ become a case is recorded by the
|
|
66
|
+
`incident.case.create` that follows it.
|
|
67
|
+
|
|
68
|
+
**Working notes and timeline entries.** During a major incident a case accumulates hundreds of notes,
|
|
69
|
+
status pings and chat-bridge entries. They are worth auditing and they are not lifecycle transitions.
|
|
70
|
+
|
|
71
|
+
**Reads.** Opening a case record in a console is a data-access event, covered by
|
|
72
|
+
[data-access.md](../../semantic-conventions/data-access.md).
|
|
73
|
+
|
|
74
|
+
The exclusion is structural, not a matter of discipline. **No selector in this profile uses a bare
|
|
75
|
+
`incident.`, `incident.case.`, `problem.` or `monitoring.` prefix.** The case lifecycle is selected by
|
|
76
|
+
exact event name precisely so that a future `incident.case.view` or `incident.case.subscribe` cannot
|
|
77
|
+
be swept in by a prefix nobody re-read. Only `incident.rca.` and `corrective-action.` are prefixes,
|
|
78
|
+
and both name low-volume families whose every member should be governed. Tests assert all of this,
|
|
79
|
+
because widening a selector later would silently impose the profile's requirements on every alert in
|
|
80
|
+
a production estate.
|
|
81
|
+
|
|
82
|
+
Excluded does not mean unaudited. Every one of those events is still a conforming OpenAuditModel
|
|
83
|
+
event.
|
|
84
|
+
|
|
85
|
+
## Rules
|
|
86
|
+
|
|
87
|
+
| Rule | Applies to | Requires |
|
|
88
|
+
| ------------------ | ------------------------------------------------------ | ---------------------------------------------------------- |
|
|
89
|
+
| `INC-CORE-001` | every governed event except `incident.sla.breach` | `/authorization`, `/metadata/incident/status` |
|
|
90
|
+
| `INC-CORE-002` | every governed event | _recommends_ `/request/correlationId`, `/relatedResources` |
|
|
91
|
+
| `INC-CREATE-001` | `incident.case.create`, `problem.case.create` | _recommends_ `/reason`, detection time, impact, urgency |
|
|
92
|
+
| `INC-STATE-001` | every state transition except reopen | `/change` |
|
|
93
|
+
| `INC-STATE-002` | reprioritisation, escalation, closure, cancellation | `/reason` |
|
|
94
|
+
| `INC-PRIORITY-001` | creation, reprioritisation, escalation, breach | `/metadata/incident/priority` |
|
|
95
|
+
| `INC-ASSIGN-001` | `incident.assignment.change`, `corrective-action.open` | `/metadata/incident/assigneeId`; recommends `/reason` |
|
|
96
|
+
| `INC-RESOLVE-001` | `incident.case.resolve` | `/metadata/incident/resolutionType` |
|
|
97
|
+
| `INC-CLOSE-001` | closure or cancellation **declared to need approval** | `/approval/status` |
|
|
98
|
+
| `INC-REOPEN-001` | `incident.case.reopen` | `/change`, `/reason`; recommends `/evidence` |
|
|
99
|
+
| `INC-RCA-001` | `incident.rca.*` | `/metadata/incident/rca/method` |
|
|
100
|
+
| `INC-RCA-002` | `incident.rca.approve` | `/approval/status` |
|
|
101
|
+
| `INC-CAPA-001` | `corrective-action.verify` | `/metadata/incident/correctiveAction/verificationMethod` |
|
|
102
|
+
| `INC-SLA-001` | `incident.sla.breach` | `/metadata/incident/sla/target` |
|
|
103
|
+
| `INC-EVIDENCE-001` | `incident.rca.*`, `corrective-action.*` | _recommends_ `/evidence` |
|
|
104
|
+
|
|
105
|
+
Each rule's full text and rationale is in [profile.json](profile.json).
|
|
106
|
+
|
|
107
|
+
## Metadata namespace
|
|
108
|
+
|
|
109
|
+
Every metadata requirement lives under **`/metadata/incident/`**, and a test asserts it. Nested,
|
|
110
|
+
domain-namespaced keys keep two profiles from assigning different meanings to the same key when one
|
|
111
|
+
event is governed by both: `status` on an incident, on a share and on a deployment are not the same
|
|
112
|
+
fact, and a root-level `/metadata/status` would make them look like one.
|
|
113
|
+
|
|
114
|
+
| Path | Type | Meaning |
|
|
115
|
+
| -------------------------------------------------------- | ------- | -------------------------------------------------- |
|
|
116
|
+
| `/metadata/incident/status` | string | Lifecycle state the record was left in |
|
|
117
|
+
| `/metadata/incident/priority` | string | Priority in force |
|
|
118
|
+
| `/metadata/incident/impact`, `/urgency` | string | Inputs a priority is usually derived from |
|
|
119
|
+
| `/metadata/incident/detectedAt`, `/resolvedAt` | string | Timeline points the record cannot recompute |
|
|
120
|
+
| `/metadata/incident/resolutionType` | string | Workaround, permanent fix, duplicate, no fault |
|
|
121
|
+
| `/metadata/incident/approvalRequired` | boolean | Producer's declaration that closure needs approval |
|
|
122
|
+
| `/metadata/incident/rca/method` | string | How the analysis was conducted |
|
|
123
|
+
| `/metadata/incident/correctiveAction/verificationMethod` | string | How the action was demonstrated to work |
|
|
124
|
+
| `/metadata/incident/correctiveAction/id`, `/verifiedAt` | string | Links and timing for the remediation |
|
|
125
|
+
| `/metadata/incident/sla/target`, `/breachedAt` | string | The commitment and when it was missed |
|
|
126
|
+
|
|
127
|
+
The **vocabularies of these fields are deliberately open.** Priority, impact, urgency, resolution
|
|
128
|
+
type and verification method mean different things in a service desk, a manufacturing quality system
|
|
129
|
+
and a safety board. The profile requires the field to be recorded and does not tell an organization
|
|
130
|
+
what to put in it; a profile that closed these vocabularies would describe one product rather than a
|
|
131
|
+
domain.
|
|
132
|
+
|
|
133
|
+
`/metadata/incident/status` is required even though a transition also appears in `/change`. They are
|
|
134
|
+
different facts recorded for different readers. `/change` shows the movement; `status` names the
|
|
135
|
+
resulting state in a single typed field that the profile can check and a consumer can index, which
|
|
136
|
+
matters because the rule language cannot look inside `/change/after` and would otherwise have no way
|
|
137
|
+
to tell that a case was left closed rather than left cancelled.
|
|
138
|
+
|
|
139
|
+
`/metadata/incident/status` is also **not** `event.severity`. `event.severity` grades the audit
|
|
140
|
+
significance of the record; the incident's own priority is business data and lives in
|
|
141
|
+
`/metadata/incident/priority`. The profile keeps a single business scale — priority — rather than
|
|
142
|
+
requiring both a priority and a severity, because organizations that use two scales already derive
|
|
143
|
+
one from the other, and an audit trail that carried both would invite them to disagree.
|
|
144
|
+
|
|
145
|
+
## Conditional-policy fields
|
|
146
|
+
|
|
147
|
+
The profile has exactly one conditional rule, and it fires on one producer-set flag.
|
|
148
|
+
|
|
149
|
+
```json
|
|
150
|
+
{ "when": { "path": "/metadata/incident/approvalRequired", "equals": true } }
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Whether closing a case needs a second pair of eyes is an organizational policy, not something this
|
|
154
|
+
specification can decide. A service desk closing thousands of routine tickets a week and a safety
|
|
155
|
+
board closing a reportable event are both conforming. The producer declares the obligation; the
|
|
156
|
+
profile enforces the consequence. This is the same shape as the IAM profile's `role.privileged` flag
|
|
157
|
+
and the document profile's `share.recipientType`, and it is the entire conditional mechanism v0.1
|
|
158
|
+
offers: one path, compared for equality, against one scalar.
|
|
159
|
+
|
|
160
|
+
When the flag is absent the condition does not hold and the rule contributes nothing. Recording
|
|
161
|
+
`approvalRequired: false` explicitly is better practice than omitting it, because it distinguishes
|
|
162
|
+
"approval was not required" from "nobody said".
|
|
163
|
+
|
|
164
|
+
## Approval model
|
|
165
|
+
|
|
166
|
+
**Approval is never required universally.** Only two rules mention `/approval`, and a test asserts
|
|
167
|
+
that no other rule can quietly acquire one:
|
|
168
|
+
|
|
169
|
+
- `INC-RCA-002` requires it on `incident.rca.approve`, an event that _is_ an approval. Requiring the
|
|
170
|
+
approval state there is not process imposition; it is a requirement that the event describe itself.
|
|
171
|
+
- `INC-CLOSE-001` requires it on a closure **only** where the producer declared it necessary.
|
|
172
|
+
|
|
173
|
+
Both require the approval **status** to be present, not to be favourable. An event whose approval is
|
|
174
|
+
still pending or was rejected and whose outcome is `success` describes a control bypass, and
|
|
175
|
+
[workflow-and-approval.md](../../semantic-conventions/workflow-and-approval.md) §3 says producers
|
|
176
|
+
MUST record that accurately. A rule demanding `status: "approved"` would make the honest record
|
|
177
|
+
non-conforming and quietly reward rewriting it.
|
|
178
|
+
|
|
179
|
+
Approvers and approval timestamps are recommended, never required: an automated approval gate has no
|
|
180
|
+
human approver to name.
|
|
181
|
+
|
|
182
|
+
### Reopening
|
|
183
|
+
|
|
184
|
+
A reopen is treated as **a new auditable lifecycle transition and nothing more**. `INC-REOPEN-001`
|
|
185
|
+
requires the transition and the reason, and recommends supporting evidence.
|
|
186
|
+
|
|
187
|
+
The profile does **not** assert that reopening invalidates the earlier closure or the approval
|
|
188
|
+
attached to it. That closure remains a true historical record of what was decided, by whom, on the
|
|
189
|
+
evidence available at the time; a trail that retroactively voided it would destroy the very thing a
|
|
190
|
+
reviewer needs in order to ask why the original decision looked right. Whether a fresh approval is
|
|
191
|
+
owed before the case can be closed again is an organizational process, and the profile expresses it
|
|
192
|
+
the only honest way available: through `approvalRequired` on the next closure. A test asserts that no
|
|
193
|
+
rule requires an approval on a reopen.
|
|
194
|
+
|
|
195
|
+
Reusing the **same `request.correlationId`** across the original handling and the reopen is what keeps
|
|
196
|
+
the record readable as one story rather than two unrelated cases. It is recommended by
|
|
197
|
+
`INC-CORE-002` rather than required, because a producer may have no correlation facility at all.
|
|
198
|
+
|
|
199
|
+
## Privacy considerations
|
|
200
|
+
|
|
201
|
+
Incident records are among the most personal-data-dense artifacts an organization keeps. A case can
|
|
202
|
+
describe a customer complaint, a safety event involving a named individual, an HR investigation or a
|
|
203
|
+
security breach with victims, and the free-text fields are where that leaks.
|
|
204
|
+
|
|
205
|
+
This profile requires **no free text anywhere**. Every requirement is a token, a boolean, a timestamp
|
|
206
|
+
or a structural object. Specifically:
|
|
207
|
+
|
|
208
|
+
- Requirements ask for `reason` as an object, whose `code` is a token. `reason.text` is scanned by the
|
|
209
|
+
privacy linter and should stay a short, factual sentence.
|
|
210
|
+
- Nothing in the profile asks for a reporter's identity, a complainant, an affected customer or any
|
|
211
|
+
contact detail. Principals are referenced by opaque identifier through `actor` and `subject`.
|
|
212
|
+
- `/evidence` references material; it never embeds it. An analysis document stays in the record
|
|
213
|
+
system it belongs to, under that system's access control, and the audit event carries a pointer.
|
|
214
|
+
- `/metadata/incident/rca/*` must not become a place to paste an investigation narrative. The profile
|
|
215
|
+
requires the method, not the findings.
|
|
216
|
+
|
|
217
|
+
Every published fixture is required by test to pass `auditmodel lint-privacy` as well as
|
|
218
|
+
`auditmodel validate`. Nothing here weakens [specification/privacy.md](../../specification/privacy.md);
|
|
219
|
+
a profile-conforming event can still carry a secret, and the linter is what looks for one.
|
|
220
|
+
|
|
221
|
+
## Known rule-language limitations
|
|
222
|
+
|
|
223
|
+
The v0.1 rule language checks presence, JSON type and strict scalar equality against one condition.
|
|
224
|
+
These consequences are real, and the profile does not paper over them:
|
|
225
|
+
|
|
226
|
+
1. **Array contents cannot be inspected.** The profile cannot require that `/evidence` contains an
|
|
227
|
+
entry of type `document`, or that `/relatedResources` includes the incident an analysis belongs to.
|
|
228
|
+
`INC-EVIDENCE-001` therefore _recommends_ `/evidence` rather than requiring it and pretending to
|
|
229
|
+
verify it. The placeholder that preceded this profile proposed "an `evidence` entry of type
|
|
230
|
+
`document` REQUIRED on `incident.rca.*`"; that requirement cannot be expressed and is not claimed.
|
|
231
|
+
2. **Numeric ranges cannot be checked.** `INC-SLA-001` requires the commitment to be _named_. It
|
|
232
|
+
cannot assert that a breach duration exceeded a threshold, that `requiredApprovals` was at least
|
|
233
|
+
two, or that a corrective action was verified before its due date.
|
|
234
|
+
3. **Fields cannot be compared to each other.** The profile cannot require that the approver differs
|
|
235
|
+
from the actor, that `resolvedAt` is later than `detectedAt`, or that `status` agrees with
|
|
236
|
+
`/change/after`. Separation of duties is recorded as facts and evaluated elsewhere;
|
|
237
|
+
OpenAuditModel is not a policy engine.
|
|
238
|
+
4. **There is no disjunction.** "Priority or severity" cannot be expressed, which is one reason the
|
|
239
|
+
profile settles on a single required scale.
|
|
240
|
+
5. **One condition per rule.** "Approval required _and_ the incident is major" needs two rules or one
|
|
241
|
+
producer-set flag. The profile uses the flag.
|
|
242
|
+
6. **Cross-event invariants are out of reach.** Nothing can require that a closure be preceded by a
|
|
243
|
+
resolution, or that every major incident eventually produce an analysis. A rule sees one event.
|
|
244
|
+
|
|
245
|
+
Where a requirement could not be expressed honestly, it appears above as guidance rather than in
|
|
246
|
+
`profile.json` as a rule that does not do what its name suggests.
|
|
247
|
+
|
|
248
|
+
## Cross-profile overlaps
|
|
249
|
+
|
|
250
|
+
| Neighbour | Overlap |
|
|
251
|
+
| ------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
252
|
+
| `workflow.approval.*` events | An approval decision is its own event; `/approval` on a governed event summarises the outcome. Both should be emitted. This profile requires the summary, never the decision events. |
|
|
253
|
+
| Deployment and change management | A corrective action is frequently delivered as a change. The link belongs in `change.ticketId` / `change.deploymentId` and in `/relatedResources`; this profile does not govern the deployment event and requires no field on it. |
|
|
254
|
+
| Identity and access management | A break-glass access grant during a major incident is an `identity.*` event governed by that profile. Correlating them is what `request.correlationId` is for. |
|
|
255
|
+
| Document management | An analysis document has its own lifecycle under `document.*`. This profile references it through `/evidence` and never governs it. |
|
|
256
|
+
| Message broker / data infrastructure | An incident about a broker names the broker in `/relatedResources`. Nothing here governs data-plane events. |
|
|
257
|
+
|
|
258
|
+
A single event governed by two profiles must satisfy both. Namespaced metadata is what makes that
|
|
259
|
+
possible without collision.
|
|
260
|
+
|
|
261
|
+
## Fixture matrix
|
|
262
|
+
|
|
263
|
+
[examples/profiles/incident-management/](../../examples/profiles/incident-management/) — thirteen
|
|
264
|
+
valid, fourteen invalid, three not-applicable. Every fixture is core-conforming and privacy-clean;
|
|
265
|
+
every invalid fixture is core-**valid** and fails exactly one profile rule with exactly one error.
|
|
266
|
+
There is one invalid fixture per _requirement_, not per rule, so the two rules that require two
|
|
267
|
+
things each carry two fixtures; a test derives that obligation from `profile.json`.
|
|
268
|
+
|
|
269
|
+
| Rule | Valid fixture | Invalid fixture |
|
|
270
|
+
| ------------------ | ------------------------------------------------------- | -------------------------------------------------------------------------- |
|
|
271
|
+
| `INC-CORE-001` | all governed fixtures | `case-close-missing-authorization.json`, `case-create-missing-status.json` |
|
|
272
|
+
| `INC-CORE-002` | all governed fixtures | advisory, never fails |
|
|
273
|
+
| `INC-CREATE-001` | `case-create.json` | advisory, never fails |
|
|
274
|
+
| `INC-STATE-001` | `assignment-change.json` | `assignment-change-missing-change.json` |
|
|
275
|
+
| `INC-STATE-002` | `case-close.json` | `case-close-missing-reason.json` |
|
|
276
|
+
| `INC-PRIORITY-001` | `priority-change.json` | `priority-change-missing-priority.json` |
|
|
277
|
+
| `INC-ASSIGN-001` | `assignment-change.json`, `corrective-action-open.json` | `assignment-change-missing-assignee.json` |
|
|
278
|
+
| `INC-RESOLVE-001` | `case-resolve.json` | `case-resolve-missing-resolution-type.json` |
|
|
279
|
+
| `INC-CLOSE-001` | `case-close.json` | `case-close-missing-approval.json` |
|
|
280
|
+
| `INC-REOPEN-001` | `case-reopen.json` | `case-reopen-missing-change.json`, `case-reopen-missing-reason.json` |
|
|
281
|
+
| `INC-RCA-001` | `rca-create.json` | `rca-create-missing-method.json` |
|
|
282
|
+
| `INC-RCA-002` | `rca-approve.json` | `rca-approve-missing-approval.json` |
|
|
283
|
+
| `INC-CAPA-001` | `corrective-action-verify.json` | `corrective-action-verify-missing-method.json` |
|
|
284
|
+
| `INC-SLA-001` | `sla-breach.json` | `sla-breach-missing-target.json` |
|
|
285
|
+
| `INC-EVIDENCE-001` | `rca-create.json` | advisory, never fails |
|
|
286
|
+
|
|
287
|
+
`problem-case-close.json` and `major-declare.json` exercise the problem domain and the escalation
|
|
288
|
+
path; `problem-case-close.json` is also the fixture that proves the closure-approval condition stays
|
|
289
|
+
quiet when the producer declares approval was not required.
|
|
290
|
+
|
|
291
|
+
## Not-applicable rationale
|
|
292
|
+
|
|
293
|
+
Three fixtures under `not-applicable/` prove the exclusions hold:
|
|
294
|
+
|
|
295
|
+
| Fixture | Event | Why it is ungoverned |
|
|
296
|
+
| ----------------------------- | ------------------------ | ------------------------------------------------------------------------------------------------- |
|
|
297
|
+
| `monitoring-alert-raise.json` | `monitoring.alert.raise` | A threshold alert that cleared in 45 seconds and never became a managed case |
|
|
298
|
+
| `note-create.json` | `incident.note.create` | A working note on the incident timeline — high volume, not a lifecycle transition |
|
|
299
|
+
| `case-view.json` | `incident.case.view` | Reading a case record; a data-access event, and the reason the case lifecycle is selected by name |
|
|
300
|
+
|
|
301
|
+
`check-profile` reports each as not applicable with exit code 3. **Not applicable is not
|
|
302
|
+
conformance**: the tool says the profile is silent, never that the profile is satisfied.
|
|
303
|
+
|
|
304
|
+
These fixtures exist to hold the exclusions in place. If a future edit widened a selector to a bare
|
|
305
|
+
`incident.` prefix, they would start conforming instead of being skipped and the test would fail —
|
|
306
|
+
which is the point, because that edit would impose the profile's heaviest requirements on every alert
|
|
307
|
+
and every note in a production estate.
|
|
308
|
+
|
|
309
|
+
## Not required, and why
|
|
310
|
+
|
|
311
|
+
- **Impact and urgency as requirements.** Recommended on creation only. Many organizations record a
|
|
312
|
+
priority directly, and demanding a derivation they do not perform produces invented values.
|
|
313
|
+
- **`relatedResources` as a requirement on declaration and closure.** The placeholder proposed it.
|
|
314
|
+
An incident about a process, a supplier or a person has no affected service to name, and the rule
|
|
315
|
+
language cannot check what an array contains anyway, so it is recommended by `INC-CORE-002`.
|
|
316
|
+
- **Trace identifiers.** `traceId` and `spanId` presume distributed tracing exists. Correlation is
|
|
317
|
+
recommended; tracing is never required.
|
|
318
|
+
- **`request.protocol`.** A case can be closed from a console, an API, an email gateway or a batch
|
|
319
|
+
reconciliation. The protocol is not the audit fact.
|
|
320
|
+
- **A closure approval for every case.** See the approval model above.
|
|
321
|
+
- **A defined priority or severity scale.** See the metadata namespace above.
|
|
322
|
+
- **An `incident.capa.*` family.** The placeholder proposed it. `corrective-action.*` says the same
|
|
323
|
+
thing without requiring the reader to know that `capa` is quality-management jargon.
|
|
324
|
+
|
|
325
|
+
## Open questions
|
|
326
|
+
|
|
327
|
+
- Should a corrective action be a resource with its own lifecycle, as modelled here, or metadata on
|
|
328
|
+
the incident? Modelling it as a resource lets it outlive the incident, which is usually what
|
|
329
|
+
happens in practice, at the cost of more events. Adoption evidence is thin either way.
|
|
330
|
+
- Should `incident.sla.breach` be emitted by the incident system or derived by a consumer? The
|
|
331
|
+
profile governs it if it is emitted and says nothing if it is not. Emitting it makes the trail
|
|
332
|
+
self-contained; deriving it avoids duplicating a computation that changes when the commitment does.
|
|
333
|
+
- Is a single `status` field sufficient for organizations that track an incident state and a workflow
|
|
334
|
+
state separately? The profile currently assumes the producer picks the one a reviewer would ask
|
|
335
|
+
about.
|
|
336
|
+
- Would a producer-set `major` flag be a better conditional discriminator than `approvalRequired` for
|
|
337
|
+
the closure rule? It would tie the obligation to the incident's own severity rather than to a
|
|
338
|
+
policy declaration, but it would also let the profile guess at a process it cannot see.
|