@flipboxlabs/aws-audit-sdk 1.2.0 → 1.2.3

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/dist/audits.js CHANGED
@@ -120,7 +120,7 @@ export class Audits {
120
120
  * @param item - The audit entry to buffer
121
121
  */
122
122
  addAudit(item) {
123
- if (Object.keys(this.storedAudits).length >= MAX_AUDITS_SIZE) {
123
+ if (this.storedAudits.length >= MAX_AUDITS_SIZE) {
124
124
  this.publishStoredAudits();
125
125
  }
126
126
  this.config.schemas.resourceReference.parse(item.target);
package/dist/config.d.ts CHANGED
@@ -1,4 +1,8 @@
1
1
  import * as z from "zod/v4";
2
+ /**
3
+ * Default TTL for audit records in seconds (90 days).
4
+ */
5
+ export declare const DEFAULT_TTL_SECONDS: number;
2
6
  /**
3
7
  * Input configuration for defining audit apps and resource types.
4
8
  */
@@ -6,6 +10,12 @@ export interface AuditConfigInput {
6
10
  readonly apps: readonly string[];
7
11
  readonly resourceTypes: readonly string[];
8
12
  readonly service?: string;
13
+ /**
14
+ * Time-to-live for audit records in seconds.
15
+ * DynamoDB will automatically delete records after this period.
16
+ * @default 7776000 (90 days)
17
+ */
18
+ readonly ttlSeconds?: number;
9
19
  }
10
20
  /**
11
21
  * Creates an audit configuration object with typed schemas.
@@ -33,9 +43,9 @@ export interface AuditConfigInput {
33
43
  * config.schemas.app.parse('Invalid'); // ❌ throws
34
44
  * ```
35
45
  */
36
- export declare function defineAuditConfig<const C extends AuditConfigInput>(input: C): {
37
- service: string | undefined;
38
- } & C & {
46
+ export declare function defineAuditConfig<const C extends AuditConfigInput>(input: C): C & {
47
+ ttlSeconds: number;
48
+ readonly service: string | undefined;
39
49
  schemas: {
40
50
  app: z.ZodEnum<{
41
51
  [x: string]: string;
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,QAAQ,CAAC;AAE5B;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,QAAQ,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1C,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,CAAC,CAAC,SAAS,gBAAgB,EAAE,KAAK,EAAE,CAAC;;;;;;;;;;;;;;;;;;;;YAkB1D;QACZ,GAAG,oBAAM;QACT,YAAY,6BAAe;KAC5B;EAEJ;AAED;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,QAAQ,CAAC;AAE5B;;GAEG;AACH,eAAO,MAAM,mBAAmB,QAAoB,CAAC;AAErD;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,QAAQ,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1C,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;OAIG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,CAAC,CAAC,SAAS,gBAAgB,EAAE,KAAK,EAAE,CAAC;;;;;;;;;;;;;;;;;;;;YAqB1D;QACZ,GAAG,oBAAM;QACT,YAAY,6BAAe;KAC5B;EAEJ;AAED;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC"}
package/dist/config.js CHANGED
@@ -1,4 +1,8 @@
1
1
  import * as z from "zod/v4";
2
+ /**
3
+ * Default TTL for audit records in seconds (90 days).
4
+ */
5
+ export const DEFAULT_TTL_SECONDS = 60 * 60 * 24 * 90;
2
6
  /**
3
7
  * Creates an audit configuration object with typed schemas.
4
8
  *
@@ -36,8 +40,11 @@ export function defineAuditConfig(input) {
36
40
  }),
37
41
  };
38
42
  return {
39
- service: process.env.SERVICE,
40
43
  ...input,
44
+ ttlSeconds: input.ttlSeconds ?? DEFAULT_TTL_SECONDS,
45
+ get service() {
46
+ return input.service ?? process.env.SERVICE;
47
+ },
41
48
  schemas,
42
49
  _types: {},
43
50
  };
@@ -1,7 +1,7 @@
1
1
  import type { Logger } from "@aws-lambda-powertools/logger";
2
2
  import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
3
3
  import type { NativeAttributeValue } from "@aws-sdk/util-dynamodb";
4
- import type { AuditConfig } from "./config.js";
4
+ import { type AuditConfig } from "./config.js";
5
5
  import { type Audit } from "./schema/audit.js";
6
6
  import { type Pagination } from "./schema/model.js";
7
7
  import type { UpsertAuditInput } from "./schema/service.js";
@@ -176,7 +176,7 @@ export declare class AuditRepository<C extends AuditConfig> {
176
176
  * - Timestamps (createdAt, updatedAt)
177
177
  *
178
178
  * @param items - Array of audit records to upsert
179
- * @returns True when the operation completes (does not check for unprocessed items)
179
+ * @returns True when the operation completes (retries unprocessed items with exponential backoff; throws if max retries exceeded)
180
180
  *
181
181
  * @example
182
182
  * ```typescript
@@ -197,6 +197,20 @@ export declare class AuditRepository<C extends AuditConfig> {
197
197
  * ```
198
198
  */
199
199
  upsertBatch(items: Array<UpsertAuditInput>): Promise<boolean>;
200
+ /**
201
+ * Writes a batch of items to DynamoDB with exponential backoff retry for unprocessed items.
202
+ *
203
+ * DynamoDB's BatchWriteItem can return unprocessed items when throttled or under transient
204
+ * errors without throwing an exception. This method retries those items automatically.
205
+ *
206
+ * @param items - Array of WriteRequests to send
207
+ * @param tableName - Target DynamoDB table name
208
+ * @param maxRetries - Maximum number of retry attempts (default: 8)
209
+ * @param baseDelayMs - Initial delay in milliseconds before first retry (default: 100)
210
+ * @throws Error if unprocessed items remain after maxRetries attempts
211
+ * @internal
212
+ */
213
+ private writeBatchWithRetry;
200
214
  /**
201
215
  * Upserts a single audit record with atomic attempt tracking.
202
216
  *
@@ -1 +1 @@
1
- {"version":3,"file":"repository.d.ts","sourceRoot":"","sources":["../src/repository.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"repository.d.ts","sourceRoot":"","sources":["../src/repository.ts"],"names":[],"mappings":"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"}
@@ -1,16 +1,12 @@
1
1
  import { BatchWriteItemCommand, DynamoDBClient, GetItemCommand, QueryCommand, UpdateItemCommand, } from "@aws-sdk/client-dynamodb";
2
2
  import { marshall, unmarshall } from "@aws-sdk/util-dynamodb";
3
+ import { DEFAULT_TTL_SECONDS } from "./config.js";
3
4
  import { DynamoDB } from "./constants.js";
4
5
  import { decodeNextPageToken, encodeNextPageToken } from "./repository.utils.js";
5
6
  import { AuditListItemPayloadSchema, AuditPayloadSchema, AuditSchema, } from "./schema/audit.js";
6
7
  import { PaginationCollectionSchema } from "./schema/model.js";
7
8
  import { AuditStorageSchema } from "./schema/storage.js";
8
9
  import { getTraceParts } from "./utils.js";
9
- /**
10
- * Default TTL for audit records in seconds (90 days).
11
- * Records will be automatically deleted by DynamoDB after this period.
12
- */
13
- const DEFAULT_TTL = 60 * 60 * 24 * 90; // 90 days
14
10
  /**
15
11
  * Repository for managing audit records in DynamoDB.
16
12
  *
@@ -126,7 +122,7 @@ export class AuditRepository {
126
122
  * - Timestamps (createdAt, updatedAt)
127
123
  *
128
124
  * @param items - Array of audit records to upsert
129
- * @returns True when the operation completes (does not check for unprocessed items)
125
+ * @returns True when the operation completes (retries unprocessed items with exponential backoff; throws if max retries exceeded)
130
126
  *
131
127
  * @example
132
128
  * ```typescript
@@ -165,7 +161,7 @@ export class AuditRepository {
165
161
  app: payload.target.app,
166
162
  }),
167
163
  ...this.constructSecondaryKeys(payload),
168
- ...this.constructTTLAttribute(DEFAULT_TTL),
164
+ ...this.constructTTLAttribute(this.config.ttlSeconds),
169
165
  ...payload,
170
166
  }, {
171
167
  removeUndefinedValues: true,
@@ -180,13 +176,53 @@ export class AuditRepository {
180
176
  for (let i = 0, j = Items.length; i < j; i += BATCH_SIZE) {
181
177
  ItemBatches.push(Items.slice(i, i + BATCH_SIZE));
182
178
  }
183
- await Promise.all(ItemBatches.map((ItemBatch) => this.client.send(new BatchWriteItemCommand({
184
- RequestItems: {
185
- [DynamoDB.Table.Name()]: ItemBatch,
186
- },
187
- }))));
179
+ // Process batches sequentially to reduce risk of triggering additional throttling
180
+ for (const ItemBatch of ItemBatches) {
181
+ await this.writeBatchWithRetry(ItemBatch, DynamoDB.Table.Name());
182
+ }
188
183
  return true;
189
184
  }
185
+ /**
186
+ * Writes a batch of items to DynamoDB with exponential backoff retry for unprocessed items.
187
+ *
188
+ * DynamoDB's BatchWriteItem can return unprocessed items when throttled or under transient
189
+ * errors without throwing an exception. This method retries those items automatically.
190
+ *
191
+ * @param items - Array of WriteRequests to send
192
+ * @param tableName - Target DynamoDB table name
193
+ * @param maxRetries - Maximum number of retry attempts (default: 8)
194
+ * @param baseDelayMs - Initial delay in milliseconds before first retry (default: 100)
195
+ * @throws Error if unprocessed items remain after maxRetries attempts
196
+ * @internal
197
+ */
198
+ async writeBatchWithRetry(items, tableName, maxRetries = 8, baseDelayMs = 100) {
199
+ let requestItems = { [tableName]: items };
200
+ let attempt = 0;
201
+ let delayMs = baseDelayMs;
202
+ while (attempt <= maxRetries) {
203
+ const response = await this.client.send(new BatchWriteItemCommand({ RequestItems: requestItems }));
204
+ const unprocessed = response.UnprocessedItems;
205
+ if (!unprocessed || Object.keys(unprocessed).length === 0) {
206
+ return; // all items written successfully
207
+ }
208
+ attempt++;
209
+ if (attempt > maxRetries) {
210
+ this.logger.error("BatchWriteItem: unprocessed items remain after max retries", {
211
+ unprocessed,
212
+ attempt,
213
+ });
214
+ throw new Error(`BatchWriteItem failed: unprocessed items after ${maxRetries} retries`);
215
+ }
216
+ this.logger.warn("BatchWriteItem: retrying unprocessed items", {
217
+ count: Object.values(unprocessed).flat().length,
218
+ attempt,
219
+ delayMs,
220
+ });
221
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
222
+ delayMs = Math.min(delayMs * 2, 30_000); // cap at 30 seconds
223
+ requestItems = unprocessed;
224
+ }
225
+ }
190
226
  /**
191
227
  * Upserts a single audit record with atomic attempt tracking.
192
228
  *
@@ -223,7 +259,7 @@ export class AuditRepository {
223
259
  app: payload.target.app,
224
260
  });
225
261
  const secondaryKeys = this.constructSecondaryKeys(payload);
226
- const ttlAttribute = this.constructTTLAttribute(DEFAULT_TTL);
262
+ const ttlAttribute = this.constructTTLAttribute(this.config.ttlSeconds);
227
263
  const expressionAttributeNames = {};
228
264
  const expressionAttributeValues = {
229
265
  ":emptyList": [],
@@ -504,7 +540,7 @@ export class AuditRepository {
504
540
  * @returns TTL attribute with Unix timestamp in seconds
505
541
  * @internal
506
542
  */
507
- constructTTLAttribute(ttlSeconds = DEFAULT_TTL, date) {
543
+ constructTTLAttribute(ttlSeconds = DEFAULT_TTL_SECONDS, date) {
508
544
  const ttl = date || new Date();
509
545
  if (ttlSeconds) {
510
546
  ttl.setSeconds(ttl.getSeconds() + ttlSeconds);
@@ -524,11 +560,11 @@ export class AuditRepository {
524
560
  * @internal
525
561
  */
526
562
  transformItem(item) {
527
- return Promise.resolve(AuditSchema.parse({
563
+ return AuditSchema.parse({
528
564
  trace: `${item.GSI1_SN_PK}:${item.GSI1_SN_SK}`,
529
565
  ...item,
530
566
  id: item.id.split("#").pop(),
531
- }));
567
+ });
532
568
  }
533
569
  /**
534
570
  * Transforms a trace listing item into an audit list item payload.
@@ -1,34 +1,33 @@
1
1
  /**
2
- * Encodes a pagination object into an encrypted token string.
2
+ * Encodes a pagination object into a base64url token string.
3
3
  *
4
- * Uses AES-256-CTR encryption to obfuscate the DynamoDB LastEvaluatedKey,
5
- * preventing clients from tampering with pagination state.
6
- *
7
- * The token format is: `{iv}:{encryptedData}` where both parts are hex-encoded.
4
+ * Pagination tokens are opaque cursors representing DynamoDB LastEvaluatedKey
5
+ * objects. They do not require confidentiality — base64url encoding provides
6
+ * a compact, URL-safe representation.
8
7
  *
9
8
  * @param nextPageObject - DynamoDB LastEvaluatedKey object to encode
10
- * @returns Encrypted pagination token string, or undefined if input is null/undefined
9
+ * @returns Base64url-encoded pagination token string, or undefined if input is null/undefined
11
10
  *
12
11
  * @example
13
12
  * ```typescript
14
13
  * const lastKey = { PK: 'App1.USER', SK: 'audit-123' };
15
14
  * const token = encodeNextPageToken(lastKey);
16
- * // Returns something like: "a1b2c3d4...:e5f6g7h8..."
15
+ * // Returns a base64url string like: "eyJQSyI6IkFwcDEuVVNFUiIsIlNLIjoiYXVkaXQtMTIzIn0"
17
16
  * ```
18
17
  */
19
18
  export declare function encodeNextPageToken(nextPageObject?: Record<string, string>): string | undefined;
20
19
  /**
21
- * Decodes an encrypted pagination token back into a DynamoDB key object.
20
+ * Decodes a base64url pagination token back into a DynamoDB key object.
22
21
  *
23
- * Reverses the encryption performed by {@link encodeNextPageToken} to
22
+ * Reverses the encoding performed by {@link encodeNextPageToken} to
24
23
  * restore the original LastEvaluatedKey for DynamoDB queries.
25
24
  *
26
- * @param nextPageString - Encrypted pagination token from a previous response
25
+ * @param nextPageString - Base64url-encoded pagination token from a previous response
27
26
  * @returns Decoded DynamoDB key object, or undefined if input is null/undefined/invalid
28
27
  *
29
28
  * @example
30
29
  * ```typescript
31
- * const token = "a1b2c3d4...:e5f6g7h8...";
30
+ * const token = "eyJQSyI6IkFwcDEuVVNFUiIsIlNLIjoiYXVkaXQtMTIzIn0";
32
31
  * const lastKey = decodeNextPageToken(token);
33
32
  * // Returns: { PK: 'App1.USER', SK: 'audit-123' }
34
33
  * ```
@@ -1 +1 @@
1
- {"version":3,"file":"repository.utils.d.ts","sourceRoot":"","sources":["../src/repository.utils.ts"],"names":[],"mappings":"AAaA;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,mBAAmB,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,GAAG,SAAS,CAgB/F;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,mBAAmB,CACjC,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GACzC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAuBpC"}
1
+ {"version":3,"file":"repository.utils.d.ts","sourceRoot":"","sources":["../src/repository.utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,mBAAmB,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,GAAG,SAAS,CAM/F;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,mBAAmB,CACjC,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GACzC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAMpC"}
@@ -1,70 +1,45 @@
1
- import * as crypto from "node:crypto";
2
1
  /**
3
- * Encryption algorithm used for pagination tokens.
4
- * AES-256-CTR provides symmetric encryption with counter mode.
5
- */
6
- const ENCRYPTION_ALGORITHM = "aes-256-ctr";
7
- /**
8
- * Key size in bytes for AES-256 encryption.
9
- */
10
- const KEY_SIZE = 32;
11
- /**
12
- * Encodes a pagination object into an encrypted token string.
2
+ * Encodes a pagination object into a base64url token string.
13
3
  *
14
- * Uses AES-256-CTR encryption to obfuscate the DynamoDB LastEvaluatedKey,
15
- * preventing clients from tampering with pagination state.
16
- *
17
- * The token format is: `{iv}:{encryptedData}` where both parts are hex-encoded.
4
+ * Pagination tokens are opaque cursors representing DynamoDB LastEvaluatedKey
5
+ * objects. They do not require confidentiality — base64url encoding provides
6
+ * a compact, URL-safe representation.
18
7
  *
19
8
  * @param nextPageObject - DynamoDB LastEvaluatedKey object to encode
20
- * @returns Encrypted pagination token string, or undefined if input is null/undefined
9
+ * @returns Base64url-encoded pagination token string, or undefined if input is null/undefined
21
10
  *
22
11
  * @example
23
12
  * ```typescript
24
13
  * const lastKey = { PK: 'App1.USER', SK: 'audit-123' };
25
14
  * const token = encodeNextPageToken(lastKey);
26
- * // Returns something like: "a1b2c3d4...:e5f6g7h8..."
15
+ * // Returns a base64url string like: "eyJQSyI6IkFwcDEuVVNFUiIsIlNLIjoiYXVkaXQtMTIzIn0"
27
16
  * ```
28
17
  */
29
18
  export function encodeNextPageToken(nextPageObject) {
30
19
  if (nextPageObject == null) {
31
20
  return undefined;
32
21
  }
33
- const nextPageString = JSON.stringify(nextPageObject);
34
- const iv = crypto.randomBytes(16);
35
- const cipher = crypto.createCipheriv(ENCRYPTION_ALGORITHM, Buffer.concat([Buffer.alloc(KEY_SIZE)], KEY_SIZE), iv);
36
- const encrypted = cipher.update(nextPageString);
37
- return [iv.toString("hex"), encrypted.toString("hex")].join(":");
22
+ return Buffer.from(JSON.stringify(nextPageObject)).toString("base64url");
38
23
  }
39
24
  /**
40
- * Decodes an encrypted pagination token back into a DynamoDB key object.
25
+ * Decodes a base64url pagination token back into a DynamoDB key object.
41
26
  *
42
- * Reverses the encryption performed by {@link encodeNextPageToken} to
27
+ * Reverses the encoding performed by {@link encodeNextPageToken} to
43
28
  * restore the original LastEvaluatedKey for DynamoDB queries.
44
29
  *
45
- * @param nextPageString - Encrypted pagination token from a previous response
30
+ * @param nextPageString - Base64url-encoded pagination token from a previous response
46
31
  * @returns Decoded DynamoDB key object, or undefined if input is null/undefined/invalid
47
32
  *
48
33
  * @example
49
34
  * ```typescript
50
- * const token = "a1b2c3d4...:e5f6g7h8...";
35
+ * const token = "eyJQSyI6IkFwcDEuVVNFUiIsIlNLIjoiYXVkaXQtMTIzIn0";
51
36
  * const lastKey = decodeNextPageToken(token);
52
37
  * // Returns: { PK: 'App1.USER', SK: 'audit-123' }
53
38
  * ```
54
39
  */
55
40
  export function decodeNextPageToken(nextPageString) {
56
- if (nextPageString == null) {
57
- return undefined;
58
- }
59
- const textParts = nextPageString.split(":");
60
- const iv = textParts.shift();
61
- if (!iv) {
41
+ if (nextPageString == null || nextPageString === "") {
62
42
  return undefined;
63
43
  }
64
- const decipher = crypto.createDecipheriv(ENCRYPTION_ALGORITHM, Buffer.concat([Buffer.alloc(KEY_SIZE)], KEY_SIZE), Buffer.from(iv, "hex"));
65
- const decrypted = Buffer.concat([
66
- decipher.update(Buffer.from(textParts.join(":"), "hex")),
67
- decipher.final(),
68
- ]);
69
- return JSON.parse(decrypted.toString());
44
+ return JSON.parse(Buffer.from(nextPageString, "base64url").toString());
70
45
  }
@@ -3,11 +3,11 @@
3
3
  * Provides sample apps and resource types for testing.
4
4
  */
5
5
  export declare const testConfig: {
6
- service: string | undefined;
7
- } & {
8
6
  readonly apps: readonly ["App1", "TestApp"];
9
7
  readonly resourceTypes: readonly ["Unknown", "User", "Order"];
10
8
  } & {
9
+ ttlSeconds: number;
10
+ readonly service: string | undefined;
11
11
  schemas: {
12
12
  app: import("zod/v4").ZodEnum<{
13
13
  [x: string]: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flipboxlabs/aws-audit-sdk",
3
- "version": "1.2.0",
3
+ "version": "1.2.3",
4
4
  "description": "AWS Audit SDK - Core SDK for audit logging and event management",
5
5
  "keywords": [
6
6
  "audit",