@flipboxlabs/aws-audit-sdk 1.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 +21 -0
- package/README.md +30 -0
- package/dist/audits.d.ts +188 -0
- package/dist/audits.d.ts.map +1 -0
- package/dist/audits.js +225 -0
- package/dist/batch/batch-processor.d.ts +81 -0
- package/dist/batch/batch-processor.d.ts.map +1 -0
- package/dist/batch/batch-processor.js +102 -0
- package/dist/batch/index.d.ts +2 -0
- package/dist/batch/index.d.ts.map +1 -0
- package/dist/batch/index.js +1 -0
- package/dist/config.d.ts +65 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +44 -0
- package/dist/constants.d.ts +147 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +191 -0
- package/dist/events/batch-handler.d.ts +62 -0
- package/dist/events/batch-handler.d.ts.map +1 -0
- package/dist/events/batch-handler.js +92 -0
- package/dist/events/bus.d.ts +79 -0
- package/dist/events/bus.d.ts.map +1 -0
- package/dist/events/bus.js +86 -0
- package/dist/events/index.d.ts +3 -0
- package/dist/events/index.d.ts.map +1 -0
- package/dist/events/index.js +2 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/middleware/middy.d.ts +66 -0
- package/dist/middleware/middy.d.ts.map +1 -0
- package/dist/middleware/middy.js +105 -0
- package/dist/repository.d.ts +421 -0
- package/dist/repository.d.ts.map +1 -0
- package/dist/repository.js +486 -0
- package/dist/repository.utils.d.ts +37 -0
- package/dist/repository.utils.d.ts.map +1 -0
- package/dist/repository.utils.js +70 -0
- package/dist/schema/audit.d.ts +202 -0
- package/dist/schema/audit.d.ts.map +1 -0
- package/dist/schema/audit.js +48 -0
- package/dist/schema/common.d.ts +128 -0
- package/dist/schema/common.d.ts.map +1 -0
- package/dist/schema/common.js +148 -0
- package/dist/schema/index.d.ts +27 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/dist/schema/index.js +26 -0
- package/dist/schema/log.d.ts +211 -0
- package/dist/schema/log.d.ts.map +1 -0
- package/dist/schema/log.js +179 -0
- package/dist/schema/model.d.ts +110 -0
- package/dist/schema/model.d.ts.map +1 -0
- package/dist/schema/model.js +86 -0
- package/dist/schema/service.d.ts +189 -0
- package/dist/schema/service.d.ts.map +1 -0
- package/dist/schema/service.js +89 -0
- package/dist/schema/storage.d.ts +105 -0
- package/dist/schema/storage.d.ts.map +1 -0
- package/dist/schema/storage.js +63 -0
- package/dist/service.d.ts +285 -0
- package/dist/service.d.ts.map +1 -0
- package/dist/service.js +237 -0
- package/dist/test-config.d.ts +56 -0
- package/dist/test-config.d.ts.map +1 -0
- package/dist/test-config.js +24 -0
- package/dist/types.d.ts +40 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/dist/utils.d.ts +231 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +313 -0
- package/package.json +85 -0
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import { EventType } from "@aws-lambda-powertools/batch";
|
|
2
|
+
import type { EventSourceDataClassTypes } from "@aws-lambda-powertools/batch/types";
|
|
3
|
+
import type { PutEventsRequestEntry } from "@aws-sdk/client-eventbridge";
|
|
4
|
+
import type { DynamoDBRecord, KinesisStreamRecord, SQSRecord } from "aws-lambda";
|
|
5
|
+
import type { MessageWithAuditOverride } from "./batch/batch-processor.js";
|
|
6
|
+
import type { LogAuditInput } from "./schema/log.js";
|
|
7
|
+
import type { EventBridgeEvent } from "./schema/model.js";
|
|
8
|
+
/**
|
|
9
|
+
* Generates a random UUID for use as a trace identifier.
|
|
10
|
+
*
|
|
11
|
+
* @returns A random UUID string
|
|
12
|
+
* @deprecated Not currently used - consider using X-Ray trace IDs instead
|
|
13
|
+
*/
|
|
14
|
+
export declare function generateTraceId(): string;
|
|
15
|
+
/**
|
|
16
|
+
* Generates a unique, time-sortable audit ID using KSUID.
|
|
17
|
+
*
|
|
18
|
+
* KSUIDs (K-Sortable Unique Identifiers) are globally unique and
|
|
19
|
+
* naturally sort by creation time, making them ideal for audit logs.
|
|
20
|
+
*
|
|
21
|
+
* @returns A KSUID string
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```typescript
|
|
25
|
+
* const id = generateAuditId();
|
|
26
|
+
* // Returns something like "2KQHp1vF5AufI6Fp4pJUJYBCWKg"
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export declare function generateAuditId(): string;
|
|
30
|
+
/**
|
|
31
|
+
* Builds an audit entry from an SQS record with optional overrides.
|
|
32
|
+
*
|
|
33
|
+
* Automatically extracts trace information from the SQS message body
|
|
34
|
+
* if the audit doesn't already have a trace ID set.
|
|
35
|
+
*
|
|
36
|
+
* @param sqsRecord - The SQS record containing the message
|
|
37
|
+
* @param audit - The base audit entry to build from
|
|
38
|
+
* @param overrides - Optional overrides to merge into the audit
|
|
39
|
+
* @returns The complete audit entry with trace information
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```typescript
|
|
43
|
+
* const audit = buildAuditFromSQSRecord(sqsRecord, {
|
|
44
|
+
* operation: 'processOrder',
|
|
45
|
+
* target: { app: 'OrderService', type: 'Order', id: '123' },
|
|
46
|
+
* });
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export declare function buildAuditFromSQSRecord(sqsRecord: SQSRecord, audit: LogAuditInput, overrides?: Partial<LogAuditInput>): LogAuditInput;
|
|
50
|
+
/**
|
|
51
|
+
* Builds an audit entry by deeply merging the base audit with overrides.
|
|
52
|
+
*
|
|
53
|
+
* Messages from both the base audit and overrides are concatenated
|
|
54
|
+
* with a space separator.
|
|
55
|
+
*
|
|
56
|
+
* @param audit - The base audit entry
|
|
57
|
+
* @param overrides - Optional overrides to merge into the audit
|
|
58
|
+
* @returns The merged audit entry
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```typescript
|
|
62
|
+
* const audit = buildAudit(
|
|
63
|
+
* { operation: 'create', message: 'Creating user' },
|
|
64
|
+
* { message: 'with admin role', status: 'success' }
|
|
65
|
+
* );
|
|
66
|
+
* // Result: { operation: 'create', message: 'Creating user with admin role', status: 'success' }
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
export declare function buildAudit(audit: LogAuditInput, overrides?: Partial<LogAuditInput>): LogAuditInput;
|
|
70
|
+
/**
|
|
71
|
+
* Input type for EventBridge event normalization.
|
|
72
|
+
*/
|
|
73
|
+
type Input = Pick<PutEventsRequestEntry, "Detail" | "DetailType" | "Source">;
|
|
74
|
+
/**
|
|
75
|
+
* Normalizes an EventBridge PutEvents request entry to a simplified event format.
|
|
76
|
+
*
|
|
77
|
+
* Converts the AWS SDK format (Detail, DetailType, Source) to the internal
|
|
78
|
+
* event format (detail, detail-type, source).
|
|
79
|
+
*
|
|
80
|
+
* @param input - The EventBridge PutEvents request entry
|
|
81
|
+
* @returns The normalized event object
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* ```typescript
|
|
85
|
+
* const event = normalizeEventBridgetInput({
|
|
86
|
+
* Detail: '{"orderId": "123"}',
|
|
87
|
+
* DetailType: 'OrderCreated',
|
|
88
|
+
* Source: 'orders.service',
|
|
89
|
+
* });
|
|
90
|
+
* // Returns: { detail: '{"orderId": "123"}', 'detail-type': 'OrderCreated', source: 'orders.service' }
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
93
|
+
export declare function normalizeEventBridgetInput(input: Input): EventBridgeEvent;
|
|
94
|
+
/**
|
|
95
|
+
* Extracts audit overrides from a batch processor result.
|
|
96
|
+
*
|
|
97
|
+
* Handles various result formats from batch record handlers:
|
|
98
|
+
* - Objects with `_audit` property for explicit overrides
|
|
99
|
+
* - Objects with `message` property for simple message extraction
|
|
100
|
+
* - Non-objects or empty objects return empty overrides
|
|
101
|
+
*
|
|
102
|
+
* When both root message and audit override message exist, they are
|
|
103
|
+
* concatenated with a space separator.
|
|
104
|
+
*
|
|
105
|
+
* @param message - The result from a batch record handler
|
|
106
|
+
* @returns Partial audit input with extracted overrides
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* ```typescript
|
|
110
|
+
* // With audit overrides
|
|
111
|
+
* extractOverridesFromBatchProcessor({
|
|
112
|
+
* message: 'Processed',
|
|
113
|
+
* _audit: { status: 'success', tier: 3 }
|
|
114
|
+
* });
|
|
115
|
+
* // Returns: { status: 'success', tier: 3, message: 'Processed' }
|
|
116
|
+
*
|
|
117
|
+
* // With just message
|
|
118
|
+
* extractOverridesFromBatchProcessor({ message: 'Done' });
|
|
119
|
+
* // Returns: { message: 'Done' }
|
|
120
|
+
*
|
|
121
|
+
* // With non-object
|
|
122
|
+
* extractOverridesFromBatchProcessor('success');
|
|
123
|
+
* // Returns: {}
|
|
124
|
+
* ```
|
|
125
|
+
*/
|
|
126
|
+
export declare function extractOverridesFromBatchProcessor(message: unknown | MessageWithAuditOverride): Partial<LogAuditInput>;
|
|
127
|
+
/**
|
|
128
|
+
* Normalizes an event source record body to an EventBridge event format.
|
|
129
|
+
*
|
|
130
|
+
* Currently only supports SQS event type. For unsupported event types,
|
|
131
|
+
* returns undefined.
|
|
132
|
+
*
|
|
133
|
+
* @param eventType - The type of event source (SQS, Kinesis, DynamoDB)
|
|
134
|
+
* @param record - The event source record
|
|
135
|
+
* @returns The normalized EventBridge event, or undefined for unsupported types
|
|
136
|
+
*/
|
|
137
|
+
export declare function normalizeEventBridgetEventBody(eventType: keyof typeof EventType, record: EventSourceDataClassTypes): EventBridgeEvent | undefined;
|
|
138
|
+
/**
|
|
139
|
+
* Extracts EventBridge event data from an SQS record body.
|
|
140
|
+
*
|
|
141
|
+
* Parses the SQS message body and extracts the EventBridge-specific
|
|
142
|
+
* fields (detail, detail-type, source).
|
|
143
|
+
*
|
|
144
|
+
* @param record - The SQS record containing the message
|
|
145
|
+
* @returns The extracted EventBridge event data
|
|
146
|
+
*/
|
|
147
|
+
export declare function normalizeSQSEventBody(record: SQSRecord): EventBridgeEvent | undefined;
|
|
148
|
+
/**
|
|
149
|
+
* Parses a trace ID into its component parts.
|
|
150
|
+
*
|
|
151
|
+
* Trace IDs follow the format "id:stage" where:
|
|
152
|
+
* - id: The unique trace identifier
|
|
153
|
+
* - stage: The processing stage number (defaults to 0 if not present)
|
|
154
|
+
*
|
|
155
|
+
* @param traceId - The trace ID string to parse
|
|
156
|
+
* @returns An object with `id` and `stage` properties
|
|
157
|
+
*
|
|
158
|
+
* @example
|
|
159
|
+
* ```typescript
|
|
160
|
+
* getTraceParts("abc-123:5");
|
|
161
|
+
* // Returns: { id: "abc-123", stage: 5 }
|
|
162
|
+
*
|
|
163
|
+
* getTraceParts("abc-123");
|
|
164
|
+
* // Returns: { id: "abc-123", stage: 0 }
|
|
165
|
+
* ```
|
|
166
|
+
*/
|
|
167
|
+
export declare function getTraceParts(traceId: string): {
|
|
168
|
+
id: string;
|
|
169
|
+
stage: number;
|
|
170
|
+
};
|
|
171
|
+
/**
|
|
172
|
+
* Extracts the unique record ID for audit correlation based on event type.
|
|
173
|
+
*
|
|
174
|
+
* Each event source has a stable unique identifier that persists across retries:
|
|
175
|
+
* - SQS: `messageId` - unique per message, stable across retry deliveries
|
|
176
|
+
* - Kinesis: `eventID` - unique per record in the stream
|
|
177
|
+
* - DynamoDB Streams: `eventID` - unique per stream record
|
|
178
|
+
*
|
|
179
|
+
* @param eventType - The type of event source (SQS, KinesisDataStreams, DynamoDBStreams)
|
|
180
|
+
* @param record - The event source record
|
|
181
|
+
* @returns The unique record ID, or undefined for unsupported event types
|
|
182
|
+
*
|
|
183
|
+
* @example
|
|
184
|
+
* ```typescript
|
|
185
|
+
* // SQS record
|
|
186
|
+
* const id = getRecordId("SQS", sqsRecord);
|
|
187
|
+
* // Returns: sqsRecord.messageId
|
|
188
|
+
*
|
|
189
|
+
* // Kinesis record
|
|
190
|
+
* const id = getRecordId("KinesisDataStreams", kinesisRecord);
|
|
191
|
+
* // Returns: kinesisRecord.eventID
|
|
192
|
+
* ```
|
|
193
|
+
*/
|
|
194
|
+
export declare function getRecordId(eventType: keyof typeof EventType, record: SQSRecord | KinesisStreamRecord | DynamoDBRecord): string | undefined;
|
|
195
|
+
/**
|
|
196
|
+
* Gets the approximate receive count from an SQS record.
|
|
197
|
+
*
|
|
198
|
+
* The receive count indicates how many times the message has been delivered:
|
|
199
|
+
* - 1 = First delivery (not a retry)
|
|
200
|
+
* - 2+ = Retry delivery
|
|
201
|
+
*
|
|
202
|
+
* @param record - The SQS record
|
|
203
|
+
* @returns The receive count as a number
|
|
204
|
+
*
|
|
205
|
+
* @example
|
|
206
|
+
* ```typescript
|
|
207
|
+
* const count = getReceiveCount(sqsRecord);
|
|
208
|
+
* if (count > 1) {
|
|
209
|
+
* console.log(`This is retry attempt ${count - 1}`);
|
|
210
|
+
* }
|
|
211
|
+
* ```
|
|
212
|
+
*/
|
|
213
|
+
export declare function getReceiveCount(record: SQSRecord): number;
|
|
214
|
+
/**
|
|
215
|
+
* Checks if an SQS message is being retried.
|
|
216
|
+
*
|
|
217
|
+
* A message is considered a retry if it has been received more than once.
|
|
218
|
+
*
|
|
219
|
+
* @param record - The SQS record
|
|
220
|
+
* @returns True if this is a retry delivery, false if first delivery
|
|
221
|
+
*
|
|
222
|
+
* @example
|
|
223
|
+
* ```typescript
|
|
224
|
+
* if (isRetry(sqsRecord)) {
|
|
225
|
+
* console.log("Processing retry...");
|
|
226
|
+
* }
|
|
227
|
+
* ```
|
|
228
|
+
*/
|
|
229
|
+
export declare function isRetry(record: SQSRecord): boolean;
|
|
230
|
+
export {};
|
|
231
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AACzD,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,oCAAoC,CAAC;AACpF,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AACzE,OAAO,KAAK,EACX,cAAc,EACd,mBAAmB,EACnB,SAAS,EACT,MAAM,YAAY,CAAC;AAGpB,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AAC3E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAE1D;;;;;GAKG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAqBD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,uBAAuB,CACtC,SAAS,EAAE,SAAS,EACpB,KAAK,EAAE,aAAa,EACpB,SAAS,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,GAChC,aAAa,CAWf;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,UAAU,CACzB,KAAK,EAAE,aAAa,EACpB,SAAS,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,GAChC,aAAa,CAIf;AAED;;GAEG;AACH,KAAK,KAAK,GAAG,IAAI,CAAC,qBAAqB,EAAE,QAAQ,GAAG,YAAY,GAAG,QAAQ,CAAC,CAAC;AAE7E;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,KAAK,GAAG,gBAAgB,CAMzE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,kCAAkC,CACjD,OAAO,EAAE,OAAO,GAAG,wBAAwB,GACzC,OAAO,CAAC,aAAa,CAAC,CA6BxB;AAED;;;;;;;;;GASG;AACH,wBAAgB,8BAA8B,CAC7C,SAAS,EAAE,MAAM,OAAO,SAAS,EACjC,MAAM,EAAE,yBAAyB,GAC/B,gBAAgB,GAAG,SAAS,CAK9B;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CACpC,MAAM,EAAE,SAAS,GACf,gBAAgB,GAAG,SAAS,CAQ9B;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAO5E;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,WAAW,CAC1B,SAAS,EAAE,MAAM,OAAO,SAAS,EACjC,MAAM,EAAE,SAAS,GAAG,mBAAmB,GAAG,cAAc,GACtD,MAAM,GAAG,SAAS,CAWpB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,CAEzD;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,OAAO,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAElD"}
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { EventType } from "@aws-lambda-powertools/batch";
|
|
3
|
+
import KSUID from "ksuid";
|
|
4
|
+
import merge from "lodash.merge";
|
|
5
|
+
/**
|
|
6
|
+
* Generates a random UUID for use as a trace identifier.
|
|
7
|
+
*
|
|
8
|
+
* @returns A random UUID string
|
|
9
|
+
* @deprecated Not currently used - consider using X-Ray trace IDs instead
|
|
10
|
+
*/
|
|
11
|
+
export function generateTraceId() {
|
|
12
|
+
return randomUUID();
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Generates a unique, time-sortable audit ID using KSUID.
|
|
16
|
+
*
|
|
17
|
+
* KSUIDs (K-Sortable Unique Identifiers) are globally unique and
|
|
18
|
+
* naturally sort by creation time, making them ideal for audit logs.
|
|
19
|
+
*
|
|
20
|
+
* @returns A KSUID string
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* const id = generateAuditId();
|
|
25
|
+
* // Returns something like "2KQHp1vF5AufI6Fp4pJUJYBCWKg"
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export function generateAuditId() {
|
|
29
|
+
return `${KSUID.randomSync().string}`;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Increments the stage number in a trace ID.
|
|
33
|
+
*
|
|
34
|
+
* Trace IDs follow the format "id:stage" where stage is an integer.
|
|
35
|
+
* If no stage exists, it defaults to 1 before incrementing.
|
|
36
|
+
*
|
|
37
|
+
* @param trace - The trace in "id:stage" format
|
|
38
|
+
* @returns The trace with incremented stage number
|
|
39
|
+
*
|
|
40
|
+
* @internal
|
|
41
|
+
*/
|
|
42
|
+
function incrementTraceStage(traceId) {
|
|
43
|
+
const [id, stage] = traceId.split(":");
|
|
44
|
+
const nextStage = Number(stage == null ? 1 : stage) + 1;
|
|
45
|
+
return [id, nextStage].join(":");
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Builds an audit entry from an SQS record with optional overrides.
|
|
49
|
+
*
|
|
50
|
+
* Automatically extracts trace information from the SQS message body
|
|
51
|
+
* if the audit doesn't already have a trace ID set.
|
|
52
|
+
*
|
|
53
|
+
* @param sqsRecord - The SQS record containing the message
|
|
54
|
+
* @param audit - The base audit entry to build from
|
|
55
|
+
* @param overrides - Optional overrides to merge into the audit
|
|
56
|
+
* @returns The complete audit entry with trace information
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```typescript
|
|
60
|
+
* const audit = buildAuditFromSQSRecord(sqsRecord, {
|
|
61
|
+
* operation: 'processOrder',
|
|
62
|
+
* target: { app: 'OrderService', type: 'Order', id: '123' },
|
|
63
|
+
* });
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
export function buildAuditFromSQSRecord(sqsRecord, audit, overrides) {
|
|
67
|
+
// If a trace isn't set, try to set it from the sqs record
|
|
68
|
+
if (!audit.trace) {
|
|
69
|
+
const { detail } = JSON.parse(sqsRecord.body);
|
|
70
|
+
if (detail.transaction?.trace) {
|
|
71
|
+
audit.trace = incrementTraceStage(detail.transaction.trace);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return buildAudit(audit, overrides);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Builds an audit entry by deeply merging the base audit with overrides.
|
|
78
|
+
*
|
|
79
|
+
* Messages from both the base audit and overrides are concatenated
|
|
80
|
+
* with a space separator.
|
|
81
|
+
*
|
|
82
|
+
* @param audit - The base audit entry
|
|
83
|
+
* @param overrides - Optional overrides to merge into the audit
|
|
84
|
+
* @returns The merged audit entry
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* ```typescript
|
|
88
|
+
* const audit = buildAudit(
|
|
89
|
+
* { operation: 'create', message: 'Creating user' },
|
|
90
|
+
* { message: 'with admin role', status: 'success' }
|
|
91
|
+
* );
|
|
92
|
+
* // Result: { operation: 'create', message: 'Creating user with admin role', status: 'success' }
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
export function buildAudit(audit, overrides) {
|
|
96
|
+
const message = [audit.message, overrides?.message].filter(Boolean).join(" ");
|
|
97
|
+
return merge(audit, overrides || {}, { message });
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Normalizes an EventBridge PutEvents request entry to a simplified event format.
|
|
101
|
+
*
|
|
102
|
+
* Converts the AWS SDK format (Detail, DetailType, Source) to the internal
|
|
103
|
+
* event format (detail, detail-type, source).
|
|
104
|
+
*
|
|
105
|
+
* @param input - The EventBridge PutEvents request entry
|
|
106
|
+
* @returns The normalized event object
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* ```typescript
|
|
110
|
+
* const event = normalizeEventBridgetInput({
|
|
111
|
+
* Detail: '{"orderId": "123"}',
|
|
112
|
+
* DetailType: 'OrderCreated',
|
|
113
|
+
* Source: 'orders.service',
|
|
114
|
+
* });
|
|
115
|
+
* // Returns: { detail: '{"orderId": "123"}', 'detail-type': 'OrderCreated', source: 'orders.service' }
|
|
116
|
+
* ```
|
|
117
|
+
*/
|
|
118
|
+
export function normalizeEventBridgetInput(input) {
|
|
119
|
+
return {
|
|
120
|
+
detail: input.Detail,
|
|
121
|
+
"detail-type": input.DetailType,
|
|
122
|
+
source: input.Source,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Extracts audit overrides from a batch processor result.
|
|
127
|
+
*
|
|
128
|
+
* Handles various result formats from batch record handlers:
|
|
129
|
+
* - Objects with `_audit` property for explicit overrides
|
|
130
|
+
* - Objects with `message` property for simple message extraction
|
|
131
|
+
* - Non-objects or empty objects return empty overrides
|
|
132
|
+
*
|
|
133
|
+
* When both root message and audit override message exist, they are
|
|
134
|
+
* concatenated with a space separator.
|
|
135
|
+
*
|
|
136
|
+
* @param message - The result from a batch record handler
|
|
137
|
+
* @returns Partial audit input with extracted overrides
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
140
|
+
* ```typescript
|
|
141
|
+
* // With audit overrides
|
|
142
|
+
* extractOverridesFromBatchProcessor({
|
|
143
|
+
* message: 'Processed',
|
|
144
|
+
* _audit: { status: 'success', tier: 3 }
|
|
145
|
+
* });
|
|
146
|
+
* // Returns: { status: 'success', tier: 3, message: 'Processed' }
|
|
147
|
+
*
|
|
148
|
+
* // With just message
|
|
149
|
+
* extractOverridesFromBatchProcessor({ message: 'Done' });
|
|
150
|
+
* // Returns: { message: 'Done' }
|
|
151
|
+
*
|
|
152
|
+
* // With non-object
|
|
153
|
+
* extractOverridesFromBatchProcessor('success');
|
|
154
|
+
* // Returns: {}
|
|
155
|
+
* ```
|
|
156
|
+
*/
|
|
157
|
+
export function extractOverridesFromBatchProcessor(message) {
|
|
158
|
+
if (message && typeof message === "object") {
|
|
159
|
+
// Get the root message
|
|
160
|
+
const rootMessage = "message" in message && typeof message.message === "string"
|
|
161
|
+
? message.message
|
|
162
|
+
: undefined;
|
|
163
|
+
// Is there an audit override?
|
|
164
|
+
if ("_audit" in message) {
|
|
165
|
+
const auditOverrides = message._audit && typeof message._audit === "object"
|
|
166
|
+
? message._audit
|
|
167
|
+
: {};
|
|
168
|
+
return {
|
|
169
|
+
...auditOverrides,
|
|
170
|
+
message: [rootMessage, auditOverrides?.message]
|
|
171
|
+
.filter(Boolean)
|
|
172
|
+
.join(" "),
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
if (rootMessage) {
|
|
176
|
+
return { message: rootMessage };
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return {};
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Normalizes an event source record body to an EventBridge event format.
|
|
183
|
+
*
|
|
184
|
+
* Currently only supports SQS event type. For unsupported event types,
|
|
185
|
+
* returns undefined.
|
|
186
|
+
*
|
|
187
|
+
* @param eventType - The type of event source (SQS, Kinesis, DynamoDB)
|
|
188
|
+
* @param record - The event source record
|
|
189
|
+
* @returns The normalized EventBridge event, or undefined for unsupported types
|
|
190
|
+
*/
|
|
191
|
+
export function normalizeEventBridgetEventBody(eventType, record) {
|
|
192
|
+
switch (eventType) {
|
|
193
|
+
case EventType.SQS:
|
|
194
|
+
return normalizeSQSEventBody(record);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Extracts EventBridge event data from an SQS record body.
|
|
199
|
+
*
|
|
200
|
+
* Parses the SQS message body and extracts the EventBridge-specific
|
|
201
|
+
* fields (detail, detail-type, source).
|
|
202
|
+
*
|
|
203
|
+
* @param record - The SQS record containing the message
|
|
204
|
+
* @returns The extracted EventBridge event data
|
|
205
|
+
*/
|
|
206
|
+
export function normalizeSQSEventBody(record) {
|
|
207
|
+
const bodyObject = JSON.parse(record.body);
|
|
208
|
+
return {
|
|
209
|
+
detail: bodyObject?.detail,
|
|
210
|
+
"detail-type": bodyObject?.["detail-type"],
|
|
211
|
+
source: bodyObject?.source,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Parses a trace ID into its component parts.
|
|
216
|
+
*
|
|
217
|
+
* Trace IDs follow the format "id:stage" where:
|
|
218
|
+
* - id: The unique trace identifier
|
|
219
|
+
* - stage: The processing stage number (defaults to 0 if not present)
|
|
220
|
+
*
|
|
221
|
+
* @param traceId - The trace ID string to parse
|
|
222
|
+
* @returns An object with `id` and `stage` properties
|
|
223
|
+
*
|
|
224
|
+
* @example
|
|
225
|
+
* ```typescript
|
|
226
|
+
* getTraceParts("abc-123:5");
|
|
227
|
+
* // Returns: { id: "abc-123", stage: 5 }
|
|
228
|
+
*
|
|
229
|
+
* getTraceParts("abc-123");
|
|
230
|
+
* // Returns: { id: "abc-123", stage: 0 }
|
|
231
|
+
* ```
|
|
232
|
+
*/
|
|
233
|
+
export function getTraceParts(traceId) {
|
|
234
|
+
const [id, stage] = traceId.split(":");
|
|
235
|
+
return {
|
|
236
|
+
id,
|
|
237
|
+
stage: Number(stage == null ? 0 : stage),
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Extracts the unique record ID for audit correlation based on event type.
|
|
242
|
+
*
|
|
243
|
+
* Each event source has a stable unique identifier that persists across retries:
|
|
244
|
+
* - SQS: `messageId` - unique per message, stable across retry deliveries
|
|
245
|
+
* - Kinesis: `eventID` - unique per record in the stream
|
|
246
|
+
* - DynamoDB Streams: `eventID` - unique per stream record
|
|
247
|
+
*
|
|
248
|
+
* @param eventType - The type of event source (SQS, KinesisDataStreams, DynamoDBStreams)
|
|
249
|
+
* @param record - The event source record
|
|
250
|
+
* @returns The unique record ID, or undefined for unsupported event types
|
|
251
|
+
*
|
|
252
|
+
* @example
|
|
253
|
+
* ```typescript
|
|
254
|
+
* // SQS record
|
|
255
|
+
* const id = getRecordId("SQS", sqsRecord);
|
|
256
|
+
* // Returns: sqsRecord.messageId
|
|
257
|
+
*
|
|
258
|
+
* // Kinesis record
|
|
259
|
+
* const id = getRecordId("KinesisDataStreams", kinesisRecord);
|
|
260
|
+
* // Returns: kinesisRecord.eventID
|
|
261
|
+
* ```
|
|
262
|
+
*/
|
|
263
|
+
export function getRecordId(eventType, record) {
|
|
264
|
+
switch (eventType) {
|
|
265
|
+
case EventType.SQS:
|
|
266
|
+
return record.messageId;
|
|
267
|
+
case EventType.KinesisDataStreams:
|
|
268
|
+
return record.eventID;
|
|
269
|
+
case EventType.DynamoDBStreams:
|
|
270
|
+
return record.eventID;
|
|
271
|
+
default:
|
|
272
|
+
return undefined;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Gets the approximate receive count from an SQS record.
|
|
277
|
+
*
|
|
278
|
+
* The receive count indicates how many times the message has been delivered:
|
|
279
|
+
* - 1 = First delivery (not a retry)
|
|
280
|
+
* - 2+ = Retry delivery
|
|
281
|
+
*
|
|
282
|
+
* @param record - The SQS record
|
|
283
|
+
* @returns The receive count as a number
|
|
284
|
+
*
|
|
285
|
+
* @example
|
|
286
|
+
* ```typescript
|
|
287
|
+
* const count = getReceiveCount(sqsRecord);
|
|
288
|
+
* if (count > 1) {
|
|
289
|
+
* console.log(`This is retry attempt ${count - 1}`);
|
|
290
|
+
* }
|
|
291
|
+
* ```
|
|
292
|
+
*/
|
|
293
|
+
export function getReceiveCount(record) {
|
|
294
|
+
return Number(record.attributes.ApproximateReceiveCount);
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Checks if an SQS message is being retried.
|
|
298
|
+
*
|
|
299
|
+
* A message is considered a retry if it has been received more than once.
|
|
300
|
+
*
|
|
301
|
+
* @param record - The SQS record
|
|
302
|
+
* @returns True if this is a retry delivery, false if first delivery
|
|
303
|
+
*
|
|
304
|
+
* @example
|
|
305
|
+
* ```typescript
|
|
306
|
+
* if (isRetry(sqsRecord)) {
|
|
307
|
+
* console.log("Processing retry...");
|
|
308
|
+
* }
|
|
309
|
+
* ```
|
|
310
|
+
*/
|
|
311
|
+
export function isRetry(record) {
|
|
312
|
+
return getReceiveCount(record) > 1;
|
|
313
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@flipboxlabs/aws-audit-sdk",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "AWS Audit SDK - Core SDK for audit logging and event management",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"import": "./dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"./batch": {
|
|
13
|
+
"types": "./dist/batch/index.d.ts",
|
|
14
|
+
"import": "./dist/batch/index.js"
|
|
15
|
+
},
|
|
16
|
+
"./config": {
|
|
17
|
+
"types": "./dist/config.d.ts",
|
|
18
|
+
"import": "./dist/config.js"
|
|
19
|
+
},
|
|
20
|
+
"./middleware": {
|
|
21
|
+
"types": "./dist/middleware/middy.d.ts",
|
|
22
|
+
"import": "./dist/middleware/middy.js"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist",
|
|
27
|
+
"README.md"
|
|
28
|
+
],
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@aws-lambda-powertools/batch": "^2.32.0",
|
|
31
|
+
"@aws-lambda-powertools/commons": "^2.32.0",
|
|
32
|
+
"@aws-lambda-powertools/logger": "^2.32.0",
|
|
33
|
+
"@aws-sdk/client-dynamodb": "^3.750.0",
|
|
34
|
+
"@aws-sdk/client-eventbridge": "^3.750.0",
|
|
35
|
+
"@aws-sdk/util-dynamodb": "^3.750.0",
|
|
36
|
+
"ksuid": "^3.0.0",
|
|
37
|
+
"lodash.merge": "^4.6.2",
|
|
38
|
+
"zod": "^4.0.0"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@tsconfig/node22": "^22.0.1",
|
|
42
|
+
"@types/aws-lambda": "^8.10.156",
|
|
43
|
+
"@types/lodash.merge": "^4.6.9",
|
|
44
|
+
"@types/node": "^22.15.0",
|
|
45
|
+
"@vitest/coverage-v8": "^2.1.1",
|
|
46
|
+
"ts-node": "^10.9.2",
|
|
47
|
+
"typescript": "^5.9.3",
|
|
48
|
+
"vitest": "^2.1.1"
|
|
49
|
+
},
|
|
50
|
+
"engines": {
|
|
51
|
+
"node": ">=24"
|
|
52
|
+
},
|
|
53
|
+
"repository": {
|
|
54
|
+
"type": "git",
|
|
55
|
+
"url": "git+https://github.com/flipboxlabs/aws-audit.git",
|
|
56
|
+
"directory": "sdk"
|
|
57
|
+
},
|
|
58
|
+
"bugs": {
|
|
59
|
+
"url": "https://github.com/flipboxlabs/aws-audit/issues"
|
|
60
|
+
},
|
|
61
|
+
"homepage": "https://github.com/flipboxlabs/aws-audit#readme",
|
|
62
|
+
"license": "MIT",
|
|
63
|
+
"keywords": [
|
|
64
|
+
"aws",
|
|
65
|
+
"audit",
|
|
66
|
+
"logging",
|
|
67
|
+
"eventbridge",
|
|
68
|
+
"dynamodb",
|
|
69
|
+
"lambda",
|
|
70
|
+
"serverless"
|
|
71
|
+
],
|
|
72
|
+
"scripts": {
|
|
73
|
+
"build": "tsc -p ./tsconfig.build.json",
|
|
74
|
+
"check-types": "tsc --noEmit -p ./tsconfig.json",
|
|
75
|
+
"lint": "npx biome lint",
|
|
76
|
+
"lint:fix": "npx biome lint --fix",
|
|
77
|
+
"format": "npx biome format",
|
|
78
|
+
"format:fix": "npx biome format --fix",
|
|
79
|
+
"pre-commit": "pnpm run check-types && pnpm run format && pnpm run lint",
|
|
80
|
+
"test": "vitest run",
|
|
81
|
+
"test:watch": "vitest",
|
|
82
|
+
"test:coverage": "vitest run --coverage"
|
|
83
|
+
},
|
|
84
|
+
"types": "./dist/index.d.ts"
|
|
85
|
+
}
|