@x12i/memorix-format 2.2.2 → 2.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +30 -0
- package/README.md +36 -86
- package/dist/base-reexport.d.ts +8 -0
- package/dist/base-reexport.d.ts.map +1 -0
- package/dist/base-reexport.js +8 -0
- package/dist/base-reexport.js.map +1 -0
- package/dist/cli.js +179 -15
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/v2/index.d.ts +7 -0
- package/dist/v2/index.d.ts.map +1 -0
- package/dist/v2/index.js +6 -0
- package/dist/v2/index.js.map +1 -0
- package/dist/v2/migrate-present.d.ts +47 -0
- package/dist/v2/migrate-present.d.ts.map +1 -0
- package/dist/v2/migrate-present.js +225 -0
- package/dist/v2/migrate-present.js.map +1 -0
- package/dist/v2/profiles.d.ts +9 -0
- package/dist/v2/profiles.d.ts.map +1 -0
- package/dist/v2/profiles.js +63 -0
- package/dist/v2/profiles.js.map +1 -0
- package/dist/v2/revision.d.ts +24 -0
- package/dist/v2/revision.d.ts.map +1 -0
- package/dist/v2/revision.js +44 -0
- package/dist/v2/revision.js.map +1 -0
- package/dist/v2/types.d.ts +94 -0
- package/dist/v2/types.d.ts.map +1 -0
- package/dist/v2/types.js +3 -0
- package/dist/v2/types.js.map +1 -0
- package/dist/v2/validate.d.ts +8 -0
- package/dist/v2/validate.d.ts.map +1 -0
- package/dist/v2/validate.js +225 -0
- package/dist/v2/validate.js.map +1 -0
- package/docs/association-format.md +26 -445
- package/docs/memorix-format.md +24 -186
- package/docs/memorix-record-v2.md +101 -0
- package/docs/memorix-workflow-format.md +13 -1
- package/docs/migrate-present-to-v2.md +49 -0
- package/examples/v2/activity-events-snapshots.json +31 -0
- package/examples/v2/employees-discovery.json +26 -0
- package/examples/v2/employees-snapshots.json +27 -0
- package/examples/v2/procedures-knowledge.json +27 -0
- package/examples/v2/work-items-analysis.json +26 -0
- package/examples/v2/work-items-context.json +32 -0
- package/examples/v2/work-items-decisions.json +26 -0
- package/examples/v2/work-items-raw.json +37 -0
- package/examples/v2/work-items-relationships-candidate.json +38 -0
- package/examples/v2/work-items-snapshots-lifecycle.json +31 -0
- package/examples/v2/work-items-snapshots-lightweight-refs.json +34 -0
- package/examples/v2/work-items-snapshots.json +28 -0
- package/examples/v2/work-items-workflow.json +28 -0
- package/package.json +7 -2
- package/docs/memorix-format-additions.md +0 -956
- package/docs/memorix-format-for-dummies-association-corrections.md +0 -113
|
@@ -1,466 +1,47 @@
|
|
|
1
1
|
# Association format
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Associated content is stored as **root-level open buckets** on snapshot records using the reserved `associated` prefix.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
**Normative naming / forms:** packaged [`meta/associated-conventions.json`](../meta/associated-conventions.json).
|
|
6
|
+
**Operational detail** (presets, apply, enrichment orchestration): [`memorix-pipeline/docs/association-format.md`](../../memorix-pipeline/docs/association-format.md) and [running-associations.md](../../memorix-pipeline/docs/running-associations.md).
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
## Core rule
|
|
10
|
-
|
|
11
|
-
Record-to-record association is always resolved from **snapshot record to snapshot record first**:
|
|
12
|
-
|
|
13
|
-
1. The lookup starts from the current object's snapshot record.
|
|
14
|
-
2. The matching target is another snapshot record in a `*-snapshots` collection.
|
|
15
|
-
3. Use the matched target snapshot's `recordId` to fetch non-snapshot content (`discovery`, `analysis`, `decisions`, etc.).
|
|
16
|
-
4. Copy flat `content.data` into `associated<ObjectType><ContentType>` on the source snapshot.
|
|
17
|
-
|
|
18
|
-
The snapshot resolves the link; embedded content is always fetched **after** snapshot match by `recordId` (or equivalent link field such as `concept._extIdentifier.recordId` or `data.sourceRecordId`).
|
|
19
|
-
|
|
20
|
-
**Never join a source snapshot directly to a non-snapshot content collection by business keys** (for example `findings-snapshots.data.sku` → `findings-discovery.data.sku`). Same-object associations still match snapshots first, then fetch content by matched snapshot `recordId`.
|
|
21
|
-
|
|
22
|
-
---
|
|
23
|
-
|
|
24
|
-
## Naming conventions
|
|
25
|
-
|
|
26
|
-
### Legacy (live data today)
|
|
27
|
-
|
|
28
|
-
Short names without object type segment: `associatedData`, `associatedDiscovery`, `associatedAnalysis`, `associatedAssets`, `associatedSubnets`, etc.
|
|
29
|
-
|
|
30
|
-
- Remain on existing records; not bulk-migrated.
|
|
31
|
-
- Registry entries carry `"namingConvention": "legacy"`.
|
|
32
|
-
- Legacy items may include inline `recordId` on copied payloads.
|
|
33
|
-
|
|
34
|
-
### v2: `associated<ObjectType><ContentType>`
|
|
35
|
-
|
|
36
|
-
| Segment | Rule | Example |
|
|
37
|
-
|---------|------|---------|
|
|
38
|
-
| Prefix | `associated` (reserved) | — |
|
|
39
|
-
| ObjectType | PascalCase canonical object type | `security-groups` → `SecurityGroups` |
|
|
40
|
-
| ContentType | PascalCase canonical content type | `discovery` → `Discovery` |
|
|
41
|
-
|
|
42
|
-
Examples:
|
|
43
|
-
|
|
44
|
-
```ts
|
|
45
|
-
associatedSecurityGroupsAnalysis
|
|
46
|
-
associatedVulnerabilitiesGroupsDiscovery
|
|
47
|
-
associatedVulnerabilitiesGroupsSnapshots
|
|
48
|
-
associatedUsersDecision
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
Do **not** pluralize the content type (`Discovery`, not `Discoveries`). The property is already an array.
|
|
52
|
-
|
|
53
|
-
Registry entries for new associations carry `"namingConvention": "v2"`.
|
|
54
|
-
|
|
55
|
-
### Ranked top associations: `associatedTop<ObjectType><ContentType>`
|
|
56
|
-
|
|
57
|
-
Same copy forms as above, but candidates are **sorted by importance** then **capped**.
|
|
58
|
-
|
|
59
|
-
| Segment | Rule | Example |
|
|
60
|
-
|---------|------|---------|
|
|
61
|
-
| Prefix | `associatedTop` | — |
|
|
62
|
-
| ObjectType | PascalCase | `Vulnerabilities` |
|
|
63
|
-
| ContentType | PascalCase | `Analysis` |
|
|
64
|
-
|
|
65
|
-
Example: `associatedTopVulnerabilitiesAnalysis`
|
|
66
|
-
|
|
67
|
-
**Importance** comes from the linked object type’s descriptor `importanceIndicator` (e.g. findings → `data.severity`), or from the association rule’s `rankBy` override.
|
|
68
|
-
|
|
69
|
-
**Default cap:** `5` (`DEFAULT_TOP_ASSOCIATED_ITEMS`). Override per rule with `maxItems`.
|
|
70
|
-
|
|
71
|
-
**Empty results:** omit the property entirely (never write `[]`).
|
|
72
|
-
|
|
73
|
-
```json
|
|
74
|
-
{
|
|
75
|
-
"associatedProperty": "associatedTopVulnerabilitiesAnalysis",
|
|
76
|
-
"maxItems": 5,
|
|
77
|
-
"rankBy": {
|
|
78
|
-
"path": "data.severity",
|
|
79
|
-
"valueKind": "severity",
|
|
80
|
-
"order": "desc"
|
|
81
|
-
},
|
|
82
|
-
"match": {
|
|
83
|
-
"collection": "findings-snapshots",
|
|
84
|
-
"joinPaths": ["data.sku"]
|
|
85
|
-
},
|
|
86
|
-
"content": {
|
|
87
|
-
"collection": "findings-analysis",
|
|
88
|
-
"linkPaths": ["data.sourceRecordId", "recordId"]
|
|
89
|
-
},
|
|
90
|
-
"payload": { "source": "target.data", "format": "v2" }
|
|
91
|
-
}
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
For snapshot-mediated rules, `rankBy.path` is evaluated on the **matched snapshot** (not the copied content). Severity order: `critical > high > medium > low > none`.
|
|
95
|
-
|
|
96
|
-
### Derived (not copy-form associations)
|
|
97
|
-
|
|
98
|
-
Resolver outputs such as `associatedTopology` are **derived** fields computed during pipeline resolution — not associator copy forms. Document separately; do not rename to v2 copy naming.
|
|
99
|
-
|
|
100
|
-
---
|
|
101
|
-
|
|
102
|
-
## Association forms
|
|
103
|
-
|
|
104
|
-
The associator supports four copy forms via `rule.payload.source` (see request schema in `@x12i/memorix-associator`).
|
|
105
|
-
|
|
106
|
-
### 1. Normal data association
|
|
107
|
-
|
|
108
|
-
**Property:** `associated<ObjectType><ContentType>`
|
|
109
|
-
|
|
110
|
-
**Copy:** linked record's `data` object (flat, not wrapped).
|
|
111
|
-
|
|
112
|
-
```json
|
|
113
|
-
{
|
|
114
|
-
"associatedSecurityGroupsAnalysis": [
|
|
115
|
-
{
|
|
116
|
-
"_system": {
|
|
117
|
-
"association": {
|
|
118
|
-
"fromProperty": "data.securityGroups",
|
|
119
|
-
"toProperty": "identifier",
|
|
120
|
-
"associatedAt": "2026-07-08T09:00:00.000Z",
|
|
121
|
-
"recordId": "security-group-record-id-1"
|
|
122
|
-
}
|
|
123
|
-
},
|
|
124
|
-
"summary": "The group appears to provide privileged access.",
|
|
125
|
-
"impact": "High",
|
|
126
|
-
"classification": "Privileged Security Group"
|
|
127
|
-
}
|
|
128
|
-
]
|
|
129
|
-
}
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
Request payload:
|
|
133
|
-
|
|
134
|
-
```json
|
|
135
|
-
{
|
|
136
|
-
"payload": {
|
|
137
|
-
"format": "v2",
|
|
138
|
-
"source": "target.data",
|
|
139
|
-
"system": { "include": true, "fromProperty": "data.securityGroups", "toProperty": "identifier" }
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
Rules:
|
|
145
|
-
|
|
146
|
-
- Business data stays flat beside `_system`.
|
|
147
|
-
- Do **not** wrap copied data inside another `data` object.
|
|
148
|
-
- `_system` is optional. Omit when consumed as business or LLM context.
|
|
149
|
-
|
|
150
|
-
### 2. Snapshot root property association
|
|
151
|
-
|
|
152
|
-
**Property:** `associated<ObjectType>Snapshots<RootProperty>`
|
|
153
|
-
|
|
154
|
-
Copies a root property from the associated **snapshot** record (not a separate content collection).
|
|
155
|
-
|
|
156
|
-
Example: `associatedVulnerabilitiesGroupsSnapshotsAnalytics` — copies `analytics` from `item-groups-snapshots`.
|
|
157
|
-
|
|
158
|
-
```json
|
|
159
|
-
{
|
|
160
|
-
"payload": {
|
|
161
|
-
"format": "v2",
|
|
162
|
-
"source": { "snapshotRootProperty": "analytics" },
|
|
163
|
-
"system": { "include": true }
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
When `system.include` is true, the item is wrapped as `{ _system, value }`. Prefer the dedicated enrichment form below when copying the `enrichment` bucket (flat copy, no `value` wrapper).
|
|
169
|
-
|
|
170
|
-
### Analytics rollups (sibling — not association)
|
|
171
|
-
|
|
172
|
-
**Do not** use association rules to *compute* counts into the source record’s own `analytics` bucket. Associations **copy** linked payloads into `associated*`. Numeric rollups are a separate JSON rule type:
|
|
173
|
-
|
|
174
|
-
- Spec: `.operational/analytics-rollups/FORMAT.md` (`memorix.analyticsRollup.v1`)
|
|
175
|
-
- Presets: `.operational/analytics-rollups/presets/*.json`
|
|
176
|
-
- Runner: `scripts/memorix-2.0-migrate/patches/run-analytics-rollups.mjs`
|
|
177
|
-
|
|
178
|
-
Use the **same join keys** as association presets (`sku`, `name`↔`vulnerabilityName`, CIDR). Writing `analytics` on the target is a rollup; copying another record’s `analytics` into an `associated*SnapshotsAnalytics` array is still an association (form 2 above).
|
|
179
|
-
|
|
180
|
-
### 2b. Enrichment association
|
|
181
|
-
|
|
182
|
-
**Property:** `associated<ObjectType>Enrichment`
|
|
183
|
-
|
|
184
|
-
Copies the linked **snapshot** root `enrichment` object **flat** (same shape as content associations: fields beside optional `_system`, not wrapped in `value`).
|
|
185
|
-
|
|
186
|
-
`enrichment` is an envelope bucket on snapshots (external feed values as received) — **not** a separate `*-enrichment` content-type collection.
|
|
187
|
-
|
|
188
|
-
Example: `associatedVulnerabilitiesGroupsEnrichment` on `findings-snapshots`.
|
|
189
|
-
|
|
190
|
-
```json
|
|
191
|
-
{
|
|
192
|
-
"associatedVulnerabilitiesGroupsEnrichment": [
|
|
193
|
-
{
|
|
194
|
-
"cvss": { "baseScore": 9.8 },
|
|
195
|
-
"epss": { "epss": 0.94 },
|
|
196
|
-
"kev": { "isKnownExploited": true },
|
|
197
|
-
"compositeScore": 0.97,
|
|
198
|
-
"riskLevel": "CRITICAL"
|
|
199
|
-
}
|
|
200
|
-
]
|
|
201
|
-
}
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
```json
|
|
205
|
-
{
|
|
206
|
-
"associatedProperty": "associatedVulnerabilitiesGroupsEnrichment",
|
|
207
|
-
"payload": {
|
|
208
|
-
"format": "v2",
|
|
209
|
-
"source": "target.enrichment",
|
|
210
|
-
"includeRecordId": false
|
|
211
|
-
},
|
|
212
|
-
"target": {
|
|
213
|
-
"collection": "item-groups-snapshots",
|
|
214
|
-
"joinPaths": ["data.name"]
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
```
|
|
218
|
-
|
|
219
|
-
Rules:
|
|
220
|
-
|
|
221
|
-
- Omit the property when no linked snapshot has a non-empty `enrichment` object.
|
|
222
|
-
- Do not invent a `item-groups-enrichment` collection for this form.
|
|
223
|
-
- Naming helper: `buildAssociatedPropertyName({ form: "enrichment", objectType: "item-groups" })`.
|
|
224
|
-
|
|
225
|
-
### 3. Concepts association
|
|
226
|
-
|
|
227
|
-
**Property:** `associated<ObjectType>Concepts`
|
|
228
|
-
|
|
229
|
-
Copies `concept` from associated snapshot records (not full `data`).
|
|
230
|
-
|
|
231
|
-
```json
|
|
232
|
-
{
|
|
233
|
-
"associatedSecurityGroupsConcepts": [
|
|
234
|
-
{
|
|
235
|
-
"identifier": { "kind": "group-name", "groupName": "Domain Admins" },
|
|
236
|
-
"title": "Domain Admins"
|
|
237
|
-
}
|
|
238
|
-
]
|
|
239
|
-
}
|
|
240
|
-
```
|
|
241
|
-
|
|
242
|
-
```json
|
|
243
|
-
{
|
|
244
|
-
"payload": {
|
|
245
|
-
"format": "v2",
|
|
246
|
-
"source": "target.concept",
|
|
247
|
-
"system": { "include": true }
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
```
|
|
251
|
-
|
|
252
|
-
### 4. Record IDs association
|
|
253
|
-
|
|
254
|
-
**Property:** `associated<ObjectType>RecordIds` (use `RecordIds`, not `RecordsIds`)
|
|
255
|
-
|
|
256
|
-
**Value:** array of strings. Must **not** include `_system`.
|
|
257
|
-
|
|
258
|
-
```json
|
|
259
|
-
{
|
|
260
|
-
"associatedSecurityGroupsRecordIds": [
|
|
261
|
-
"security-group-record-id-1",
|
|
262
|
-
"security-group-record-id-2"
|
|
263
|
-
]
|
|
264
|
-
}
|
|
265
|
-
```
|
|
266
|
-
|
|
267
|
-
```json
|
|
268
|
-
{
|
|
269
|
-
"payload": {
|
|
270
|
-
"format": "v2",
|
|
271
|
-
"source": "target.recordId"
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
```
|
|
8
|
+
This package validates association **shape** (ASSOC rules in [memorix-format-requirements.md](./memorix-format-requirements.md)); it does not invent object-type names.
|
|
275
9
|
|
|
276
10
|
---
|
|
277
11
|
|
|
278
|
-
##
|
|
279
|
-
|
|
280
|
-
Reserved for technical traceability — not business data.
|
|
281
|
-
|
|
282
|
-
| Field | Meaning |
|
|
283
|
-
|-------|---------|
|
|
284
|
-
| `fromProperty` | Path on the source snapshot that supplied the join value |
|
|
285
|
-
| `toProperty` | Path on the linked snapshot used for matching |
|
|
286
|
-
| `associatedAt` | ISO date-time when the association was created or refreshed |
|
|
287
|
-
| `recordId` | Linked snapshot `recordId` (v2, when enabled) |
|
|
288
|
-
|
|
289
|
-
Included when `payload.format === "v2"` and `payload.system.include === true`. All fields are optional even when `_system` is present.
|
|
290
|
-
|
|
291
|
-
Strip `_system` when presenting associated content to humans, LLMs, or downstream analysis.
|
|
292
|
-
|
|
293
|
-
---
|
|
294
|
-
|
|
295
|
-
## Identifier and identity matching
|
|
296
|
-
|
|
297
|
-
Prefer the linked snapshot's `identity` or `concept._extIdentifier`:
|
|
298
|
-
|
|
299
|
-
```json
|
|
300
|
-
{ "identity": "group-name:Domain Admins" }
|
|
301
|
-
```
|
|
302
|
-
|
|
303
|
-
```json
|
|
304
|
-
{
|
|
305
|
-
"identifier": { "kind": "group-name", "groupName": "Domain Admins" }
|
|
306
|
-
}
|
|
307
|
-
```
|
|
308
|
-
|
|
309
|
-
Canonical location is `concept._extIdentifier`. Root `identifier` / `identity` are accepted when present.
|
|
310
|
-
|
|
311
|
-
Generate identity strings deterministically: join identifier values in order with `:` (e.g. `sku:10.100.18.64`).
|
|
12
|
+
## Core rule
|
|
312
13
|
|
|
313
|
-
Association
|
|
14
|
+
Association is always **snapshot-first**:
|
|
314
15
|
|
|
315
|
-
|
|
16
|
+
1. Start from the source object’s snapshot.
|
|
17
|
+
2. Match a target **snapshot** (`*-snapshots`).
|
|
18
|
+
3. Use the matched snapshot’s `recordId` to fetch non-snapshot content (`discovery`, `analysis`, `decisions`, …).
|
|
19
|
+
4. Copy flat content into an `associated…` array on the source snapshot.
|
|
316
20
|
|
|
317
|
-
|
|
318
|
-
{
|
|
319
|
-
"sourceJoinPaths": ["data.sku"],
|
|
320
|
-
"match": {
|
|
321
|
-
"collection": "findings-snapshots",
|
|
322
|
-
"joinPaths": ["data.sku", "concept._extIdentifier.sku", "recordId"]
|
|
323
|
-
},
|
|
324
|
-
"content": {
|
|
325
|
-
"collection": "findings-discovery",
|
|
326
|
-
"contentType": "discovery",
|
|
327
|
-
"linkPaths": ["concept._extIdentifier.recordId", "data.sourceRecordId", "recordId"]
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
```
|
|
21
|
+
Never join a source snapshot directly to a non-snapshot collection by business keys.
|
|
331
22
|
|
|
332
23
|
---
|
|
333
24
|
|
|
334
|
-
##
|
|
335
|
-
|
|
336
|
-
Collections:
|
|
337
|
-
|
|
338
|
-
```text
|
|
339
|
-
users-snapshots
|
|
340
|
-
security-groups-snapshots
|
|
341
|
-
security-groups-analysis
|
|
342
|
-
```
|
|
343
|
-
|
|
344
|
-
User snapshot source:
|
|
345
|
-
|
|
346
|
-
```json
|
|
347
|
-
{
|
|
348
|
-
"data": { "securityGroups": ["Domain Admins", "VPN Users"] }
|
|
349
|
-
}
|
|
350
|
-
```
|
|
25
|
+
## Naming
|
|
351
26
|
|
|
352
|
-
|
|
27
|
+
| Form | Pattern | Example (abstract) |
|
|
28
|
+
|---|---|---|
|
|
29
|
+
| Legacy short names | `associatedData`, `associatedDiscovery`, … | still present on older records |
|
|
30
|
+
| v2 content | `associated<ObjectType><ContentType>` | `associatedItemGroupsAnalysis` |
|
|
31
|
+
| Ranked top | `associatedTop<ObjectType><ContentType>` | `associatedTopItemsAnalysis` |
|
|
32
|
+
| Enrichment copy | `associated<ObjectType>Enrichment` | `associatedItemGroupsEnrichment` |
|
|
353
33
|
|
|
354
|
-
|
|
355
|
-
{
|
|
356
|
-
"recordId": "security-group-record-id-1",
|
|
357
|
-
"concept": {
|
|
358
|
-
"identifier": { "kind": "group-name", "groupName": "Domain Admins" },
|
|
359
|
-
"title": "Domain Admins"
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
```
|
|
363
|
-
|
|
364
|
-
Process:
|
|
365
|
-
|
|
366
|
-
1. Start from `users-snapshots` record.
|
|
367
|
-
2. Read join values from `data.securityGroups`.
|
|
368
|
-
3. Match against `security-groups-snapshots` via `concept._extIdentifier` / identity.
|
|
369
|
-
4. Use matched snapshot `recordId` to fetch `security-groups-analysis` records.
|
|
370
|
-
5. Copy each analysis record's `data` flat into `associatedSecurityGroupsAnalysis`.
|
|
371
|
-
|
|
372
|
-
Result:
|
|
373
|
-
|
|
374
|
-
```json
|
|
375
|
-
{
|
|
376
|
-
"recordId": "user-record-id-1",
|
|
377
|
-
"data": {
|
|
378
|
-
"username": "ami",
|
|
379
|
-
"securityGroups": ["Domain Admins"]
|
|
380
|
-
},
|
|
381
|
-
"associatedSecurityGroupsAnalysis": [
|
|
382
|
-
{
|
|
383
|
-
"_system": {
|
|
384
|
-
"association": {
|
|
385
|
-
"fromProperty": "data.securityGroups",
|
|
386
|
-
"toProperty": "concept._extIdentifier",
|
|
387
|
-
"associatedAt": "2026-07-08T09:00:00.000Z"
|
|
388
|
-
}
|
|
389
|
-
},
|
|
390
|
-
"summary": "The group appears to provide privileged access.",
|
|
391
|
-
"impact": "High",
|
|
392
|
-
"classification": "Privileged Security Group"
|
|
393
|
-
}
|
|
394
|
-
]
|
|
395
|
-
}
|
|
396
|
-
```
|
|
34
|
+
Object-type segments come from Catalox descriptors — not from this format package.
|
|
397
35
|
|
|
398
36
|
---
|
|
399
37
|
|
|
400
|
-
##
|
|
401
|
-
|
|
402
|
-
| Capability | Status |
|
|
403
|
-
|------------|--------|
|
|
404
|
-
| Legacy `associatedData` / `associatedDiscovery` / … presets | **Live** — registry `namingConvention: "legacy"` |
|
|
405
|
-
| v2 property naming | **Target** — new registry entries use `associated<ObjectType><ContentType>` |
|
|
406
|
-
| v2 payload forms (`target.data`, `target.concept`, `target.recordId`, snapshot root) | **Supported** in `@x12i/memorix-associator` request schema |
|
|
407
|
-
| v2 `_system.association` metadata | **Supported** when `format: "v2"` and `system.include: true` |
|
|
408
|
-
| Registry-driven preset generation | **Live** — `generate-presets-from-registry.mjs` |
|
|
409
|
-
| Full enrichment orchestration | **Live** — `run-enrichment-pipeline.mjs` |
|
|
410
|
-
|
|
411
|
-
New associations should be added as registry entries + presets using v2 naming and payload format. Existing legacy properties continue to refresh through their current presets without rename.
|
|
412
|
-
|
|
413
|
-
### Pilot: vulnerability discovery and decisions (v2)
|
|
38
|
+
## Payload rules
|
|
414
39
|
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
2. Match target `findings-snapshots` by snapshot paths (`data.sku`, `concept._extIdentifier.sku`, `identity`, `recordId`).
|
|
419
|
-
3. Take matched target snapshot `recordId`.
|
|
420
|
-
4. Fetch `findings-discovery` / `findings-decisions` by record-id link (`concept._extIdentifier.recordId`, `data.sourceRecordId`, or root `recordId`).
|
|
421
|
-
5. Copy flat `content.data` into `associatedVulnerabilitiesDiscovery` / `associatedVulnerabilitiesDecisions`.
|
|
422
|
-
|
|
423
|
-
| Property | Match collection | Content collection | Join key |
|
|
424
|
-
|----------|------------------|--------------------|----------|
|
|
425
|
-
| `associatedVulnerabilitiesDiscovery` | `findings-snapshots` | `findings-discovery` | `data.sku` → snapshot → `recordId` |
|
|
426
|
-
| `associatedVulnerabilitiesDecisions` | `findings-snapshots` | `findings-decisions` | `data.sku` → snapshot → `recordId` |
|
|
427
|
-
|
|
428
|
-
Target source collection: `findings-snapshots` (`MEMORIX_EVENTS_DB`).
|
|
429
|
-
|
|
430
|
-
Refresh rule keys:
|
|
431
|
-
|
|
432
|
-
- `findings.findings.discovery.by-sku`
|
|
433
|
-
- `findings.findings.decisions.by-sku`
|
|
434
|
-
|
|
435
|
-
### Pilot: asset-side vulnerability analysis (v2)
|
|
436
|
-
|
|
437
|
-
Snapshot-mediated flow (source is **items**, not findings):
|
|
438
|
-
|
|
439
|
-
1. Read source `items-snapshots.data.skus` (also `data.sku`, `data.ip_address`).
|
|
440
|
-
2. Match `findings-snapshots` by `data.sku` / `concept._extIdentifier.sku`.
|
|
441
|
-
3. Take matched snapshot `recordId`.
|
|
442
|
-
4. Fetch `findings-analysis` by record-id link.
|
|
443
|
-
5. Copy flat `content.data` into `associatedVulnerabilitiesAnalysis` on the asset snapshot.
|
|
444
|
-
|
|
445
|
-
| Property | Match collection | Content collection | Join key |
|
|
446
|
-
|----------|------------------|--------------------|----------|
|
|
447
|
-
| `associatedVulnerabilitiesAnalysis` | `findings-snapshots` | `findings-analysis` | `data.skus` → snapshot `data.sku` → `recordId` |
|
|
448
|
-
|
|
449
|
-
Target source collection: `items-snapshots` (`MEMORIX_ENTITIES_DB`).
|
|
450
|
-
|
|
451
|
-
Refresh rule key: `items.findings.analysis.by-sku`
|
|
452
|
-
|
|
453
|
-
---
|
|
40
|
+
- Associated properties are **arrays of plain objects** (linked payloads), never full envelopes.
|
|
41
|
+
- On **memorix-record/2**, root `associated*` may hold **lightweight refs only** (Option A — format-valid): `relationshipDefinitionId` + `recordId`, optional `relationshipId`. Copied related business payload is rejected.
|
|
42
|
+
- **Authoritative relationship storage (Gate G4)** is centralized per-category collections (Candidate C), not Option A roots or sibling `relationships` content. See `docs/re-factoring-and-ops-agent/RELATIONSHIP-STORAGE-DECISION.md`.
|
|
454
43
|
|
|
455
|
-
|
|
44
|
+
- **Omit** the property when empty — never write `[]`.
|
|
45
|
+
- Prefer traceability in optional item-level `_system.association` (v2) rather than wrapping envelopes.
|
|
456
46
|
|
|
457
|
-
|
|
458
|
-
|----------|--------------|
|
|
459
|
-
| `associatedData` | Linked snapshots / generic data |
|
|
460
|
-
| `associatedDiscovery` | Linked discovery |
|
|
461
|
-
| `associatedAnalysis` | Linked analysis |
|
|
462
|
-
| `associatedAssets` | Asset snapshots by IP |
|
|
463
|
-
| `associatedSubnets` | Subnet snapshots (explicit) |
|
|
464
|
-
| `associatedTopology` | Derived (resolver, not associator copy) |
|
|
465
|
-
| `associatedVulnerabilityGroup` | Vuln-group snapshots (explicit) |
|
|
466
|
-
| `associatedZoneAnalysis` | Topology-zone analysis |
|
|
47
|
+
See requirements **ASSOC-*** and the pipeline association doc for matching / payload forms.
|