@echovisionlab/geul-telemetry 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.md +6 -0
- package/README.md +38 -0
- package/fixtures/audit-records.json +316 -0
- package/fixtures/collaboration-checkpoint-entity-types.json +15 -0
- package/fixtures/domain-audit-actions.json +132 -0
- package/fixtures/domain-audit-wire-parity.json +1751 -0
- package/fixtures/http-request-results.json +21 -0
- package/fixtures/job-catalog.json +23 -0
- package/fixtures/locale-content-audit.json +126 -0
- package/fixtures/log-redaction.json +72 -0
- package/fixtures/request-record.json +14 -0
- package/fixtures/security-access-records.json +61 -0
- package/fixtures/service-identities.json +11 -0
- package/fixtures/system-catalog.json +28 -0
- package/fixtures/translation-job-terminal-catalog.json +45 -0
- package/fixtures/translation-settings-fields.json +1 -0
- package/package.json +61 -0
- package/policy/log-redaction.json +113 -0
- package/src/actor.ts +81 -0
- package/src/audit/account.ts +206 -0
- package/src/audit/actor-policy.ts +63 -0
- package/src/audit/artist-label.ts +312 -0
- package/src/audit/attributes.ts +107 -0
- package/src/audit/builder.ts +25 -0
- package/src/audit/catalog.ts +116 -0
- package/src/audit/content.ts +723 -0
- package/src/audit/email-authoring.ts +220 -0
- package/src/audit/file.ts +168 -0
- package/src/audit/form.ts +229 -0
- package/src/audit/integrations.ts +207 -0
- package/src/audit/locale-code.ts +6 -0
- package/src/audit/locale-content.ts +358 -0
- package/src/audit/member-account.ts +313 -0
- package/src/audit/member.ts +171 -0
- package/src/audit/program.ts +339 -0
- package/src/audit/reference-data.ts +230 -0
- package/src/audit/reference-entities.ts +332 -0
- package/src/audit/relation-download-policy.ts +292 -0
- package/src/audit/release-campaign.ts +422 -0
- package/src/audit/series-type.ts +298 -0
- package/src/audit/settings.ts +306 -0
- package/src/audit/source-locale.ts +337 -0
- package/src/audit/types.ts +9 -0
- package/src/audit.ts +21 -0
- package/src/authentication.ts +183 -0
- package/src/error-metadata.ts +11 -0
- package/src/index.ts +11 -0
- package/src/job.ts +56 -0
- package/src/log-attribute-policy.ts +21 -0
- package/src/otel.ts +64 -0
- package/src/records.ts +1244 -0
- package/src/redaction.ts +35 -0
- package/src/request-context.ts +73 -0
- package/src/request-id.ts +13 -0
- package/src/request-result.ts +73 -0
- package/src/request.ts +60 -0
- package/src/system.ts +355 -0
- package/src/trace.ts +13 -0
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
import type { AuditAction, AuditRecord } from "../records.ts";
|
|
2
|
+
import { assertOnlyAuditAttributes } from "./attributes.ts";
|
|
3
|
+
import { buildAuditRecord } from "./builder.ts";
|
|
4
|
+
import { AUDIT_CATALOG, requireCatalogTarget } from "./catalog.ts";
|
|
5
|
+
import { isCanonicalAuditLocale } from "./locale-code.ts";
|
|
6
|
+
import type { AuditMetadata } from "./types.ts";
|
|
7
|
+
|
|
8
|
+
type SourceLocaleAction = Extract<
|
|
9
|
+
AuditAction,
|
|
10
|
+
| "post.updated"
|
|
11
|
+
| "page.updated"
|
|
12
|
+
| "work.updated"
|
|
13
|
+
| "post_series.updated"
|
|
14
|
+
| "program_event.updated"
|
|
15
|
+
| "release.updated"
|
|
16
|
+
| "artist.updated"
|
|
17
|
+
| "label.updated"
|
|
18
|
+
| "menu.updated"
|
|
19
|
+
| "campaign.updated"
|
|
20
|
+
| "form.updated"
|
|
21
|
+
| "email_template.updated"
|
|
22
|
+
| "email_layout.updated"
|
|
23
|
+
| "legal_policy.updated"
|
|
24
|
+
>;
|
|
25
|
+
|
|
26
|
+
const sourceLocaleActions = new Set<SourceLocaleAction>([
|
|
27
|
+
"post.updated",
|
|
28
|
+
"page.updated",
|
|
29
|
+
"work.updated",
|
|
30
|
+
"post_series.updated",
|
|
31
|
+
"program_event.updated",
|
|
32
|
+
"release.updated",
|
|
33
|
+
"artist.updated",
|
|
34
|
+
"label.updated",
|
|
35
|
+
"menu.updated",
|
|
36
|
+
"campaign.updated",
|
|
37
|
+
"form.updated",
|
|
38
|
+
"email_template.updated",
|
|
39
|
+
"email_layout.updated",
|
|
40
|
+
"legal_policy.updated",
|
|
41
|
+
]);
|
|
42
|
+
|
|
43
|
+
/** Recognizes exactly the reviewed source-locale variant, never a generic update. */
|
|
44
|
+
export function validateSourceLocaleAuditRecord(record: AuditRecord): boolean {
|
|
45
|
+
if (
|
|
46
|
+
record.changed_fields?.length !== 1 ||
|
|
47
|
+
record.changed_fields[0] !== "source_locale"
|
|
48
|
+
)
|
|
49
|
+
return false;
|
|
50
|
+
if (!sourceLocaleActions.has(record.action as SourceLocaleAction))
|
|
51
|
+
throw new TypeError(
|
|
52
|
+
`audit action ${record.action} does not support source_locale`,
|
|
53
|
+
);
|
|
54
|
+
requireCatalogTarget(record.action, record.target_type, record.target_id);
|
|
55
|
+
if (record.actor_kind !== "member")
|
|
56
|
+
throw new TypeError("source_locale requires a member actor");
|
|
57
|
+
if (
|
|
58
|
+
!isCanonicalAuditLocale(record.previous_locale) ||
|
|
59
|
+
!isCanonicalAuditLocale(record.new_locale) ||
|
|
60
|
+
record.previous_locale === record.new_locale
|
|
61
|
+
)
|
|
62
|
+
throw new TypeError(
|
|
63
|
+
"source_locale requires distinct bounded previous_locale and new_locale",
|
|
64
|
+
);
|
|
65
|
+
const allowed = ["changed_fields", "previous_locale", "new_locale"];
|
|
66
|
+
if (record.action === "legal_policy.updated") {
|
|
67
|
+
if (
|
|
68
|
+
(record.policy_type !== "terms" && record.policy_type !== "privacy") ||
|
|
69
|
+
!Number.isSafeInteger(record.version_number) ||
|
|
70
|
+
record.version_number === undefined ||
|
|
71
|
+
record.version_number < 1
|
|
72
|
+
)
|
|
73
|
+
throw new TypeError(
|
|
74
|
+
"legal policy requires policy_type and version_number",
|
|
75
|
+
);
|
|
76
|
+
allowed.push("policy_type", "version_number");
|
|
77
|
+
}
|
|
78
|
+
assertOnlyAuditAttributes(record, allowed);
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function buildSourceLocaleAuditRecord(
|
|
83
|
+
metadata: AuditMetadata,
|
|
84
|
+
action: SourceLocaleAction,
|
|
85
|
+
id: string,
|
|
86
|
+
previousLocale: string,
|
|
87
|
+
newLocale: string,
|
|
88
|
+
): AuditRecord {
|
|
89
|
+
return buildAuditRecord(
|
|
90
|
+
metadata,
|
|
91
|
+
{ action, target_type: AUDIT_CATALOG[action], target_id: id },
|
|
92
|
+
{
|
|
93
|
+
changed_fields: ["source_locale"],
|
|
94
|
+
previous_locale: previousLocale,
|
|
95
|
+
new_locale: newLocale,
|
|
96
|
+
},
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function buildPostSourceLocaleAuditRecord(
|
|
101
|
+
m: AuditMetadata,
|
|
102
|
+
id: string,
|
|
103
|
+
previousLocale: string,
|
|
104
|
+
newLocale: string,
|
|
105
|
+
): AuditRecord {
|
|
106
|
+
return buildSourceLocaleAuditRecord(
|
|
107
|
+
m,
|
|
108
|
+
"post.updated",
|
|
109
|
+
id,
|
|
110
|
+
previousLocale,
|
|
111
|
+
newLocale,
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
export function buildPageSourceLocaleAuditRecord(
|
|
115
|
+
m: AuditMetadata,
|
|
116
|
+
id: string,
|
|
117
|
+
previousLocale: string,
|
|
118
|
+
newLocale: string,
|
|
119
|
+
): AuditRecord {
|
|
120
|
+
return buildSourceLocaleAuditRecord(
|
|
121
|
+
m,
|
|
122
|
+
"page.updated",
|
|
123
|
+
id,
|
|
124
|
+
previousLocale,
|
|
125
|
+
newLocale,
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
export function buildWorkSourceLocaleAuditRecord(
|
|
129
|
+
m: AuditMetadata,
|
|
130
|
+
id: string,
|
|
131
|
+
previousLocale: string,
|
|
132
|
+
newLocale: string,
|
|
133
|
+
): AuditRecord {
|
|
134
|
+
return buildSourceLocaleAuditRecord(
|
|
135
|
+
m,
|
|
136
|
+
"work.updated",
|
|
137
|
+
id,
|
|
138
|
+
previousLocale,
|
|
139
|
+
newLocale,
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
export function buildPostSeriesSourceLocaleAuditRecord(
|
|
143
|
+
m: AuditMetadata,
|
|
144
|
+
id: string,
|
|
145
|
+
previousLocale: string,
|
|
146
|
+
newLocale: string,
|
|
147
|
+
): AuditRecord {
|
|
148
|
+
return buildSourceLocaleAuditRecord(
|
|
149
|
+
m,
|
|
150
|
+
"post_series.updated",
|
|
151
|
+
id,
|
|
152
|
+
previousLocale,
|
|
153
|
+
newLocale,
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
export function buildProgramEventSourceLocaleAuditRecord(
|
|
157
|
+
m: AuditMetadata,
|
|
158
|
+
id: string,
|
|
159
|
+
previousLocale: string,
|
|
160
|
+
newLocale: string,
|
|
161
|
+
): AuditRecord {
|
|
162
|
+
return buildSourceLocaleAuditRecord(
|
|
163
|
+
m,
|
|
164
|
+
"program_event.updated",
|
|
165
|
+
id,
|
|
166
|
+
previousLocale,
|
|
167
|
+
newLocale,
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
export function buildReleaseSourceLocaleAuditRecord(
|
|
171
|
+
m: AuditMetadata,
|
|
172
|
+
id: string,
|
|
173
|
+
previousLocale: string,
|
|
174
|
+
newLocale: string,
|
|
175
|
+
): AuditRecord {
|
|
176
|
+
return buildSourceLocaleAuditRecord(
|
|
177
|
+
m,
|
|
178
|
+
"release.updated",
|
|
179
|
+
id,
|
|
180
|
+
previousLocale,
|
|
181
|
+
newLocale,
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
export function buildArtistSourceLocaleAuditRecord(
|
|
185
|
+
m: AuditMetadata,
|
|
186
|
+
id: string,
|
|
187
|
+
previousLocale: string,
|
|
188
|
+
newLocale: string,
|
|
189
|
+
): AuditRecord {
|
|
190
|
+
return buildSourceLocaleAuditRecord(
|
|
191
|
+
m,
|
|
192
|
+
"artist.updated",
|
|
193
|
+
id,
|
|
194
|
+
previousLocale,
|
|
195
|
+
newLocale,
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
export function buildLabelSourceLocaleAuditRecord(
|
|
199
|
+
m: AuditMetadata,
|
|
200
|
+
id: string,
|
|
201
|
+
previousLocale: string,
|
|
202
|
+
newLocale: string,
|
|
203
|
+
): AuditRecord {
|
|
204
|
+
return buildSourceLocaleAuditRecord(
|
|
205
|
+
m,
|
|
206
|
+
"label.updated",
|
|
207
|
+
id,
|
|
208
|
+
previousLocale,
|
|
209
|
+
newLocale,
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
export function buildMenuSourceLocaleAuditRecord(
|
|
213
|
+
m: AuditMetadata,
|
|
214
|
+
id: string,
|
|
215
|
+
previousLocale: string,
|
|
216
|
+
newLocale: string,
|
|
217
|
+
): AuditRecord {
|
|
218
|
+
return buildSourceLocaleAuditRecord(
|
|
219
|
+
m,
|
|
220
|
+
"menu.updated",
|
|
221
|
+
id,
|
|
222
|
+
previousLocale,
|
|
223
|
+
newLocale,
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
export function buildCampaignSourceLocaleAuditRecord(
|
|
227
|
+
m: AuditMetadata,
|
|
228
|
+
id: string,
|
|
229
|
+
previousLocale: string,
|
|
230
|
+
newLocale: string,
|
|
231
|
+
): AuditRecord {
|
|
232
|
+
return buildSourceLocaleAuditRecord(
|
|
233
|
+
m,
|
|
234
|
+
"campaign.updated",
|
|
235
|
+
id,
|
|
236
|
+
previousLocale,
|
|
237
|
+
newLocale,
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
export function buildFormSourceLocaleAuditRecord(
|
|
241
|
+
m: AuditMetadata,
|
|
242
|
+
id: string,
|
|
243
|
+
previousLocale: string,
|
|
244
|
+
newLocale: string,
|
|
245
|
+
): AuditRecord {
|
|
246
|
+
return buildSourceLocaleAuditRecord(
|
|
247
|
+
m,
|
|
248
|
+
"form.updated",
|
|
249
|
+
id,
|
|
250
|
+
previousLocale,
|
|
251
|
+
newLocale,
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
export function buildEmailTemplateSourceLocaleAuditRecord(
|
|
255
|
+
m: AuditMetadata,
|
|
256
|
+
id: string,
|
|
257
|
+
previousLocale: string,
|
|
258
|
+
newLocale: string,
|
|
259
|
+
): AuditRecord {
|
|
260
|
+
return buildSourceLocaleAuditRecord(
|
|
261
|
+
m,
|
|
262
|
+
"email_template.updated",
|
|
263
|
+
id,
|
|
264
|
+
previousLocale,
|
|
265
|
+
newLocale,
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
export function buildEmailLayoutSourceLocaleAuditRecord(
|
|
269
|
+
m: AuditMetadata,
|
|
270
|
+
id: string,
|
|
271
|
+
previousLocale: string,
|
|
272
|
+
newLocale: string,
|
|
273
|
+
): AuditRecord {
|
|
274
|
+
return buildSourceLocaleAuditRecord(
|
|
275
|
+
m,
|
|
276
|
+
"email_layout.updated",
|
|
277
|
+
id,
|
|
278
|
+
previousLocale,
|
|
279
|
+
newLocale,
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
function buildLegalPolicySourceLocaleAuditRecord(
|
|
283
|
+
metadata: AuditMetadata,
|
|
284
|
+
id: string,
|
|
285
|
+
policyType: "terms" | "privacy",
|
|
286
|
+
versionNumber: number,
|
|
287
|
+
previousLocale: string,
|
|
288
|
+
newLocale: string,
|
|
289
|
+
): AuditRecord {
|
|
290
|
+
return buildAuditRecord(
|
|
291
|
+
metadata,
|
|
292
|
+
{
|
|
293
|
+
action: "legal_policy.updated",
|
|
294
|
+
target_type: AUDIT_CATALOG["legal_policy.updated"],
|
|
295
|
+
target_id: id,
|
|
296
|
+
},
|
|
297
|
+
{
|
|
298
|
+
changed_fields: ["source_locale"],
|
|
299
|
+
policy_type: policyType,
|
|
300
|
+
version_number: versionNumber,
|
|
301
|
+
previous_locale: previousLocale,
|
|
302
|
+
new_locale: newLocale,
|
|
303
|
+
},
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
export function buildPrivacySourceLocaleAuditRecord(
|
|
307
|
+
m: AuditMetadata,
|
|
308
|
+
id: string,
|
|
309
|
+
versionNumber: number,
|
|
310
|
+
previousLocale: string,
|
|
311
|
+
newLocale: string,
|
|
312
|
+
): AuditRecord {
|
|
313
|
+
return buildLegalPolicySourceLocaleAuditRecord(
|
|
314
|
+
m,
|
|
315
|
+
id,
|
|
316
|
+
"privacy",
|
|
317
|
+
versionNumber,
|
|
318
|
+
previousLocale,
|
|
319
|
+
newLocale,
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
export function buildTermsSourceLocaleAuditRecord(
|
|
323
|
+
m: AuditMetadata,
|
|
324
|
+
id: string,
|
|
325
|
+
versionNumber: number,
|
|
326
|
+
previousLocale: string,
|
|
327
|
+
newLocale: string,
|
|
328
|
+
): AuditRecord {
|
|
329
|
+
return buildLegalPolicySourceLocaleAuditRecord(
|
|
330
|
+
m,
|
|
331
|
+
id,
|
|
332
|
+
"terms",
|
|
333
|
+
versionNumber,
|
|
334
|
+
previousLocale,
|
|
335
|
+
newLocale,
|
|
336
|
+
);
|
|
337
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { RecordActor } from "../actor.ts";
|
|
2
|
+
import type { Correlation } from "../records.ts";
|
|
3
|
+
|
|
4
|
+
/** Metadata shared by every domain-audit record. */
|
|
5
|
+
export type AuditMetadata = Correlation &
|
|
6
|
+
RecordActor & {
|
|
7
|
+
readonly audit_id: string;
|
|
8
|
+
readonly occurred_at: string;
|
|
9
|
+
};
|
package/src/audit.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Public compatibility facade. Domain modules remain internal implementation
|
|
2
|
+
// boundaries; package consumers import the supported root export.
|
|
3
|
+
export * from "./audit/account.ts";
|
|
4
|
+
export * from "./audit/attributes.ts";
|
|
5
|
+
export * from "./audit/artist-label.ts";
|
|
6
|
+
export * from "./audit/content.ts";
|
|
7
|
+
export * from "./audit/email-authoring.ts";
|
|
8
|
+
export * from "./audit/file.ts";
|
|
9
|
+
export * from "./audit/form.ts";
|
|
10
|
+
export * from "./audit/integrations.ts";
|
|
11
|
+
export * from "./audit/member.ts";
|
|
12
|
+
export * from "./audit/program.ts";
|
|
13
|
+
export * from "./audit/reference-data.ts";
|
|
14
|
+
export * from "./audit/reference-entities.ts";
|
|
15
|
+
export * from "./audit/relation-download-policy.ts";
|
|
16
|
+
export * from "./audit/release-campaign.ts";
|
|
17
|
+
export * from "./audit/settings.ts";
|
|
18
|
+
export * from "./audit/series-type.ts";
|
|
19
|
+
export * from "./audit/source-locale.ts";
|
|
20
|
+
export * from "./audit/locale-content.ts";
|
|
21
|
+
export * from "./audit/types.ts";
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import type { RecordActor } from "./actor.ts";
|
|
2
|
+
import {
|
|
3
|
+
validateSecurityAccessRecord,
|
|
4
|
+
type AuthenticationBlockReason,
|
|
5
|
+
type AuthenticationFailureReason,
|
|
6
|
+
type AuthenticationFlowKind,
|
|
7
|
+
type AuthenticationMethod,
|
|
8
|
+
type AuthenticationPrincipalState,
|
|
9
|
+
type AuthorizationDenialReason,
|
|
10
|
+
type Correlation,
|
|
11
|
+
type SecurityAccessRecord,
|
|
12
|
+
} from "./records.ts";
|
|
13
|
+
|
|
14
|
+
export type SecurityAccessMetadata = Correlation &
|
|
15
|
+
RecordActor & {
|
|
16
|
+
readonly access_id: string;
|
|
17
|
+
readonly occurred_at: string;
|
|
18
|
+
readonly source_ip: string;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export interface AuthenticationContext {
|
|
22
|
+
readonly flow_kind?: AuthenticationFlowKind;
|
|
23
|
+
readonly authentication_method?: AuthenticationMethod;
|
|
24
|
+
readonly principal_state?: AuthenticationPrincipalState;
|
|
25
|
+
readonly provider?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function buildAuthenticationSucceededRecord(
|
|
29
|
+
metadata: SecurityAccessMetadata,
|
|
30
|
+
authentication: Required<
|
|
31
|
+
Pick<
|
|
32
|
+
AuthenticationContext,
|
|
33
|
+
"flow_kind" | "authentication_method" | "principal_state"
|
|
34
|
+
>
|
|
35
|
+
> &
|
|
36
|
+
Pick<AuthenticationContext, "provider">,
|
|
37
|
+
): SecurityAccessRecord {
|
|
38
|
+
return buildSecurityAccessRecord(metadata, {
|
|
39
|
+
action: "authentication.succeeded",
|
|
40
|
+
...authentication,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function buildAuthenticationFailedRecord(
|
|
45
|
+
metadata: SecurityAccessMetadata,
|
|
46
|
+
authentication: Required<
|
|
47
|
+
Pick<AuthenticationContext, "flow_kind" | "authentication_method">
|
|
48
|
+
> &
|
|
49
|
+
Pick<AuthenticationContext, "provider">,
|
|
50
|
+
reason: AuthenticationFailureReason,
|
|
51
|
+
): SecurityAccessRecord {
|
|
52
|
+
return buildSecurityAccessRecord(metadata, {
|
|
53
|
+
action: "authentication.failed",
|
|
54
|
+
...authentication,
|
|
55
|
+
reason,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function buildAuthenticationBlockedRecord(
|
|
60
|
+
metadata: SecurityAccessMetadata,
|
|
61
|
+
authentication: AuthenticationContext,
|
|
62
|
+
reason: AuthenticationBlockReason,
|
|
63
|
+
): SecurityAccessRecord {
|
|
64
|
+
return buildSecurityAccessRecord(metadata, {
|
|
65
|
+
action: "authentication.blocked",
|
|
66
|
+
...authentication,
|
|
67
|
+
reason,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function buildAuthorizationDeniedRecord(
|
|
72
|
+
metadata: SecurityAccessMetadata,
|
|
73
|
+
procedure: string,
|
|
74
|
+
reason: AuthorizationDenialReason,
|
|
75
|
+
): SecurityAccessRecord {
|
|
76
|
+
return buildSecurityAccessRecord(metadata, {
|
|
77
|
+
action: "authorization.denied",
|
|
78
|
+
attempted_action: procedure,
|
|
79
|
+
permission: "procedure:invoke",
|
|
80
|
+
reason,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function buildMemberCollectionAccessedRecord(
|
|
85
|
+
metadata: SecurityAccessMetadata,
|
|
86
|
+
): SecurityAccessRecord {
|
|
87
|
+
return buildPersonalDataAccessedRecord(
|
|
88
|
+
metadata,
|
|
89
|
+
"member_collection",
|
|
90
|
+
"1",
|
|
91
|
+
"member_administration",
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function buildMemberAccessedRecord(
|
|
96
|
+
metadata: SecurityAccessMetadata,
|
|
97
|
+
memberId: string,
|
|
98
|
+
): SecurityAccessRecord {
|
|
99
|
+
return buildPersonalDataAccessedRecord(
|
|
100
|
+
metadata,
|
|
101
|
+
"member",
|
|
102
|
+
memberId,
|
|
103
|
+
"member_administration",
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function buildCampaignRecipientsAccessedRecord(
|
|
108
|
+
metadata: SecurityAccessMetadata,
|
|
109
|
+
campaignId: string,
|
|
110
|
+
): SecurityAccessRecord {
|
|
111
|
+
return buildPersonalDataAccessedRecord(
|
|
112
|
+
metadata,
|
|
113
|
+
"campaign",
|
|
114
|
+
campaignId,
|
|
115
|
+
"campaign_recipients",
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function buildFormSubmissionsAccessedRecord(
|
|
120
|
+
metadata: SecurityAccessMetadata,
|
|
121
|
+
formId: string,
|
|
122
|
+
): SecurityAccessRecord {
|
|
123
|
+
return buildPersonalDataAccessedRecord(
|
|
124
|
+
metadata,
|
|
125
|
+
"form",
|
|
126
|
+
formId,
|
|
127
|
+
"form_submissions",
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function buildFormSubmissionAccessedRecord(
|
|
132
|
+
metadata: SecurityAccessMetadata,
|
|
133
|
+
submissionId: string,
|
|
134
|
+
): SecurityAccessRecord {
|
|
135
|
+
return buildPersonalDataAccessedRecord(
|
|
136
|
+
metadata,
|
|
137
|
+
"form_submission",
|
|
138
|
+
submissionId,
|
|
139
|
+
"form_submission",
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function buildPersonalDataAccessedRecord(
|
|
144
|
+
metadata: SecurityAccessMetadata,
|
|
145
|
+
subjectType: string,
|
|
146
|
+
subjectId: string,
|
|
147
|
+
dataCategory: string,
|
|
148
|
+
): SecurityAccessRecord {
|
|
149
|
+
return buildSecurityAccessRecord(metadata, {
|
|
150
|
+
action: "personal_data.accessed",
|
|
151
|
+
subject_type: subjectType,
|
|
152
|
+
subject_id: subjectId,
|
|
153
|
+
access_kind: "read",
|
|
154
|
+
data_category: dataCategory,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export function authenticationMethodFromKratos(
|
|
159
|
+
value: string,
|
|
160
|
+
): AuthenticationMethod {
|
|
161
|
+
switch (value) {
|
|
162
|
+
case "code":
|
|
163
|
+
return "email_code";
|
|
164
|
+
case "oidc":
|
|
165
|
+
return "oidc";
|
|
166
|
+
case "passkey":
|
|
167
|
+
return "passkey";
|
|
168
|
+
default:
|
|
169
|
+
throw new TypeError("unsupported authentication method");
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function buildSecurityAccessRecord(
|
|
174
|
+
metadata: SecurityAccessMetadata,
|
|
175
|
+
attributes: Omit<
|
|
176
|
+
SecurityAccessRecord,
|
|
177
|
+
keyof SecurityAccessMetadata | keyof Correlation | keyof RecordActor
|
|
178
|
+
>,
|
|
179
|
+
): SecurityAccessRecord {
|
|
180
|
+
const record = { ...metadata, ...attributes } as SecurityAccessRecord;
|
|
181
|
+
validateSecurityAccessRecord(record);
|
|
182
|
+
return record;
|
|
183
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { normalizeKey } from "./log-attribute-policy.ts";
|
|
2
|
+
|
|
3
|
+
export function stableErrorType(value: unknown): string {
|
|
4
|
+
if (value instanceof Error) {
|
|
5
|
+
const errorName = normalizeKey(value.name);
|
|
6
|
+
if (errorName !== "" && errorName !== "error") return errorName;
|
|
7
|
+
const constructorName = normalizeKey(value.constructor.name);
|
|
8
|
+
if (constructorName !== "") return constructorName;
|
|
9
|
+
}
|
|
10
|
+
return "reported_error";
|
|
11
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export * from "./actor.ts";
|
|
2
|
+
export * from "./audit.ts";
|
|
3
|
+
export * from "./authentication.ts";
|
|
4
|
+
export * from "./job.ts";
|
|
5
|
+
export * from "./otel.ts";
|
|
6
|
+
export * from "./records.ts";
|
|
7
|
+
export * from "./redaction.ts";
|
|
8
|
+
export * from "./request.ts";
|
|
9
|
+
export * from "./request-context.ts";
|
|
10
|
+
export * from "./system.ts";
|
|
11
|
+
export * from "./trace.ts";
|
package/src/job.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export const JOB_KIND_MESH_OPTIMIZATION = "mesh_optimization";
|
|
2
|
+
export const JOB_KIND_OG_GENERATION = "og_generation";
|
|
3
|
+
|
|
4
|
+
export const CANONICAL_JOB_KINDS = [
|
|
5
|
+
JOB_KIND_MESH_OPTIMIZATION,
|
|
6
|
+
JOB_KIND_OG_GENERATION,
|
|
7
|
+
] as const;
|
|
8
|
+
|
|
9
|
+
export type JobKind = (typeof CANONICAL_JOB_KINDS)[number];
|
|
10
|
+
|
|
11
|
+
export const JOB_FAILURE_REASONS = {
|
|
12
|
+
[JOB_KIND_MESH_OPTIMIZATION]: [
|
|
13
|
+
"rejected",
|
|
14
|
+
"source_not_found",
|
|
15
|
+
"download_failed",
|
|
16
|
+
"optimization_failed",
|
|
17
|
+
"upload_failed",
|
|
18
|
+
"internal",
|
|
19
|
+
],
|
|
20
|
+
[JOB_KIND_OG_GENERATION]: [
|
|
21
|
+
"invalid_claim",
|
|
22
|
+
"source_rejected",
|
|
23
|
+
"processing_failed",
|
|
24
|
+
"integrity_failed",
|
|
25
|
+
"completion_rejected",
|
|
26
|
+
],
|
|
27
|
+
} as const satisfies Record<JobKind, readonly string[]>;
|
|
28
|
+
|
|
29
|
+
export type JobFailureReason = (typeof JOB_FAILURE_REASONS)[JobKind][number];
|
|
30
|
+
|
|
31
|
+
const canonicalJobKinds = new Set<string>(CANONICAL_JOB_KINDS);
|
|
32
|
+
|
|
33
|
+
export function parseJobKind(value: string): JobKind {
|
|
34
|
+
if (!canonicalJobKinds.has(value)) {
|
|
35
|
+
throw new TypeError(`unknown canonical job kind ${JSON.stringify(value)}`);
|
|
36
|
+
}
|
|
37
|
+
return value as JobKind;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function parseJobFailureReason(
|
|
41
|
+
jobKind: JobKind,
|
|
42
|
+
value: string,
|
|
43
|
+
): JobFailureReason {
|
|
44
|
+
const reasons = JOB_FAILURE_REASONS[jobKind] as readonly string[] | undefined;
|
|
45
|
+
if (reasons === undefined) {
|
|
46
|
+
throw new TypeError(
|
|
47
|
+
`unknown canonical job kind ${JSON.stringify(jobKind)}`,
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
if (!reasons.includes(value)) {
|
|
51
|
+
throw new TypeError(
|
|
52
|
+
`unknown failure reason ${JSON.stringify(value)} for job kind ${JSON.stringify(jobKind)}`,
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
return value as JobFailureReason;
|
|
56
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import policy from "../policy/log-redaction.json" with { type: "json" };
|
|
2
|
+
|
|
3
|
+
const forbiddenKeys = new Set<string>(policy.exact);
|
|
4
|
+
|
|
5
|
+
export function normalizeKey(key: string): string {
|
|
6
|
+
return key
|
|
7
|
+
.trim()
|
|
8
|
+
.replace(/([a-z\d])([A-Z])/g, "$1_$2")
|
|
9
|
+
.replace(/[^\p{L}\p{N}]+/gu, "_")
|
|
10
|
+
.replace(/^_+|_+$/g, "")
|
|
11
|
+
.toLowerCase();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function isForbiddenKey(key: string): boolean {
|
|
15
|
+
const normalized = normalizeKey(key);
|
|
16
|
+
return (
|
|
17
|
+
forbiddenKeys.has(normalized) ||
|
|
18
|
+
policy.suffixes.some((suffix) => normalized.endsWith(suffix)) ||
|
|
19
|
+
policy.prefixes.some((prefix) => normalized.startsWith(prefix))
|
|
20
|
+
);
|
|
21
|
+
}
|