@mcp-abap-adt/connection 1.9.0 → 1.10.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.
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Detect whether a WWW-Authenticate header (or a Negotiate challenge token) indicates NTLM.
3
+ * NTLM is rejected: only Kerberos/SPNEGO Negotiate is acceptable.
4
+ *
5
+ * Two cases:
6
+ * - the header offers the NTLM scheme directly: `WWW-Authenticate: NTLM`
7
+ * - an NTLM token tunneled via Negotiate: `Negotiate <base64>` where the base64 decodes to
8
+ * bytes starting with the NTLM signature "NTLMSSP\0" (base64 prefix "TlRMTVNTUAA").
9
+ */
10
+ export declare function isNtlmChallenge(wwwAuthenticate: string | undefined): boolean;
11
+ //# sourceMappingURL=ntlm.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ntlm.d.ts","sourceRoot":"","sources":["../../src/auth/ntlm.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,eAAe,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAiB5E"}
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isNtlmChallenge = isNtlmChallenge;
4
+ /**
5
+ * Detect whether a WWW-Authenticate header (or a Negotiate challenge token) indicates NTLM.
6
+ * NTLM is rejected: only Kerberos/SPNEGO Negotiate is acceptable.
7
+ *
8
+ * Two cases:
9
+ * - the header offers the NTLM scheme directly: `WWW-Authenticate: NTLM`
10
+ * - an NTLM token tunneled via Negotiate: `Negotiate <base64>` where the base64 decodes to
11
+ * bytes starting with the NTLM signature "NTLMSSP\0" (base64 prefix "TlRMTVNTUAA").
12
+ */
13
+ function isNtlmChallenge(wwwAuthenticate) {
14
+ if (!wwwAuthenticate)
15
+ return false;
16
+ const value = wwwAuthenticate.trim();
17
+ if (!value)
18
+ return false;
19
+ // Split on commas to handle multiple offered schemes, e.g. "Negotiate, NTLM"
20
+ const parts = value.split(',').map((p) => p.trim());
21
+ for (const part of parts) {
22
+ const lower = part.toLowerCase();
23
+ // direct NTLM scheme offer (word-boundary, not e.g. "Negotiate")
24
+ if (/^ntlm\b/i.test(part))
25
+ return true;
26
+ // NTLM tunneled inside a Negotiate token
27
+ if (lower.startsWith('negotiate ')) {
28
+ const token = part.slice('negotiate '.length).trim();
29
+ if (token.startsWith('TlRMTVNTUAA'))
30
+ return true; // base64("NTLMSSP\0")
31
+ }
32
+ }
33
+ return false;
34
+ }
@@ -13,6 +13,18 @@ declare abstract class AbstractAbapConnection implements AbapConnection {
13
13
  private sessionId;
14
14
  private sessionMode;
15
15
  private skipSessionType;
16
+ /**
17
+ * When true, requests are treated as part of an uninterruptible critical
18
+ * section (e.g. a lock → modify → unlock chain). In this state a short
19
+ * per-request timeout must NOT abort the request mid-flight, because
20
+ * aborting the socket drops the stateful ADT session and orphans the lock
21
+ * handle (leaving the object locked and inactive). While set, makeAdtRequest
22
+ * raises the effective timeout to CRITICAL_SECTION_TIMEOUT (a large ceiling)
23
+ * so the request runs to completion instead of being interrupted.
24
+ */
25
+ private inCriticalSection;
26
+ /** Reference count for nested beginCriticalSection()/endCriticalSection() pairs. */
27
+ private criticalSectionDepth;
16
28
  protected constructor(config: SapConfig, logger: ILogger | null, sessionId?: string, options?: {
17
29
  skipSessionType?: boolean;
18
30
  });
@@ -33,6 +45,30 @@ declare abstract class AbstractAbapConnection implements AbapConnection {
33
45
  * Get current session mode
34
46
  */
35
47
  getSessionMode(): 'stateless' | 'stateful';
48
+ /**
49
+ * Enter an uninterruptible critical section.
50
+ *
51
+ * Call this BEFORE acquiring a lock (and pair it with endCriticalSection()
52
+ * in a finally, AFTER unlocking). While in a critical section, a short
53
+ * per-request timeout is not applied — makeAdtRequest uses a large ceiling
54
+ * (CRITICAL_SECTION_TIMEOUT, env SAP_TIMEOUT_CRITICAL) instead — so a slow
55
+ * PUT/activate/unlock is not aborted mid-flight. Aborting mid-flight tears
56
+ * down the socket, which drops the stateful ADT session and orphans the
57
+ * lock handle, leaving the object locked and inactive.
58
+ *
59
+ * Nesting is reference-counted so nested begin/end pairs are safe.
60
+ */
61
+ beginCriticalSection(): void;
62
+ /**
63
+ * Leave the uninterruptible critical section. See beginCriticalSection().
64
+ * Safe to call more times than begin (clamped at 0). Normal per-request
65
+ * timeouts resume once the outermost section ends.
66
+ */
67
+ endCriticalSection(): void;
68
+ /**
69
+ * Whether requests are currently in an uninterruptible critical section.
70
+ */
71
+ isInCriticalSection(): boolean;
36
72
  /**
37
73
  * Set session ID
38
74
  * @deprecated Session ID is auto-generated, use setSessionType() to control session mode
@@ -1 +1 @@
1
- {"version":3,"file":"AbstractAbapConnection.d.ts","sourceRoot":"","sources":["../../src/connection/AbstractAbapConnection.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,YAAY,EAAkB,MAAM,0BAA0B,CAAC;AAM7E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5C,OAAO,KAAK,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAG9E,uBAAe,sBAAuB,YAAW,cAAc;IAW3D,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI;IAX3C,OAAO,CAAC,aAAa,CAA8B;IACnD,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,OAAO,CAAuB;IACtC,OAAO,CAAC,WAAW,CAAkC;IACrD,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,WAAW,CAAyC;IAC5D,OAAO,CAAC,eAAe,CAAU;IAEjC,SAAS,aACU,MAAM,EAAE,SAAS,EACf,MAAM,EAAE,OAAO,GAAG,IAAI,EACzC,SAAS,CAAC,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE;QAAE,eAAe,CAAC,EAAE,OAAO,CAAA;KAAE;IAqBzC;;;;;;;;;;;OAWG;IACH,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,WAAW,GAAG,IAAI;IAUpD;;OAEG;IACH,cAAc,IAAI,WAAW,GAAG,UAAU;IAI1C;;;OAGG;IACH,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAKrC;;OAEG;IACH,YAAY,IAAI,MAAM,GAAG,IAAI;IAI7B,SAAS,IAAI,SAAS;IAItB,KAAK,IAAI,IAAI;IAYP,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC;IAI7B,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAevD;;;;;;;;OAQG;IACH,QAAQ,CAAC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAE3B,cAAc,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,GAAG,GAAG,EACnC,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IA+R9B,SAAS,CAAC,QAAQ,CAAC,wBAAwB,IAAI,MAAM;IAErD;;;OAGG;cACa,cAAc,CAC5B,GAAG,EAAE,MAAM,EACX,UAAU,GAAE,MAAgC,EAC5C,UAAU,GAAE,MAAgC,GAC3C,OAAO,CAAC,MAAM,CAAC;IA2ClB;;OAEG;YACW,0BAA0B;IAsKxC;;OAEG;IACH,SAAS,CAAC,YAAY,IAAI,MAAM,GAAG,IAAI;IAIvC;;OAEG;IACH,SAAS,CAAC,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAIlD;;OAEG;IACH,SAAS,CAAC,UAAU,IAAI,MAAM,GAAG,IAAI;IAIrC,SAAS,CAAC,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAIlD,OAAO,CAAC,yBAAyB;IAkEjC;;;OAGG;IACH,SAAS,CAAC,oBAAoB,IAAI,OAAO,YAAY,EAAE,YAAY;IAInE,OAAO,CAAC,gBAAgB;YAsBV,oBAAoB;IAiClC;;;;;;OAMG;IACH,OAAO,CAAC,iBAAiB;IAMzB,OAAO,CAAC,eAAe;CA+BxB;AAGD,OAAO,EAAE,sBAAsB,EAAE,CAAC"}
1
+ {"version":3,"file":"AbstractAbapConnection.d.ts","sourceRoot":"","sources":["../../src/connection/AbstractAbapConnection.ts"],"names":[],"mappings":"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"}
@@ -51,6 +51,18 @@ class AbstractAbapConnection {
51
51
  sessionId = null;
52
52
  sessionMode = 'stateless';
53
53
  skipSessionType;
54
+ /**
55
+ * When true, requests are treated as part of an uninterruptible critical
56
+ * section (e.g. a lock → modify → unlock chain). In this state a short
57
+ * per-request timeout must NOT abort the request mid-flight, because
58
+ * aborting the socket drops the stateful ADT session and orphans the lock
59
+ * handle (leaving the object locked and inactive). While set, makeAdtRequest
60
+ * raises the effective timeout to CRITICAL_SECTION_TIMEOUT (a large ceiling)
61
+ * so the request runs to completion instead of being interrupted.
62
+ */
63
+ inCriticalSection = false;
64
+ /** Reference count for nested beginCriticalSection()/endCriticalSection() pairs. */
65
+ criticalSectionDepth = 0;
54
66
  constructor(config, logger, sessionId, options) {
55
67
  this.config = config;
56
68
  this.logger = logger;
@@ -94,6 +106,44 @@ class AbstractAbapConnection {
94
106
  getSessionMode() {
95
107
  return this.sessionMode;
96
108
  }
109
+ /**
110
+ * Enter an uninterruptible critical section.
111
+ *
112
+ * Call this BEFORE acquiring a lock (and pair it with endCriticalSection()
113
+ * in a finally, AFTER unlocking). While in a critical section, a short
114
+ * per-request timeout is not applied — makeAdtRequest uses a large ceiling
115
+ * (CRITICAL_SECTION_TIMEOUT, env SAP_TIMEOUT_CRITICAL) instead — so a slow
116
+ * PUT/activate/unlock is not aborted mid-flight. Aborting mid-flight tears
117
+ * down the socket, which drops the stateful ADT session and orphans the
118
+ * lock handle, leaving the object locked and inactive.
119
+ *
120
+ * Nesting is reference-counted so nested begin/end pairs are safe.
121
+ */
122
+ beginCriticalSection() {
123
+ this.criticalSectionDepth++;
124
+ this.inCriticalSection = true;
125
+ this.logger?.debug(`Entered critical section (depth ${this.criticalSectionDepth})`);
126
+ }
127
+ /**
128
+ * Leave the uninterruptible critical section. See beginCriticalSection().
129
+ * Safe to call more times than begin (clamped at 0). Normal per-request
130
+ * timeouts resume once the outermost section ends.
131
+ */
132
+ endCriticalSection() {
133
+ if (this.criticalSectionDepth > 0) {
134
+ this.criticalSectionDepth--;
135
+ }
136
+ if (this.criticalSectionDepth === 0) {
137
+ this.inCriticalSection = false;
138
+ }
139
+ this.logger?.debug(`Left critical section (depth ${this.criticalSectionDepth})`);
140
+ }
141
+ /**
142
+ * Whether requests are currently in an uninterruptible critical section.
143
+ */
144
+ isInCriticalSection() {
145
+ return this.inCriticalSection;
146
+ }
97
147
  /**
98
148
  * Set session ID
99
149
  * @deprecated Session ID is auto-generated, use setSessionType() to control session mode
@@ -207,11 +257,19 @@ class AbstractAbapConnection {
207
257
  }
208
258
  }
209
259
  }
260
+ // Inside an uninterruptible critical section (lock → modify → unlock), a
261
+ // short per-request timeout must not abort the request mid-flight — that
262
+ // would drop the stateful session and orphan the lock. Raise the effective
263
+ // timeout to the large critical-section ceiling for the whole request
264
+ // (also honoured on the retry paths below, which reuse requestConfig).
265
+ const effectiveTimeout = this.inCriticalSection
266
+ ? Math.max(timeout ?? 0, (0, timeouts_js_1.getCriticalSectionTimeout)())
267
+ : timeout;
210
268
  const requestConfig = {
211
269
  method: normalizedMethod,
212
270
  url: requestUrl,
213
271
  headers: requestHeaders,
214
- timeout,
272
+ timeout: effectiveTimeout,
215
273
  params,
216
274
  };
217
275
  if (data !== undefined) {
@@ -1 +1 @@
1
- {"version":3,"file":"KerberosAbapConnection.d.ts","sourceRoot":"","sources":["../../src/connection/KerberosAbapConnection.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAErE,uGAAuG;AACvG,qBAAa,sBAAuB,SAAQ,sBAAsB;IAChE,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,YAAY,CAAM;gBAEd,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,EAAE,SAAS,CAAC,EAAE,MAAM;IAQ1E,OAAO,CAAC,MAAM,CAAC,cAAc;IAa7B,mDAAmD;cACnC,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IAK5C;;;;OAIG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IA0B9B,SAAS,CAAC,wBAAwB,IAAI,MAAM;CAS7C"}
1
+ {"version":3,"file":"KerberosAbapConnection.d.ts","sourceRoot":"","sources":["../../src/connection/KerberosAbapConnection.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAErE,uGAAuG;AACvG,qBAAa,sBAAuB,SAAQ,sBAAsB;IAChE,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,YAAY,CAAM;gBAEd,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,EAAE,SAAS,CAAC,EAAE,MAAM;IAQ1E,OAAO,CAAC,MAAM,CAAC,cAAc;IAa7B,mDAAmD;cACnC,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IAK5C;;;;OAIG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAiC9B,SAAS,CAAC,wBAAwB,IAAI,MAAM;CAS7C"}
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.KerberosAbapConnection = void 0;
4
4
  const axios_1 = require("axios");
5
5
  const kerberosSpnego_js_1 = require("../auth/kerberosSpnego.js");
6
+ const ntlm_js_1 = require("../auth/ntlm.js");
6
7
  const AbstractAbapConnection_js_1 = require("./AbstractAbapConnection.js");
7
8
  /** Kerberos / SPNEGO single-leg auth: send Negotiate token, reuse the resulting SAP session cookie. */
8
9
  class KerberosAbapConnection extends AbstractAbapConnection_js_1.AbstractAbapConnection {
@@ -43,12 +44,17 @@ class KerberosAbapConnection extends AbstractAbapConnection_js_1.AbstractAbapCon
43
44
  this.setCsrfToken(token);
44
45
  }
45
46
  catch (error) {
46
- this.logger?.warn(`[WARN] KerberosAbapConnection - connect deferred: ${error instanceof Error ? error.message : String(error)}`);
47
- if (error instanceof axios_1.AxiosError &&
48
- error.response?.headers &&
49
- this.getCookies()) {
50
- this.logger?.debug('[DEBUG] KerberosAbapConnection - cookies captured from error response during connect');
47
+ if (error instanceof axios_1.AxiosError && error.response?.headers) {
48
+ const wwwAuth = error.response.headers['www-authenticate'];
49
+ if ((0, ntlm_js_1.isNtlmChallenge)(wwwAuth)) {
50
+ throw new Error('KerberosAbapConnection: server offered NTLM authentication, which is rejected. ' +
51
+ 'Only Kerberos/SPNEGO is supported. Ensure the SAP system accepts Kerberos (SPNEGO) for your user.');
52
+ }
53
+ if (this.getCookies()) {
54
+ this.logger?.debug('[DEBUG] KerberosAbapConnection - cookies captured from error response during connect');
55
+ }
51
56
  }
57
+ this.logger?.warn(`[WARN] KerberosAbapConnection - connect deferred: ${error instanceof Error ? error.message : String(error)}`);
52
58
  }
53
59
  }
54
60
  buildAuthorizationHeader() {
@@ -2,4 +2,14 @@ import type { ITimeoutConfig } from '@mcp-abap-adt/interfaces';
2
2
  export type TimeoutConfig = ITimeoutConfig;
3
3
  export declare function getTimeoutConfig(): ITimeoutConfig;
4
4
  export declare function getTimeout(type?: 'default' | 'csrf' | 'long' | number): number;
5
+ /**
6
+ * Large ceiling timeout applied while a connection is inside an uninterruptible
7
+ * critical section (lock → modify → unlock). A short per-request timeout must
8
+ * not abort such a request mid-flight: aborting tears down the socket, which
9
+ * drops the stateful ADT session and orphans the lock handle (object left
10
+ * locked and inactive). Configurable via SAP_TIMEOUT_CRITICAL (ms).
11
+ * Default 600000 (10 minutes) — a generous ceiling that still guards against a
12
+ * permanently dead socket hanging forever.
13
+ */
14
+ export declare function getCriticalSectionTimeout(): number;
5
15
  //# sourceMappingURL=timeouts.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"timeouts.d.ts","sourceRoot":"","sources":["../../src/utils/timeouts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAG/D,MAAM,MAAM,aAAa,GAAG,cAAc,CAAC;AAE3C,wBAAgB,gBAAgB,IAAI,cAAc,CAajD;AAED,wBAAgB,UAAU,CACxB,IAAI,GAAE,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,MAAkB,GACrD,MAAM,CAOR"}
1
+ {"version":3,"file":"timeouts.d.ts","sourceRoot":"","sources":["../../src/utils/timeouts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAG/D,MAAM,MAAM,aAAa,GAAG,cAAc,CAAC;AAE3C,wBAAgB,gBAAgB,IAAI,cAAc,CAajD;AAED,wBAAgB,UAAU,CACxB,IAAI,GAAE,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,MAAkB,GACrD,MAAM,CAOR;AAED;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,IAAI,MAAM,CAElD"}
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getTimeoutConfig = getTimeoutConfig;
4
4
  exports.getTimeout = getTimeout;
5
+ exports.getCriticalSectionTimeout = getCriticalSectionTimeout;
5
6
  function getTimeoutConfig() {
6
7
  const defaultTimeout = parseInt(process.env.SAP_TIMEOUT_DEFAULT || '45000', 10);
7
8
  const csrfTimeout = parseInt(process.env.SAP_TIMEOUT_CSRF || '15000', 10);
@@ -19,3 +20,15 @@ function getTimeout(type = 'default') {
19
20
  const config = getTimeoutConfig();
20
21
  return config[type];
21
22
  }
23
+ /**
24
+ * Large ceiling timeout applied while a connection is inside an uninterruptible
25
+ * critical section (lock → modify → unlock). A short per-request timeout must
26
+ * not abort such a request mid-flight: aborting tears down the socket, which
27
+ * drops the stateful ADT session and orphans the lock handle (object left
28
+ * locked and inactive). Configurable via SAP_TIMEOUT_CRITICAL (ms).
29
+ * Default 600000 (10 minutes) — a generous ceiling that still guards against a
30
+ * permanently dead socket hanging forever.
31
+ */
32
+ function getCriticalSectionTimeout() {
33
+ return parseInt(process.env.SAP_TIMEOUT_CRITICAL || '600000', 10);
34
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcp-abap-adt/connection",
3
- "version": "1.9.0",
3
+ "version": "1.10.0",
4
4
  "description": "ABAP connection layer for MCP ABAP ADT server",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -47,7 +47,7 @@
47
47
  },
48
48
  "dependencies": {
49
49
  "@mcp-abap-adt/interfaces": "^7.2.0",
50
- "axios": "^1.13.5",
50
+ "axios": "^1.16.0",
51
51
  "commander": "^14.0.3",
52
52
  "express": "^5.1.0",
53
53
  "open": "^11.0.0"