@usebetterdev/audit-core 0.6.1 → 0.8.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/dist/index.cjs +348 -26
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +141 -9
- package/dist/index.d.ts +141 -9
- package/dist/index.js +334 -25
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -22,20 +22,33 @@ var index_exports = {};
|
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
AUDIT_LOG_SCHEMA: () => AUDIT_LOG_SCHEMA,
|
|
24
24
|
AuditQueryBuilder: () => AuditQueryBuilder,
|
|
25
|
+
VALID_OPERATIONS: () => VALID_OPERATIONS2,
|
|
26
|
+
VALID_SEVERITIES: () => VALID_SEVERITIES2,
|
|
27
|
+
assembleStats: () => assembleStats,
|
|
25
28
|
betterAudit: () => betterAudit,
|
|
26
29
|
createAuditApi: () => createAuditApi,
|
|
27
30
|
createAuditConsoleEndpoints: () => createAuditConsoleEndpoints,
|
|
28
31
|
createExportResponse: () => createExportResponse,
|
|
32
|
+
decodeCursor: () => decodeCursor,
|
|
33
|
+
defaultExtractor: () => defaultExtractor,
|
|
34
|
+
encodeCursor: () => encodeCursor,
|
|
35
|
+
escapeLikePattern: () => escapeLikePattern,
|
|
29
36
|
fromBearerToken: () => fromBearerToken,
|
|
30
37
|
fromCookie: () => fromCookie,
|
|
31
38
|
fromHeader: () => fromHeader,
|
|
32
39
|
getAuditContext: () => getAuditContext,
|
|
33
40
|
handleMiddleware: () => handleMiddleware,
|
|
41
|
+
interpretFilters: () => interpretFilters,
|
|
42
|
+
isAuditOperation: () => isAuditOperation,
|
|
43
|
+
isAuditSeverity: () => isAuditSeverity,
|
|
34
44
|
mergeAuditContext: () => mergeAuditContext,
|
|
35
45
|
normalizeInput: () => normalizeInput,
|
|
36
46
|
parseDuration: () => parseDuration,
|
|
47
|
+
resolveTimeFilter: () => resolveTimeFilter,
|
|
37
48
|
runExport: () => runExport,
|
|
38
|
-
runWithAuditContext: () => runWithAuditContext
|
|
49
|
+
runWithAuditContext: () => runWithAuditContext,
|
|
50
|
+
safeExtract: () => safeExtract,
|
|
51
|
+
toCount: () => toCount
|
|
39
52
|
});
|
|
40
53
|
module.exports = __toCommonJS(index_exports);
|
|
41
54
|
|
|
@@ -766,8 +779,41 @@ async function runExport(executor, options) {
|
|
|
766
779
|
return { rowCount };
|
|
767
780
|
}
|
|
768
781
|
|
|
782
|
+
// src/cursor.ts
|
|
783
|
+
var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
784
|
+
function encodeCursor(timestamp, id) {
|
|
785
|
+
const payload = JSON.stringify({ t: timestamp.toISOString(), i: id });
|
|
786
|
+
return btoa(payload);
|
|
787
|
+
}
|
|
788
|
+
function decodeCursor(cursor) {
|
|
789
|
+
let parsed;
|
|
790
|
+
try {
|
|
791
|
+
parsed = JSON.parse(atob(cursor));
|
|
792
|
+
} catch {
|
|
793
|
+
throw new Error("Invalid cursor: failed to decode");
|
|
794
|
+
}
|
|
795
|
+
if (typeof parsed !== "object" || parsed === null || !("t" in parsed) || !("i" in parsed)) {
|
|
796
|
+
throw new Error("Invalid cursor: missing required fields");
|
|
797
|
+
}
|
|
798
|
+
const record = parsed;
|
|
799
|
+
const t = record["t"];
|
|
800
|
+
const i = record["i"];
|
|
801
|
+
if (typeof t !== "string" || typeof i !== "string") {
|
|
802
|
+
throw new Error("Invalid cursor: fields must be strings");
|
|
803
|
+
}
|
|
804
|
+
const timestamp = new Date(t);
|
|
805
|
+
if (isNaN(timestamp.getTime())) {
|
|
806
|
+
throw new Error("Invalid cursor: invalid timestamp");
|
|
807
|
+
}
|
|
808
|
+
if (!UUID_PATTERN.test(i)) {
|
|
809
|
+
throw new Error("Invalid cursor: id must be a valid UUID");
|
|
810
|
+
}
|
|
811
|
+
return { timestamp, id: i };
|
|
812
|
+
}
|
|
813
|
+
|
|
769
814
|
// src/audit-api.ts
|
|
770
|
-
var
|
|
815
|
+
var SEVERITY_VALUES = ["low", "medium", "high", "critical"];
|
|
816
|
+
var VALID_SEVERITIES = new Set(SEVERITY_VALUES);
|
|
771
817
|
var VALID_OPERATIONS = /* @__PURE__ */ new Set(["INSERT", "UPDATE", "DELETE"]);
|
|
772
818
|
function toTimeFilter(date) {
|
|
773
819
|
return { date };
|
|
@@ -779,18 +825,23 @@ function buildQuerySpec(filters, effectiveLimit) {
|
|
|
779
825
|
limit
|
|
780
826
|
};
|
|
781
827
|
if (filters.tableName !== void 0) {
|
|
782
|
-
spec.filters.resource = { tableName: filters.tableName };
|
|
828
|
+
spec.filters.resource = filters.recordId !== void 0 ? { tableName: filters.tableName, recordId: filters.recordId } : { tableName: filters.tableName };
|
|
783
829
|
}
|
|
784
830
|
if (filters.actorId !== void 0) {
|
|
785
|
-
|
|
831
|
+
const raw = filters.actorId.split(",").map((v) => v.trim()).filter((v) => v.length > 0);
|
|
832
|
+
spec.filters.actorIds = [...new Set(raw)];
|
|
786
833
|
}
|
|
787
834
|
if (filters.severity !== void 0) {
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
)
|
|
835
|
+
const raw = filters.severity.split(",").map((v) => v.trim()).filter((v) => v.length > 0);
|
|
836
|
+
const unique = [...new Set(raw)];
|
|
837
|
+
for (const value of unique) {
|
|
838
|
+
if (!VALID_SEVERITIES.has(value)) {
|
|
839
|
+
throw new Error(
|
|
840
|
+
`Invalid severity "${value}". Must be one of: low, medium, high, critical`
|
|
841
|
+
);
|
|
842
|
+
}
|
|
792
843
|
}
|
|
793
|
-
spec.filters.severities =
|
|
844
|
+
spec.filters.severities = unique.filter((v) => VALID_SEVERITIES.has(v));
|
|
794
845
|
}
|
|
795
846
|
if (filters.compliance !== void 0) {
|
|
796
847
|
spec.filters.compliance = [filters.compliance];
|
|
@@ -816,6 +867,12 @@ function buildQuerySpec(filters, effectiveLimit) {
|
|
|
816
867
|
if (filters.cursor !== void 0) {
|
|
817
868
|
spec.cursor = filters.cursor;
|
|
818
869
|
}
|
|
870
|
+
if (filters.direction === "before") {
|
|
871
|
+
spec.sortOrder = "asc";
|
|
872
|
+
}
|
|
873
|
+
if (filters.order !== void 0) {
|
|
874
|
+
spec.sortOrder = filters.order;
|
|
875
|
+
}
|
|
819
876
|
return spec;
|
|
820
877
|
}
|
|
821
878
|
function createAuditApi(adapter, registry, maxQueryLimit) {
|
|
@@ -854,12 +911,66 @@ function createAuditApi(adapter, registry, maxQueryLimit) {
|
|
|
854
911
|
}
|
|
855
912
|
async function queryLogs(filters) {
|
|
856
913
|
const queryFn = requireQueryLogs();
|
|
857
|
-
const
|
|
914
|
+
const resolved = filters ?? {};
|
|
915
|
+
const spec = buildQuerySpec(resolved, effectiveLimit);
|
|
858
916
|
const result = await queryFn(spec);
|
|
917
|
+
if (resolved.direction === "before") {
|
|
918
|
+
const entries = [...result.entries].reverse();
|
|
919
|
+
const lastEntry = entries[entries.length - 1];
|
|
920
|
+
return {
|
|
921
|
+
entries,
|
|
922
|
+
// Adapter's "next" in ASC = newer entries = our prev
|
|
923
|
+
...result.nextCursor !== void 0 && { prevCursor: result.nextCursor },
|
|
924
|
+
hasPrevPage: result.nextCursor !== void 0,
|
|
925
|
+
// Since we started from a cursor, there are older entries behind it
|
|
926
|
+
...resolved.cursor !== void 0 && lastEntry !== void 0 && { nextCursor: encodeCursor(lastEntry.timestamp, lastEntry.id) },
|
|
927
|
+
hasNextPage: resolved.cursor !== void 0
|
|
928
|
+
};
|
|
929
|
+
}
|
|
930
|
+
const firstEntry = result.entries[0];
|
|
931
|
+
const prevCursor = resolved.cursor !== void 0 && firstEntry !== void 0 ? encodeCursor(firstEntry.timestamp, firstEntry.id) : void 0;
|
|
859
932
|
return {
|
|
860
933
|
entries: result.entries,
|
|
861
934
|
...result.nextCursor !== void 0 && { nextCursor: result.nextCursor },
|
|
862
|
-
hasNextPage: result.nextCursor !== void 0
|
|
935
|
+
hasNextPage: result.nextCursor !== void 0,
|
|
936
|
+
hasPrevPage: resolved.cursor !== void 0,
|
|
937
|
+
...prevCursor !== void 0 && { prevCursor }
|
|
938
|
+
};
|
|
939
|
+
}
|
|
940
|
+
async function queryLogsAround(anchorId, filters) {
|
|
941
|
+
const queryFn = requireQueryLogs();
|
|
942
|
+
const getLogFn = requireGetLogById();
|
|
943
|
+
const anchor = await getLogFn(anchorId);
|
|
944
|
+
if (anchor === null) {
|
|
945
|
+
return { entries: [], hasNextPage: false, hasPrevPage: false };
|
|
946
|
+
}
|
|
947
|
+
const resolved = filters ?? {};
|
|
948
|
+
const limit = Math.min(resolved.limit ?? effectiveLimit, effectiveLimit);
|
|
949
|
+
const remaining = Math.max(limit - 1, 0);
|
|
950
|
+
const olderCount = Math.ceil(remaining / 2);
|
|
951
|
+
const newerCount = Math.floor(remaining / 2);
|
|
952
|
+
const anchorCursor = encodeCursor(anchor.timestamp, anchor.id);
|
|
953
|
+
const { direction: _dir, ...baseFilters } = resolved;
|
|
954
|
+
const olderSpec = buildQuerySpec({ ...baseFilters, cursor: anchorCursor, limit: olderCount }, effectiveLimit);
|
|
955
|
+
olderSpec.sortOrder = "desc";
|
|
956
|
+
const newerSpec = buildQuerySpec({ ...baseFilters, cursor: anchorCursor, limit: newerCount }, effectiveLimit);
|
|
957
|
+
newerSpec.sortOrder = "asc";
|
|
958
|
+
const [olderResult, newerResult] = await Promise.all([
|
|
959
|
+
queryFn(olderSpec),
|
|
960
|
+
queryFn(newerSpec)
|
|
961
|
+
]);
|
|
962
|
+
const newerEntries = [...newerResult.entries].reverse();
|
|
963
|
+
const entries = [...newerEntries, anchor, ...olderResult.entries];
|
|
964
|
+
const hasNextPage = olderResult.nextCursor !== void 0;
|
|
965
|
+
const hasPrevPage = newerResult.nextCursor !== void 0;
|
|
966
|
+
const oldest = entries[entries.length - 1];
|
|
967
|
+
const newest = entries[0];
|
|
968
|
+
return {
|
|
969
|
+
entries,
|
|
970
|
+
hasNextPage,
|
|
971
|
+
hasPrevPage,
|
|
972
|
+
...hasNextPage && oldest !== void 0 && { nextCursor: encodeCursor(oldest.timestamp, oldest.id) },
|
|
973
|
+
...hasPrevPage && newest !== void 0 && { prevCursor: encodeCursor(newest.timestamp, newest.id) }
|
|
863
974
|
};
|
|
864
975
|
}
|
|
865
976
|
async function getLog(id) {
|
|
@@ -904,7 +1015,7 @@ function createAuditApi(adapter, registry, maxQueryLimit) {
|
|
|
904
1015
|
}
|
|
905
1016
|
async function exportLogs(filters, format) {
|
|
906
1017
|
const queryFn = requireQueryLogs();
|
|
907
|
-
const exportFormat = format ?? "
|
|
1018
|
+
const exportFormat = format ?? "csv";
|
|
908
1019
|
const spec = buildQuerySpec(filters ?? {}, effectiveLimit);
|
|
909
1020
|
const queryBuilder = new AuditQueryBuilder(
|
|
910
1021
|
(s) => queryFn(s),
|
|
@@ -930,7 +1041,7 @@ function createAuditApi(adapter, registry, maxQueryLimit) {
|
|
|
930
1041
|
const purgeFn = requirePurgeLogs();
|
|
931
1042
|
return purgeFn(options);
|
|
932
1043
|
}
|
|
933
|
-
return { queryLogs, getLog, getStats, getEnrichments, exportLogs, purgeLogs };
|
|
1044
|
+
return { queryLogs, queryLogsAround, getLog, getStats, getEnrichments, exportLogs, purgeLogs };
|
|
934
1045
|
}
|
|
935
1046
|
|
|
936
1047
|
// ../../shared/console-utils/src/index.ts
|
|
@@ -964,7 +1075,7 @@ function exceedsMaxLength(value) {
|
|
|
964
1075
|
return value !== void 0 && value.length > MAX_PARAM_LENGTH;
|
|
965
1076
|
}
|
|
966
1077
|
function hasLongQueryParam(query) {
|
|
967
|
-
return exceedsMaxLength(query.tableName) || exceedsMaxLength(query.actorId) || exceedsMaxLength(query.cursor) || exceedsMaxLength(query.operation) || exceedsMaxLength(query.severity) || exceedsMaxLength(query.compliance) || exceedsMaxLength(query.search);
|
|
1078
|
+
return exceedsMaxLength(query.tableName) || exceedsMaxLength(query.recordId) || exceedsMaxLength(query.actorId) || exceedsMaxLength(query.cursor) || exceedsMaxLength(query.operation) || exceedsMaxLength(query.severity) || exceedsMaxLength(query.compliance) || exceedsMaxLength(query.search) || exceedsMaxLength(query.around) || exceedsMaxLength(query.direction) || exceedsMaxLength(query.order);
|
|
968
1079
|
}
|
|
969
1080
|
function parseConsoleQueryFilters(query) {
|
|
970
1081
|
const filters = {};
|
|
@@ -978,6 +1089,12 @@ function parseConsoleQueryFilters(query) {
|
|
|
978
1089
|
if (query.tableName !== void 0) {
|
|
979
1090
|
filters.tableName = query.tableName;
|
|
980
1091
|
}
|
|
1092
|
+
if (query.recordId !== void 0) {
|
|
1093
|
+
if (query.tableName === void 0) {
|
|
1094
|
+
return { error: "'recordId' requires 'tableName'" };
|
|
1095
|
+
}
|
|
1096
|
+
filters.recordId = query.recordId;
|
|
1097
|
+
}
|
|
981
1098
|
if (query.operation !== void 0) {
|
|
982
1099
|
filters.operation = query.operation;
|
|
983
1100
|
}
|
|
@@ -996,6 +1113,18 @@ function parseConsoleQueryFilters(query) {
|
|
|
996
1113
|
if (query.cursor !== void 0) {
|
|
997
1114
|
filters.cursor = query.cursor;
|
|
998
1115
|
}
|
|
1116
|
+
if (query.direction !== void 0) {
|
|
1117
|
+
if (query.direction !== "after" && query.direction !== "before") {
|
|
1118
|
+
return { error: "Invalid 'direction': must be 'after' or 'before'" };
|
|
1119
|
+
}
|
|
1120
|
+
filters.direction = query.direction;
|
|
1121
|
+
}
|
|
1122
|
+
if (query.order !== void 0) {
|
|
1123
|
+
if (query.order !== "asc" && query.order !== "desc") {
|
|
1124
|
+
return { error: "Invalid 'order': must be 'asc' or 'desc'" };
|
|
1125
|
+
}
|
|
1126
|
+
filters.order = query.order;
|
|
1127
|
+
}
|
|
999
1128
|
if (query.since !== void 0) {
|
|
1000
1129
|
const since = parseIsoDate(query.since);
|
|
1001
1130
|
if (since === void 0) {
|
|
@@ -1010,6 +1139,9 @@ function parseConsoleQueryFilters(query) {
|
|
|
1010
1139
|
}
|
|
1011
1140
|
filters.until = until;
|
|
1012
1141
|
}
|
|
1142
|
+
if (filters.since !== void 0 && filters.until !== void 0 && filters.since >= filters.until) {
|
|
1143
|
+
return { error: "'since' must be before 'until'" };
|
|
1144
|
+
}
|
|
1013
1145
|
return { filters };
|
|
1014
1146
|
}
|
|
1015
1147
|
function serializeLog(log) {
|
|
@@ -1026,14 +1158,22 @@ function createAuditConsoleEndpoints(api) {
|
|
|
1026
1158
|
requiredPermission: "read",
|
|
1027
1159
|
async handler(request) {
|
|
1028
1160
|
try {
|
|
1029
|
-
|
|
1161
|
+
const query = request.query;
|
|
1162
|
+
if (hasLongQueryParam(query)) {
|
|
1030
1163
|
return { status: 400, body: { error: "Query parameter exceeds maximum length" } };
|
|
1031
1164
|
}
|
|
1032
|
-
const parsed = parseConsoleQueryFilters(
|
|
1165
|
+
const parsed = parseConsoleQueryFilters(query);
|
|
1033
1166
|
if ("error" in parsed) {
|
|
1034
1167
|
return { status: 400, body: { error: parsed.error } };
|
|
1035
1168
|
}
|
|
1036
|
-
|
|
1169
|
+
if (parsed.filters.order !== void 0 && parsed.filters.direction !== void 0) {
|
|
1170
|
+
return { status: 400, body: { error: "'order' cannot be combined with 'direction'" } };
|
|
1171
|
+
}
|
|
1172
|
+
const around = query.around;
|
|
1173
|
+
if (around !== void 0 && (parsed.filters.cursor !== void 0 || parsed.filters.direction !== void 0 || parsed.filters.order !== void 0)) {
|
|
1174
|
+
return { status: 400, body: { error: "'around' cannot be combined with 'cursor', 'direction', or 'order'" } };
|
|
1175
|
+
}
|
|
1176
|
+
const result = around !== void 0 ? await api.queryLogsAround(around, parsed.filters) : await api.queryLogs(parsed.filters);
|
|
1037
1177
|
const body = {
|
|
1038
1178
|
entries: result.entries.map(serializeLog),
|
|
1039
1179
|
hasNextPage: result.hasNextPage
|
|
@@ -1041,6 +1181,12 @@ function createAuditConsoleEndpoints(api) {
|
|
|
1041
1181
|
if (result.nextCursor !== void 0) {
|
|
1042
1182
|
body.nextCursor = result.nextCursor;
|
|
1043
1183
|
}
|
|
1184
|
+
if (result.prevCursor !== void 0) {
|
|
1185
|
+
body.prevCursor = result.prevCursor;
|
|
1186
|
+
}
|
|
1187
|
+
if (result.hasPrevPage) {
|
|
1188
|
+
body.hasPrevPage = result.hasPrevPage;
|
|
1189
|
+
}
|
|
1044
1190
|
return { status: 200, body };
|
|
1045
1191
|
} catch {
|
|
1046
1192
|
return { status: 500, body: { error: "Internal server error" } };
|
|
@@ -1073,14 +1219,28 @@ function createAuditConsoleEndpoints(api) {
|
|
|
1073
1219
|
requiredPermission: "read",
|
|
1074
1220
|
async handler(request) {
|
|
1075
1221
|
try {
|
|
1222
|
+
const query = request.query;
|
|
1223
|
+
if (exceedsMaxLength(query.since) || exceedsMaxLength(query.until)) {
|
|
1224
|
+
return { status: 400, body: { error: "Query parameter exceeds maximum length" } };
|
|
1225
|
+
}
|
|
1076
1226
|
const options = {};
|
|
1077
|
-
if (
|
|
1078
|
-
const since = parseIsoDate(
|
|
1227
|
+
if (query.since !== void 0) {
|
|
1228
|
+
const since = parseIsoDate(query.since);
|
|
1079
1229
|
if (since === void 0) {
|
|
1080
1230
|
return { status: 400, body: { error: "Invalid 'since': must be an ISO-8601 date" } };
|
|
1081
1231
|
}
|
|
1082
1232
|
options.since = since;
|
|
1083
1233
|
}
|
|
1234
|
+
if (query.until !== void 0) {
|
|
1235
|
+
const until = parseIsoDate(query.until);
|
|
1236
|
+
if (until === void 0) {
|
|
1237
|
+
return { status: 400, body: { error: "Invalid 'until': must be an ISO-8601 date" } };
|
|
1238
|
+
}
|
|
1239
|
+
options.until = until;
|
|
1240
|
+
}
|
|
1241
|
+
if (options.since !== void 0 && options.until !== void 0 && options.since >= options.until) {
|
|
1242
|
+
return { status: 400, body: { error: "'since' must be before 'until'" } };
|
|
1243
|
+
}
|
|
1084
1244
|
const stats = await api.getStats(options);
|
|
1085
1245
|
return { status: 200, body: stats };
|
|
1086
1246
|
} catch {
|
|
@@ -1107,20 +1267,33 @@ function createAuditConsoleEndpoints(api) {
|
|
|
1107
1267
|
requiredPermission: "read",
|
|
1108
1268
|
async handler(request) {
|
|
1109
1269
|
try {
|
|
1110
|
-
const
|
|
1270
|
+
const query = request.query;
|
|
1271
|
+
const format = query.format;
|
|
1111
1272
|
if (format !== void 0 && format !== "csv" && format !== "json") {
|
|
1112
1273
|
return { status: 400, body: { error: "Invalid format. Must be 'csv' or 'json'" } };
|
|
1113
1274
|
}
|
|
1114
|
-
if (hasLongQueryParam(
|
|
1275
|
+
if (hasLongQueryParam(query)) {
|
|
1115
1276
|
return { status: 400, body: { error: "Query parameter exceeds maximum length" } };
|
|
1116
1277
|
}
|
|
1117
|
-
const parsed = parseConsoleQueryFilters(
|
|
1278
|
+
const parsed = parseConsoleQueryFilters(query);
|
|
1118
1279
|
if ("error" in parsed) {
|
|
1119
1280
|
return { status: 400, body: { error: parsed.error } };
|
|
1120
1281
|
}
|
|
1121
|
-
const
|
|
1122
|
-
const data = await api.exportLogs(parsed.filters,
|
|
1123
|
-
|
|
1282
|
+
const effectiveFormat = format ?? "csv";
|
|
1283
|
+
const data = await api.exportLogs(parsed.filters, effectiveFormat);
|
|
1284
|
+
const contentType = effectiveFormat === "csv" ? "text/csv; charset=utf-8" : "application/json; charset=utf-8";
|
|
1285
|
+
const ext = effectiveFormat === "csv" ? "csv" : "json";
|
|
1286
|
+
const dateStamp = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
1287
|
+
const filename = `audit-export-${dateStamp}.${ext}`;
|
|
1288
|
+
return {
|
|
1289
|
+
status: 200,
|
|
1290
|
+
body: data,
|
|
1291
|
+
headers: {
|
|
1292
|
+
"content-type": contentType,
|
|
1293
|
+
"content-disposition": `attachment; filename="${filename}"`,
|
|
1294
|
+
"cache-control": "no-cache"
|
|
1295
|
+
}
|
|
1296
|
+
};
|
|
1124
1297
|
} catch {
|
|
1125
1298
|
return { status: 500, body: { error: "Internal server error" } };
|
|
1126
1299
|
}
|
|
@@ -1509,6 +1682,139 @@ var AUDIT_LOG_SCHEMA = {
|
|
|
1509
1682
|
}
|
|
1510
1683
|
};
|
|
1511
1684
|
|
|
1685
|
+
// src/escape.ts
|
|
1686
|
+
function escapeLikePattern(input) {
|
|
1687
|
+
return input.replace(/[%_\\]/g, "\\$&");
|
|
1688
|
+
}
|
|
1689
|
+
|
|
1690
|
+
// src/time-filter.ts
|
|
1691
|
+
function resolveTimeFilter(filter) {
|
|
1692
|
+
if ("date" in filter && filter.date !== void 0) {
|
|
1693
|
+
return filter.date;
|
|
1694
|
+
}
|
|
1695
|
+
if ("duration" in filter && filter.duration !== void 0) {
|
|
1696
|
+
return parseDuration(filter.duration);
|
|
1697
|
+
}
|
|
1698
|
+
throw new Error("TimeFilter must have either date or duration");
|
|
1699
|
+
}
|
|
1700
|
+
|
|
1701
|
+
// src/filter-builder.ts
|
|
1702
|
+
function interpretFilters(filters, options) {
|
|
1703
|
+
const conditions = [];
|
|
1704
|
+
if (filters.resource !== void 0) {
|
|
1705
|
+
conditions.push({ kind: "eq", field: "tableName", value: filters.resource.tableName });
|
|
1706
|
+
if (filters.resource.recordId !== void 0) {
|
|
1707
|
+
conditions.push({ kind: "eq", field: "recordId", value: filters.resource.recordId });
|
|
1708
|
+
}
|
|
1709
|
+
}
|
|
1710
|
+
pushArrayFilter(conditions, "actorId", filters.actorIds);
|
|
1711
|
+
pushArrayFilter(conditions, "severity", filters.severities);
|
|
1712
|
+
pushArrayFilter(conditions, "operation", filters.operations);
|
|
1713
|
+
if (filters.since !== void 0) {
|
|
1714
|
+
conditions.push({ kind: "timestampGte", value: resolveTimeFilter(filters.since) });
|
|
1715
|
+
}
|
|
1716
|
+
if (filters.until !== void 0) {
|
|
1717
|
+
conditions.push({ kind: "timestampLte", value: resolveTimeFilter(filters.until) });
|
|
1718
|
+
}
|
|
1719
|
+
if (filters.searchText !== void 0 && filters.searchText.length > 0) {
|
|
1720
|
+
const escaped = escapeLikePattern(filters.searchText);
|
|
1721
|
+
conditions.push({ kind: "search", pattern: `%${escaped}%` });
|
|
1722
|
+
}
|
|
1723
|
+
if (filters.compliance !== void 0 && filters.compliance.length > 0) {
|
|
1724
|
+
conditions.push({ kind: "compliance", tags: [...filters.compliance] });
|
|
1725
|
+
}
|
|
1726
|
+
if (options?.cursor !== void 0) {
|
|
1727
|
+
conditions.push({
|
|
1728
|
+
kind: "cursor",
|
|
1729
|
+
timestamp: options.cursor.timestamp,
|
|
1730
|
+
id: options.cursor.id,
|
|
1731
|
+
sortOrder: options.sortOrder ?? "desc"
|
|
1732
|
+
});
|
|
1733
|
+
}
|
|
1734
|
+
return conditions;
|
|
1735
|
+
}
|
|
1736
|
+
function pushArrayFilter(conditions, field, values) {
|
|
1737
|
+
if (values === void 0 || values.length === 0) {
|
|
1738
|
+
return;
|
|
1739
|
+
}
|
|
1740
|
+
if (values.length === 1) {
|
|
1741
|
+
const first = values[0];
|
|
1742
|
+
if (first !== void 0) {
|
|
1743
|
+
conditions.push({ kind: "eq", field, value: first });
|
|
1744
|
+
}
|
|
1745
|
+
} else {
|
|
1746
|
+
conditions.push({ kind: "in", field, values: [...values] });
|
|
1747
|
+
}
|
|
1748
|
+
}
|
|
1749
|
+
|
|
1750
|
+
// src/stats.ts
|
|
1751
|
+
function toCount(value) {
|
|
1752
|
+
if (typeof value === "number") {
|
|
1753
|
+
return value;
|
|
1754
|
+
}
|
|
1755
|
+
if (typeof value === "string") {
|
|
1756
|
+
const n = Number(value);
|
|
1757
|
+
return Number.isNaN(n) ? 0 : n;
|
|
1758
|
+
}
|
|
1759
|
+
if (typeof value === "bigint") {
|
|
1760
|
+
return Number(value);
|
|
1761
|
+
}
|
|
1762
|
+
return 0;
|
|
1763
|
+
}
|
|
1764
|
+
function assembleStats(summaryRows, eventsPerDayRows, topActorsRows, topTablesRows, operationRows, severityRows) {
|
|
1765
|
+
const summary = summaryRows[0];
|
|
1766
|
+
const totalLogs = summary !== void 0 ? toCount(summary.totalLogs) : 0;
|
|
1767
|
+
const tablesAudited = summary !== void 0 ? toCount(summary.tablesAudited) : 0;
|
|
1768
|
+
const eventsPerDay = eventsPerDayRows.map((row) => ({
|
|
1769
|
+
date: row.date instanceof Date ? row.date.toISOString().split("T")[0] ?? "" : String(row.date),
|
|
1770
|
+
count: toCount(row.count)
|
|
1771
|
+
}));
|
|
1772
|
+
const topActors = topActorsRows.map((row) => ({
|
|
1773
|
+
actorId: String(row.actorId),
|
|
1774
|
+
count: toCount(row.count)
|
|
1775
|
+
}));
|
|
1776
|
+
const topTables = topTablesRows.map((row) => ({
|
|
1777
|
+
tableName: String(row.tableName),
|
|
1778
|
+
count: toCount(row.count)
|
|
1779
|
+
}));
|
|
1780
|
+
const operationBreakdown = {};
|
|
1781
|
+
for (const row of operationRows) {
|
|
1782
|
+
operationBreakdown[String(row.operation)] = toCount(row.count);
|
|
1783
|
+
}
|
|
1784
|
+
const severityBreakdown = {};
|
|
1785
|
+
for (const row of severityRows) {
|
|
1786
|
+
severityBreakdown[String(row.severity)] = toCount(row.count);
|
|
1787
|
+
}
|
|
1788
|
+
return {
|
|
1789
|
+
totalLogs,
|
|
1790
|
+
tablesAudited,
|
|
1791
|
+
eventsPerDay,
|
|
1792
|
+
topActors,
|
|
1793
|
+
topTables,
|
|
1794
|
+
operationBreakdown,
|
|
1795
|
+
severityBreakdown
|
|
1796
|
+
};
|
|
1797
|
+
}
|
|
1798
|
+
|
|
1799
|
+
// src/validation.ts
|
|
1800
|
+
var VALID_OPERATIONS2 = /* @__PURE__ */ new Set([
|
|
1801
|
+
"INSERT",
|
|
1802
|
+
"UPDATE",
|
|
1803
|
+
"DELETE"
|
|
1804
|
+
]);
|
|
1805
|
+
var VALID_SEVERITIES2 = /* @__PURE__ */ new Set([
|
|
1806
|
+
"low",
|
|
1807
|
+
"medium",
|
|
1808
|
+
"high",
|
|
1809
|
+
"critical"
|
|
1810
|
+
]);
|
|
1811
|
+
function isAuditOperation(value) {
|
|
1812
|
+
return VALID_OPERATIONS2.has(value);
|
|
1813
|
+
}
|
|
1814
|
+
function isAuditSeverity(value) {
|
|
1815
|
+
return VALID_SEVERITIES2.has(value);
|
|
1816
|
+
}
|
|
1817
|
+
|
|
1512
1818
|
// src/context-extractor.ts
|
|
1513
1819
|
function decodeJwtPayload(token) {
|
|
1514
1820
|
try {
|
|
@@ -1586,6 +1892,9 @@ function fromHeader(headerName) {
|
|
|
1586
1892
|
return value.trim();
|
|
1587
1893
|
};
|
|
1588
1894
|
}
|
|
1895
|
+
var defaultExtractor = {
|
|
1896
|
+
actor: fromBearerToken("sub")
|
|
1897
|
+
};
|
|
1589
1898
|
async function safeExtract(extractor, request, onError) {
|
|
1590
1899
|
if (!extractor) {
|
|
1591
1900
|
return void 0;
|
|
@@ -1612,19 +1921,32 @@ async function handleMiddleware(extractor, request, next, options = {}) {
|
|
|
1612
1921
|
0 && (module.exports = {
|
|
1613
1922
|
AUDIT_LOG_SCHEMA,
|
|
1614
1923
|
AuditQueryBuilder,
|
|
1924
|
+
VALID_OPERATIONS,
|
|
1925
|
+
VALID_SEVERITIES,
|
|
1926
|
+
assembleStats,
|
|
1615
1927
|
betterAudit,
|
|
1616
1928
|
createAuditApi,
|
|
1617
1929
|
createAuditConsoleEndpoints,
|
|
1618
1930
|
createExportResponse,
|
|
1931
|
+
decodeCursor,
|
|
1932
|
+
defaultExtractor,
|
|
1933
|
+
encodeCursor,
|
|
1934
|
+
escapeLikePattern,
|
|
1619
1935
|
fromBearerToken,
|
|
1620
1936
|
fromCookie,
|
|
1621
1937
|
fromHeader,
|
|
1622
1938
|
getAuditContext,
|
|
1623
1939
|
handleMiddleware,
|
|
1940
|
+
interpretFilters,
|
|
1941
|
+
isAuditOperation,
|
|
1942
|
+
isAuditSeverity,
|
|
1624
1943
|
mergeAuditContext,
|
|
1625
1944
|
normalizeInput,
|
|
1626
1945
|
parseDuration,
|
|
1946
|
+
resolveTimeFilter,
|
|
1627
1947
|
runExport,
|
|
1628
|
-
runWithAuditContext
|
|
1948
|
+
runWithAuditContext,
|
|
1949
|
+
safeExtract,
|
|
1950
|
+
toCount
|
|
1629
1951
|
});
|
|
1630
1952
|
//# sourceMappingURL=index.cjs.map
|