@atproto/xrpc-server 0.10.14 → 0.10.16
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/CHANGELOG.md +23 -0
- package/dist/auth.d.ts +3 -2
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +18 -0
- package/dist/auth.js.map +1 -1
- package/dist/errors.d.ts +7 -14
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +19 -6
- package/dist/errors.js.map +1 -1
- package/dist/server.d.ts +27 -11
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +115 -78
- package/dist/server.js.map +1 -1
- package/dist/stream/frames.d.ts +5 -1
- package/dist/stream/frames.d.ts.map +1 -1
- package/dist/stream/frames.js +32 -5
- package/dist/stream/frames.js.map +1 -1
- package/dist/stream/types.d.ts +18 -44
- package/dist/stream/types.d.ts.map +1 -1
- package/dist/stream/types.js +10 -10
- package/dist/stream/types.js.map +1 -1
- package/dist/types.d.ts +47 -70
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +28 -15
- package/dist/types.js.map +1 -1
- package/dist/util.d.ts +18 -9
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js +180 -37
- package/dist/util.js.map +1 -1
- package/package.json +11 -7
- package/src/auth.ts +28 -2
- package/src/errors.ts +23 -7
- package/src/server.ts +307 -111
- package/src/stream/frames.ts +39 -6
- package/src/stream/types.ts +14 -14
- package/src/types.ts +106 -25
- package/src/util.ts +272 -60
- package/tests/_util.ts +62 -5
- package/tests/bodies.test.ts +442 -387
- package/tests/procedures.test.ts +71 -52
- package/tests/queries.test.ts +56 -39
- package/tests/subscriptions.test.ts +234 -221
package/dist/util.js
CHANGED
|
@@ -5,11 +5,15 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.extractUrlNsid = exports.parseUrlNsid = exports.parseReqNsid = exports.ServerTimer = exports.asArray = void 0;
|
|
7
7
|
exports.setHeaders = setHeaders;
|
|
8
|
-
exports.decodeQueryParams = decodeQueryParams;
|
|
9
8
|
exports.decodeQueryParam = decodeQueryParam;
|
|
9
|
+
exports.getSearchParams = getSearchParams;
|
|
10
10
|
exports.getQueryParams = getQueryParams;
|
|
11
|
-
exports.
|
|
12
|
-
exports.
|
|
11
|
+
exports.createLexiconParamsVerifier = createLexiconParamsVerifier;
|
|
12
|
+
exports.createSchemaParamsVerifier = createSchemaParamsVerifier;
|
|
13
|
+
exports.createLexiconInputVerifier = createLexiconInputVerifier;
|
|
14
|
+
exports.createSchemaInputVerifier = createSchemaInputVerifier;
|
|
15
|
+
exports.createLexiconOutputVerifier = createLexiconOutputVerifier;
|
|
16
|
+
exports.createSchemaOutputVerifier = createSchemaOutputVerifier;
|
|
13
17
|
exports.parseReqEncoding = parseReqEncoding;
|
|
14
18
|
exports.serverTimingHeader = serverTimingHeader;
|
|
15
19
|
const node_assert_1 = __importDefault(require("node:assert"));
|
|
@@ -17,6 +21,8 @@ const node_stream_1 = require("node:stream");
|
|
|
17
21
|
const express_1 = require("express");
|
|
18
22
|
const mime_types_1 = require("mime-types");
|
|
19
23
|
const common_1 = require("@atproto/common");
|
|
24
|
+
const lex_json_1 = require("@atproto/lex-json");
|
|
25
|
+
const lex_schema_1 = require("@atproto/lex-schema");
|
|
20
26
|
const lexicon_1 = require("@atproto/lexicon");
|
|
21
27
|
const xrpc_1 = require("@atproto/xrpc");
|
|
22
28
|
const errors_1 = require("./errors");
|
|
@@ -69,28 +75,66 @@ function decodeQueryParam(type, value) {
|
|
|
69
75
|
return value === 'true';
|
|
70
76
|
}
|
|
71
77
|
}
|
|
72
|
-
function
|
|
73
|
-
|
|
78
|
+
function getSearchParams(url) {
|
|
79
|
+
if (!url)
|
|
80
|
+
return undefined;
|
|
74
81
|
const queryStringIdx = url.indexOf('?');
|
|
75
82
|
if (queryStringIdx === -1)
|
|
76
|
-
return
|
|
83
|
+
return undefined;
|
|
77
84
|
const queryString = url.slice(queryStringIdx + 1);
|
|
78
|
-
if (queryString ===
|
|
85
|
+
if (queryString.length === 0)
|
|
86
|
+
return undefined;
|
|
87
|
+
return new URLSearchParams(queryString);
|
|
88
|
+
}
|
|
89
|
+
function getQueryParams(req) {
|
|
90
|
+
if ('query' in req)
|
|
91
|
+
return req.query;
|
|
92
|
+
const result = Object.create(null);
|
|
93
|
+
const searchParams = getSearchParams(req.url);
|
|
94
|
+
if (!searchParams)
|
|
79
95
|
return result;
|
|
80
|
-
|
|
96
|
+
if (searchParams.has('__proto__')) {
|
|
97
|
+
// Prevent prototype pollution
|
|
98
|
+
throw new errors_1.InvalidRequestError(`Invalid query parameter: __proto__`, 'InvalidQueryParameter');
|
|
99
|
+
}
|
|
81
100
|
for (const key of searchParams.keys()) {
|
|
82
|
-
if (key === '__proto__') {
|
|
83
|
-
// Prevent prototype pollution
|
|
84
|
-
throw new errors_1.InvalidRequestError(`Invalid query parameter: ${key}`, 'InvalidQueryParameter');
|
|
85
|
-
}
|
|
86
101
|
const values = searchParams.getAll(key);
|
|
87
102
|
result[key] = values.length === 1 ? values[0] : values;
|
|
88
103
|
}
|
|
89
104
|
return result;
|
|
90
105
|
}
|
|
91
|
-
function
|
|
106
|
+
function createLexiconParamsVerifier(nsid, def, lexicons) {
|
|
107
|
+
return (req) => {
|
|
108
|
+
const queryParams = getQueryParams(req);
|
|
109
|
+
const params = decodeQueryParams(def, queryParams);
|
|
110
|
+
try {
|
|
111
|
+
return lexicons.assertValidXrpcParams(nsid, params);
|
|
112
|
+
}
|
|
113
|
+
catch (e) {
|
|
114
|
+
// @NOTE WE historically did not check for specific error types here,
|
|
115
|
+
throw new errors_1.InvalidRequestError(String(e));
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
function createSchemaParamsVerifier(ns) {
|
|
120
|
+
const schema = lex_schema_1.l.getMain(ns);
|
|
121
|
+
return (req) => {
|
|
122
|
+
const urlSearchParams = getSearchParams(req.url) ?? new URLSearchParams();
|
|
123
|
+
try {
|
|
124
|
+
const params = schema.parameters.fromURLSearchParams(urlSearchParams);
|
|
125
|
+
return params;
|
|
126
|
+
}
|
|
127
|
+
catch (err) {
|
|
128
|
+
if (err instanceof lex_schema_1.l.LexValidationError) {
|
|
129
|
+
throw new errors_1.InvalidRequestError(err.message);
|
|
130
|
+
}
|
|
131
|
+
throw err;
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
function createLexiconInputVerifier(nsid, def, options, lexicons) {
|
|
92
136
|
if (def.type === 'query' || !def.input) {
|
|
93
|
-
return (req) => {
|
|
137
|
+
return async (req) => {
|
|
94
138
|
// @NOTE We allow (and ignore) "empty" bodies
|
|
95
139
|
if (getBodyPresence(req) === 'present') {
|
|
96
140
|
throw new errors_1.InvalidRequestError(`A request body was provided when none was expected`);
|
|
@@ -122,8 +166,8 @@ function createInputVerifier(nsid, def, options, lexicons) {
|
|
|
122
166
|
const lexBody = req.body ? (0, lexicon_1.jsonToLex)(req.body) : req.body;
|
|
123
167
|
req.body = lexicons.assertValidXrpcInput(nsid, lexBody);
|
|
124
168
|
}
|
|
125
|
-
catch (
|
|
126
|
-
throw new errors_1.InvalidRequestError(
|
|
169
|
+
catch (cause) {
|
|
170
|
+
throw new errors_1.InvalidRequestError(cause instanceof Error ? cause.message : String(cause), undefined, { cause });
|
|
127
171
|
}
|
|
128
172
|
}
|
|
129
173
|
// if middleware already got the body, we pass that along as input
|
|
@@ -132,40 +176,124 @@ function createInputVerifier(nsid, def, options, lexicons) {
|
|
|
132
176
|
return { encoding: reqEncoding, body };
|
|
133
177
|
};
|
|
134
178
|
}
|
|
135
|
-
function
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
179
|
+
function createSchemaInputVerifier(ns, options) {
|
|
180
|
+
const schema = lex_schema_1.l.getMain(ns);
|
|
181
|
+
const { blobLimit } = options;
|
|
182
|
+
const input = 'input' in schema ? schema.input : undefined;
|
|
183
|
+
if (!input?.encoding) {
|
|
184
|
+
//
|
|
185
|
+
return async (req) => {
|
|
186
|
+
if (getBodyPresence(req) === 'present') {
|
|
187
|
+
throw new errors_1.InvalidRequestError(`A request body was provided when none was expected`);
|
|
188
|
+
}
|
|
189
|
+
return undefined;
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
const bodyParser = createBodyParser(input.encoding, options);
|
|
193
|
+
return async (req, res) => {
|
|
194
|
+
if (getBodyPresence(req) === 'missing') {
|
|
195
|
+
throw new errors_1.InvalidRequestError(`A request body is expected but none was provided`);
|
|
196
|
+
}
|
|
197
|
+
const reqEncoding = parseReqEncoding(req);
|
|
198
|
+
if (!input.matchesEncoding(reqEncoding)) {
|
|
199
|
+
throw new errors_1.InvalidRequestError(`Wrong request encoding (Content-Type): ${reqEncoding}`);
|
|
200
|
+
}
|
|
201
|
+
if (bodyParser) {
|
|
202
|
+
await bodyParser(req, res);
|
|
203
|
+
}
|
|
204
|
+
if (input.schema) {
|
|
205
|
+
try {
|
|
206
|
+
const lexBody = req.body ? (0, lex_json_1.jsonToLex)(req.body) : req.body;
|
|
207
|
+
req.body = input.schema.parse(lexBody);
|
|
208
|
+
}
|
|
209
|
+
catch (cause) {
|
|
210
|
+
throw new errors_1.InvalidRequestError(cause instanceof Error ? cause.message : String(cause), undefined, { cause });
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
// if middleware already got the body, we pass that along as input
|
|
214
|
+
// otherwise, we pass along a decoded readable stream
|
|
215
|
+
const body = req.readableEnded ? req.body : decodeBodyStream(req, blobLimit);
|
|
216
|
+
return { encoding: reqEncoding, body };
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
function createLexiconOutputVerifier(nsid, def, lexicons) {
|
|
220
|
+
const outputDef = def.output;
|
|
221
|
+
// Expects no output
|
|
222
|
+
if (!outputDef) {
|
|
223
|
+
return (handlerOutput) => {
|
|
224
|
+
if (handlerOutput !== undefined) {
|
|
225
|
+
throw new errors_1.InternalServerError(`A response body was provided when none was expected`);
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
// An output is expected
|
|
230
|
+
return (handlerOutput) => {
|
|
231
|
+
if (handlerOutput === undefined) {
|
|
139
232
|
throw new errors_1.InternalServerError(`A response body is expected but none was provided`);
|
|
140
233
|
}
|
|
234
|
+
if (!('encoding' in handlerOutput)) {
|
|
235
|
+
// Ensure handlerOutput is valid ErrorResult
|
|
236
|
+
if ('status' in handlerOutput && handlerOutput.status >= 400) {
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
throw new errors_1.InternalServerError(`Invalid handler output: missing encoding`);
|
|
240
|
+
}
|
|
241
|
+
if (!('body' in handlerOutput)) {
|
|
242
|
+
// Ensure handlerOutput is valid HandlerPipeThrough
|
|
243
|
+
if ('stream' in handlerOutput || 'buffer' in handlerOutput) {
|
|
244
|
+
return; // Validation is ignored for pipe-through outputs
|
|
245
|
+
}
|
|
246
|
+
throw new errors_1.InternalServerError(`Invalid handler output: missing body`);
|
|
247
|
+
}
|
|
141
248
|
// Fool-proofing (should not be necessary due to type system)
|
|
142
|
-
const result = types_1.handlerSuccess.safeParse(
|
|
249
|
+
const result = types_1.handlerSuccess.safeParse(handlerOutput);
|
|
143
250
|
if (!result.success) {
|
|
144
251
|
throw new errors_1.InternalServerError(`Invalid handler output`, undefined, {
|
|
145
|
-
cause: result.
|
|
252
|
+
cause: result.reason,
|
|
146
253
|
});
|
|
147
254
|
}
|
|
148
255
|
// output mime
|
|
149
|
-
const { encoding } =
|
|
150
|
-
if (!
|
|
256
|
+
const { encoding } = handlerOutput;
|
|
257
|
+
if (!isValidEncoding(outputDef, encoding)) {
|
|
151
258
|
throw new errors_1.InternalServerError(`Invalid response encoding: ${encoding}`);
|
|
152
259
|
}
|
|
153
260
|
// output schema
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
261
|
+
try {
|
|
262
|
+
lexicons.assertValidXrpcOutput(nsid, handlerOutput.body);
|
|
263
|
+
// @TODO Since the output verifier is typically enabled in dev/tests and
|
|
264
|
+
// disabled in production, we don't want to assign the (altered) output
|
|
265
|
+
// back to the handlerOutput object, as this would cause different
|
|
266
|
+
// behaviors between environments. Instead, we should compare the value
|
|
267
|
+
// returned by assertValidXrpcOutput with the original output and throw if
|
|
268
|
+
// they differ (indicating that the output was mutated during validation,
|
|
269
|
+
// e.g. due to default values being applied).
|
|
270
|
+
}
|
|
271
|
+
catch (cause) {
|
|
272
|
+
const message = cause instanceof Error ? cause.message : 'Output body validation failed';
|
|
273
|
+
throw new errors_1.InternalServerError(message, undefined, { cause });
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
function createSchemaOutputVerifier(ns) {
|
|
278
|
+
const outputSchema = lex_schema_1.l.getMain(ns).output;
|
|
279
|
+
return (handlerOutput) => {
|
|
280
|
+
// @NOTE If the user of the lib wants to return an output that doesn't
|
|
281
|
+
// conform to the schema, they can use HandlerPipeThrough return types
|
|
282
|
+
if (!outputSchema.matchesEncoding(handlerOutput?.encoding)) {
|
|
283
|
+
throw new errors_1.InternalServerError('Output encoding mismatch');
|
|
284
|
+
}
|
|
285
|
+
if (outputSchema.schema) {
|
|
286
|
+
const result = outputSchema.schema.safeValidate(handlerOutput?.body);
|
|
287
|
+
if (!result.success) {
|
|
288
|
+
throw new errors_1.InternalServerError(result.reason.message, undefined, {
|
|
289
|
+
cause: result.reason,
|
|
290
|
+
});
|
|
160
291
|
}
|
|
161
292
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
// Expects no output
|
|
165
|
-
if (output !== undefined) {
|
|
166
|
-
throw new errors_1.InternalServerError(`A response body was provided when none was expected`);
|
|
293
|
+
else if (!outputSchema.encoding && handlerOutput?.body !== undefined) {
|
|
294
|
+
throw new errors_1.InternalServerError('Output body not expected');
|
|
167
295
|
}
|
|
168
|
-
}
|
|
296
|
+
};
|
|
169
297
|
}
|
|
170
298
|
function parseReqEncoding(req) {
|
|
171
299
|
const encoding = normalizeMime(req.headers['content-type']);
|
|
@@ -192,11 +320,26 @@ function trimString(str) {
|
|
|
192
320
|
return str.trim();
|
|
193
321
|
}
|
|
194
322
|
function isValidEncoding(output, encoding) {
|
|
323
|
+
if (!encoding)
|
|
324
|
+
return false;
|
|
195
325
|
const normalized = normalizeMime(encoding);
|
|
196
326
|
if (!normalized)
|
|
197
327
|
return false;
|
|
198
328
|
const allowed = parseDefEncoding(output);
|
|
199
|
-
|
|
329
|
+
if (!allowed.length)
|
|
330
|
+
return false;
|
|
331
|
+
if (allowed.includes(ENCODING_ANY))
|
|
332
|
+
return true;
|
|
333
|
+
if (allowed.includes(normalized))
|
|
334
|
+
return true;
|
|
335
|
+
// Check for wildcard matches (e.g. normalized=application/json, allowed=application/*)
|
|
336
|
+
for (const allowedEnc of allowed) {
|
|
337
|
+
if (allowedEnc.endsWith('/*') &&
|
|
338
|
+
normalized.startsWith(allowedEnc.slice(0, -1))) {
|
|
339
|
+
return true;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
return false;
|
|
200
343
|
}
|
|
201
344
|
function getBodyPresence(req) {
|
|
202
345
|
if (req.headers['transfer-encoding'] != null)
|
package/dist/util.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"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","sourcesContent":["import assert from 'node:assert'\nimport { IncomingMessage, OutgoingMessage } from 'node:http'\nimport { Duplex, Readable, pipeline } from 'node:stream'\nimport { Request, Response, json, text } from 'express'\nimport { contentType } from 'mime-types'\nimport { MaxSizeChecker, createDecoders } from '@atproto/common'\nimport {\n LexXrpcBody,\n LexXrpcProcedure,\n LexXrpcQuery,\n LexXrpcSubscription,\n Lexicons,\n jsonToLex,\n} from '@atproto/lexicon'\nimport { ResponseType } from '@atproto/xrpc'\nimport { InternalServerError, InvalidRequestError, XRPCError } from './errors'\nimport {\n Awaitable,\n HandlerSuccess,\n Input,\n Params,\n RouteOptions,\n UndecodedParams,\n handlerSuccess,\n} from './types'\n\nexport const asArray = <T>(arr: T | T[]): T[] =>\n Array.isArray(arr) ? arr : [arr]\n\nexport function setHeaders(\n res: OutgoingMessage,\n headers?: Record<string, string | number>,\n) {\n if (headers) {\n for (const [name, val] of Object.entries(headers)) {\n if (val != null) res.setHeader(name, val)\n }\n }\n}\n\nexport function decodeQueryParams(\n def: LexXrpcProcedure | LexXrpcQuery | LexXrpcSubscription,\n params: UndecodedParams,\n): Params {\n const decoded: Params = {}\n for (const k in params) {\n const val = params[k]\n const property = def.parameters?.properties?.[k]\n if (property) {\n if (property.type === 'array') {\n const vals: (typeof val)[] = []\n decoded[k] = val\n ? vals\n .concat(val) // Cast to array\n .flatMap((v) => decodeQueryParam(property.items.type, v) ?? [])\n : undefined\n } else {\n decoded[k] = decodeQueryParam(property.type, val)\n }\n }\n }\n return decoded\n}\n\nexport function decodeQueryParam(\n type: string,\n value: unknown,\n): string | number | boolean | undefined {\n if (!value) {\n return undefined\n }\n if (type === 'string' || type === 'datetime') {\n return String(value)\n }\n if (type === 'float') {\n return Number(String(value))\n } else if (type === 'integer') {\n return parseInt(String(value), 10) || 0\n } else if (type === 'boolean') {\n return value === 'true'\n }\n}\n\nexport type QueryParams = Record<string, undefined | string | string[]>\nexport function getQueryParams(url = ''): QueryParams {\n const result: QueryParams = Object.create(null)\n\n const queryStringIdx = url.indexOf('?')\n if (queryStringIdx === -1) return result\n\n const queryString = url.slice(queryStringIdx + 1)\n if (queryString === '') return result\n\n const searchParams = new URLSearchParams(queryString)\n for (const key of searchParams.keys()) {\n if (key === '__proto__') {\n // Prevent prototype pollution\n throw new InvalidRequestError(\n `Invalid query parameter: ${key}`,\n 'InvalidQueryParameter',\n )\n }\n\n const values = searchParams.getAll(key)\n result[key] = values.length === 1 ? values[0] : values\n }\n\n return result\n}\n\nexport function createInputVerifier(\n nsid: string,\n def: LexXrpcProcedure | LexXrpcQuery,\n options: RouteOptions,\n lexicons: Lexicons,\n): (req: Request, res: Response) => Awaitable<Input> {\n if (def.type === 'query' || !def.input) {\n return (req) => {\n // @NOTE We allow (and ignore) \"empty\" bodies\n if (getBodyPresence(req) === 'present') {\n throw new InvalidRequestError(\n `A request body was provided when none was expected`,\n )\n }\n\n return undefined\n }\n }\n\n // Lexicon definition expects a request body\n\n const { input } = def\n const { blobLimit } = options\n\n const allowedEncodings = parseDefEncoding(input)\n const checkEncoding = allowedEncodings.includes(ENCODING_ANY)\n ? undefined // No need to check\n : (encoding: string) => allowedEncodings.includes(encoding)\n\n const bodyParser = createBodyParser(input.encoding, options)\n\n return async (req, res) => {\n if (getBodyPresence(req) === 'missing') {\n throw new InvalidRequestError(\n `A request body is expected but none was provided`,\n )\n }\n\n const reqEncoding = parseReqEncoding(req)\n if (checkEncoding && !checkEncoding(reqEncoding)) {\n throw new InvalidRequestError(\n `Wrong request encoding (Content-Type): ${reqEncoding}`,\n )\n }\n\n if (bodyParser) {\n await bodyParser(req, res)\n }\n\n if (input.schema) {\n try {\n const lexBody = req.body ? jsonToLex(req.body) : req.body\n req.body = lexicons.assertValidXrpcInput(nsid, lexBody)\n } catch (e) {\n throw new InvalidRequestError(\n e instanceof Error ? e.message : String(e),\n )\n }\n }\n\n // if middleware already got the body, we pass that along as input\n // otherwise, we pass along a decoded readable stream\n const body = req.readableEnded ? req.body : decodeBodyStream(req, blobLimit)\n\n return { encoding: reqEncoding, body }\n }\n}\n\nexport function validateOutput(\n nsid: string,\n def: LexXrpcProcedure | LexXrpcQuery,\n output: HandlerSuccess | void,\n lexicons: Lexicons,\n): void {\n if (def.output) {\n // An output is expected\n if (output === undefined) {\n throw new InternalServerError(\n `A response body is expected but none was provided`,\n )\n }\n\n // Fool-proofing (should not be necessary due to type system)\n const result = handlerSuccess.safeParse(output)\n if (!result.success) {\n throw new InternalServerError(`Invalid handler output`, undefined, {\n cause: result.error,\n })\n }\n\n // output mime\n const { encoding } = output\n if (!encoding || !isValidEncoding(def.output, encoding)) {\n throw new InternalServerError(`Invalid response encoding: ${encoding}`)\n }\n\n // output schema\n if (def.output.schema) {\n try {\n output.body = lexicons.assertValidXrpcOutput(nsid, output.body)\n } catch (e) {\n throw new InternalServerError(\n e instanceof Error ? e.message : String(e),\n )\n }\n }\n } else {\n // Expects no output\n if (output !== undefined) {\n throw new InternalServerError(\n `A response body was provided when none was expected`,\n )\n }\n }\n}\n\nexport function parseReqEncoding(req: IncomingMessage): string {\n const encoding = normalizeMime(req.headers['content-type'])\n if (encoding) return encoding\n throw new InvalidRequestError(\n `Request encoding (Content-Type) required but not provided`,\n )\n}\n\nfunction normalizeMime(v?: string): string | null {\n if (!v) return null\n const fullType = contentType(v)\n if (!fullType) return null\n const shortType = fullType.split(';')[0]\n if (!shortType) return null\n return shortType\n}\n\nconst ENCODING_ANY = '*/*'\n\nfunction parseDefEncoding({ encoding }: LexXrpcBody) {\n return encoding.split(',').map(trimString)\n}\n\nfunction trimString(str: string): string {\n return str.trim()\n}\n\nfunction isValidEncoding(output: LexXrpcBody, encoding: string) {\n const normalized = normalizeMime(encoding)\n if (!normalized) return false\n\n const allowed = parseDefEncoding(output)\n return allowed.includes(ENCODING_ANY) || allowed.includes(normalized)\n}\n\ntype BodyPresence = 'missing' | 'empty' | 'present'\n\nfunction getBodyPresence(req: IncomingMessage): BodyPresence {\n if (req.headers['transfer-encoding'] != null) return 'present'\n if (req.headers['content-length'] === '0') return 'empty'\n if (req.headers['content-length'] != null) return 'present'\n return 'missing'\n}\n\nfunction createBodyParser(inputEncoding: string, options: RouteOptions) {\n if (inputEncoding === ENCODING_ANY) {\n // When the lexicon's input encoding is */*, the handler will determine how to process it\n return\n }\n const { jsonLimit, textLimit } = options\n const jsonParser = json({ limit: jsonLimit })\n const textParser = text({ limit: textLimit })\n // Transform json and text parser middlewares into a single function\n return (req: Request, res: Response) => {\n return new Promise<void>((resolve, reject) => {\n jsonParser(req, res, (err) => {\n if (err) return reject(XRPCError.fromError(err))\n textParser(req, res, (err) => {\n if (err) return reject(XRPCError.fromError(err))\n resolve()\n })\n })\n })\n }\n}\n\nfunction decodeBodyStream(\n req: IncomingMessage,\n maxSize: number | undefined,\n): Readable {\n const contentEncoding = req.headers['content-encoding']\n const contentLength = req.headers['content-length']\n\n const contentLengthParsed = contentLength\n ? parseInt(contentLength, 10)\n : undefined\n\n if (Number.isNaN(contentLengthParsed)) {\n throw new XRPCError(ResponseType.InvalidRequest, 'invalid content-length')\n }\n\n if (\n maxSize !== undefined &&\n contentLengthParsed !== undefined &&\n contentLengthParsed > maxSize\n ) {\n throw new XRPCError(\n ResponseType.PayloadTooLarge,\n 'request entity too large',\n )\n }\n\n let transforms: Duplex[]\n try {\n transforms = createDecoders(contentEncoding)\n } catch (cause) {\n throw new XRPCError(\n ResponseType.UnsupportedMediaType,\n 'unsupported content-encoding',\n undefined,\n { cause },\n )\n }\n\n if (maxSize !== undefined) {\n const maxSizeChecker = new MaxSizeChecker(\n maxSize,\n () =>\n new XRPCError(ResponseType.PayloadTooLarge, 'request entity too large'),\n )\n transforms.push(maxSizeChecker)\n }\n\n return transforms.length > 0\n ? (pipeline([req, ...transforms], () => {}) as Duplex)\n : req\n}\n\nexport function serverTimingHeader(timings: ServerTiming[]) {\n return timings\n .map((timing) => {\n let header = timing.name\n if (timing.duration) header += `;dur=${timing.duration}`\n if (timing.description) header += `;desc=\"${timing.description}\"`\n return header\n })\n .join(', ')\n}\n\nexport class ServerTimer implements ServerTiming {\n public duration?: number\n private startMs?: number\n constructor(\n public name: string,\n public description?: string,\n ) {}\n start() {\n this.startMs = Date.now()\n return this\n }\n stop() {\n assert(this.startMs, \"timer hasn't been started\")\n this.duration = Date.now() - this.startMs\n return this\n }\n}\n\nexport interface ServerTiming {\n name: string\n duration?: number\n description?: string\n}\n\nexport const parseReqNsid = (req: Request | IncomingMessage) =>\n parseUrlNsid('originalUrl' in req ? req.originalUrl : req.url || '/')\n\n/**\n * Validates and extracts the nsid from an xrpc path\n */\nexport const parseUrlNsid = (url: string): string => {\n const nsid = extractUrlNsid(url)\n if (nsid) return nsid\n throw new InvalidRequestError('invalid xrpc path')\n}\n\nexport const extractUrlNsid = (url: string): string | undefined => {\n // /!\\ Hot path\n\n if (\n // Ordered by likelihood of failure\n url.length <= 6 ||\n url[5] !== '/' ||\n url[4] !== 'c' ||\n url[3] !== 'p' ||\n url[2] !== 'r' ||\n url[1] !== 'x' ||\n url[0] !== '/'\n ) {\n return undefined\n }\n\n const startOfNsid = 6\n\n let curr = startOfNsid\n let char: number\n let alphaNumRequired = true\n for (; curr < url.length; curr++) {\n char = url.charCodeAt(curr)\n if (\n (char >= 48 && char <= 57) || // 0-9\n (char >= 65 && char <= 90) || // A-Z\n (char >= 97 && char <= 122) // a-z\n ) {\n alphaNumRequired = false\n } else if (char === 45 /* \"-\" */ || char === 46 /* \".\" */) {\n if (alphaNumRequired) {\n return undefined\n }\n alphaNumRequired = true\n } else if (char === 47 /* \"/\" */) {\n // Allow trailing slash (next char is either EOS or \"?\")\n if (curr === url.length - 1 || url.charCodeAt(curr + 1) === 63) {\n break\n }\n return undefined\n } else if (char === 63 /* \"?\"\" */) {\n break\n } else {\n return undefined\n }\n }\n\n // last char was one of: '-', '.', '/'\n if (alphaNumRequired) {\n return undefined\n }\n\n // A domain name consists of minimum two characters\n if (curr - startOfNsid < 2) {\n return undefined\n }\n\n // @TODO check max length of nsid\n\n return url.slice(startOfNsid, curr)\n}\n"]}
|
|
1
|
+
{"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"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","sourcesContent":["import assert from 'node:assert'\nimport { IncomingMessage, OutgoingMessage } from 'node:http'\nimport { Duplex, Readable, pipeline } from 'node:stream'\nimport {\n Request as ExpressRequest,\n Response as ExpressResponse,\n json,\n text,\n} from 'express'\nimport { contentType } from 'mime-types'\nimport { MaxSizeChecker, createDecoders } from '@atproto/common'\nimport { jsonToLex } from '@atproto/lex-json'\nimport { l } from '@atproto/lex-schema'\nimport {\n type LexXrpcBody,\n type LexXrpcProcedure,\n type LexXrpcQuery,\n type LexXrpcSubscription,\n Lexicons,\n jsonToLex as jsonToLexWithBlobRef,\n} from '@atproto/lexicon'\nimport { ResponseType } from '@atproto/xrpc'\nimport {\n ErrorResult,\n InternalServerError,\n InvalidRequestError,\n XRPCError,\n} from './errors'\nimport {\n Auth,\n Input,\n LexMethodInput,\n LexMethodOutput,\n LexMethodParams,\n Output,\n Params,\n RouteOptions,\n UndecodedParams,\n handlerSuccess,\n} from './types'\n\nexport type ParamsVerifierInternal<P extends Params = Params> = (\n req: IncomingMessage | ExpressRequest,\n) => P\n\nexport type AuthVerifierInternal<C, A extends Auth = Auth> = (\n ctx: C,\n) => Promise<Exclude<A, ErrorResult>>\n\nexport type InputVerifierInternal<I extends Input = Input> = (\n req: ExpressRequest,\n res: ExpressResponse,\n) => Promise<I>\n\nexport type OutputVerifierInternal<O extends Output = Output> = (\n handleOutput: O,\n) => void\n\nexport const asArray = <T>(arr: T | T[]): T[] =>\n Array.isArray(arr) ? arr : [arr]\n\nexport function setHeaders(\n res: OutgoingMessage,\n headers?: Record<string, string | number>,\n) {\n if (headers) {\n for (const [name, val] of Object.entries(headers)) {\n if (val != null) res.setHeader(name, val)\n }\n }\n}\n\nfunction decodeQueryParams(\n def: LexXrpcProcedure | LexXrpcQuery | LexXrpcSubscription,\n params: UndecodedParams,\n): Params {\n const decoded: Params = {}\n for (const k in params) {\n const val = params[k]\n const property = def.parameters?.properties?.[k]\n if (property) {\n if (property.type === 'array') {\n const vals: (typeof val)[] = []\n decoded[k] = val\n ? vals\n .concat(val) // Cast to array\n .flatMap((v) => decodeQueryParam(property.items.type, v) ?? [])\n : undefined\n } else {\n decoded[k] = decodeQueryParam(property.type, val)\n }\n }\n }\n return decoded\n}\n\nexport function decodeQueryParam(\n type: string,\n value: unknown,\n): string | number | boolean | undefined {\n if (!value) {\n return undefined\n }\n if (type === 'string' || type === 'datetime') {\n return String(value)\n }\n if (type === 'float') {\n return Number(String(value))\n } else if (type === 'integer') {\n return parseInt(String(value), 10) || 0\n } else if (type === 'boolean') {\n return value === 'true'\n }\n}\n\nexport function getSearchParams(url?: string): URLSearchParams | undefined {\n if (!url) return undefined\n\n const queryStringIdx = url.indexOf('?')\n if (queryStringIdx === -1) return undefined\n\n const queryString = url.slice(queryStringIdx + 1)\n if (queryString.length === 0) return undefined\n\n return new URLSearchParams(queryString)\n}\n\nexport function getQueryParams(\n req: IncomingMessage | ExpressRequest,\n): UndecodedParams {\n if ('query' in req) return req.query\n\n const result: UndecodedParams = Object.create(null)\n\n const searchParams = getSearchParams(req.url)\n if (!searchParams) return result\n\n if (searchParams.has('__proto__')) {\n // Prevent prototype pollution\n throw new InvalidRequestError(\n `Invalid query parameter: __proto__`,\n 'InvalidQueryParameter',\n )\n }\n\n for (const key of searchParams.keys()) {\n const values = searchParams.getAll(key)\n result[key] = values.length === 1 ? values[0] : values\n }\n\n return result\n}\n\nexport function createLexiconParamsVerifier<P extends Params = Params>(\n nsid: string,\n def: LexXrpcQuery | LexXrpcProcedure | LexXrpcSubscription,\n lexicons: Lexicons,\n): ParamsVerifierInternal<P> {\n return (req) => {\n const queryParams = getQueryParams(req)\n const params = decodeQueryParams(def, queryParams)\n try {\n return lexicons.assertValidXrpcParams(nsid, params) as P\n } catch (e) {\n // @NOTE WE historically did not check for specific error types here,\n throw new InvalidRequestError(String(e))\n }\n }\n}\n\nexport function createSchemaParamsVerifier<\n M extends l.Procedure | l.Query | l.Subscription,\n>(ns: l.Main<M>): ParamsVerifierInternal<LexMethodParams<M>> {\n const schema = l.getMain(ns)\n return (req) => {\n const urlSearchParams = getSearchParams(req.url) ?? new URLSearchParams()\n try {\n const params = schema.parameters.fromURLSearchParams(urlSearchParams)\n return params as LexMethodParams<M>\n } catch (err) {\n if (err instanceof l.LexValidationError) {\n throw new InvalidRequestError(err.message)\n }\n throw err\n }\n }\n}\n\nexport function createLexiconInputVerifier<I extends Input = Input>(\n nsid: string,\n def: LexXrpcProcedure | LexXrpcQuery,\n options: RouteOptions,\n lexicons: Lexicons,\n): InputVerifierInternal<I> {\n if (def.type === 'query' || !def.input) {\n return async (req) => {\n // @NOTE We allow (and ignore) \"empty\" bodies\n if (getBodyPresence(req) === 'present') {\n throw new InvalidRequestError(\n `A request body was provided when none was expected`,\n )\n }\n\n return undefined as I\n }\n }\n\n // Lexicon definition expects a request body\n\n const { input } = def\n const { blobLimit } = options\n\n const allowedEncodings = parseDefEncoding(input)\n const checkEncoding = allowedEncodings.includes(ENCODING_ANY)\n ? undefined // No need to check\n : (encoding: string) => allowedEncodings.includes(encoding)\n\n const bodyParser = createBodyParser(input.encoding, options)\n\n return async (req, res) => {\n if (getBodyPresence(req) === 'missing') {\n throw new InvalidRequestError(\n `A request body is expected but none was provided`,\n )\n }\n\n const reqEncoding = parseReqEncoding(req)\n if (checkEncoding && !checkEncoding(reqEncoding)) {\n throw new InvalidRequestError(\n `Wrong request encoding (Content-Type): ${reqEncoding}`,\n )\n }\n\n if (bodyParser) {\n await bodyParser(req, res)\n }\n\n if (input.schema) {\n try {\n const lexBody = req.body ? jsonToLexWithBlobRef(req.body) : req.body\n req.body = lexicons.assertValidXrpcInput(nsid, lexBody)\n } catch (cause) {\n throw new InvalidRequestError(\n cause instanceof Error ? cause.message : String(cause),\n undefined,\n { cause },\n )\n }\n }\n\n // if middleware already got the body, we pass that along as input\n // otherwise, we pass along a decoded readable stream\n const body = req.readableEnded ? req.body : decodeBodyStream(req, blobLimit)\n\n return { encoding: reqEncoding, body } as I\n }\n}\n\nexport function createSchemaInputVerifier<M extends l.Procedure | l.Query>(\n ns: l.Main<M>,\n options: RouteOptions,\n): InputVerifierInternal<LexMethodInput<M>> {\n const schema = l.getMain(ns)\n const { blobLimit } = options\n\n const input: l.Payload | undefined =\n 'input' in schema ? schema.input : undefined\n\n if (!input?.encoding) {\n //\n return async (req) => {\n if (getBodyPresence(req) === 'present') {\n throw new InvalidRequestError(\n `A request body was provided when none was expected`,\n )\n }\n\n return undefined as LexMethodInput<M>\n }\n }\n\n const bodyParser = createBodyParser(input.encoding, options)\n\n return async (req, res) => {\n if (getBodyPresence(req) === 'missing') {\n throw new InvalidRequestError(\n `A request body is expected but none was provided`,\n )\n }\n\n const reqEncoding = parseReqEncoding(req)\n if (!input.matchesEncoding(reqEncoding)) {\n throw new InvalidRequestError(\n `Wrong request encoding (Content-Type): ${reqEncoding}`,\n )\n }\n\n if (bodyParser) {\n await bodyParser(req, res)\n }\n\n if (input.schema) {\n try {\n const lexBody = req.body ? jsonToLex(req.body) : req.body\n req.body = input.schema.parse(lexBody)\n } catch (cause) {\n throw new InvalidRequestError(\n cause instanceof Error ? cause.message : String(cause),\n undefined,\n { cause },\n )\n }\n }\n\n // if middleware already got the body, we pass that along as input\n // otherwise, we pass along a decoded readable stream\n const body = req.readableEnded ? req.body : decodeBodyStream(req, blobLimit)\n\n return { encoding: reqEncoding, body } as LexMethodInput<M>\n }\n}\n\nexport function createLexiconOutputVerifier<O extends Output = Output>(\n nsid: string,\n def: LexXrpcProcedure | LexXrpcQuery,\n lexicons: Lexicons,\n): OutputVerifierInternal<O> {\n const outputDef = def.output\n\n // Expects no output\n if (!outputDef) {\n return (handlerOutput) => {\n if (handlerOutput !== undefined) {\n throw new InternalServerError(\n `A response body was provided when none was expected`,\n )\n }\n }\n }\n\n // An output is expected\n return (handlerOutput) => {\n if (handlerOutput === undefined) {\n throw new InternalServerError(\n `A response body is expected but none was provided`,\n )\n }\n\n if (!('encoding' in handlerOutput)) {\n // Ensure handlerOutput is valid ErrorResult\n if ('status' in handlerOutput && handlerOutput.status >= 400) {\n return\n }\n\n throw new InternalServerError(`Invalid handler output: missing encoding`)\n }\n\n if (!('body' in handlerOutput)) {\n // Ensure handlerOutput is valid HandlerPipeThrough\n if ('stream' in handlerOutput || 'buffer' in handlerOutput) {\n return // Validation is ignored for pipe-through outputs\n }\n\n throw new InternalServerError(`Invalid handler output: missing body`)\n }\n\n // Fool-proofing (should not be necessary due to type system)\n const result = handlerSuccess.safeParse(handlerOutput)\n if (!result.success) {\n throw new InternalServerError(`Invalid handler output`, undefined, {\n cause: result.reason,\n })\n }\n\n // output mime\n const { encoding } = handlerOutput\n if (!isValidEncoding(outputDef, encoding)) {\n throw new InternalServerError(`Invalid response encoding: ${encoding}`)\n }\n\n // output schema\n try {\n lexicons.assertValidXrpcOutput(nsid, handlerOutput.body)\n // @TODO Since the output verifier is typically enabled in dev/tests and\n // disabled in production, we don't want to assign the (altered) output\n // back to the handlerOutput object, as this would cause different\n // behaviors between environments. Instead, we should compare the value\n // returned by assertValidXrpcOutput with the original output and throw if\n // they differ (indicating that the output was mutated during validation,\n // e.g. due to default values being applied).\n } catch (cause) {\n const message =\n cause instanceof Error ? cause.message : 'Output body validation failed'\n throw new InternalServerError(message, undefined, { cause })\n }\n }\n}\n\nexport function createSchemaOutputVerifier<M extends l.Procedure | l.Query>(\n ns: l.Main<M>,\n): OutputVerifierInternal<LexMethodOutput<M>> {\n const outputSchema = l.getMain(ns).output\n return (handlerOutput) => {\n // @NOTE If the user of the lib wants to return an output that doesn't\n // conform to the schema, they can use HandlerPipeThrough return types\n if (!outputSchema.matchesEncoding(handlerOutput?.encoding)) {\n throw new InternalServerError('Output encoding mismatch')\n }\n if (outputSchema.schema) {\n const result = outputSchema.schema.safeValidate(handlerOutput?.body)\n if (!result.success) {\n throw new InternalServerError(result.reason.message, undefined, {\n cause: result.reason,\n })\n }\n } else if (!outputSchema.encoding && handlerOutput?.body !== undefined) {\n throw new InternalServerError('Output body not expected')\n }\n }\n}\n\nexport function parseReqEncoding(req: IncomingMessage): string {\n const encoding = normalizeMime(req.headers['content-type'])\n if (encoding) return encoding\n throw new InvalidRequestError(\n `Request encoding (Content-Type) required but not provided`,\n )\n}\n\nfunction normalizeMime(v?: string): string | null {\n if (!v) return null\n const fullType = contentType(v)\n if (!fullType) return null\n const shortType = fullType.split(';')[0]\n if (!shortType) return null\n return shortType\n}\n\nconst ENCODING_ANY = '*/*'\n\nfunction parseDefEncoding({ encoding }: LexXrpcBody) {\n return encoding.split(',').map(trimString)\n}\n\nfunction trimString(str: string): string {\n return str.trim()\n}\n\nfunction isValidEncoding(output: LexXrpcBody, encoding?: string) {\n if (!encoding) return false\n\n const normalized = normalizeMime(encoding)\n if (!normalized) return false\n\n const allowed = parseDefEncoding(output)\n if (!allowed.length) return false\n\n if (allowed.includes(ENCODING_ANY)) return true\n if (allowed.includes(normalized)) return true\n\n // Check for wildcard matches (e.g. normalized=application/json, allowed=application/*)\n for (const allowedEnc of allowed) {\n if (\n allowedEnc.endsWith('/*') &&\n normalized.startsWith(allowedEnc.slice(0, -1))\n ) {\n return true\n }\n }\n\n return false\n}\n\ntype BodyPresence = 'missing' | 'empty' | 'present'\n\nfunction getBodyPresence(req: IncomingMessage): BodyPresence {\n if (req.headers['transfer-encoding'] != null) return 'present'\n if (req.headers['content-length'] === '0') return 'empty'\n if (req.headers['content-length'] != null) return 'present'\n return 'missing'\n}\n\nfunction createBodyParser(inputEncoding: string, options: RouteOptions) {\n if (inputEncoding === ENCODING_ANY) {\n // When the lexicon's input encoding is */*, the handler will determine how to process it\n return\n }\n const { jsonLimit, textLimit } = options\n const jsonParser = json({ limit: jsonLimit })\n const textParser = text({ limit: textLimit })\n // Transform json and text parser middlewares into a single function\n return (req: ExpressRequest, res: ExpressResponse) => {\n return new Promise<void>((resolve, reject) => {\n jsonParser(req, res, (err) => {\n if (err) return reject(XRPCError.fromError(err))\n textParser(req, res, (err) => {\n if (err) return reject(XRPCError.fromError(err))\n resolve()\n })\n })\n })\n }\n}\n\nfunction decodeBodyStream(\n req: IncomingMessage,\n maxSize: number | undefined,\n): Readable {\n const contentEncoding = req.headers['content-encoding']\n const contentLength = req.headers['content-length']\n\n const contentLengthParsed = contentLength\n ? parseInt(contentLength, 10)\n : undefined\n\n if (Number.isNaN(contentLengthParsed)) {\n throw new XRPCError(ResponseType.InvalidRequest, 'invalid content-length')\n }\n\n if (\n maxSize !== undefined &&\n contentLengthParsed !== undefined &&\n contentLengthParsed > maxSize\n ) {\n throw new XRPCError(\n ResponseType.PayloadTooLarge,\n 'request entity too large',\n )\n }\n\n let transforms: Duplex[]\n try {\n transforms = createDecoders(contentEncoding)\n } catch (cause) {\n throw new XRPCError(\n ResponseType.UnsupportedMediaType,\n 'unsupported content-encoding',\n undefined,\n { cause },\n )\n }\n\n if (maxSize !== undefined) {\n const maxSizeChecker = new MaxSizeChecker(\n maxSize,\n () =>\n new XRPCError(ResponseType.PayloadTooLarge, 'request entity too large'),\n )\n transforms.push(maxSizeChecker)\n }\n\n return transforms.length > 0\n ? (pipeline([req, ...transforms], () => {}) as Duplex)\n : req\n}\n\nexport function serverTimingHeader(timings: ServerTiming[]) {\n return timings\n .map((timing) => {\n let header = timing.name\n if (timing.duration) header += `;dur=${timing.duration}`\n if (timing.description) header += `;desc=\"${timing.description}\"`\n return header\n })\n .join(', ')\n}\n\nexport class ServerTimer implements ServerTiming {\n public duration?: number\n private startMs?: number\n constructor(\n public name: string,\n public description?: string,\n ) {}\n start() {\n this.startMs = Date.now()\n return this\n }\n stop() {\n assert(this.startMs, \"timer hasn't been started\")\n this.duration = Date.now() - this.startMs\n return this\n }\n}\n\nexport interface ServerTiming {\n name: string\n duration?: number\n description?: string\n}\n\nexport const parseReqNsid = (req: ExpressRequest | IncomingMessage) =>\n parseUrlNsid('originalUrl' in req ? req.originalUrl : req.url || '/')\n\n/**\n * Validates and extracts the nsid from an xrpc path\n */\nexport const parseUrlNsid = (url: string): string => {\n const nsid = extractUrlNsid(url)\n if (nsid) return nsid\n throw new InvalidRequestError('invalid xrpc path')\n}\n\nexport const extractUrlNsid = (url: string): string | undefined => {\n // /!\\ Hot path\n\n if (\n // Ordered by likelihood of failure\n url.length <= 6 ||\n url[5] !== '/' ||\n url[4] !== 'c' ||\n url[3] !== 'p' ||\n url[2] !== 'r' ||\n url[1] !== 'x' ||\n url[0] !== '/'\n ) {\n return undefined\n }\n\n const startOfNsid = 6\n\n let curr = startOfNsid\n let char: number\n let alphaNumRequired = true\n for (; curr < url.length; curr++) {\n char = url.charCodeAt(curr)\n if (\n (char >= 48 && char <= 57) || // 0-9\n (char >= 65 && char <= 90) || // A-Z\n (char >= 97 && char <= 122) // a-z\n ) {\n alphaNumRequired = false\n } else if (char === 45 /* \"-\" */ || char === 46 /* \".\" */) {\n if (alphaNumRequired) {\n return undefined\n }\n alphaNumRequired = true\n } else if (char === 47 /* \"/\" */) {\n // Allow trailing slash (next char is either EOS or \"?\")\n if (curr === url.length - 1 || url.charCodeAt(curr + 1) === 63) {\n break\n }\n return undefined\n } else if (char === 63 /* \"?\"\" */) {\n break\n } else {\n return undefined\n }\n }\n\n // last char was one of: '-', '.', '/'\n if (alphaNumRequired) {\n return undefined\n }\n\n // A domain name consists of minimum two characters\n if (curr - startOfNsid < 2) {\n return undefined\n }\n\n // @TODO check max length of nsid\n\n return url.slice(startOfNsid, curr)\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atproto/xrpc-server",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.16",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "atproto HTTP API (XRPC) server library",
|
|
6
6
|
"keywords": [
|
|
@@ -24,12 +24,14 @@
|
|
|
24
24
|
"mime-types": "^2.1.35",
|
|
25
25
|
"rate-limiter-flexible": "^2.4.1",
|
|
26
26
|
"ws": "^8.12.0",
|
|
27
|
-
"
|
|
28
|
-
"@atproto/common": "^0.5.13",
|
|
27
|
+
"@atproto/common": "^0.5.14",
|
|
29
28
|
"@atproto/crypto": "^0.4.5",
|
|
30
|
-
"@atproto/lex-data": "^0.0.
|
|
31
|
-
"@atproto/lex-cbor": "^0.0.
|
|
32
|
-
"@atproto/
|
|
29
|
+
"@atproto/lex-data": "^0.0.13",
|
|
30
|
+
"@atproto/lex-cbor": "^0.0.14",
|
|
31
|
+
"@atproto/lex-client": "^0.0.16",
|
|
32
|
+
"@atproto/lex-json": "^0.0.13",
|
|
33
|
+
"@atproto/lex-schema": "^0.0.15",
|
|
34
|
+
"@atproto/lexicon": "^0.6.2",
|
|
33
35
|
"@atproto/ws-client": "^0.0.4",
|
|
34
36
|
"@atproto/xrpc": "^0.7.7"
|
|
35
37
|
},
|
|
@@ -45,7 +47,9 @@
|
|
|
45
47
|
"key-encoder": "^2.0.3",
|
|
46
48
|
"multiformats": "^9.9.0",
|
|
47
49
|
"typescript": "^5.6.3",
|
|
48
|
-
"@atproto/crypto": "^0.4.5"
|
|
50
|
+
"@atproto/crypto": "^0.4.5",
|
|
51
|
+
"@atproto/lex": "^0.0.21",
|
|
52
|
+
"@atproto/lex-document": "^0.0.16"
|
|
49
53
|
},
|
|
50
54
|
"scripts": {
|
|
51
55
|
"test": "jest",
|
package/src/auth.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as common from '@atproto/common'
|
|
2
2
|
import { MINUTE } from '@atproto/common'
|
|
3
3
|
import * as crypto from '@atproto/crypto'
|
|
4
|
+
import { DidString, isDidString } from '@atproto/lex-schema'
|
|
4
5
|
import { AuthRequiredError } from './errors'
|
|
5
6
|
|
|
6
7
|
type ServiceJwtParams = {
|
|
@@ -17,7 +18,7 @@ type ServiceJwtHeaders = {
|
|
|
17
18
|
} & Record<string, unknown>
|
|
18
19
|
|
|
19
20
|
type ServiceJwtPayload = {
|
|
20
|
-
iss: string
|
|
21
|
+
iss: DidString | `${DidString}#${string}`
|
|
21
22
|
aud: string
|
|
22
23
|
exp: number
|
|
23
24
|
lxm?: string
|
|
@@ -72,7 +73,10 @@ export const verifyJwt = async (
|
|
|
72
73
|
jwtStr: string,
|
|
73
74
|
ownDid: string | null, // null indicates to skip the audience check
|
|
74
75
|
lxm: string | null, // null indicates to skip the lxm check
|
|
75
|
-
getSigningKey: (
|
|
76
|
+
getSigningKey: (
|
|
77
|
+
iss: DidString | `${DidString}#${string}`,
|
|
78
|
+
forceRefresh: boolean,
|
|
79
|
+
) => Promise<string>,
|
|
76
80
|
verifySignatureWithKey: VerifySignatureWithKeyFn = cryptoVerifySignatureWithKey,
|
|
77
81
|
): Promise<ServiceJwtPayload> => {
|
|
78
82
|
const parts = jwtStr.split('.')
|
|
@@ -120,6 +124,9 @@ export const verifyJwt = async (
|
|
|
120
124
|
'BadJwtLexiconMethod',
|
|
121
125
|
)
|
|
122
126
|
}
|
|
127
|
+
if (!payload.iss || !isDidStringOrService(payload.iss)) {
|
|
128
|
+
throw new AuthRequiredError('jwt iss is not a valid did', 'BadJwtIss')
|
|
129
|
+
}
|
|
123
130
|
|
|
124
131
|
const msgBytes = Buffer.from(parts.slice(0, 2).join('.'), 'utf8')
|
|
125
132
|
const sigBytes = Buffer.from(sig, 'base64url')
|
|
@@ -207,3 +214,22 @@ const parsePayload = (b64: string): ServiceJwtPayload => {
|
|
|
207
214
|
}
|
|
208
215
|
return payload
|
|
209
216
|
}
|
|
217
|
+
|
|
218
|
+
function isDidStringOrService(
|
|
219
|
+
value: string,
|
|
220
|
+
): value is DidString | `${DidString}#${string}` {
|
|
221
|
+
const hashIdx = value.indexOf('#')
|
|
222
|
+
if (hashIdx === -1) {
|
|
223
|
+
return isDidString(value)
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// basic validation of the fragment part
|
|
227
|
+
const fragmentLen = value.length - hashIdx - 1
|
|
228
|
+
if (fragmentLen < 1 || value.includes('#', hashIdx + 1)) {
|
|
229
|
+
return false
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Validate the did part
|
|
233
|
+
const didPart = value.slice(0, hashIdx)
|
|
234
|
+
return isDidString(didPart)
|
|
235
|
+
}
|
package/src/errors.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { isHttpError } from 'http-errors'
|
|
2
|
-
import {
|
|
2
|
+
import { LexError, XrpcError } from '@atproto/lex-client'
|
|
3
|
+
import { l } from '@atproto/lex-schema'
|
|
3
4
|
import {
|
|
4
5
|
ResponseType,
|
|
5
6
|
ResponseTypeStrings,
|
|
@@ -11,12 +12,12 @@ import {
|
|
|
11
12
|
// @NOTE Do not depend (directly or indirectly) on "./types" here, as it would
|
|
12
13
|
// create a circular dependency.
|
|
13
14
|
|
|
14
|
-
export const errorResult =
|
|
15
|
-
status:
|
|
16
|
-
error:
|
|
17
|
-
message:
|
|
15
|
+
export const errorResult = l.object({
|
|
16
|
+
status: l.integer({ minimum: 400 }),
|
|
17
|
+
error: l.optional(l.string()),
|
|
18
|
+
message: l.optional(l.string()),
|
|
18
19
|
})
|
|
19
|
-
export type ErrorResult =
|
|
20
|
+
export type ErrorResult = l.Infer<typeof errorResult>
|
|
20
21
|
|
|
21
22
|
export function isErrorResult(v: unknown): v is ErrorResult {
|
|
22
23
|
return errorResult.safeParse(v).success
|
|
@@ -52,9 +53,13 @@ export class XRPCError extends Error {
|
|
|
52
53
|
return type
|
|
53
54
|
}
|
|
54
55
|
|
|
56
|
+
get error(): string | undefined {
|
|
57
|
+
return this.customErrorName ?? this.typeName
|
|
58
|
+
}
|
|
59
|
+
|
|
55
60
|
get payload() {
|
|
56
61
|
return {
|
|
57
|
-
error: this.
|
|
62
|
+
error: this.error,
|
|
58
63
|
message:
|
|
59
64
|
this.type === ResponseType.InternalServerError
|
|
60
65
|
? this.typeStr // Do not respond with error details for 500s
|
|
@@ -80,6 +85,17 @@ export class XRPCError extends Error {
|
|
|
80
85
|
return new XRPCError(type, message, error, { cause })
|
|
81
86
|
}
|
|
82
87
|
|
|
88
|
+
if (cause instanceof XrpcError) {
|
|
89
|
+
const { status, body } = cause.toDownstreamError()
|
|
90
|
+
return new XRPCError(status, body.message, body.error, { cause })
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (cause instanceof LexError) {
|
|
94
|
+
const data = cause.toJSON()
|
|
95
|
+
const type = ResponseType.InternalServerError
|
|
96
|
+
return new XRPCError(type, data.message, data.error, { cause })
|
|
97
|
+
}
|
|
98
|
+
|
|
83
99
|
if (isHttpError(cause)) {
|
|
84
100
|
return new XRPCError(cause.status, cause.message, cause.name, { cause })
|
|
85
101
|
}
|