@unchainedshop/events 4.8.30 → 4.8.32

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.
@@ -2,7 +2,7 @@ import { createHash } from 'node:crypto';
2
2
  import { mkdir, readFile, appendFile, readdir } from 'node:fs/promises';
3
3
  import { join } from 'node:path';
4
4
  import { createLogger } from '@unchainedshop/logger';
5
- import { OCSF_CLASS, OCSF_CATEGORY, OCSF_SEVERITY, OCSF_STATUS, OCSF_AUTH_ACTIVITY, OCSF_ACCOUNT_ACTIVITY, OCSF_API_ACTIVITY, } from "./ocsf-types.js";
5
+ import { OCSF_CLASS, OCSF_CATEGORY, OCSF_SEVERITY, OCSF_STATUS, OCSF_USER_TYPE, OCSF_AUTH_ACTIVITY, OCSF_ACCOUNT_ACTIVITY, OCSF_API_ACTIVITY, } from "./ocsf-types.js";
6
6
  export * from "./ocsf-types.js";
7
7
  const logger = createLogger('unchained:audit');
8
8
  const OCSF_VERSION = '1.4.0';
@@ -77,7 +77,12 @@ export class AuditLog {
77
77
  uid,
78
78
  };
79
79
  }
80
- createUser(userId, userName) {
80
+ createUser(userId, userName, remoteAddress) {
81
+ if (!userId && !userName) {
82
+ return remoteAddress
83
+ ? { name: 'anonymous', type_id: OCSF_USER_TYPE.UNKNOWN }
84
+ : { name: PRODUCT_NAME, type_id: OCSF_USER_TYPE.SYSTEM };
85
+ }
81
86
  return {
82
87
  uid: userId,
83
88
  name: userName,
@@ -182,7 +187,7 @@ export class AuditLog {
182
187
  time: Date.now(),
183
188
  message: input.message || activityMessages[activityId] || 'Authentication Event',
184
189
  metadata: this.createMetadata(crypto.randomUUID()),
185
- user: this.createUser(input.userId, input.userName),
190
+ user: this.createUser(input.userId, input.userName, input.remoteAddress),
186
191
  src_endpoint: this.createEndpoint(input.remoteAddress),
187
192
  is_mfa: input.isMfa,
188
193
  auth_protocol: input.authProtocol,
@@ -211,7 +216,7 @@ export class AuditLog {
211
216
  time: Date.now(),
212
217
  message: input.message || activityMessages[activityId] || 'Account Change',
213
218
  metadata: this.createMetadata(crypto.randomUUID()),
214
- user: this.createUser(input.userId, input.userName),
219
+ user: this.createUser(input.userId, input.userName, input.remoteAddress),
215
220
  actor: input.actorUserId
216
221
  ? {
217
222
  user: this.createUser(input.actorUserId, input.actorUserName),
@@ -249,7 +254,7 @@ export class AuditLog {
249
254
  message: input.message || activityMessages[activityId] || 'API Activity',
250
255
  metadata: this.createMetadata(crypto.randomUUID()),
251
256
  actor: {
252
- user: this.createUser(input.userId, input.userName),
257
+ user: this.createUser(input.userId, input.userName, input.remoteAddress),
253
258
  session: input.sessionId ? { uid: input.sessionId } : undefined,
254
259
  },
255
260
  api: {
@@ -17,6 +17,13 @@ export declare const OCSF_SEVERITY: {
17
17
  readonly CRITICAL: 5;
18
18
  readonly FATAL: 6;
19
19
  };
20
+ export declare const OCSF_USER_TYPE: {
21
+ readonly UNKNOWN: 0;
22
+ readonly USER: 1;
23
+ readonly ADMIN: 2;
24
+ readonly SYSTEM: 3;
25
+ readonly OTHER: 99;
26
+ };
20
27
  export declare const OCSF_STATUS: {
21
28
  readonly UNKNOWN: 0;
22
29
  readonly SUCCESS: 1;
@@ -17,6 +17,13 @@ export const OCSF_SEVERITY = {
17
17
  CRITICAL: 5,
18
18
  FATAL: 6,
19
19
  };
20
+ export const OCSF_USER_TYPE = {
21
+ UNKNOWN: 0,
22
+ USER: 1,
23
+ ADMIN: 2,
24
+ SYSTEM: 3,
25
+ OTHER: 99,
26
+ };
20
27
  export const OCSF_STATUS = {
21
28
  UNKNOWN: 0,
22
29
  SUCCESS: 1,
@@ -2,5 +2,5 @@ import { type EmitAdapter, type RawPayloadType } from './EventDirector.ts';
2
2
  declare const emit: (eventName: string, data?: Record<string, any>) => Promise<void>, getEmitAdapter: () => EmitAdapter, getEmitHistoryAdapter: () => EmitAdapter, getRegisteredEvents: () => string[], registerEvents: (events: string[]) => void, setEmitAdapter: (adapter: EmitAdapter) => void, setEmitHistoryAdapter: (adapter: EmitAdapter) => void, subscribe: <T extends Record<string, any>>(eventName: string, callback: (payload: RawPayloadType<T>) => void) => void;
3
3
  export { emit, getEmitAdapter, getEmitHistoryAdapter, getRegisteredEvents, registerEvents, setEmitAdapter, setEmitHistoryAdapter, subscribe, };
4
4
  export type { EmitAdapter, RawPayloadType };
5
- export { AuditLog, createAuditLog, OCSF_CLASS, OCSF_CATEGORY, OCSF_SEVERITY, OCSF_STATUS, OCSF_AUTH_ACTIVITY, OCSF_ACCOUNT_ACTIVITY, OCSF_API_ACTIVITY, type AuditLogConfig, type AuthenticationInput, type AccountChangeInput, type ApiActivityInput, type AuditLogQuery, type VerifyResult, type OCSFBaseEvent, type OCSFAuthenticationEvent, type OCSFAccountChangeEvent, type OCSFApiActivityEvent, type OCSFEvent, type OCSFMetadata, type OCSFUser, type OCSFActor, type OCSFEndpoint, type OCSFApi, type OCSFSession, } from './audit/index.ts';
5
+ export { AuditLog, createAuditLog, OCSF_CLASS, OCSF_CATEGORY, OCSF_SEVERITY, OCSF_STATUS, OCSF_USER_TYPE, OCSF_AUTH_ACTIVITY, OCSF_ACCOUNT_ACTIVITY, OCSF_API_ACTIVITY, type AuditLogConfig, type AuthenticationInput, type AccountChangeInput, type ApiActivityInput, type AuditLogQuery, type VerifyResult, type OCSFBaseEvent, type OCSFAuthenticationEvent, type OCSFAccountChangeEvent, type OCSFApiActivityEvent, type OCSFEvent, type OCSFMetadata, type OCSFUser, type OCSFActor, type OCSFEndpoint, type OCSFApi, type OCSFSession, } from './audit/index.ts';
6
6
  export { configureAuditIntegration, AUDITED_EVENTS } from './audit/audit-integration.ts';
@@ -3,5 +3,5 @@ const { emit, getEmitAdapter, getEmitHistoryAdapter, getRegisteredEvents, regist
3
3
  const GLOBAL_EVENTS = ['PAGE_VIEW'];
4
4
  registerEvents(GLOBAL_EVENTS);
5
5
  export { emit, getEmitAdapter, getEmitHistoryAdapter, getRegisteredEvents, registerEvents, setEmitAdapter, setEmitHistoryAdapter, subscribe, };
6
- export { AuditLog, createAuditLog, OCSF_CLASS, OCSF_CATEGORY, OCSF_SEVERITY, OCSF_STATUS, OCSF_AUTH_ACTIVITY, OCSF_ACCOUNT_ACTIVITY, OCSF_API_ACTIVITY, } from "./audit/index.js";
6
+ export { AuditLog, createAuditLog, OCSF_CLASS, OCSF_CATEGORY, OCSF_SEVERITY, OCSF_STATUS, OCSF_USER_TYPE, OCSF_AUTH_ACTIVITY, OCSF_ACCOUNT_ACTIVITY, OCSF_API_ACTIVITY, } from "./audit/index.js";
7
7
  export { configureAuditIntegration, AUDITED_EVENTS } from "./audit/audit-integration.js";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@unchainedshop/events",
3
3
  "description": "Event emitter abstraction layer for the Unchained Engine",
4
- "version": "4.8.30",
4
+ "version": "4.8.32",
5
5
  "main": "lib/events-index.js",
6
6
  "types": "lib/events-index.d.ts",
7
7
  "type": "module",
@@ -34,10 +34,10 @@
34
34
  },
35
35
  "homepage": "https://github.com/unchainedshop/unchained#readme",
36
36
  "dependencies": {
37
- "@unchainedshop/logger": "^4.8.27"
37
+ "@unchainedshop/logger": "^4.8.31"
38
38
  },
39
39
  "devDependencies": {
40
- "@types/node": "^26.2.0",
41
- "typescript": "^5.8.3"
40
+ "@types/node": "^26.6.5",
41
+ "typescript": "^6.0.3"
42
42
  }
43
43
  }
@@ -1,62 +0,0 @@
1
- export type OTLPAnyValue = {
2
- stringValue: string;
3
- } | {
4
- boolValue: boolean;
5
- } | {
6
- intValue: string;
7
- } | {
8
- doubleValue: number;
9
- } | {
10
- arrayValue: {
11
- values: OTLPAnyValue[];
12
- };
13
- } | {
14
- kvlistValue: {
15
- values: OTLPKeyValue[];
16
- };
17
- };
18
- export interface OTLPKeyValue {
19
- key: string;
20
- value: OTLPAnyValue;
21
- }
22
- export interface OTLPResource {
23
- attributes: OTLPKeyValue[];
24
- }
25
- export interface OTLPInstrumentationScope {
26
- name: string;
27
- version?: string;
28
- }
29
- export declare const OTLP_SEVERITY_NUMBER: {
30
- readonly UNSPECIFIED: 0;
31
- readonly INFO: 9;
32
- readonly INFO2: 10;
33
- readonly WARN: 13;
34
- readonly ERROR: 17;
35
- readonly FATAL: 21;
36
- readonly FATAL4: 24;
37
- };
38
- export interface OTLPLogRecord {
39
- timeUnixNano: string;
40
- observedTimeUnixNano: string;
41
- severityNumber: number;
42
- severityText?: string;
43
- body: OTLPAnyValue;
44
- attributes: OTLPKeyValue[];
45
- }
46
- export interface OTLPScopeLogs {
47
- scope: OTLPInstrumentationScope;
48
- logRecords: OTLPLogRecord[];
49
- }
50
- export interface OTLPResourceLogs {
51
- resource: OTLPResource;
52
- scopeLogs: OTLPScopeLogs[];
53
- }
54
- export interface OTLPExportLogsServiceRequest {
55
- resourceLogs: OTLPResourceLogs[];
56
- }
57
- export interface OTLPExportLogsServiceResponse {
58
- partialSuccess?: {
59
- rejectedLogRecords?: number | string;
60
- errorMessage?: string;
61
- };
62
- }
@@ -1,9 +0,0 @@
1
- export const OTLP_SEVERITY_NUMBER = {
2
- UNSPECIFIED: 0,
3
- INFO: 9,
4
- INFO2: 10,
5
- WARN: 13,
6
- ERROR: 17,
7
- FATAL: 21,
8
- FATAL4: 24,
9
- };
@@ -1,9 +0,0 @@
1
- import type { OCSFEvent } from './ocsf-types.ts';
2
- import { type OTLPAnyValue, type OTLPLogRecord, type OTLPExportLogsServiceRequest } from './otlp-types.ts';
3
- export declare function toAnyValue(value: unknown): OTLPAnyValue;
4
- export declare function encodeOCSFLogRecord(event: OCSFEvent, observedTimeMs: number): OTLPLogRecord;
5
- export declare function buildExportLogsRequest(events: OCSFEvent[], observedTimeMs?: number): OTLPExportLogsServiceRequest;
6
- export declare function parseOtlpHeaders(raw?: string): Record<string, string>;
7
- export declare function resolveCollectorUrl(explicit?: string): string | undefined;
8
- export declare function resolveCollectorHeaders(explicit?: Record<string, string>): Record<string, string>;
9
- export declare function exportLogs(collectorUrl: string, collectorHeaders: Record<string, string>, events: OCSFEvent[]): Promise<void>;
package/lib/audit/otlp.js DELETED
@@ -1,164 +0,0 @@
1
- import { createLogger } from '@unchainedshop/logger';
2
- import { OTLP_SEVERITY_NUMBER, } from "./otlp-types.js";
3
- const logger = createLogger('unchained:audit');
4
- const SEVERITY_NUMBER_MAP = {
5
- 0: OTLP_SEVERITY_NUMBER.UNSPECIFIED,
6
- 1: OTLP_SEVERITY_NUMBER.INFO,
7
- 2: OTLP_SEVERITY_NUMBER.INFO2,
8
- 3: OTLP_SEVERITY_NUMBER.WARN,
9
- 4: OTLP_SEVERITY_NUMBER.ERROR,
10
- 5: OTLP_SEVERITY_NUMBER.FATAL,
11
- 6: OTLP_SEVERITY_NUMBER.FATAL4,
12
- };
13
- const SEVERITY_TEXT_MAP = {
14
- 0: 'Unknown',
15
- 1: 'Informational',
16
- 2: 'Low',
17
- 3: 'Medium',
18
- 4: 'High',
19
- 5: 'Critical',
20
- 6: 'Fatal',
21
- };
22
- export function toAnyValue(value) {
23
- if (typeof value === 'string')
24
- return { stringValue: value };
25
- if (typeof value === 'boolean')
26
- return { boolValue: value };
27
- if (typeof value === 'bigint')
28
- return { intValue: value.toString() };
29
- if (typeof value === 'number') {
30
- return Number.isSafeInteger(value) ? { intValue: String(value) } : { doubleValue: value };
31
- }
32
- if (Array.isArray(value))
33
- return { arrayValue: { values: value.map(toAnyValue) } };
34
- if (value !== null && typeof value === 'object') {
35
- const values = [];
36
- for (const [key, entry] of Object.entries(value)) {
37
- if (entry === undefined)
38
- continue;
39
- values.push({ key, value: toAnyValue(entry) });
40
- }
41
- return { kvlistValue: { values } };
42
- }
43
- return { stringValue: String(value ?? null) };
44
- }
45
- function toMillisTimeUnixNano(timeMs) {
46
- return (BigInt(Math.round(timeMs)) * 1000000n).toString();
47
- }
48
- export function encodeOCSFLogRecord(event, observedTimeMs) {
49
- const attributes = [];
50
- const attr = (key, value) => {
51
- if (value === undefined)
52
- return;
53
- attributes.push({ key, value: toAnyValue(value) });
54
- };
55
- const user = event.user ?? event.actor?.user;
56
- const sessionId = event.session?.uid ?? event.actor?.session?.uid;
57
- attr('ocsf.class_uid', event.class_uid);
58
- attr('ocsf.category_uid', event.category_uid);
59
- attr('ocsf.activity_id', event.activity_id);
60
- attr('ocsf.type_uid', event.type_uid);
61
- attr('ocsf.severity_id', event.severity_id);
62
- attr('ocsf.status_id', event.status_id);
63
- attr('user.id', user?.uid);
64
- attr('user.name', user?.name);
65
- attr('client.address', event.src_endpoint?.ip);
66
- attr('session.id', sessionId);
67
- attr('ocsf.api.operation', event.api?.operation);
68
- attr('log.record.uid', event.metadata.uid);
69
- return {
70
- timeUnixNano: toMillisTimeUnixNano(event.time),
71
- observedTimeUnixNano: toMillisTimeUnixNano(observedTimeMs),
72
- severityNumber: SEVERITY_NUMBER_MAP[event.severity_id] ?? OTLP_SEVERITY_NUMBER.UNSPECIFIED,
73
- severityText: SEVERITY_TEXT_MAP[event.severity_id],
74
- body: toAnyValue(event),
75
- attributes,
76
- };
77
- }
78
- export function buildExportLogsRequest(events, observedTimeMs = Date.now()) {
79
- const resourceAttributes = [
80
- {
81
- key: 'service.name',
82
- value: toAnyValue(process.env.OTEL_SERVICE_NAME || 'unchained-engine'),
83
- },
84
- ];
85
- if (process.env.npm_package_version) {
86
- resourceAttributes.push({
87
- key: 'service.version',
88
- value: toAnyValue(process.env.npm_package_version),
89
- });
90
- }
91
- return {
92
- resourceLogs: [
93
- {
94
- resource: { attributes: resourceAttributes },
95
- scopeLogs: [
96
- {
97
- scope: { name: 'unchained:audit' },
98
- logRecords: events.map((event) => encodeOCSFLogRecord(event, observedTimeMs)),
99
- },
100
- ],
101
- },
102
- ],
103
- };
104
- }
105
- export function parseOtlpHeaders(raw) {
106
- const headers = {};
107
- if (!raw)
108
- return headers;
109
- for (const pair of raw.split(',')) {
110
- const separatorIndex = pair.indexOf('=');
111
- if (separatorIndex <= 0)
112
- continue;
113
- const key = pair.slice(0, separatorIndex).trim();
114
- let value = pair.slice(separatorIndex + 1).trim();
115
- try {
116
- value = decodeURIComponent(value);
117
- }
118
- catch {
119
- }
120
- if (key)
121
- headers[key] = value;
122
- }
123
- return headers;
124
- }
125
- export function resolveCollectorUrl(explicit) {
126
- if (explicit)
127
- return explicit;
128
- if (process.env.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT) {
129
- return process.env.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT;
130
- }
131
- if (process.env.OTEL_EXPORTER_OTLP_ENDPOINT) {
132
- return `${process.env.OTEL_EXPORTER_OTLP_ENDPOINT.replace(/\/+$/, '')}/v1/logs`;
133
- }
134
- return undefined;
135
- }
136
- export function resolveCollectorHeaders(explicit) {
137
- return {
138
- ...parseOtlpHeaders(process.env.OTEL_EXPORTER_OTLP_HEADERS),
139
- ...parseOtlpHeaders(process.env.OTEL_EXPORTER_OTLP_LOGS_HEADERS),
140
- ...explicit,
141
- };
142
- }
143
- export async function exportLogs(collectorUrl, collectorHeaders, events) {
144
- const response = await fetch(collectorUrl, {
145
- method: 'POST',
146
- headers: {
147
- 'Content-Type': 'application/json',
148
- ...collectorHeaders,
149
- },
150
- body: JSON.stringify(buildExportLogsRequest(events)),
151
- });
152
- if (!response.ok) {
153
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
154
- }
155
- try {
156
- const body = (await response.json());
157
- const rejected = Number(body?.partialSuccess?.rejectedLogRecords || 0);
158
- if (rejected > 0) {
159
- logger.warn(`Collector rejected ${rejected} audit log records: ${body.partialSuccess?.errorMessage || 'unknown reason'}`);
160
- }
161
- }
162
- catch {
163
- }
164
- }
@@ -1,8 +0,0 @@
1
- export interface AuditRequestContext {
2
- userId?: string;
3
- userName?: string;
4
- remoteAddress?: string;
5
- sessionId?: string;
6
- }
7
- export declare function runWithAuditContext<T>(context: AuditRequestContext, fn: () => T): T;
8
- export declare function getAuditContext(): AuditRequestContext | undefined;
@@ -1,8 +0,0 @@
1
- import { AsyncLocalStorage } from 'node:async_hooks';
2
- const asyncLocalStorage = new AsyncLocalStorage();
3
- export function runWithAuditContext(context, fn) {
4
- return asyncLocalStorage.run(context, fn);
5
- }
6
- export function getAuditContext() {
7
- return asyncLocalStorage.getStore();
8
- }