@ya-accelerators/web-backend-framework 0.0.100 → 0.0.101
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/@types/express.d.ts +0 -4
- package/dist/connector/google-auth/index.js +0 -30
- package/dist/connector/google-auth/index.js.map +1 -1
- package/dist/constants.d.ts +7 -0
- package/dist/constants.js +71 -4
- package/dist/constants.js.map +1 -1
- package/dist/context/audit/index.js.map +1 -1
- package/dist/context/request/index.js +0 -15
- package/dist/context/request/index.js.map +1 -1
- package/dist/coordination/lock/index.d.ts +0 -8
- package/dist/coordination/lock/index.js +0 -29
- package/dist/coordination/lock/index.js.map +1 -1
- package/dist/coordination/rate-limit/index.d.ts +0 -5
- package/dist/coordination/rate-limit/index.js +0 -23
- package/dist/coordination/rate-limit/index.js.map +1 -1
- package/dist/coordination/throttle/index.d.ts +0 -13
- package/dist/coordination/throttle/index.js +0 -13
- package/dist/coordination/throttle/index.js.map +1 -1
- package/dist/env.d.ts +1 -1
- package/dist/env.js +5 -15
- package/dist/env.js.map +1 -1
- package/dist/internal/federation/index.d.ts +1 -1
- package/dist/internal/federation/index.js +0 -20
- package/dist/internal/federation/index.js.map +1 -1
- package/dist/logger/index.js +0 -18
- package/dist/logger/index.js.map +1 -1
- package/dist/repository/prisma/index.js +5 -7
- package/dist/repository/prisma/index.js.map +1 -1
- package/dist/repository/prisma/lifecycle.js +1 -28
- package/dist/repository/prisma/lifecycle.js.map +1 -1
- package/dist/repository/prisma/rds-log-record.d.ts +10 -0
- package/dist/repository/prisma/rds-log-record.js +20 -0
- package/dist/repository/prisma/rds-log-record.js.map +1 -0
- package/dist/repository/redis/index.js +0 -4
- package/dist/repository/redis/index.js.map +1 -1
- package/dist/repository/s3/index.js +1 -25
- package/dist/repository/s3/index.js.map +1 -1
- package/dist/server/express/index.js +13 -51
- package/dist/server/express/index.js.map +1 -1
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.js +1 -0
- package/dist/server/index.js.map +1 -1
- package/dist/server/trpc/declaration/index.d.ts +38 -0
- package/dist/server/trpc/declaration/index.js +2 -0
- package/dist/server/trpc/declaration/index.js.map +1 -0
- package/dist/server/trpc/index.d.ts +10 -9
- package/dist/server/trpc/index.js +12 -28
- package/dist/server/trpc/index.js.map +1 -1
- package/dist/server/trpc/schema/index.js +0 -10
- package/dist/server/trpc/schema/index.js.map +1 -1
- package/dist/server/uploads/index.js +8 -9
- package/dist/server/uploads/index.js.map +1 -1
- package/dist/util/index.js +87 -96
- package/dist/util/index.js.map +1 -1
- package/package.json +13 -9
package/dist/server/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,oBAAoB,CAAA;AAC7C,OAAO,KAAK,IAAI,MAAM,iBAAiB,CAAA;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAA;AAE5C,cAAc,uBAAuB,CAAA;AACrC,cAAc,wBAAwB,CAAA;AAEtC,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,OAAO;IACP,IAAI;IACJ,OAAO,EAAE,EAAE,OAAO,EAAE;CACrB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,oBAAoB,CAAA;AAC7C,OAAO,KAAK,IAAI,MAAM,iBAAiB,CAAA;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAA;AAE5C,cAAc,6BAA6B,CAAA;AAC3C,cAAc,uBAAuB,CAAA;AACrC,cAAc,wBAAwB,CAAA;AAEtC,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,OAAO;IACP,IAAI;IACJ,OAAO,EAAE,EAAE,OAAO,EAAE;CACrB,CAAA"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export declare namespace Auth {
|
|
2
|
+
type Tier<Name extends string> = {
|
|
3
|
+
readonly kind: 'tier';
|
|
4
|
+
readonly name: Name;
|
|
5
|
+
};
|
|
6
|
+
type Upstream<Reason extends string> = {
|
|
7
|
+
readonly kind: 'upstream';
|
|
8
|
+
readonly reason: Reason;
|
|
9
|
+
};
|
|
10
|
+
type None<Reason extends string> = {
|
|
11
|
+
readonly kind: 'auth-none';
|
|
12
|
+
readonly reason: Reason;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export declare namespace Ropa {
|
|
16
|
+
type Activity<Key extends string> = {
|
|
17
|
+
readonly kind: 'activity';
|
|
18
|
+
readonly key: Key;
|
|
19
|
+
};
|
|
20
|
+
type None<Reason extends string> = {
|
|
21
|
+
readonly kind: 'ropa-none';
|
|
22
|
+
readonly reason: Reason;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export type AuthDeclaration = Auth.Tier<string> | Auth.Upstream<string> | Auth.None<string>;
|
|
26
|
+
export type RopaDeclaration<Key extends string> = Ropa.Activity<Key> | Ropa.None<string>;
|
|
27
|
+
export type DeclarationRequired = {
|
|
28
|
+
readonly __declarationRequired: never;
|
|
29
|
+
};
|
|
30
|
+
type ParameterOf<Declaration> = Declaration extends Auth.Tier<infer Name> ? Name : Declaration extends Ropa.Activity<infer Key> ? Key : Declaration extends {
|
|
31
|
+
readonly reason: infer Reason;
|
|
32
|
+
} ? Reason : never;
|
|
33
|
+
type IsDeclared<Declaration> = [Declaration] extends [never] ? false : [ParameterOf<Declaration>] extends [never] ? false : string extends ParameterOf<Declaration> ? false : true;
|
|
34
|
+
export type DeclarationArgs<A, R> = IsDeclared<A> extends true ? (IsDeclared<R> extends true ? [] : [DeclarationRequired]) : [DeclarationRequired];
|
|
35
|
+
export type DeclarableProcedure<TProcedure, TRopaKey extends string> = {
|
|
36
|
+
declare<A extends AuthDeclaration = never, R extends RopaDeclaration<TRopaKey> = never>(...undeclared: DeclarationArgs<A, R>): TProcedure;
|
|
37
|
+
};
|
|
38
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/server/trpc/declaration/index.ts"],"names":[],"mappings":""}
|
|
@@ -2,6 +2,7 @@ import { type AnyTRPCProcedure, type AnyTRPCRouter } from '@trpc/server';
|
|
|
2
2
|
import * as trpcExpress from '@trpc/server/adapters/express';
|
|
3
3
|
import type express from 'express';
|
|
4
4
|
import superjson from 'superjson';
|
|
5
|
+
import type { DeclarableProcedure } from './declaration/index.ts';
|
|
5
6
|
declare const createRequestContext: (opts: trpcExpress.CreateExpressContextOptions) => {
|
|
6
7
|
requestHeaders: import("node:http").IncomingHttpHeaders;
|
|
7
8
|
ip: string | undefined;
|
|
@@ -13,14 +14,14 @@ type UploadProcedure = {
|
|
|
13
14
|
url: string;
|
|
14
15
|
}>) => AnyTRPCProcedure;
|
|
15
16
|
};
|
|
16
|
-
export declare function generator<TContext extends object = EmptyObject>(options?: {
|
|
17
|
+
export declare function generator<TContext extends object = EmptyObject, TRopaKey extends string = never>(options?: {
|
|
17
18
|
customRoutes?: (app: express.Express) => void;
|
|
18
19
|
resolveAuditActor?: (ctx: RequestContext & TContext) => Promise<{
|
|
19
20
|
actorUserId?: string;
|
|
20
21
|
}> | {
|
|
21
22
|
actorUserId?: string;
|
|
22
23
|
};
|
|
23
|
-
}): Generator<import("@trpc/server").TRPCRootObject<{
|
|
24
|
+
}): Generator<Omit<import("@trpc/server").TRPCRootObject<{
|
|
24
25
|
requestHeaders: import("node:http").IncomingHttpHeaders;
|
|
25
26
|
ip: string | undefined;
|
|
26
27
|
} & TContext extends infer T ? T extends {
|
|
@@ -85,14 +86,14 @@ export declare function generator<TContext extends object = EmptyObject>(options
|
|
|
85
86
|
};
|
|
86
87
|
};
|
|
87
88
|
transformer: true;
|
|
88
|
-
}> & {
|
|
89
|
-
procedure: import("@trpc/server").TRPCProcedureBuilder<{
|
|
89
|
+
}>, "procedure"> & {
|
|
90
|
+
procedure: DeclarableProcedure<import("@trpc/server").TRPCProcedureBuilder<{
|
|
90
91
|
requestHeaders: import("node:http").IncomingHttpHeaders;
|
|
91
92
|
ip: string | undefined;
|
|
92
93
|
} & TContext extends infer T_1 ? T_1 extends {
|
|
93
94
|
requestHeaders: import("node:http").IncomingHttpHeaders;
|
|
94
95
|
ip: string | undefined;
|
|
95
|
-
} & TContext ? T_1 extends (...args: any[]) => object | Promise<object> ? import("@trpc/server/unstable-core-do-not-import").Unwrap<T_1> : T_1 : never : never, object, {}, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, false>;
|
|
96
|
+
} & TContext ? T_1 extends (...args: any[]) => object | Promise<object> ? import("@trpc/server/unstable-core-do-not-import").Unwrap<T_1> : T_1 : never : never, object, {}, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, false>, TRopaKey>;
|
|
96
97
|
router: <T_1 extends Parameters<import("@trpc/server").TRPCRouterBuilder<{
|
|
97
98
|
ctx: {
|
|
98
99
|
requestHeaders: import("node:http").IncomingHttpHeaders;
|
|
@@ -153,7 +154,7 @@ export declare function generator<TContext extends object = EmptyObject>(options
|
|
|
153
154
|
};
|
|
154
155
|
}>>;
|
|
155
156
|
uploadProcedure: (procedure: UploadProcedure, bucket: string) => void;
|
|
156
|
-
}, import("@trpc/server").TRPCRootObject<{
|
|
157
|
+
}, Omit<import("@trpc/server").TRPCRootObject<{
|
|
157
158
|
requestHeaders: import("node:http").IncomingHttpHeaders;
|
|
158
159
|
ip: string | undefined;
|
|
159
160
|
} & TContext extends infer T ? T extends {
|
|
@@ -218,14 +219,14 @@ export declare function generator<TContext extends object = EmptyObject>(options
|
|
|
218
219
|
};
|
|
219
220
|
};
|
|
220
221
|
transformer: true;
|
|
221
|
-
}> & {
|
|
222
|
-
procedure: import("@trpc/server").TRPCProcedureBuilder<{
|
|
222
|
+
}>, "procedure"> & {
|
|
223
|
+
procedure: DeclarableProcedure<import("@trpc/server").TRPCProcedureBuilder<{
|
|
223
224
|
requestHeaders: import("node:http").IncomingHttpHeaders;
|
|
224
225
|
ip: string | undefined;
|
|
225
226
|
} & TContext extends infer T_1 ? T_1 extends {
|
|
226
227
|
requestHeaders: import("node:http").IncomingHttpHeaders;
|
|
227
228
|
ip: string | undefined;
|
|
228
|
-
} & TContext ? T_1 extends (...args: any[]) => object | Promise<object> ? import("@trpc/server/unstable-core-do-not-import").Unwrap<T_1> : T_1 : never : never, object, {}, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, false>;
|
|
229
|
+
} & TContext ? T_1 extends (...args: any[]) => object | Promise<object> ? import("@trpc/server/unstable-core-do-not-import").Unwrap<T_1> : T_1 : never : never, object, {}, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, false>, TRopaKey>;
|
|
229
230
|
router: <T_1 extends Parameters<import("@trpc/server").TRPCRouterBuilder<{
|
|
230
231
|
ctx: {
|
|
231
232
|
requestHeaders: import("node:http").IncomingHttpHeaders;
|
|
@@ -11,9 +11,6 @@ import { Util } from '../../util/index.js';
|
|
|
11
11
|
import { createApp } from '../express/index.js';
|
|
12
12
|
import { mint } from '../uploads/index.js';
|
|
13
13
|
import { throwForbiddenError } from './error/index.js';
|
|
14
|
-
// Cap the auto-propagated audit `reason` (a free-form, un-erasable `Event<Domain>Lifecycle.reason`
|
|
15
|
-
// `@db.Text` column) so a client cannot stuff it into an unbounded audit-log / PII-injection sink.
|
|
16
|
-
const MAX_AUDIT_REASON_LENGTH = 512;
|
|
17
14
|
const createRequestContext = (opts) => {
|
|
18
15
|
const requestHeaders = opts.req.headers;
|
|
19
16
|
return {
|
|
@@ -67,20 +64,7 @@ export function* generator(options = {}) {
|
|
|
67
64
|
const actor = resolveAuditActor
|
|
68
65
|
? await resolveAuditActor(opts.ctx)
|
|
69
66
|
: { actorUserId: undefined };
|
|
70
|
-
|
|
71
|
-
// `baseProcedure` runs BEFORE the `.input()` parser (tRPC parses input at its own position in the
|
|
72
|
-
// middleware chain), so only the raw pre-validation body is reachable here — never the Zod-validated
|
|
73
|
-
// input. Accept `reason` solely as a top-level string and cap its length. The cap bounds the audit
|
|
74
|
-
// column's SIZE only — it does NOT strip PII from the content; keeping `reason` free of subject
|
|
75
|
-
// identifiers is the caller's responsibility (coding-style.md § Pseudonymization: free-form text
|
|
76
|
-
// fields must not contain PII, callers strip before writing), and matters because this column is
|
|
77
|
-
// un-erasable (not a `/// PII:` field, so an `/// erase: anonymize` pass never nulls it). A
|
|
78
|
-
// procedure needing a longer / structured reason writes it explicitly.
|
|
79
|
-
const rawReason = rawInput && typeof rawInput === 'object' && 'reason' in rawInput && typeof rawInput.reason === 'string'
|
|
80
|
-
? rawInput.reason
|
|
81
|
-
: undefined;
|
|
82
|
-
const reason = rawReason === undefined ? undefined : rawReason.slice(0, MAX_AUDIT_REASON_LENGTH);
|
|
83
|
-
return runWithAuditActor({ actorUserId: actor.actorUserId, reason }, async () => {
|
|
67
|
+
return runWithAuditActor({ actorUserId: actor.actorUserId }, async () => {
|
|
84
68
|
return await runWithRequestPath(opts.path, async () => {
|
|
85
69
|
const startTime = Date.now();
|
|
86
70
|
const contextWithTiming = opts.ctx;
|
|
@@ -89,17 +73,14 @@ export function* generator(options = {}) {
|
|
|
89
73
|
if (typeof result === 'object' && result !== null && 'error' in result && result.error instanceof Error) {
|
|
90
74
|
throw result.error;
|
|
91
75
|
}
|
|
92
|
-
// Deliberately carries NO payload. This fires on every request, and key-based redaction cannot
|
|
93
|
-
// keep up with an arbitrary payload: `Util.sanitize` masks by key name, so every schema column the
|
|
94
|
-
// keyword list has not caught yet passes through — subject references, address parts, anything a
|
|
95
|
-
// fork adds tomorrow. Spreading the context dragged `requestHeaders` in on top of that. `path` is
|
|
96
|
-
// injected by the logger from the request scope, so path + type + duration is what an incident is
|
|
97
|
-
// actually grepped by; the values themselves are recoverable from the database, not needed here.
|
|
98
76
|
logger.debug('operation', { type: opts.type, duration: Date.now() - startTime });
|
|
99
77
|
return result;
|
|
100
78
|
});
|
|
101
79
|
});
|
|
102
80
|
});
|
|
81
|
+
const declarableProcedure = {
|
|
82
|
+
declare: () => baseProcedure,
|
|
83
|
+
};
|
|
103
84
|
let uploadProcedure;
|
|
104
85
|
let uploadBucket;
|
|
105
86
|
let routerBuilt = false;
|
|
@@ -112,14 +93,17 @@ export function* generator(options = {}) {
|
|
|
112
93
|
if ('file' in routes) {
|
|
113
94
|
throw new Error("router namespace 'file' is reserved by the framework upload procedure");
|
|
114
95
|
}
|
|
115
|
-
// `file.upload`'s gating procedure is frozen here at router-build time, so `uploadProcedure` MUST be
|
|
116
|
-
// set first. Flag the build so a later `trpc.uploadProcedure(...)` throws instead of silently leaving
|
|
117
|
-
// the mutation on the unauthenticated `baseProcedure`.
|
|
118
96
|
routerBuilt = true;
|
|
119
|
-
return baseRouter({
|
|
97
|
+
return baseRouter({
|
|
98
|
+
...routes,
|
|
99
|
+
file: {
|
|
100
|
+
upload: (uploadProcedure ??
|
|
101
|
+
declarableProcedure.declare()).mutation(uploadHandler),
|
|
102
|
+
},
|
|
103
|
+
});
|
|
120
104
|
};
|
|
121
105
|
const enhancedTrpc = tprc;
|
|
122
|
-
enhancedTrpc.procedure =
|
|
106
|
+
enhancedTrpc.procedure = declarableProcedure;
|
|
123
107
|
enhancedTrpc.router = wrappedRouter;
|
|
124
108
|
enhancedTrpc.uploadProcedure = (procedure, bucket) => {
|
|
125
109
|
if (routerBuilt) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/server/trpc/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAC9C,OAAO,EAA6C,QAAQ,EAAE,MAAM,cAAc,CAAA;AAClF,OAAO,KAAK,WAAW,MAAM,+BAA+B,CAAA;AAE5D,OAAO,SAAS,MAAM,WAAW,CAAA;AAEjC,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAA;AAChE,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAA;AACnE,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAA;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,kCAAkC,CAAA;AACzD,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAA;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAC/C,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/server/trpc/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -26,16 +26,10 @@ export const JapaneseInvoiceNumberSchema = z
|
|
|
26
26
|
export const JapaneseZipCodeSchema = z
|
|
27
27
|
.string()
|
|
28
28
|
.refine((val) => Util.isValidJapaneseZipCode(val), { message: 'Invalid Japanese zip code format.' });
|
|
29
|
-
// ISO 4217 alphabetic currency code, canonical case required (no normalization) — a wrong-case value
|
|
30
|
-
// (`usd`) is a hard error. Sourced from the full-ICU `Intl.supportedValuesOf('currency')` set (built once
|
|
31
|
-
// at module scope, so there is no hand-maintained list to drift). The dev / release node:*-slim images ship
|
|
32
|
-
// full ICU; a small-ICU build silently shrinks this set — a unit test guards the set size.
|
|
33
29
|
const CURRENCY_CODES = new Set(Intl.supportedValuesOf('currency'));
|
|
34
30
|
export const CurrencyCodeSchema = z
|
|
35
31
|
.string()
|
|
36
32
|
.refine((code) => CURRENCY_CODES.has(code), { message: 'not a valid ISO 4217 currency code' });
|
|
37
|
-
// ISO 3166-1 alpha-2 country code, canonical uppercase, no normalization. `Intl` exposes no country
|
|
38
|
-
// enumeration, so unlike the currency set this is a direct code constant — the currently-assigned alpha-2 codes.
|
|
39
33
|
const COUNTRY_CODES = new Set([
|
|
40
34
|
'AD',
|
|
41
35
|
'AE',
|
|
@@ -291,12 +285,8 @@ export const CountryCodeSchema = z
|
|
|
291
285
|
.string()
|
|
292
286
|
.refine((code) => COUNTRY_CODES.has(code), { message: 'not a valid ISO 3166-1 alpha-2 country code' });
|
|
293
287
|
export const FileSchema = (accept, options) => z.object({
|
|
294
|
-
// The key composes the `_uploads/<key>` S3 object path, so pin it to the ULID shape `storeUpload` mints —
|
|
295
|
-
// a client string with `/` or `..` must not reach the storage path.
|
|
296
288
|
key: z.string().regex(/^[0-9A-Z]{26}$/),
|
|
297
289
|
name: z.string(),
|
|
298
|
-
// `z.enum` (not `z.string().refine`) so the accept list survives as a literal union in the emitted
|
|
299
|
-
// type — the form-items codegen reads it to set the file input's `accept`; a refine erases it to `string`.
|
|
300
290
|
mimeType: accept.some((a) => a === '*') ? z.string() : z.enum(accept),
|
|
301
291
|
size: options?.maxSize === undefined
|
|
302
292
|
? z.number().int().nonnegative()
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/server/trpc/schema/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAA;AAE7C,MAAM,CAAC,MAAM,eAAe,GAAoC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAC1E,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,eAAe,CAAC,CAAC,CAAC,CAChH,CAAA;AAED,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC;KACzB,MAAM,EAAE;KACR,GAAG,CAAC,GAAG,CAAC;KACR,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,EAAE,OAAO,EAAE,+BAA+B,EAAE,CAAC,CAAA;AAExF,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC;KACtC,MAAM,EAAE;KACR,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,yBAAyB,CAAC,GAAG,CAAC,EAAE,EAAE,OAAO,EAAE,8BAA8B,EAAE,CAAC,CAAA;AAEpG,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC;KAC5B,MAAM,EAAE;KACR,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,EAAE,EAAE,OAAO,EAAE,0BAA0B,EAAE,CAAC,CAAA;AAEtF,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC;KAC7B,MAAM,EAAE;KACR,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,EAAE,EAAE,OAAO,EAAE,8BAA8B,EAAE,CAAC,CAAA;AAE3F,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC;KACvC,MAAM,EAAE;KACR,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,0BAA0B,CAAC,GAAG,CAAC,EAAE,EAAE,OAAO,EAAE,uCAAuC,EAAE,CAAC,CAAA;AAE9G,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC;KACvC,MAAM,EAAE;KACR,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,0BAA0B,CAAC,GAAG,CAAC,EAAE,EAAE,OAAO,EAAE,uCAAuC,EAAE,CAAC,CAAA;AAE9G,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC;KACzC,MAAM,EAAE;KACR,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,EAAE,EAAE,OAAO,EAAE,gCAAgC,EAAE,CAAC,CAAA;AAEjG,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC;KACnC,MAAM,EAAE;KACR,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC,EAAE,EAAE,OAAO,EAAE,mCAAmC,EAAE,CAAC,CAAA;AAEtG,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/server/trpc/schema/index.ts"],"names":[],"mappings":"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;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;CACL,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC;KAC/B,MAAM,EAAE;KACR,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,OAAO,EAAE,6CAA6C,EAAE,CAAC,CAAA;AAExG,MAAM,CAAC,MAAM,UAAU,GAAG,CAAiD,MAAS,EAAE,OAA8B,EAAE,EAAE,CACtH,CAAC,CAAC,MAAM,CAAC;IACP,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,gBAAgB,CAAC;IACvC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC;IACrE,IAAI,EACF,OAAO,EAAE,OAAO,KAAK,SAAS;QAC5B,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;QAChC,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC;CAC1D,CAAC,CAAA"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { constants } from '../../constants.js';
|
|
1
2
|
import { coordination } from '../../coordination/index.js';
|
|
2
3
|
import { defaultEnv } from '../../env.js';
|
|
3
4
|
import { redis } from '../../repository/redis/index.js';
|
|
@@ -21,9 +22,10 @@ export const mint = async (bucket) => {
|
|
|
21
22
|
await redis.set(tokenKey(token), bucket, UPLOAD_TOKEN_TTL_SEC);
|
|
22
23
|
return { token, url: `${defaultEnv.BASE_URL}/_uploads` };
|
|
23
24
|
};
|
|
24
|
-
// Single-use claim: returns the token's target bucket, or undefined when it is missing / expired / already
|
|
25
|
-
// claimed. Called BEFORE the body is buffered so an invalid token is rejected without buffering.
|
|
26
25
|
export const claimUploadToken = (token) => redis.getdel(tokenKey(token));
|
|
26
|
+
// A deployed environment takes this rule from infra.md § 12 rather than from here: the task role is
|
|
27
|
+
// not granted PutLifecycleConfiguration, and an unredeemed upload has no deletion path but consume,
|
|
28
|
+
// so removing the requirement there leaves user-submitted bodies held indefinitely.
|
|
27
29
|
export const storeUpload = async (params) => {
|
|
28
30
|
// dev-only orphan reaper: expire abandoned `_uploads/` objects (uploaded but never consumed) via a bucket
|
|
29
31
|
// lifecycle rule. Idempotent; infra provisions this in stg/prd. Fire-and-forget so a lifecycle failure
|
|
@@ -33,31 +35,28 @@ export const storeUpload = async (params) => {
|
|
|
33
35
|
.ensureBucketLifecycleRule({
|
|
34
36
|
bucket: params.bucket,
|
|
35
37
|
ruleId: '_uploads-expire',
|
|
36
|
-
prefix:
|
|
37
|
-
expireDays:
|
|
38
|
+
prefix: constants.UPLOADS_PREFIX,
|
|
39
|
+
expireDays: constants.UPLOADS_EXPIRE_DAYS,
|
|
38
40
|
})
|
|
39
41
|
.catch(() => undefined);
|
|
40
42
|
}
|
|
41
43
|
const key = Util.createULID();
|
|
42
44
|
await s3.putObject({
|
|
43
45
|
bucket: params.bucket,
|
|
44
|
-
key:
|
|
46
|
+
key: `${constants.UPLOADS_PREFIX}${key}`,
|
|
45
47
|
body: params.body,
|
|
46
48
|
contentType: params.contentType,
|
|
47
49
|
metadata: { name: encodeURIComponent(params.name) },
|
|
48
50
|
});
|
|
49
51
|
return { key, name: params.name, mimeType: params.contentType, size: params.body.length };
|
|
50
52
|
};
|
|
51
|
-
// `accept` / `maxSize` are enforced against the REAL stored object here, not the client-supplied `input.file`
|
|
52
|
-
// reference (which the caller controls and can fabricate) — pass the same constraints the procedure's
|
|
53
|
-
// `FileSchema` declares. Throws 415 / 422 on a mismatch.
|
|
54
53
|
export const consume = async (key, bucket, constraints) => {
|
|
55
54
|
// Defence in depth: `key` composes the S3 object path that is then deleted; reject anything but the ULID
|
|
56
55
|
// shape `storeUpload` mints so a stray `/` or `..` can never reach the storage path.
|
|
57
56
|
if (!/^[0-9A-Z]{26}$/.test(key)) {
|
|
58
57
|
return undefined;
|
|
59
58
|
}
|
|
60
|
-
const objectKey =
|
|
59
|
+
const objectKey = `${constants.UPLOADS_PREFIX}${key}`;
|
|
61
60
|
const object = await s3.getObject({ bucket, key: objectKey });
|
|
62
61
|
if (object === undefined) {
|
|
63
62
|
return undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/server/uploads/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAA;AAC1D,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACzC,OAAO,EAAE,KAAK,EAAE,MAAM,iCAAiC,CAAA;AACvD,OAAO,EAAE,EAAE,EAAE,MAAM,8BAA8B,CAAA;AACjD,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAA;AAC1C,OAAO,EACL,yBAAyB,EACzB,8BAA8B,EAC9B,8BAA8B,GAC/B,MAAM,wBAAwB,CAAA;AAE/B,MAAM,eAAe,GAAG,EAAE,CAAA;AAC1B,MAAM,sBAAsB,GAAG,EAAE,CAAA;AACjC,MAAM,oBAAoB,GAAG,CAAC,GAAG,EAAE,CAAA;AAEnC,MAAM,QAAQ,GAAG,CAAC,KAAa,EAAE,EAAE,CAAC,eAAe,KAAK,EAAE,CAAA;AAE1D,MAAM,CAAC,MAAM,IAAI,GAAG,KAAK,EAAE,MAAc,EAAE,EAAE;IAC3C,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,YAAY,CAAC,SAAS,CAAC,OAAO,CAAC;QACvD,GAAG,EAAE,oBAAoB,UAAU,CAAC,OAAO,EAAE;QAC7C,GAAG,EAAE,eAAe;QACpB,SAAS,EAAE,sBAAsB;KAClC,CAAC,CAAA;IACF,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,yBAAyB,CAAC,4BAA4B,CAAC,CAAA;IACzD,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,EAAE,CAAA;IAC/B,MAAM,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,oBAAoB,CAAC,CAAA;IAC9D,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,UAAU,CAAC,QAAQ,WAAW,EAAE,CAAA;AAC1D,CAAC,CAAA;AAED,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/server/uploads/index.ts"],"names":[],"mappings":"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"}
|
package/dist/util/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ulid } from 'ulid';
|
|
2
|
+
import { constants } from '../constants.js';
|
|
2
3
|
const PASSWORD_ALLOWED_CHARACTERS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_+=-[]{}|;:,.?/~';
|
|
3
4
|
export const Util = {
|
|
4
5
|
isArray: (maybeArray) => Array.isArray(maybeArray),
|
|
@@ -6,65 +7,10 @@ export const Util = {
|
|
|
6
7
|
isString: (arg) => typeof arg === 'string',
|
|
7
8
|
normalize: (value) => value.trim().toLowerCase(),
|
|
8
9
|
sanitize: (input, options) => {
|
|
9
|
-
const DEFAULT_SANITIZE_KEYWORDS = [
|
|
10
|
-
'authorization',
|
|
11
|
-
'proxy-authorization',
|
|
12
|
-
'cookie',
|
|
13
|
-
'set-cookie',
|
|
14
|
-
'x-api-key',
|
|
15
|
-
'apikey',
|
|
16
|
-
'api_key',
|
|
17
|
-
'api-key',
|
|
18
|
-
'token',
|
|
19
|
-
'secret',
|
|
20
|
-
'password',
|
|
21
|
-
'passwd',
|
|
22
|
-
'signature',
|
|
23
|
-
'private',
|
|
24
|
-
'session',
|
|
25
|
-
'jwt',
|
|
26
|
-
'bearer',
|
|
27
|
-
// PII keys — masked so a thrown error's logged input/result never emits subject data to the log stream.
|
|
28
|
-
// Only low-false-positive keys (substring match): bare 'name' (a direct identifier, db-schema.md) is
|
|
29
|
-
// redacted separately in `walk` on EXACT match — a substring entry here would mask serviceName/fileName/….
|
|
30
|
-
'email',
|
|
31
|
-
'fullname',
|
|
32
|
-
'isikukood',
|
|
33
|
-
'dateofbirth',
|
|
34
|
-
'phone',
|
|
35
|
-
'wage',
|
|
36
|
-
'screenedname',
|
|
37
|
-
'familyname',
|
|
38
|
-
'givenname',
|
|
39
|
-
'middlename',
|
|
40
|
-
'holdername',
|
|
41
|
-
'namekana',
|
|
42
|
-
'nickname',
|
|
43
|
-
'fax',
|
|
44
|
-
'workplaceaddress',
|
|
45
|
-
'complainantcontact',
|
|
46
|
-
'affiliatedorganization',
|
|
47
|
-
'nationalid',
|
|
48
|
-
'zipcode',
|
|
49
|
-
'taxnumber',
|
|
50
|
-
'corporateid',
|
|
51
|
-
'useragent',
|
|
52
|
-
'hostcountry',
|
|
53
|
-
'residencycountry',
|
|
54
|
-
'jobtitle',
|
|
55
|
-
'department',
|
|
56
|
-
];
|
|
57
|
-
// Exact-match-only PII keys. A substring entry would be catastrophic here — 'ip' matches recipient /
|
|
58
|
-
// description / zip / script, so it is compared whole, the same treatment bare 'name' gets in `walk`.
|
|
59
|
-
const EXACT_SANITIZE_KEYWORDS = ['ip'];
|
|
60
10
|
const normalizeKey = (k) => k.trim().toLowerCase();
|
|
61
11
|
const shouldRedactKey = (key, keywords) => keywords.some((kw) => normalizeKey(key) === kw || normalizeKey(key).includes(kw)) ||
|
|
62
|
-
|
|
12
|
+
constants.SANITIZE_EXACT_KEYS.some((kw) => normalizeKey(key) === kw);
|
|
63
13
|
const maybeTruncate = (s, max) => !max || max <= 0 ? s : s.length <= max ? s : `${s.slice(0, max)}...<truncated>`;
|
|
64
|
-
// `toJsonLikeSeen` tracks only the ANCESTOR path (added before recursing into a value's children,
|
|
65
|
-
// removed after) — so a genuine cycle is `[Circular]`, but a shared-but-acyclic reference (a DAG /
|
|
66
|
-
// diamond, e.g. two fields pointing at one object) still serializes fully on each path instead of
|
|
67
|
-
// being falsely collapsed on its second appearance.
|
|
68
14
|
const toJsonLikeSeen = new WeakSet();
|
|
69
15
|
// Objects produced from an Error: their `name` is the error class (not PII), exempt from the
|
|
70
16
|
// exact-match 'name' redaction in `walk`.
|
|
@@ -135,17 +81,84 @@ export const Util = {
|
|
|
135
81
|
toJsonLikeSeen.delete(v);
|
|
136
82
|
}
|
|
137
83
|
};
|
|
138
|
-
|
|
139
|
-
|
|
84
|
+
// The three patterns below run before any truncation does, so nothing bounds the length of what they run
|
|
85
|
+
// over, and three things keep their total cost proportional to that length. First, the opener scans the
|
|
86
|
+
// string once, and its unbounded `\w+` is reached only from an `Incorrect ` occurrence, whose trailing
|
|
87
|
+
// space keeps the runs two such occurrences produce from overlapping. Second, a keeper, where one runs,
|
|
88
|
+
// scans from its opener's end to the next opener's start, or to the string's end, and those segments are
|
|
89
|
+
// disjoint, so keeper work sums over the string once rather than once per opener. Third, the keeper
|
|
90
|
+
// quantifiers are bounded, so backtracking is capped per start position within a segment.
|
|
91
|
+
const driverOpener = /Duplicate entry '?|Incorrect (\w+) value: '?|( - )?parameters:/g;
|
|
92
|
+
const driverKeyKeeper = / for key '[A-Za-z0-9_$.-]{1,192}'/g;
|
|
93
|
+
const driverColumnKeeper = / for column '[A-Za-z0-9_$.-]{1,192}'(?: at row \d{1,9}(?!\d))?/g;
|
|
94
|
+
const INCORRECT_VALUE_TYPES = [
|
|
95
|
+
'string',
|
|
96
|
+
'integer',
|
|
97
|
+
'decimal',
|
|
98
|
+
'double',
|
|
99
|
+
'float',
|
|
100
|
+
'datetime',
|
|
101
|
+
'date',
|
|
102
|
+
'time',
|
|
103
|
+
'timestamp',
|
|
104
|
+
'year',
|
|
105
|
+
'bit',
|
|
106
|
+
'boolean',
|
|
107
|
+
'json',
|
|
108
|
+
'char',
|
|
109
|
+
'binary',
|
|
110
|
+
];
|
|
111
|
+
const lastKeeperIn = (segment, keeper) => {
|
|
112
|
+
keeper.lastIndex = 0;
|
|
113
|
+
let found = '';
|
|
114
|
+
for (let m = keeper.exec(segment); m !== null; m = keeper.exec(segment)) {
|
|
115
|
+
found = m[0];
|
|
116
|
+
}
|
|
117
|
+
return found;
|
|
118
|
+
};
|
|
119
|
+
const maskDriverValues = (text) => {
|
|
120
|
+
const openers = [];
|
|
121
|
+
driverOpener.lastIndex = 0;
|
|
122
|
+
for (let m = driverOpener.exec(text); m !== null; m = driverOpener.exec(text)) {
|
|
123
|
+
const [matched, valueType, paramsLead] = m;
|
|
124
|
+
const terminal = matched.endsWith('parameters:');
|
|
125
|
+
openers.push({
|
|
126
|
+
start: m.index,
|
|
127
|
+
end: m.index + matched.length,
|
|
128
|
+
canonical: terminal
|
|
129
|
+
? `${paramsLead ?? ''}parameters:[***]`
|
|
130
|
+
: valueType === undefined
|
|
131
|
+
? "Duplicate entry '***'"
|
|
132
|
+
: INCORRECT_VALUE_TYPES.some((type) => type === valueType.toLowerCase())
|
|
133
|
+
? `Incorrect ${valueType} value: '***'`
|
|
134
|
+
: "Incorrect value: '***'",
|
|
135
|
+
terminal,
|
|
136
|
+
keeper: valueType === undefined ? driverKeyKeeper : driverColumnKeeper,
|
|
137
|
+
});
|
|
138
|
+
if (terminal) {
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
if (openers.length === 0) {
|
|
143
|
+
return text;
|
|
144
|
+
}
|
|
145
|
+
let out = text.slice(0, openers[0].start);
|
|
146
|
+
for (const [i, opener] of openers.entries()) {
|
|
147
|
+
out += opener.canonical;
|
|
148
|
+
if (opener.terminal) {
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
out += lastKeeperIn(text.slice(opener.end, i + 1 < openers.length ? openers[i + 1].start : text.length), opener.keeper);
|
|
152
|
+
}
|
|
153
|
+
return out;
|
|
154
|
+
};
|
|
155
|
+
const maskInText = (text) => maskDriverValues(text)
|
|
140
156
|
.replace(/(\bpassword\b\s*:\s*)"(?:[^"\\]|\\.)*"/gi, '$1"***"')
|
|
141
157
|
.replace(/(\bpasswd\b\s*:\s*)"(?:[^"\\]|\\.)*"/gi, '$1"***"')
|
|
142
|
-
// authorization: "Bearer xxx"
|
|
143
158
|
.replace(/(\bauthorization\b\s*:\s*)"(?:[^"\\]|\\.)*"/gi, '$1"***"')
|
|
144
159
|
.replace(/(\bproxy-authorization\b\s*:\s*)"(?:[^"\\]|\\.)*"/gi, '$1"***"')
|
|
145
|
-
// cookie / set-cookie
|
|
146
160
|
.replace(/(\bcookie\b\s*:\s*)"(?:[^"\\]|\\.)*"/gi, '$1"***"')
|
|
147
161
|
.replace(/(\bset-cookie\b\s*:\s*)"(?:[^"\\]|\\.)*"/gi, '$1"***"')
|
|
148
|
-
// token / secret
|
|
149
162
|
.replace(/(\btoken\b\s*:\s*)"(?:[^"\\]|\\.)*"/gi, '$1"***"')
|
|
150
163
|
.replace(/(\bsecret\b\s*:\s*)"(?:[^"\\]|\\.)*"/gi, '$1"***"')
|
|
151
164
|
// key=value form (URL query string / form body): secrets in a logged `req.url` or error text.
|
|
@@ -153,9 +166,8 @@ export const Util = {
|
|
|
153
166
|
// the `token` inside `id_token` (the `_t` seam is word/word, so `\b` does not match there), and
|
|
154
167
|
// an OIDC id_token is a PII-bearing JWT that must not survive into logs.
|
|
155
168
|
.replace(/(\b(?:password|passwd|token|id_token|access_token|refresh_token|secret|api_key|apikey|api-key|authorization|session_state|session|jwt)=)[^&\s"'#]+/gi, '$1***')
|
|
156
|
-
// bare bearer token in free text (error message / stack / url)
|
|
157
169
|
.replace(/(\bBearer\s+)[\w.~+/-]+=*/gi, '$1***');
|
|
158
|
-
const keywords = options?.keywords ??
|
|
170
|
+
const keywords = options?.keywords ?? constants.SANITIZE_KEYWORDS;
|
|
159
171
|
const maxStringLength = options?.maxStringLength;
|
|
160
172
|
const seen = new WeakSet();
|
|
161
173
|
// A logged string carrying embedded newlines — a JSON-encoded error message (Zod issues), a
|
|
@@ -174,16 +186,9 @@ export const Util = {
|
|
|
174
186
|
return parsed;
|
|
175
187
|
}
|
|
176
188
|
}
|
|
177
|
-
catch {
|
|
178
|
-
// not JSON — fall through to a line split
|
|
179
|
-
}
|
|
189
|
+
catch { }
|
|
180
190
|
}
|
|
181
|
-
|
|
182
|
-
// A stack trace begins with a `${name}: ${message}` head before its indented `at …` frames.
|
|
183
|
-
// That head duplicates the record's own name / message (and, for a JSON error message, is a
|
|
184
|
-
// wall of escaped quotes) — keep only the frames when the string is a stack.
|
|
185
|
-
const frames = lines.filter((line) => /^\s+at\s/.test(line));
|
|
186
|
-
return frames.length > 0 ? frames : lines;
|
|
191
|
+
return s.split('\n');
|
|
187
192
|
};
|
|
188
193
|
const walk = (value, keyHint) => {
|
|
189
194
|
if (value === null) {
|
|
@@ -196,11 +201,15 @@ export const Util = {
|
|
|
196
201
|
if (keyHint && shouldRedactKey(keyHint, keywords)) {
|
|
197
202
|
return '***';
|
|
198
203
|
}
|
|
199
|
-
|
|
200
|
-
|
|
204
|
+
// Masked before the split, because the redacted span runs to the end of the whole message.
|
|
205
|
+
// Splitting first makes each line a message of its own, so the tail of a value carrying a
|
|
206
|
+
// newline lands on a line holding no marker and is emitted verbatim.
|
|
207
|
+
const masked = maskInText(value);
|
|
208
|
+
const expanded = expandMultiline(masked);
|
|
209
|
+
if (expanded !== masked) {
|
|
201
210
|
return walk(expanded);
|
|
202
211
|
}
|
|
203
|
-
return maybeTruncate(
|
|
212
|
+
return maybeTruncate(masked, maxStringLength);
|
|
204
213
|
}
|
|
205
214
|
if (typeof value === 'number') {
|
|
206
215
|
return Number.isFinite(value) ? value : String(value);
|
|
@@ -211,8 +220,6 @@ export const Util = {
|
|
|
211
220
|
if (typeof value !== 'object') {
|
|
212
221
|
return String(value);
|
|
213
222
|
}
|
|
214
|
-
// Ancestor-path tracking (add before recursing, remove after) so a shared-but-acyclic reference is
|
|
215
|
-
// not mistaken for a cycle — same reason as `toJsonLikeSeen`.
|
|
216
223
|
if (seen.has(value)) {
|
|
217
224
|
return '[Circular]';
|
|
218
225
|
}
|
|
@@ -223,13 +230,9 @@ export const Util = {
|
|
|
223
230
|
}
|
|
224
231
|
const out = {};
|
|
225
232
|
for (const [k, v] of Object.entries(value)) {
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
// case-insensitive.
|
|
230
|
-
// Bare 'name' is a direct identifier (db-schema.md) redacted on exact match only — a substring
|
|
231
|
-
// keyword would mask serviceName/fileName/…; an Error-shaped object keeps its `name` (error class).
|
|
232
|
-
if (shouldRedactKey(k, keywords) || (normalizeKey(k) === 'name' && !errorShapes.has(value))) {
|
|
233
|
+
if (shouldRedactKey(k, keywords) ||
|
|
234
|
+
(constants.SANITIZE_EXACT_KEYS_EXCEPT_ERRORS.some((kw) => normalizeKey(k) === kw) &&
|
|
235
|
+
!errorShapes.has(value))) {
|
|
233
236
|
out[k] = '***';
|
|
234
237
|
continue;
|
|
235
238
|
}
|
|
@@ -260,7 +263,6 @@ export const Util = {
|
|
|
260
263
|
},
|
|
261
264
|
});
|
|
262
265
|
}),
|
|
263
|
-
// backend only
|
|
264
266
|
isValidEmail: (value) => value === undefined
|
|
265
267
|
? false
|
|
266
268
|
: /^[a-zA-Z0-9_.+-]+@([a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]*\.)+[a-zA-Z]{2,}$/.test(value),
|
|
@@ -285,22 +287,13 @@ export const Util = {
|
|
|
285
287
|
}
|
|
286
288
|
})
|
|
287
289
|
.join(''))).test(value),
|
|
288
|
-
// E.164 phone number format +819012345678
|
|
289
290
|
isValidPhoneE164: (value) => value !== undefined && /^\+[1-9]\d{1,14}$/.test(value),
|
|
290
|
-
// Japanese phone number 012-3456-7890
|
|
291
291
|
isValidJapanesePhoneNumber: (value) => value !== undefined && /^0\d{1,4}-\d{1,4}-\d{3,4}$/.test(value) && /^0\d{9,10}$/.test(value.replace(/-/g, '')),
|
|
292
|
-
// Corporate number (13-digit number)
|
|
293
292
|
isValidJapaneseCorporateId: (value) => (value === undefined ? false : /^[0-9]{13}$/.test(value)),
|
|
294
|
-
// Invoice number (T + 13-digit number)
|
|
295
293
|
isValidInvoiceNumber: (value) => (value === undefined ? false : /^T[0-9]{13}$/.test(value)),
|
|
296
|
-
// Zip code (3-digit + 4-digit number)
|
|
297
294
|
isValidJapaneseZipCode: (value) => (value === undefined ? false : /^\d{3}-?\d{4}$/.test(value)),
|
|
298
295
|
createOTP: (n, isNumberOnly) => {
|
|
299
296
|
const seed = isNumberOnly ? '0123456789' : 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
|
300
|
-
// Rejection sampling: discard bytes in the incomplete final block (>= the largest multiple of
|
|
301
|
-
// seed.length that fits in a byte) so every character is equiprobable — a plain `x % seed.length`
|
|
302
|
-
// slightly favours the first `256 % seed.length` characters. Matters because this also backs the
|
|
303
|
-
// initial Google Workspace password minted in the access-execution batch.
|
|
304
297
|
const limit = 256 - (256 % seed.length);
|
|
305
298
|
const out = [];
|
|
306
299
|
while (out.length < n) {
|
|
@@ -315,8 +308,6 @@ export const Util = {
|
|
|
315
308
|
createULID: ulid,
|
|
316
309
|
getHeaderValue: (headers, name) => {
|
|
317
310
|
const key = name.toLowerCase();
|
|
318
|
-
// IncomingHttpHeaders keys are already lowercased, so headers[key] resolves directly; the
|
|
319
|
-
// case-insensitive scan is the fallback for a plain record whose keys may be mixed-case.
|
|
320
311
|
const value = headers[name] ?? headers[key] ?? Object.entries(headers).find(([k]) => k.toLowerCase() === key)?.[1];
|
|
321
312
|
if (Array.isArray(value)) {
|
|
322
313
|
return value.length > 0 && value[0].length > 0 ? value[0] : undefined;
|