@vfarcic/dot-ai 1.21.1 → 1.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/git-utils.d.ts +46 -0
- package/dist/core/git-utils.d.ts.map +1 -1
- package/dist/core/git-utils.js +192 -1
- package/dist/core/user-prompts-loader.d.ts +129 -1
- package/dist/core/user-prompts-loader.d.ts.map +1 -1
- package/dist/core/user-prompts-loader.js +654 -68
- package/dist/interfaces/cors-headers.d.ts +41 -0
- package/dist/interfaces/cors-headers.d.ts.map +1 -0
- package/dist/interfaces/cors-headers.js +43 -0
- package/dist/interfaces/header-redaction.d.ts +28 -0
- package/dist/interfaces/header-redaction.d.ts.map +1 -0
- package/dist/interfaces/header-redaction.js +48 -0
- package/dist/interfaces/mcp.d.ts +22 -0
- package/dist/interfaces/mcp.d.ts.map +1 -1
- package/dist/interfaces/mcp.js +131 -7
- package/dist/interfaces/openapi-generator.d.ts.map +1 -1
- package/dist/interfaces/openapi-generator.js +11 -5
- package/dist/interfaces/rest-api.d.ts +59 -15
- package/dist/interfaces/rest-api.d.ts.map +1 -1
- package/dist/interfaces/rest-api.js +288 -52
- package/dist/interfaces/routes/index.d.ts.map +1 -1
- package/dist/interfaces/routes/index.js +35 -0
- package/dist/interfaces/schemas/common.d.ts +33 -0
- package/dist/interfaces/schemas/common.d.ts.map +1 -1
- package/dist/interfaces/schemas/common.js +20 -1
- package/dist/interfaces/schemas/index.d.ts +2 -2
- package/dist/interfaces/schemas/index.d.ts.map +1 -1
- package/dist/interfaces/schemas/index.js +14 -3
- package/dist/interfaces/schemas/prompts.d.ts +155 -0
- package/dist/interfaces/schemas/prompts.d.ts.map +1 -1
- package/dist/interfaces/schemas/prompts.js +134 -1
- package/dist/tools/prompts.d.ts.map +1 -1
- package/dist/tools/prompts.js +37 -2
- package/package.json +6 -4
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared CORS allow-header lists (PRD #621 M2, Decision 1).
|
|
3
|
+
*
|
|
4
|
+
* The REST (src/interfaces/rest-api.ts) and front HTTP (src/interfaces/mcp.ts)
|
|
5
|
+
* layers each answer CORS preflight with their own `Access-Control-Allow-Headers`
|
|
6
|
+
* value, and the two were historically OUT OF SYNC. Centralizing the credential
|
|
7
|
+
* header name here guarantees the new `X-Dot-AI-Git-Token` header is advertised
|
|
8
|
+
* by BOTH preflight responses (Decision 1) and that the two lists can never
|
|
9
|
+
* silently drift apart on this header again.
|
|
10
|
+
*
|
|
11
|
+
* The two lists intentionally differ on the OTHER headers (mcp.ts also allows
|
|
12
|
+
* X-Session-Id / X-Dot-AI-Authorization), which is preserved.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Per-request git credential header (PRD #621 M2/M3, Decision 1). The CLI
|
|
16
|
+
* forwards its `DOT_AI_GIT_TOKEN` here so the server can authenticate an
|
|
17
|
+
* overridden (`?repo=`) clone against a second auth realm. Always a request
|
|
18
|
+
* header — never a query param or body field.
|
|
19
|
+
*/
|
|
20
|
+
export declare const GIT_TOKEN_HEADER = "X-Dot-AI-Git-Token";
|
|
21
|
+
/**
|
|
22
|
+
* Lowercased form for reading the header off Node's `req.headers` (Node
|
|
23
|
+
* lowercases all incoming header names).
|
|
24
|
+
*/
|
|
25
|
+
export declare const GIT_TOKEN_HEADER_LC: string;
|
|
26
|
+
/**
|
|
27
|
+
* `Access-Control-Allow-Headers` value for the REST API layer
|
|
28
|
+
* (rest-api.ts `setCorsHeaders`).
|
|
29
|
+
*/
|
|
30
|
+
export declare const REST_CORS_ALLOW_HEADERS = "Content-Type, Authorization, X-Dot-AI-Git-Token";
|
|
31
|
+
/**
|
|
32
|
+
* `Access-Control-Allow-Headers` value for the front HTTP layer (mcp.ts).
|
|
33
|
+
* Retains X-Session-Id and X-Dot-AI-Authorization from the pre-existing list.
|
|
34
|
+
*
|
|
35
|
+
* Includes `Mcp-Session-Id` because the MCP session router in mcp.ts routes
|
|
36
|
+
* requests by `req.headers['mcp-session-id']` (the Streamable HTTP transport's
|
|
37
|
+
* session header); without it a browser preflight requesting that header would
|
|
38
|
+
* fail (CodeRabbit Finding 3). `X-Session-Id` is kept for backward compat.
|
|
39
|
+
*/
|
|
40
|
+
export declare const MCP_CORS_ALLOW_HEADERS = "Content-Type, X-Session-Id, Mcp-Session-Id, Authorization, X-Dot-AI-Authorization, X-Dot-AI-Git-Token";
|
|
41
|
+
//# sourceMappingURL=cors-headers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cors-headers.d.ts","sourceRoot":"","sources":["../../src/interfaces/cors-headers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,uBAAuB,CAAC;AAErD;;;GAGG;AACH,eAAO,MAAM,mBAAmB,QAAiC,CAAC;AAElE;;;GAGG;AACH,eAAO,MAAM,uBAAuB,oDAAqD,CAAC;AAE1F;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB,0GAA2G,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Shared CORS allow-header lists (PRD #621 M2, Decision 1).
|
|
4
|
+
*
|
|
5
|
+
* The REST (src/interfaces/rest-api.ts) and front HTTP (src/interfaces/mcp.ts)
|
|
6
|
+
* layers each answer CORS preflight with their own `Access-Control-Allow-Headers`
|
|
7
|
+
* value, and the two were historically OUT OF SYNC. Centralizing the credential
|
|
8
|
+
* header name here guarantees the new `X-Dot-AI-Git-Token` header is advertised
|
|
9
|
+
* by BOTH preflight responses (Decision 1) and that the two lists can never
|
|
10
|
+
* silently drift apart on this header again.
|
|
11
|
+
*
|
|
12
|
+
* The two lists intentionally differ on the OTHER headers (mcp.ts also allows
|
|
13
|
+
* X-Session-Id / X-Dot-AI-Authorization), which is preserved.
|
|
14
|
+
*/
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.MCP_CORS_ALLOW_HEADERS = exports.REST_CORS_ALLOW_HEADERS = exports.GIT_TOKEN_HEADER_LC = exports.GIT_TOKEN_HEADER = void 0;
|
|
17
|
+
/**
|
|
18
|
+
* Per-request git credential header (PRD #621 M2/M3, Decision 1). The CLI
|
|
19
|
+
* forwards its `DOT_AI_GIT_TOKEN` here so the server can authenticate an
|
|
20
|
+
* overridden (`?repo=`) clone against a second auth realm. Always a request
|
|
21
|
+
* header — never a query param or body field.
|
|
22
|
+
*/
|
|
23
|
+
exports.GIT_TOKEN_HEADER = 'X-Dot-AI-Git-Token';
|
|
24
|
+
/**
|
|
25
|
+
* Lowercased form for reading the header off Node's `req.headers` (Node
|
|
26
|
+
* lowercases all incoming header names).
|
|
27
|
+
*/
|
|
28
|
+
exports.GIT_TOKEN_HEADER_LC = exports.GIT_TOKEN_HEADER.toLowerCase();
|
|
29
|
+
/**
|
|
30
|
+
* `Access-Control-Allow-Headers` value for the REST API layer
|
|
31
|
+
* (rest-api.ts `setCorsHeaders`).
|
|
32
|
+
*/
|
|
33
|
+
exports.REST_CORS_ALLOW_HEADERS = `Content-Type, Authorization, ${exports.GIT_TOKEN_HEADER}`;
|
|
34
|
+
/**
|
|
35
|
+
* `Access-Control-Allow-Headers` value for the front HTTP layer (mcp.ts).
|
|
36
|
+
* Retains X-Session-Id and X-Dot-AI-Authorization from the pre-existing list.
|
|
37
|
+
*
|
|
38
|
+
* Includes `Mcp-Session-Id` because the MCP session router in mcp.ts routes
|
|
39
|
+
* requests by `req.headers['mcp-session-id']` (the Streamable HTTP transport's
|
|
40
|
+
* session header); without it a browser preflight requesting that header would
|
|
41
|
+
* fail (CodeRabbit Finding 3). `X-Session-Id` is kept for backward compat.
|
|
42
|
+
*/
|
|
43
|
+
exports.MCP_CORS_ALLOW_HEADERS = `Content-Type, X-Session-Id, Mcp-Session-Id, Authorization, X-Dot-AI-Authorization, ${exports.GIT_TOKEN_HEADER}`;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Redaction of credential-bearing request headers before they are logged.
|
|
3
|
+
*
|
|
4
|
+
* The front HTTP layer (mcp.ts) logs incoming requests at debug level. Logging
|
|
5
|
+
* `req.headers` verbatim would leak credentials — `Authorization`,
|
|
6
|
+
* `X-Dot-AI-Authorization`, and (PRD #621) the per-request `X-Dot-AI-Git-Token`
|
|
7
|
+
* — bypassing the loader-level scrubbing. PRD #621 requires the forwarded token
|
|
8
|
+
* NEVER appear in logs, so these header values are replaced with a fixed
|
|
9
|
+
* placeholder before logging.
|
|
10
|
+
*
|
|
11
|
+
* Kept in its own module so the redaction is unit-testable without importing
|
|
12
|
+
* the heavy mcp.ts server module.
|
|
13
|
+
*/
|
|
14
|
+
/** Fixed placeholder substituted for a redacted header value. */
|
|
15
|
+
export declare const REDACTED_PLACEHOLDER = "***REDACTED***";
|
|
16
|
+
/**
|
|
17
|
+
* Lowercased names of headers whose values are credential-bearing and must be
|
|
18
|
+
* redacted before logging. Node lowercases all incoming header names, so the
|
|
19
|
+
* comparison is done in lowercase.
|
|
20
|
+
*/
|
|
21
|
+
export declare const SENSITIVE_HEADER_NAMES: ReadonlySet<string>;
|
|
22
|
+
/**
|
|
23
|
+
* Return a shallow copy of `headers` with the value of every credential-bearing
|
|
24
|
+
* header replaced by REDACTED_PLACEHOLDER. Non-sensitive headers are preserved
|
|
25
|
+
* verbatim. The input object is never mutated.
|
|
26
|
+
*/
|
|
27
|
+
export declare function redactSensitiveHeaders(headers: Record<string, unknown> | undefined): Record<string, unknown>;
|
|
28
|
+
//# sourceMappingURL=header-redaction.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"header-redaction.d.ts","sourceRoot":"","sources":["../../src/interfaces/header-redaction.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,iEAAiE;AACjE,eAAO,MAAM,oBAAoB,mBAAmB,CAAC;AAErD;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,EAAE,WAAW,CAAC,MAAM,CAMrD,CAAC;AAEH;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GAC3C,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CASzB"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Redaction of credential-bearing request headers before they are logged.
|
|
4
|
+
*
|
|
5
|
+
* The front HTTP layer (mcp.ts) logs incoming requests at debug level. Logging
|
|
6
|
+
* `req.headers` verbatim would leak credentials — `Authorization`,
|
|
7
|
+
* `X-Dot-AI-Authorization`, and (PRD #621) the per-request `X-Dot-AI-Git-Token`
|
|
8
|
+
* — bypassing the loader-level scrubbing. PRD #621 requires the forwarded token
|
|
9
|
+
* NEVER appear in logs, so these header values are replaced with a fixed
|
|
10
|
+
* placeholder before logging.
|
|
11
|
+
*
|
|
12
|
+
* Kept in its own module so the redaction is unit-testable without importing
|
|
13
|
+
* the heavy mcp.ts server module.
|
|
14
|
+
*/
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.SENSITIVE_HEADER_NAMES = exports.REDACTED_PLACEHOLDER = void 0;
|
|
17
|
+
exports.redactSensitiveHeaders = redactSensitiveHeaders;
|
|
18
|
+
const cors_headers_1 = require("./cors-headers");
|
|
19
|
+
/** Fixed placeholder substituted for a redacted header value. */
|
|
20
|
+
exports.REDACTED_PLACEHOLDER = '***REDACTED***';
|
|
21
|
+
/**
|
|
22
|
+
* Lowercased names of headers whose values are credential-bearing and must be
|
|
23
|
+
* redacted before logging. Node lowercases all incoming header names, so the
|
|
24
|
+
* comparison is done in lowercase.
|
|
25
|
+
*/
|
|
26
|
+
exports.SENSITIVE_HEADER_NAMES = new Set([
|
|
27
|
+
'authorization',
|
|
28
|
+
'x-dot-ai-authorization',
|
|
29
|
+
cors_headers_1.GIT_TOKEN_HEADER_LC,
|
|
30
|
+
'cookie',
|
|
31
|
+
'proxy-authorization',
|
|
32
|
+
]);
|
|
33
|
+
/**
|
|
34
|
+
* Return a shallow copy of `headers` with the value of every credential-bearing
|
|
35
|
+
* header replaced by REDACTED_PLACEHOLDER. Non-sensitive headers are preserved
|
|
36
|
+
* verbatim. The input object is never mutated.
|
|
37
|
+
*/
|
|
38
|
+
function redactSensitiveHeaders(headers) {
|
|
39
|
+
if (!headers)
|
|
40
|
+
return {};
|
|
41
|
+
const redacted = {};
|
|
42
|
+
for (const [name, value] of Object.entries(headers)) {
|
|
43
|
+
redacted[name] = exports.SENSITIVE_HEADER_NAMES.has(name.toLowerCase())
|
|
44
|
+
? exports.REDACTED_PLACEHOLDER
|
|
45
|
+
: value;
|
|
46
|
+
}
|
|
47
|
+
return redacted;
|
|
48
|
+
}
|
package/dist/interfaces/mcp.d.ts
CHANGED
|
@@ -73,7 +73,29 @@ export declare class MCPServer {
|
|
|
73
73
|
private generateRequestId;
|
|
74
74
|
start(): Promise<void>;
|
|
75
75
|
private startHttpTransport;
|
|
76
|
+
/**
|
|
77
|
+
* Buffer and JSON-parse the request body.
|
|
78
|
+
*
|
|
79
|
+
* PRD #647 F1: when `maxBytes` is supplied (the untrusted ingest route), the
|
|
80
|
+
* body is rejected with RequestBodyTooLargeError as soon as the declared
|
|
81
|
+
* Content-Length OR the accumulated bytes exceed the ceiling — so an
|
|
82
|
+
* authenticated multi-GB POST can't buffer unbounded and OOM the shared
|
|
83
|
+
* process. When `maxBytes` is omitted (every other endpoint), behavior is
|
|
84
|
+
* exactly as before: no cap.
|
|
85
|
+
*/
|
|
76
86
|
private parseRequestBody;
|
|
87
|
+
/**
|
|
88
|
+
* PRD #647 F1: true only for the prompts source ingest endpoint, the one
|
|
89
|
+
* untrusted route the raw-body cap is scoped to. Parses the pathname so a
|
|
90
|
+
* query string can't bypass (or wrongly trip) the cap.
|
|
91
|
+
*
|
|
92
|
+
* PRD #647 C2 (CodeRabbit): the cap check runs BEFORE route dispatch, so a
|
|
93
|
+
* non-canonical pathname must be normalized here or it slips past the cap and
|
|
94
|
+
* buffers an uncapped body (DoS). A strict `===` let `POST /api/v1/prompts/
|
|
95
|
+
* sources/` (trailing slash) — the same ingest surface — skip `maxBytes`.
|
|
96
|
+
* Collapse trailing slashes (keeping a bare "/" intact) before comparing.
|
|
97
|
+
*/
|
|
98
|
+
private isPromptsIngestRequest;
|
|
77
99
|
stop(): Promise<void>;
|
|
78
100
|
isReady(): boolean;
|
|
79
101
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../../src/interfaces/mcp.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAYH,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../../src/interfaces/mcp.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAYH,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAgFtC,OAAO,EAAgB,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAcvD,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,+DAA+D;IAC/D,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAqDD,qBAAa,SAAS;IACpB,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,WAAW,CAAkB;IACrC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,gBAAgB,CAAa;IACrC,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,UAAU,CAAC,CAAkC;IACrD,4EAA4E;IAC5E,OAAO,CAAC,QAAQ,CAAiC;IACjD,OAAO,CAAC,cAAc,CAAC,CAAiC;IACxD,OAAO,CAAC,YAAY,CAAmB;IACvC,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,aAAa,CAAC,CAAgB;IACtC,OAAO,CAAC,QAAQ,CAAC,CAA6B;IAC9C,OAAO,CAAC,aAAa,CAAC,CAAqB;IAC3C,OAAO,CAAC,SAAS,CAAC,CAAM;gBAEZ,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,eAAe;IA6BjD;;;OAGG;IACH,gBAAgB,IAAI,aAAa,GAAG,SAAS;IAQ7C;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAuBxB;;OAEG;IACH,OAAO,CAAC,eAAe;IA2CvB;;OAEG;IACH,OAAO,CAAC,WAAW;IA8KnB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAmBzB;;;;OAIG;YACW,mBAAmB;IA6CjC;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAkCzB,OAAO,CAAC,qBAAqB;IAS7B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAWzB,OAAO,CAAC,iBAAiB;IAInB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YAed,kBAAkB;IA0ThC;;;;;;;;;OASG;YACW,gBAAgB;IAgD9B;;;;;;;;;;OAUG;IACH,OAAO,CAAC,sBAAsB;IAWxB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAoC3B,OAAO,IAAI,OAAO;CAGnB"}
|
package/dist/interfaces/mcp.js
CHANGED
|
@@ -28,6 +28,8 @@ const impact_analysis_1 = require("../tools/impact-analysis");
|
|
|
28
28
|
const prompts_1 = require("../tools/prompts");
|
|
29
29
|
const rest_registry_1 = require("./rest-registry");
|
|
30
30
|
const rest_api_1 = require("./rest-api");
|
|
31
|
+
const cors_headers_1 = require("./cors-headers");
|
|
32
|
+
const header_redaction_1 = require("./header-redaction");
|
|
31
33
|
const oauth_1 = require("./oauth");
|
|
32
34
|
const request_context_1 = require("./request-context");
|
|
33
35
|
const rbac_1 = require("../core/rbac");
|
|
@@ -42,6 +44,37 @@ const plugin_registry_1 = require("../core/plugin-registry");
|
|
|
42
44
|
const SESSION_TTL_MS = 60 * 60 * 1000;
|
|
43
45
|
/** How often to check for expired sessions. */
|
|
44
46
|
const SESSION_GC_INTERVAL_MS = 5 * 60 * 1000;
|
|
47
|
+
/**
|
|
48
|
+
* PRD #647 F1 — hard raw-body byte ceiling for the untrusted prompts source
|
|
49
|
+
* ingest endpoint (POST /api/v1/prompts/sources).
|
|
50
|
+
*
|
|
51
|
+
* parseRequestBody buffers the whole body before JSON.parse + per-file
|
|
52
|
+
* base64-decode, all of which allocate fully BEFORE the 256 KiB *decoded* cap
|
|
53
|
+
* in ingestPromptsSource — so without a raw ceiling a multi-GB authenticated
|
|
54
|
+
* POST OOMs the shared process. 512 KiB is:
|
|
55
|
+
* - comfortably ABOVE the largest valid manifest: 256 KiB decoded ≈ ~342 KiB
|
|
56
|
+
* base64 + JSON/path/mode overhead (~350 KiB raw), and
|
|
57
|
+
* - testably BELOW the ~1 MiB nginx-ingress limit the integration suite
|
|
58
|
+
* traverses, so the app returns 413 before the proxy would.
|
|
59
|
+
* The cap is scoped to the ingest route only (see parseRequestBody's maxBytes
|
|
60
|
+
* argument); every other endpoint keeps today's uncapped behavior.
|
|
61
|
+
*/
|
|
62
|
+
const INGEST_MAX_RAW_BODY_BYTES = 512 * 1024;
|
|
63
|
+
/** Pathname of the prompts source ingest endpoint the raw-body cap is scoped to. */
|
|
64
|
+
const PROMPTS_INGEST_PATHNAME = '/api/v1/prompts/sources';
|
|
65
|
+
/**
|
|
66
|
+
* PRD #647 F1 — thrown by parseRequestBody when the raw request body exceeds the
|
|
67
|
+
* per-route ceiling, so the HTTP handler can map it to a 413 (matching the mock
|
|
68
|
+
* server's read-json-body.ts) instead of buffering an unbounded body.
|
|
69
|
+
*/
|
|
70
|
+
class RequestBodyTooLargeError extends Error {
|
|
71
|
+
limit;
|
|
72
|
+
constructor(limit) {
|
|
73
|
+
super(`Request body exceeds ${limit} bytes`);
|
|
74
|
+
this.name = 'RequestBodyTooLargeError';
|
|
75
|
+
this.limit = limit;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
45
78
|
class MCPServer {
|
|
46
79
|
dotAI;
|
|
47
80
|
initialized = false;
|
|
@@ -423,15 +456,23 @@ class MCPServer {
|
|
|
423
456
|
// Execute entire request within the span's context for proper propagation
|
|
424
457
|
await api_1.context.with(api_1.trace.setSpan(api_1.context.active(), span), async () => {
|
|
425
458
|
try {
|
|
459
|
+
// PRD #621: the forwarded token must never appear in logs. Redact
|
|
460
|
+
// credential-bearing headers (HIGH-1) AND scrub credentials from the
|
|
461
|
+
// request URL — a ?repo=https://user:token@host or a
|
|
462
|
+
// credential-bearing query param would otherwise leak verbatim
|
|
463
|
+
// (CodeRabbit Finding 1). sanitizeRequestUrlForLogging is the shared
|
|
464
|
+
// helper already used by the REST layer.
|
|
426
465
|
this.logger.debug('HTTP request received', {
|
|
427
466
|
method: req.method,
|
|
428
|
-
url: req.url,
|
|
429
|
-
headers: req.headers,
|
|
467
|
+
url: (0, rest_api_1.sanitizeRequestUrlForLogging)(req.url),
|
|
468
|
+
headers: (0, header_redaction_1.redactSensitiveHeaders)(req.headers),
|
|
430
469
|
});
|
|
431
470
|
// Handle CORS for browser-based clients
|
|
432
471
|
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
433
472
|
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, DELETE, OPTIONS');
|
|
434
|
-
|
|
473
|
+
// PRD #621 M2 / Decision 1: includes X-Dot-AI-Git-Token, kept in
|
|
474
|
+
// sync with the REST allowlist via cors-headers.ts.
|
|
475
|
+
res.setHeader('Access-Control-Allow-Headers', cors_headers_1.MCP_CORS_ALLOW_HEADERS);
|
|
435
476
|
if (req.method === 'OPTIONS') {
|
|
436
477
|
res.writeHead(204);
|
|
437
478
|
res.end();
|
|
@@ -486,12 +527,34 @@ class MCPServer {
|
|
|
486
527
|
// Parse request body for POST requests
|
|
487
528
|
let body = undefined;
|
|
488
529
|
if (req.method === 'POST') {
|
|
489
|
-
body
|
|
530
|
+
// PRD #647 F1: cap the raw body for the untrusted ingest route
|
|
531
|
+
// only (default = today's uncapped behavior elsewhere) and map
|
|
532
|
+
// an oversize body to 413 (matches the mock server).
|
|
533
|
+
const maxBytes = this.isPromptsIngestRequest(req.url)
|
|
534
|
+
? INGEST_MAX_RAW_BODY_BYTES
|
|
535
|
+
: undefined;
|
|
536
|
+
try {
|
|
537
|
+
body = await this.parseRequestBody(req, maxBytes);
|
|
538
|
+
}
|
|
539
|
+
catch (error) {
|
|
540
|
+
if (error instanceof RequestBodyTooLargeError) {
|
|
541
|
+
this.logger.warn('Request body too large', {
|
|
542
|
+
url: (0, rest_api_1.sanitizeRequestUrlForLogging)(req.url),
|
|
543
|
+
limit: error.limit,
|
|
544
|
+
});
|
|
545
|
+
(0, error_response_1.sendErrorResponse)(res, 413, 'PAYLOAD_TOO_LARGE', error.message);
|
|
546
|
+
endSpan(413);
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
throw error;
|
|
550
|
+
}
|
|
490
551
|
}
|
|
491
552
|
// Check if this is a REST API request
|
|
492
553
|
if (this.restApiRouter.isApiRequest(req.url || '')) {
|
|
493
554
|
this.logger.debug('Routing to REST API handler', {
|
|
494
|
-
|
|
555
|
+
// PRD #647 M5 (F2): scrub credential-bearing ?repo=/?source=
|
|
556
|
+
// values before they reach the log (matches the REST handler).
|
|
557
|
+
url: (0, rest_api_1.sanitizeRequestUrlForLogging)(req.url),
|
|
495
558
|
});
|
|
496
559
|
// Mark span as REST API request
|
|
497
560
|
span.setAttribute('request.type', 'rest-api');
|
|
@@ -593,11 +656,49 @@ class MCPServer {
|
|
|
593
656
|
}).on('error', reject);
|
|
594
657
|
});
|
|
595
658
|
}
|
|
596
|
-
|
|
659
|
+
/**
|
|
660
|
+
* Buffer and JSON-parse the request body.
|
|
661
|
+
*
|
|
662
|
+
* PRD #647 F1: when `maxBytes` is supplied (the untrusted ingest route), the
|
|
663
|
+
* body is rejected with RequestBodyTooLargeError as soon as the declared
|
|
664
|
+
* Content-Length OR the accumulated bytes exceed the ceiling — so an
|
|
665
|
+
* authenticated multi-GB POST can't buffer unbounded and OOM the shared
|
|
666
|
+
* process. When `maxBytes` is omitted (every other endpoint), behavior is
|
|
667
|
+
* exactly as before: no cap.
|
|
668
|
+
*/
|
|
669
|
+
async parseRequestBody(req, maxBytes) {
|
|
597
670
|
return new Promise((resolve, reject) => {
|
|
671
|
+
// Reject up front on a declared Content-Length over the cap, so we never
|
|
672
|
+
// start buffering a body we already know is too large.
|
|
673
|
+
if (maxBytes !== undefined) {
|
|
674
|
+
const declaredLen = parseInt(req.headers['content-length'] || '0', 10);
|
|
675
|
+
if (!Number.isNaN(declaredLen) && declaredLen > maxBytes) {
|
|
676
|
+
reject(new RequestBodyTooLargeError(maxBytes));
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
}
|
|
598
680
|
let body = '';
|
|
599
|
-
|
|
681
|
+
let received = 0;
|
|
682
|
+
let aborted = false;
|
|
683
|
+
req.on('data', chunk => {
|
|
684
|
+
if (aborted)
|
|
685
|
+
return;
|
|
686
|
+
// Defense-in-depth: enforce the cap against actual bytes in case the
|
|
687
|
+
// Content-Length header is absent or lies.
|
|
688
|
+
if (maxBytes !== undefined) {
|
|
689
|
+
received += chunk.length;
|
|
690
|
+
if (received > maxBytes) {
|
|
691
|
+
aborted = true;
|
|
692
|
+
req.destroy();
|
|
693
|
+
reject(new RequestBodyTooLargeError(maxBytes));
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
body += chunk.toString();
|
|
698
|
+
});
|
|
600
699
|
req.on('end', () => {
|
|
700
|
+
if (aborted)
|
|
701
|
+
return;
|
|
601
702
|
try {
|
|
602
703
|
resolve(body ? JSON.parse(body) : undefined);
|
|
603
704
|
}
|
|
@@ -608,6 +709,29 @@ class MCPServer {
|
|
|
608
709
|
req.on('error', reject);
|
|
609
710
|
});
|
|
610
711
|
}
|
|
712
|
+
/**
|
|
713
|
+
* PRD #647 F1: true only for the prompts source ingest endpoint, the one
|
|
714
|
+
* untrusted route the raw-body cap is scoped to. Parses the pathname so a
|
|
715
|
+
* query string can't bypass (or wrongly trip) the cap.
|
|
716
|
+
*
|
|
717
|
+
* PRD #647 C2 (CodeRabbit): the cap check runs BEFORE route dispatch, so a
|
|
718
|
+
* non-canonical pathname must be normalized here or it slips past the cap and
|
|
719
|
+
* buffers an uncapped body (DoS). A strict `===` let `POST /api/v1/prompts/
|
|
720
|
+
* sources/` (trailing slash) — the same ingest surface — skip `maxBytes`.
|
|
721
|
+
* Collapse trailing slashes (keeping a bare "/" intact) before comparing.
|
|
722
|
+
*/
|
|
723
|
+
isPromptsIngestRequest(url) {
|
|
724
|
+
if (!url)
|
|
725
|
+
return false;
|
|
726
|
+
try {
|
|
727
|
+
const pathname = new URL(url, 'http://internal.invalid').pathname;
|
|
728
|
+
const normalized = pathname.replace(/\/+$/, '') || '/';
|
|
729
|
+
return normalized === PROMPTS_INGEST_PATHNAME;
|
|
730
|
+
}
|
|
731
|
+
catch {
|
|
732
|
+
return false;
|
|
733
|
+
}
|
|
734
|
+
}
|
|
611
735
|
async stop() {
|
|
612
736
|
// Stop OAuth provider pruning timer
|
|
613
737
|
if (this.oauthProvider) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openapi-generator.d.ts","sourceRoot":"","sources":["../../src/interfaces/openapi-generator.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"openapi-generator.d.ts","sourceRoot":"","sources":["../../src/interfaces/openapi-generator.ts"],"names":[],"mappings":"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"}
|
|
@@ -26,7 +26,13 @@ class OpenApiGenerator {
|
|
|
26
26
|
specCache;
|
|
27
27
|
lastCacheUpdate = 0;
|
|
28
28
|
cacheValidityMs = 60000; // 1 minute
|
|
29
|
-
|
|
29
|
+
// Keyed on the Zod schema OBJECT identity, not a structural stringify: two
|
|
30
|
+
// distinct schemas that differ only in their `.describe()` text serialize to
|
|
31
|
+
// the same string, so a string key would conflate them and leak one schema's
|
|
32
|
+
// descriptions into the other's endpoint (e.g. the prompts list vs. render
|
|
33
|
+
// `?source=` query). Identity keying gives each schema object its own entry
|
|
34
|
+
// while a schema reused across routes still hits the cache.
|
|
35
|
+
schemaCache = new WeakMap();
|
|
30
36
|
constructor(toolRegistry, logger, config = {}, routeRegistry) {
|
|
31
37
|
this.toolRegistry = toolRegistry;
|
|
32
38
|
this.routeRegistry = routeRegistry;
|
|
@@ -472,6 +478,7 @@ class OpenApiGenerator {
|
|
|
472
478
|
404: 'Not found',
|
|
473
479
|
405: 'Method not allowed',
|
|
474
480
|
409: 'Conflict',
|
|
481
|
+
413: 'Payload too large',
|
|
475
482
|
422: 'Unprocessable entity',
|
|
476
483
|
500: 'Internal server error',
|
|
477
484
|
502: 'Bad gateway',
|
|
@@ -483,16 +490,15 @@ class OpenApiGenerator {
|
|
|
483
490
|
* Convert Zod schema to JSON Schema with caching
|
|
484
491
|
*/
|
|
485
492
|
zodSchemaToJsonSchema(schema) {
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
return this.schemaCache.get(cacheKey);
|
|
493
|
+
if (this.schemaCache.has(schema)) {
|
|
494
|
+
return this.schemaCache.get(schema);
|
|
489
495
|
}
|
|
490
496
|
try {
|
|
491
497
|
const result = zod_1.z.toJSONSchema(schema);
|
|
492
498
|
// Remove $schema and additionalProperties (not valid in OpenAPI component schemas)
|
|
493
499
|
delete result.$schema;
|
|
494
500
|
delete result.additionalProperties;
|
|
495
|
-
this.schemaCache.set(
|
|
501
|
+
this.schemaCache.set(schema, result);
|
|
496
502
|
return result;
|
|
497
503
|
}
|
|
498
504
|
catch (error) {
|
|
@@ -9,6 +9,7 @@ import { RestToolRegistry, ToolInfo } from './rest-registry';
|
|
|
9
9
|
import { RestRouteRegistry } from './rest-route-registry';
|
|
10
10
|
import { Logger } from '../core/error-handling';
|
|
11
11
|
import { DotAI } from '../core/index';
|
|
12
|
+
import { UserPromptsOverride } from '../core/user-prompts-loader';
|
|
12
13
|
import { PluginManager } from '../core/plugin-manager';
|
|
13
14
|
/**
|
|
14
15
|
* Constant placeholder used when the request URL fails to parse and the
|
|
@@ -19,9 +20,11 @@ import { PluginManager } from '../core/plugin-manager';
|
|
|
19
20
|
export declare const UNPARSEABLE_QUERY_PLACEHOLDER = "?<redacted-unparseable>";
|
|
20
21
|
/**
|
|
21
22
|
* F3: req.url is logged on every request; with PRD #581 the query string may
|
|
22
|
-
* carry `?repo=<user-supplied-url>` whose value can include credentials
|
|
23
|
-
*
|
|
24
|
-
*
|
|
23
|
+
* carry `?repo=<user-supplied-url>` whose value can include credentials, and
|
|
24
|
+
* PRD #647 adds `?source=<identifier>` which is equally credential-bearing (it
|
|
25
|
+
* may be a `https://user:tok@host` git URL). This helper rewrites BOTH values
|
|
26
|
+
* to their credential-scrubbed form so the raw token doesn't reach the log.
|
|
27
|
+
* Everything else is preserved verbatim.
|
|
25
28
|
*
|
|
26
29
|
* CodeRabbit Major B: on parse failure, we no longer return the input
|
|
27
30
|
* verbatim — an unparseable URL is more likely than a parseable one to hide
|
|
@@ -31,6 +34,47 @@ export declare const UNPARSEABLE_QUERY_PLACEHOLDER = "?<redacted-unparseable>";
|
|
|
31
34
|
* '?' are pass-through (no risk).
|
|
32
35
|
*/
|
|
33
36
|
export declare function sanitizeRequestUrlForLogging(url: string | undefined): string | undefined;
|
|
37
|
+
/**
|
|
38
|
+
* Extract and validate the per-request prompts override.
|
|
39
|
+
*
|
|
40
|
+
* Threads three optional, additive inputs from the request into a
|
|
41
|
+
* UserPromptsOverride (PRD #581 introduced `repo`; PRD #621 M1 adds `path`
|
|
42
|
+
* and `branch`):
|
|
43
|
+
* - repoParam → override.repoUrl (GET ?repo= / POST body `repo`)
|
|
44
|
+
* - pathParam → override.subPath (GET ?path= / POST body `path`)
|
|
45
|
+
* - branchParam → override.branch (GET ?branch= / POST body `branch`)
|
|
46
|
+
* - gitToken → override.gitToken (X-Dot-AI-Git-Token request header; M2)
|
|
47
|
+
*
|
|
48
|
+
* Returns:
|
|
49
|
+
* - { ok: true, override } when no `repo` is supplied (override undefined;
|
|
50
|
+
* any path/branch/token are ignored, since they only qualify an override —
|
|
51
|
+
* this keeps the no-`repo` / env-var-configured path unchanged).
|
|
52
|
+
* - { ok: true, override } when a syntactically valid override is supplied.
|
|
53
|
+
* - { ok: false, message } when the override fails validation (HTTP 400).
|
|
54
|
+
*
|
|
55
|
+
* The validation message is run through scrubCredentials so embedded tokens
|
|
56
|
+
* never reach the wire response.
|
|
57
|
+
*
|
|
58
|
+
* Backward compatibility (PRD #621, non-negotiable): when path/branch are
|
|
59
|
+
* absent or empty, the override carries `repoUrl` only — byte-identical to
|
|
60
|
+
* the PRD #581 behavior (same clone target: repo root, `main`). subPath and
|
|
61
|
+
* branch are populated ONLY for a non-empty value, so downstream defaults are
|
|
62
|
+
* untouched. The credential header is INERT unless a `?repo=` override is
|
|
63
|
+
* present: without a repo this returns `override: undefined` before the token
|
|
64
|
+
* is ever read, so the env-var path is unaffected by a forwarded header.
|
|
65
|
+
*
|
|
66
|
+
* Validation is delegated to getUserPromptsConfigFromOverride (scheme,
|
|
67
|
+
* sanitizeRelativePath for subPath, isValidGitBranch for branch) and happens
|
|
68
|
+
* BEFORE any clone or shared-cache mutation, so a rejected override can never
|
|
69
|
+
* corrupt the env-var-configured cache.
|
|
70
|
+
*/
|
|
71
|
+
export declare function extractPromptsOverride(repoParam: unknown, pathParam?: unknown, branchParam?: unknown, gitToken?: string, sourceParam?: unknown): {
|
|
72
|
+
ok: true;
|
|
73
|
+
override?: UserPromptsOverride;
|
|
74
|
+
} | {
|
|
75
|
+
ok: false;
|
|
76
|
+
message: string;
|
|
77
|
+
};
|
|
34
78
|
/**
|
|
35
79
|
* HTTP status codes for REST responses
|
|
36
80
|
*/
|
|
@@ -233,18 +277,6 @@ export declare class RestApiRouter {
|
|
|
233
277
|
* Returns container logs for a pod
|
|
234
278
|
*/
|
|
235
279
|
private handleGetLogs;
|
|
236
|
-
/**
|
|
237
|
-
* Extract and validate the per-request `repo` override (PRD #581).
|
|
238
|
-
*
|
|
239
|
-
* Returns:
|
|
240
|
-
* - { ok: true, override } when no repo param is supplied, override is undefined.
|
|
241
|
-
* - { ok: true, override } when a syntactically valid override URL is supplied.
|
|
242
|
-
* - { ok: false, message } when the override fails validation (HTTP 400).
|
|
243
|
-
*
|
|
244
|
-
* The validation message is run through scrubCredentials so embedded tokens
|
|
245
|
-
* never reach the wire response.
|
|
246
|
-
*/
|
|
247
|
-
private extractPromptsOverride;
|
|
248
280
|
/**
|
|
249
281
|
* Handle prompts list requests
|
|
250
282
|
*/
|
|
@@ -257,6 +289,18 @@ export declare class RestApiRouter {
|
|
|
257
289
|
* Handle prompts cache refresh requests (PRD #386, extended PRD #581)
|
|
258
290
|
*/
|
|
259
291
|
private handlePromptsCacheRefresh;
|
|
292
|
+
/**
|
|
293
|
+
* Handle prompts source ingestion (PRD #647 M2).
|
|
294
|
+
*
|
|
295
|
+
* Accepts a JSON manifest { source, contentHash, files:[{path, content(base64),
|
|
296
|
+
* mode}] }, base64-decodes and caches the uploaded skill source keyed by its
|
|
297
|
+
* `source` identifier in the in-memory ingested cache. A later
|
|
298
|
+
* POST /api/v1/prompts/:promptName?source=<identifier> renders it through the
|
|
299
|
+
* existing render path with no git operation. The (scrubbed) source is echoed
|
|
300
|
+
* back. Bearer-gated by the same checkBearerAuth path as every non-OpenAPI
|
|
301
|
+
* request.
|
|
302
|
+
*/
|
|
303
|
+
private handlePromptsSourceIngest;
|
|
260
304
|
/**
|
|
261
305
|
* Handle visualization requests (PRD #317)
|
|
262
306
|
* Returns structured visualization data for a query session
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rest-api.d.ts","sourceRoot":"","sources":["../../src/interfaces/rest-api.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAE5D,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAE7D,OAAO,EAAE,iBAAiB,EAAc,MAAM,uBAAuB,CAAC;AAEtE,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChD,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"rest-api.d.ts","sourceRoot":"","sources":["../../src/interfaces/rest-api.ts"],"names":[],"mappings":"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"}
|