@st-gr/sail-proxy 0.9.2 → 0.9.4

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.
Files changed (244) hide show
  1. package/bundled/gateway/node_modules/.package-lock.json +142 -93
  2. package/bundled/gateway/node_modules/axios/CHANGELOG.md +0 -42
  3. package/bundled/gateway/node_modules/axios/README.md +237 -237
  4. package/bundled/gateway/node_modules/axios/dist/axios.js +21 -113
  5. package/bundled/gateway/node_modules/axios/dist/axios.js.map +1 -0
  6. package/bundled/gateway/node_modules/axios/dist/axios.min.js +2 -2
  7. package/bundled/gateway/node_modules/axios/dist/axios.min.js.map +1 -1
  8. package/bundled/gateway/node_modules/axios/dist/browser/axios.cjs +15 -121
  9. package/bundled/gateway/node_modules/axios/dist/browser/axios.cjs.map +1 -0
  10. package/bundled/gateway/node_modules/axios/dist/esm/axios.js +15 -121
  11. package/bundled/gateway/node_modules/axios/dist/esm/axios.js.map +1 -0
  12. package/bundled/gateway/node_modules/axios/dist/esm/axios.min.js +2 -2
  13. package/bundled/gateway/node_modules/axios/dist/esm/axios.min.js.map +1 -1
  14. package/bundled/gateway/node_modules/axios/dist/node/axios.cjs +107 -249
  15. package/bundled/gateway/node_modules/axios/dist/node/axios.cjs.map +1 -0
  16. package/bundled/gateway/node_modules/axios/index.d.cts +3 -8
  17. package/bundled/gateway/node_modules/axios/index.d.ts +1 -4
  18. package/bundled/gateway/node_modules/axios/lib/adapters/fetch.js +0 -79
  19. package/bundled/gateway/node_modules/axios/lib/adapters/http.js +118 -62
  20. package/bundled/gateway/node_modules/axios/lib/core/Axios.js +0 -1
  21. package/bundled/gateway/node_modules/axios/lib/core/AxiosHeaders.js +2 -2
  22. package/bundled/gateway/node_modules/axios/lib/defaults/transitional.js +0 -1
  23. package/bundled/gateway/node_modules/axios/lib/env/data.js +1 -1
  24. package/bundled/gateway/node_modules/axios/lib/helpers/buildURL.js +1 -1
  25. package/bundled/gateway/node_modules/axios/lib/helpers/formDataToStream.js +2 -2
  26. package/bundled/gateway/node_modules/axios/lib/helpers/resolveConfig.js +6 -12
  27. package/bundled/gateway/node_modules/axios/lib/helpers/toFormData.js +1 -1
  28. package/bundled/gateway/node_modules/axios/lib/utils.js +2 -23
  29. package/bundled/gateway/node_modules/axios/package.json +13 -29
  30. package/bundled/gateway/node_modules/body-parser/HISTORY.md +0 -14
  31. package/bundled/gateway/node_modules/body-parser/SECURITY.md +25 -0
  32. package/bundled/gateway/node_modules/body-parser/lib/types/json.js +5 -1
  33. package/bundled/gateway/node_modules/body-parser/lib/types/urlencoded.js +15 -7
  34. package/bundled/gateway/node_modules/body-parser/node_modules/qs/.editorconfig +46 -0
  35. package/bundled/gateway/node_modules/body-parser/node_modules/qs/.eslintrc +38 -0
  36. package/bundled/gateway/node_modules/body-parser/node_modules/qs/.github/FUNDING.yml +12 -0
  37. package/bundled/gateway/node_modules/body-parser/node_modules/qs/.nycrc +13 -0
  38. package/bundled/gateway/node_modules/body-parser/node_modules/qs/CHANGELOG.md +600 -0
  39. package/bundled/gateway/node_modules/body-parser/node_modules/qs/LICENSE.md +29 -0
  40. package/bundled/gateway/node_modules/body-parser/node_modules/qs/README.md +709 -0
  41. package/bundled/gateway/node_modules/body-parser/node_modules/qs/dist/qs.js +90 -0
  42. package/bundled/gateway/node_modules/body-parser/node_modules/qs/lib/formats.js +23 -0
  43. package/bundled/gateway/node_modules/body-parser/node_modules/qs/lib/index.js +11 -0
  44. package/bundled/gateway/node_modules/body-parser/node_modules/qs/lib/parse.js +296 -0
  45. package/bundled/gateway/node_modules/body-parser/node_modules/qs/lib/stringify.js +351 -0
  46. package/bundled/gateway/node_modules/body-parser/node_modules/qs/lib/utils.js +265 -0
  47. package/bundled/gateway/node_modules/body-parser/node_modules/qs/package.json +91 -0
  48. package/bundled/gateway/node_modules/body-parser/node_modules/qs/test/empty-keys-cases.js +267 -0
  49. package/bundled/gateway/node_modules/body-parser/node_modules/qs/test/parse.js +1170 -0
  50. package/bundled/gateway/node_modules/body-parser/node_modules/qs/test/stringify.js +1298 -0
  51. package/bundled/gateway/node_modules/body-parser/node_modules/qs/test/utils.js +136 -0
  52. package/bundled/gateway/node_modules/body-parser/package.json +10 -9
  53. package/bundled/gateway/node_modules/cookie/index.js +35 -24
  54. package/bundled/gateway/node_modules/cookie/package.json +1 -1
  55. package/bundled/gateway/node_modules/cookie-signature/History.md +1 -5
  56. package/bundled/gateway/node_modules/cookie-signature/index.js +6 -6
  57. package/bundled/gateway/node_modules/cookie-signature/package.json +2 -2
  58. package/bundled/gateway/node_modules/express/History.md +0 -43
  59. package/bundled/gateway/node_modules/express/lib/response.js +0 -1
  60. package/bundled/gateway/node_modules/express/lib/utils.js +1 -2
  61. package/bundled/gateway/node_modules/express/node_modules/qs/.editorconfig +43 -0
  62. package/bundled/gateway/node_modules/express/node_modules/qs/.eslintrc +38 -0
  63. package/bundled/gateway/node_modules/express/node_modules/qs/.github/FUNDING.yml +12 -0
  64. package/bundled/gateway/node_modules/express/node_modules/qs/.nycrc +13 -0
  65. package/bundled/gateway/node_modules/express/node_modules/qs/CHANGELOG.md +546 -0
  66. package/bundled/gateway/node_modules/express/node_modules/qs/LICENSE.md +29 -0
  67. package/bundled/gateway/node_modules/express/node_modules/qs/README.md +625 -0
  68. package/bundled/gateway/node_modules/express/node_modules/qs/dist/qs.js +2054 -0
  69. package/bundled/gateway/node_modules/express/node_modules/qs/lib/formats.js +23 -0
  70. package/bundled/gateway/node_modules/express/node_modules/qs/lib/index.js +11 -0
  71. package/bundled/gateway/node_modules/express/node_modules/qs/lib/parse.js +263 -0
  72. package/bundled/gateway/node_modules/express/node_modules/qs/lib/stringify.js +326 -0
  73. package/bundled/gateway/node_modules/express/node_modules/qs/lib/utils.js +252 -0
  74. package/bundled/gateway/node_modules/express/node_modules/qs/package.json +77 -0
  75. package/bundled/gateway/node_modules/express/node_modules/qs/test/parse.js +855 -0
  76. package/bundled/gateway/node_modules/express/node_modules/qs/test/stringify.js +909 -0
  77. package/bundled/gateway/node_modules/express/node_modules/qs/test/utils.js +136 -0
  78. package/bundled/gateway/node_modules/express/package.json +17 -21
  79. package/bundled/gateway/node_modules/finalhandler/HISTORY.md +0 -21
  80. package/bundled/gateway/node_modules/finalhandler/README.md +2 -2
  81. package/bundled/gateway/node_modules/finalhandler/index.js +2 -7
  82. package/bundled/gateway/node_modules/finalhandler/node_modules/encodeurl/HISTORY.md +14 -0
  83. package/bundled/gateway/node_modules/finalhandler/node_modules/encodeurl/LICENSE +22 -0
  84. package/bundled/gateway/node_modules/finalhandler/node_modules/encodeurl/README.md +128 -0
  85. package/bundled/gateway/node_modules/finalhandler/node_modules/encodeurl/index.js +60 -0
  86. package/bundled/gateway/node_modules/finalhandler/node_modules/encodeurl/package.json +40 -0
  87. package/bundled/gateway/node_modules/finalhandler/package.json +9 -10
  88. package/bundled/gateway/node_modules/form-data/CHANGELOG.md +29 -2
  89. package/bundled/gateway/node_modules/form-data/README.md +4 -4
  90. package/bundled/gateway/node_modules/form-data/lib/form_data.js +14 -2
  91. package/bundled/gateway/node_modules/form-data/package.json +7 -7
  92. package/bundled/gateway/node_modules/http-errors/HISTORY.md +0 -6
  93. package/bundled/gateway/node_modules/http-errors/index.js +3 -4
  94. package/bundled/gateway/node_modules/http-errors/package.json +8 -12
  95. package/bundled/gateway/node_modules/raw-body/HISTORY.md +308 -0
  96. package/bundled/gateway/node_modules/raw-body/SECURITY.md +24 -0
  97. package/bundled/gateway/node_modules/raw-body/package.json +7 -5
  98. package/bundled/gateway/node_modules/semver/classes/range.js +17 -4
  99. package/bundled/gateway/node_modules/semver/package.json +2 -2
  100. package/bundled/gateway/node_modules/send/HISTORY.md +7 -19
  101. package/bundled/gateway/node_modules/send/node_modules/encodeurl/HISTORY.md +14 -0
  102. package/bundled/gateway/node_modules/send/node_modules/encodeurl/LICENSE +22 -0
  103. package/bundled/gateway/node_modules/send/node_modules/encodeurl/README.md +128 -0
  104. package/bundled/gateway/node_modules/send/node_modules/encodeurl/index.js +60 -0
  105. package/bundled/gateway/node_modules/send/node_modules/encodeurl/package.json +40 -0
  106. package/bundled/gateway/node_modules/send/package.json +6 -6
  107. package/bundled/gateway/node_modules/serve-static/HISTORY.md +0 -16
  108. package/bundled/gateway/node_modules/serve-static/node_modules/encodeurl/HISTORY.md +14 -0
  109. package/bundled/gateway/node_modules/serve-static/node_modules/encodeurl/LICENSE +22 -0
  110. package/bundled/gateway/node_modules/serve-static/node_modules/encodeurl/README.md +128 -0
  111. package/bundled/gateway/node_modules/serve-static/node_modules/encodeurl/index.js +60 -0
  112. package/bundled/gateway/node_modules/serve-static/node_modules/encodeurl/package.json +40 -0
  113. package/bundled/gateway/node_modules/serve-static/package.json +3 -3
  114. package/bundled/gateway/node_modules/side-channel/CHANGELOG.md +10 -0
  115. package/bundled/gateway/node_modules/side-channel/README.md +1 -1
  116. package/bundled/gateway/node_modules/side-channel/index.js +5 -2
  117. package/bundled/gateway/node_modules/side-channel/package.json +10 -10
  118. package/bundled/gateway/node_modules/side-channel/test/index.js +16 -0
  119. package/bundled/gateway/node_modules/statuses/HISTORY.md +0 -5
  120. package/bundled/gateway/node_modules/statuses/README.md +0 -3
  121. package/bundled/gateway/node_modules/statuses/package.json +7 -7
  122. package/bundled/gateway/package-lock.json +150 -101
  123. package/bundled/gateway/package.json +31 -5
  124. package/bundled/gateway/services/gateway/src/controllers/anthropicController.js +1 -1
  125. package/bundled/gateway/services/gateway/src/controllers/awsBedrockController.js +5 -1
  126. package/bundled/gateway/services/gateway/src/index.js +14 -0
  127. package/bundled/gateway/services/gateway/src/plugins/pseudonymization/detectors/index.js +23 -1
  128. package/bundled/gateway/services/gateway/src/plugins/pseudonymization/detectors/regexDetectors.js +149 -10
  129. package/bundled/gateway/services/gateway/src/plugins/pseudonymization/entityToggles.d.ts +24 -0
  130. package/bundled/gateway/services/gateway/src/plugins/pseudonymization/entityToggles.js +54 -0
  131. package/bundled/gateway/services/gateway/src/plugins/pseudonymization/index.d.ts +2 -1
  132. package/bundled/gateway/services/gateway/src/plugins/pseudonymization/index.js +441 -92
  133. package/bundled/gateway/services/gateway/src/plugins/pseudonymization/replacementMap.d.ts +24 -0
  134. package/bundled/gateway/services/gateway/src/plugins/pseudonymization/replacementMap.js +93 -4
  135. package/bundled/gateway/services/gateway/src/plugins/pseudonymization/replacer.d.ts +22 -0
  136. package/bundled/gateway/services/gateway/src/plugins/pseudonymization/replacer.js +77 -0
  137. package/bundled/gateway/services/gateway/src/plugins/pseudonymization/streamBuffer.js +26 -8
  138. package/bundled/gateway/services/gateway/src/services/awsBedrockService.js +118 -46
  139. package/bundled/gateway/services/gateway/src/services/betaFlagQuarantine.d.ts +18 -0
  140. package/bundled/gateway/services/gateway/src/services/betaFlagQuarantine.js +111 -0
  141. package/bundled/gateway/services/gateway/src/services/configService.d.ts +8 -0
  142. package/bundled/gateway/services/gateway/src/services/configService.js +61 -53
  143. package/bundled/gateway/services/gateway/src/utils/bedrockStreamParser.d.ts +9 -0
  144. package/bundled/gateway/services/gateway/src/utils/bedrockStreamParser.js +69 -3
  145. package/bundled/gateway/services/gateway/src/utils/betaFeatureFilter.d.ts +31 -0
  146. package/bundled/gateway/services/gateway/src/utils/betaFeatureFilter.js +58 -0
  147. package/bundled/gateway/services/gateway/src/utils/payloadLogger.d.ts +6 -0
  148. package/bundled/gateway/services/gateway/src/utils/payloadLogger.js +11 -7
  149. package/bundled/gateway/services/gateway/src/utils/pluginCacheSelector.d.ts +15 -0
  150. package/bundled/gateway/services/gateway/src/utils/pluginCacheSelector.js +24 -0
  151. package/bundled/gateway/services/gateway/src/utils/sseWriter.js +4 -2
  152. package/bundled/gateway/services/gateway/src/utils/upstreamErrorBody.d.ts +10 -0
  153. package/bundled/gateway/services/gateway/src/utils/upstreamErrorBody.js +71 -0
  154. package/bundled/ollama/node_modules/.package-lock.json +111 -92
  155. package/bundled/ollama/node_modules/axios/CHANGELOG.md +0 -42
  156. package/bundled/ollama/node_modules/axios/README.md +237 -237
  157. package/bundled/ollama/node_modules/axios/dist/axios.js +21 -113
  158. package/bundled/ollama/node_modules/axios/dist/axios.js.map +1 -0
  159. package/bundled/ollama/node_modules/axios/dist/axios.min.js +2 -2
  160. package/bundled/ollama/node_modules/axios/dist/axios.min.js.map +1 -1
  161. package/bundled/ollama/node_modules/axios/dist/browser/axios.cjs +15 -121
  162. package/bundled/ollama/node_modules/axios/dist/browser/axios.cjs.map +1 -0
  163. package/bundled/ollama/node_modules/axios/dist/esm/axios.js +15 -121
  164. package/bundled/ollama/node_modules/axios/dist/esm/axios.js.map +1 -0
  165. package/bundled/ollama/node_modules/axios/dist/esm/axios.min.js +2 -2
  166. package/bundled/ollama/node_modules/axios/dist/esm/axios.min.js.map +1 -1
  167. package/bundled/ollama/node_modules/axios/dist/node/axios.cjs +107 -249
  168. package/bundled/ollama/node_modules/axios/dist/node/axios.cjs.map +1 -0
  169. package/bundled/ollama/node_modules/axios/index.d.cts +3 -8
  170. package/bundled/ollama/node_modules/axios/index.d.ts +1 -4
  171. package/bundled/ollama/node_modules/axios/lib/adapters/fetch.js +0 -79
  172. package/bundled/ollama/node_modules/axios/lib/adapters/http.js +118 -62
  173. package/bundled/ollama/node_modules/axios/lib/core/Axios.js +0 -1
  174. package/bundled/ollama/node_modules/axios/lib/core/AxiosHeaders.js +2 -2
  175. package/bundled/ollama/node_modules/axios/lib/defaults/transitional.js +0 -1
  176. package/bundled/ollama/node_modules/axios/lib/env/data.js +1 -1
  177. package/bundled/ollama/node_modules/axios/lib/helpers/buildURL.js +1 -1
  178. package/bundled/ollama/node_modules/axios/lib/helpers/formDataToStream.js +2 -2
  179. package/bundled/ollama/node_modules/axios/lib/helpers/resolveConfig.js +6 -12
  180. package/bundled/ollama/node_modules/axios/lib/helpers/toFormData.js +1 -1
  181. package/bundled/ollama/node_modules/axios/lib/utils.js +2 -23
  182. package/bundled/ollama/node_modules/axios/package.json +13 -29
  183. package/bundled/ollama/node_modules/body-parser/HISTORY.md +0 -14
  184. package/bundled/ollama/node_modules/body-parser/SECURITY.md +25 -0
  185. package/bundled/ollama/node_modules/body-parser/lib/types/json.js +5 -1
  186. package/bundled/ollama/node_modules/body-parser/lib/types/urlencoded.js +15 -7
  187. package/bundled/ollama/node_modules/body-parser/package.json +10 -9
  188. package/bundled/ollama/node_modules/cookie/index.js +35 -24
  189. package/bundled/ollama/node_modules/cookie/package.json +1 -1
  190. package/bundled/ollama/node_modules/cookie-signature/History.md +1 -5
  191. package/bundled/ollama/node_modules/cookie-signature/index.js +6 -6
  192. package/bundled/ollama/node_modules/cookie-signature/package.json +2 -2
  193. package/bundled/ollama/node_modules/express/History.md +0 -43
  194. package/bundled/ollama/node_modules/express/lib/response.js +0 -1
  195. package/bundled/ollama/node_modules/express/lib/utils.js +1 -2
  196. package/bundled/ollama/node_modules/express/package.json +17 -21
  197. package/bundled/ollama/node_modules/finalhandler/HISTORY.md +0 -21
  198. package/bundled/ollama/node_modules/finalhandler/README.md +2 -2
  199. package/bundled/ollama/node_modules/finalhandler/index.js +2 -7
  200. package/bundled/ollama/node_modules/finalhandler/node_modules/encodeurl/HISTORY.md +14 -0
  201. package/bundled/ollama/node_modules/finalhandler/node_modules/encodeurl/LICENSE +22 -0
  202. package/bundled/ollama/node_modules/finalhandler/node_modules/encodeurl/README.md +128 -0
  203. package/bundled/ollama/node_modules/finalhandler/node_modules/encodeurl/index.js +60 -0
  204. package/bundled/ollama/node_modules/finalhandler/node_modules/encodeurl/package.json +40 -0
  205. package/bundled/ollama/node_modules/finalhandler/package.json +9 -10
  206. package/bundled/ollama/node_modules/form-data/CHANGELOG.md +29 -2
  207. package/bundled/ollama/node_modules/form-data/README.md +4 -4
  208. package/bundled/ollama/node_modules/form-data/lib/form_data.js +14 -2
  209. package/bundled/ollama/node_modules/form-data/package.json +7 -7
  210. package/bundled/ollama/node_modules/http-errors/HISTORY.md +0 -6
  211. package/bundled/ollama/node_modules/http-errors/index.js +3 -4
  212. package/bundled/ollama/node_modules/http-errors/package.json +8 -12
  213. package/bundled/ollama/node_modules/raw-body/HISTORY.md +308 -0
  214. package/bundled/ollama/node_modules/raw-body/SECURITY.md +24 -0
  215. package/bundled/ollama/node_modules/raw-body/package.json +7 -5
  216. package/bundled/ollama/node_modules/send/HISTORY.md +7 -19
  217. package/bundled/ollama/node_modules/send/node_modules/encodeurl/HISTORY.md +14 -0
  218. package/bundled/ollama/node_modules/send/node_modules/encodeurl/LICENSE +22 -0
  219. package/bundled/ollama/node_modules/send/node_modules/encodeurl/README.md +128 -0
  220. package/bundled/ollama/node_modules/send/node_modules/encodeurl/index.js +60 -0
  221. package/bundled/ollama/node_modules/send/node_modules/encodeurl/package.json +40 -0
  222. package/bundled/ollama/node_modules/send/package.json +6 -6
  223. package/bundled/ollama/node_modules/serve-static/HISTORY.md +0 -16
  224. package/bundled/ollama/node_modules/serve-static/node_modules/encodeurl/HISTORY.md +14 -0
  225. package/bundled/ollama/node_modules/serve-static/node_modules/encodeurl/LICENSE +22 -0
  226. package/bundled/ollama/node_modules/serve-static/node_modules/encodeurl/README.md +128 -0
  227. package/bundled/ollama/node_modules/serve-static/node_modules/encodeurl/index.js +60 -0
  228. package/bundled/ollama/node_modules/serve-static/node_modules/encodeurl/package.json +40 -0
  229. package/bundled/ollama/node_modules/serve-static/package.json +3 -3
  230. package/bundled/ollama/node_modules/side-channel/CHANGELOG.md +10 -0
  231. package/bundled/ollama/node_modules/side-channel/README.md +1 -1
  232. package/bundled/ollama/node_modules/side-channel/index.js +5 -2
  233. package/bundled/ollama/node_modules/side-channel/package.json +10 -10
  234. package/bundled/ollama/node_modules/side-channel/test/index.js +16 -0
  235. package/bundled/ollama/node_modules/statuses/HISTORY.md +0 -5
  236. package/bundled/ollama/node_modules/statuses/README.md +0 -3
  237. package/bundled/ollama/node_modules/statuses/package.json +7 -7
  238. package/bundled/ollama/package-lock.json +114 -95
  239. package/bundled/ollama/package.json +32 -4
  240. package/dist/templates/api_config.template.json +266 -18
  241. package/node_modules/@sap-llm-gateway/service-key-parser/package.json +1 -1
  242. package/package.json +2 -2
  243. package/bundled/gateway/node_modules/axios/lib/helpers/Http2Sessions.js +0 -119
  244. package/bundled/ollama/node_modules/axios/lib/helpers/Http2Sessions.js +0 -119
@@ -13,11 +13,15 @@ const pluginExecutor_1 = require("../services/pluginExecutor");
13
13
  const logger_1 = require("@libs/logger");
14
14
  const logger = (0, logger_1.getDefaultLogger)();
15
15
  const usageTracker_1 = require("../utils/usageTracker");
16
+ const payloadLogger_1 = require("../utils/payloadLogger");
16
17
  /**
17
18
  * Handle AWS Bedrock requests for all model operations
18
19
  */
19
20
  const handleBedrockRequest = async (req, res, _next) => {
20
- const debugRequestId = process.env.DEBUG === 'true' ? (0, uuid_1.v4)() : undefined;
21
+ // Generate a debug request ID whenever payload logging can occur — via the
22
+ // legacy DEBUG env or the dynamic config switch (hot-reloadable per request).
23
+ const payloadLoggingActive = process.env.DEBUG === 'true' || (0, payloadLogger_1.isPayloadLoggingEnabled)();
24
+ const debugRequestId = payloadLoggingActive ? (req.debugRequestId || (0, uuid_1.v4)()) : undefined;
21
25
  req.debugRequestId = debugRequestId;
22
26
  logger.info('AwsBedrockController', `Processing request for model: ${req.params.modelId}, subpath: ${req.params.subpath}, Debug ID: ${debugRequestId}`);
23
27
  // Initialize usage tracking
@@ -379,6 +379,20 @@ initializeGatewayService().then(() => {
379
379
  logger.info('Gateway Service', '');
380
380
  logger.info('Gateway Service', '💡 Press Ctrl+C to gracefully shutdown the service');
381
381
  });
382
+ // Fail loudly if the port is taken: otherwise a stale gateway instance keeps serving
383
+ // requests while this (newer) process dies quietly — old code masquerades as the
384
+ // current build, which makes debugging almost impossible.
385
+ server.on('error', (error) => {
386
+ if (error.code === 'EADDRINUSE') {
387
+ logger.error('Gateway Service', `FATAL: ${config.host}:${config.port} is already in use — ANOTHER gateway instance is running. ` +
388
+ `Requests are being served by THAT process (possibly stale code), not this one. ` +
389
+ `Find it with: lsof -nP -iTCP:${config.port} -sTCP:LISTEN`);
390
+ }
391
+ else {
392
+ logger.error('Gateway Service', 'HTTP server error:', error instanceof Error ? error : new Error(String(error)));
393
+ }
394
+ process.exit(1);
395
+ });
382
396
  // Store server reference for cleanup
383
397
  cleanupResources.server = server;
384
398
  }).catch((error) => {
@@ -12,6 +12,23 @@ const customDetector_1 = require("./customDetector");
12
12
  const regexDetectors_1 = require("./regexDetectors");
13
13
  const nerDetector_1 = require("./nerDetector");
14
14
  const dictionaryDetector_1 = require("./dictionaryDetector");
15
+ /**
16
+ * Matches placeholders that ALREADY exist in the text so they are never re-masked.
17
+ * Covers the `MASKED_<TYPE>_<id>` family and the URL-shaped `masked-url-<id>.invalid`
18
+ * form. Re-masking a placeholder produces layered tokens ("keeps getting re-masked")
19
+ * that can never be unmasked back to the original value.
20
+ */
21
+ const EXISTING_PLACEHOLDER = /MASKED_[A-Z_]+_[0-9a-f]+|(?:(?:https?|wss?|ftps?):\/\/)?masked-url-\d+\.invalid/g;
22
+ /** Spans in `text` occupied by placeholders that must be excluded from detection. */
23
+ function placeholderSpans(text) {
24
+ const spans = [];
25
+ EXISTING_PLACEHOLDER.lastIndex = 0;
26
+ let m;
27
+ while ((m = EXISTING_PLACEHOLDER.exec(text)) !== null) {
28
+ spans.push({ start: m.index, end: m.index + m[0].length });
29
+ }
30
+ return spans;
31
+ }
15
32
  /**
16
33
  * Check if a match is in the allow list
17
34
  */
@@ -65,8 +82,13 @@ function detectEntities(text, config) {
65
82
  allMatches.push(...(0, nerDetector_1.detectNerEntities)(text, config.entities));
66
83
  // Tier 3: Dictionary
67
84
  allMatches.push(...(0, dictionaryDetector_1.detectDictionaryEntities)(text, config.entities));
85
+ // Never re-mask an existing placeholder: drop any match overlapping one.
86
+ const reserved = placeholderSpans(text);
87
+ const notPlaceholder = reserved.length === 0
88
+ ? allMatches
89
+ : allMatches.filter(m => !reserved.some(s => m.start < s.end && m.end > s.start));
68
90
  // Filter allow-list
69
- const filtered = allMatches.filter(m => !isAllowed(m.original, config.allow_list));
91
+ const filtered = notPlaceholder.filter(m => !isAllowed(m.original, config.allow_list));
70
92
  // Resolve overlaps
71
93
  return resolveOverlaps(filtered);
72
94
  }
@@ -8,6 +8,51 @@
8
8
  */
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
10
  exports.detectRegexEntities = detectRegexEntities;
11
+ /**
12
+ * Decide whether a URL is worth masking. We SKIP:
13
+ * - loopback / private / link-local hosts (localhost, 127.x, ::1, 10.x,
14
+ * 192.168.x, 172.16-31.x, *.local, *.internal) — these are dev/infra
15
+ * endpoints with ~no privacy value, and masking them breaks agent edit
16
+ * workflows (the model must reproduce an opaque token to match file text);
17
+ * - template / non-literal URLs containing <...>, {...}, ${...} or an
18
+ * unresolved-looking host — masking these fragments one logical endpoint
19
+ * into many bogus tokens.
20
+ * Real external hosts still get masked.
21
+ */
22
+ function isMaskableUrl(fullMatch) {
23
+ // Template markers anywhere in the matched URL → not a real value.
24
+ if (/[<>{}]|\$\{/.test(fullMatch))
25
+ return false;
26
+ // Trailing colon = incomplete authority, i.e. the port was a template variable
27
+ // (`http://host:{PORT}` matches only up to `http://host:` since `{` is excluded).
28
+ if (/:$/.test(fullMatch))
29
+ return false;
30
+ const m = fullMatch.match(/^(?:(?:https?|wss?|ftps?):\/\/)?([^/?#]+)/);
31
+ const authority = (m ? m[1] : fullMatch).toLowerCase();
32
+ // Authority must be a well-formed host[:port] (or [ipv6][:port]). Anything else —
33
+ // e.g. a leftover `http://host:$` from `${PORT}`, or a bare scheme — is a template
34
+ // fragment, not a real endpoint.
35
+ if (!/^(?:\[[0-9a-f:]+\]|[a-z0-9._-]+)(?::\d+)?$/.test(authority))
36
+ return false;
37
+ const host = authority.replace(/:\d*$/, '').replace(/^\[|\]$/g, '');
38
+ if (host === '' || host === 'localhost' || host.endsWith('.local') || host.endsWith('.internal'))
39
+ return false;
40
+ if (host === '::1' || host.startsWith('127.') || host.startsWith('0.'))
41
+ return false;
42
+ if (host.startsWith('10.') || host.startsWith('192.168.'))
43
+ return false;
44
+ // 172.16.0.0 – 172.31.255.255
45
+ const m172 = host.match(/^172\.(\d{1,3})\./);
46
+ if (m172) {
47
+ const o = Number(m172[1]);
48
+ if (o >= 16 && o <= 31)
49
+ return false;
50
+ }
51
+ // 169.254.0.0/16 link-local
52
+ if (host.startsWith('169.254.'))
53
+ return false;
54
+ return true;
55
+ }
11
56
  // Luhn algorithm for credit card validation
12
57
  function luhnCheck(digits) {
13
58
  const nums = digits.split('').map(Number);
@@ -54,6 +99,13 @@ function phoneValidate(match) {
54
99
  const digits = match.replace(/\D/g, '');
55
100
  return digits.length >= 7 && digits.length <= 15;
56
101
  }
102
+ // Credential value validation: reject template placeholders (`<token>`, `${VAR}`,
103
+ // `$VAR`) and values too short to be real secrets. Applied to the captured VALUE.
104
+ function credentialValueValidate(value) {
105
+ if (/[<>{}]|\$\{|^\$/.test(value))
106
+ return false;
107
+ return value.length >= 4;
108
+ }
57
109
  const DETECTORS = [
58
110
  {
59
111
  type: 'profile-email',
@@ -88,7 +140,7 @@ const DETECTORS = [
88
140
  },
89
141
  {
90
142
  type: 'profile-url',
91
- pattern: /(?:https?:\/\/|www\.)[^\s<>"{}|\\^`\[\]]+/g,
143
+ pattern: /(?:(?:https?|wss?|ftps?):\/\/|www\.)[^\s<>"{}|\\^`\[\]]+/g,
92
144
  },
93
145
  {
94
146
  type: 'profile-nationalid',
@@ -104,20 +156,55 @@ const DETECTORS = [
104
156
  type: 'profile-nationalid',
105
157
  // Generic context-anchored
106
158
  pattern: /(?:national\s*id|identification\s*(?:number|no\.?))\s*[:=]?\s*([A-Z0-9\-]{6,20})/gi,
159
+ captureValue: true,
107
160
  },
108
161
  {
109
162
  type: 'profile-passport',
110
163
  // Context-anchored
111
164
  pattern: /(?:passport\s*(?:number|no\.?|#))\s*[:=]?\s*([A-Z]{0,2}\d{6,9})/gi,
165
+ captureValue: true,
112
166
  },
113
167
  {
114
168
  type: 'profile-driverlicense',
115
169
  // Context-anchored generic
116
170
  pattern: /(?:driver'?s?\s*licen[sc]e|DL)\s*(?:number|no\.?|#)?\s*[:=]?\s*([A-Z0-9\-]{5,15})/gi,
171
+ captureValue: true,
117
172
  },
118
173
  {
119
174
  type: 'profile-username-password',
120
- pattern: /(?:user(?:name)?|login|pass(?:word|wd)?|pwd|secret|token|api[_\-]?key)\s*[:=]\s*\S+/gi,
175
+ // key=value / key: value credentials. The leading (?<![A-Za-z0-9_]) + greedy
176
+ // identifier prefix make the match consume the WHOLE identifier ending in a
177
+ // credential word — so `$env:SSHPASS="x"` masks only `x` (not `SSHMASKED_…`),
178
+ // and `SSH_ASKPASS_REQUIRE=force` does not match (identifier ends in REQUIRE).
179
+ // Only the value is captured, preserving surrounding script structure.
180
+ pattern: /(?<![A-Za-z0-9_])[A-Za-z0-9_$-]*(?:user(?:name)?|login|pass(?:word|wd)?|pwd|secret|token|api[_\-]?key)\s*[:=]\s*(?:"([^"]+)"|'([^']+)'|(\S+))/gi,
181
+ validate: credentialValueValidate,
182
+ captureValue: true,
183
+ },
184
+ {
185
+ type: 'profile-username-password',
186
+ // JSON-style credentials: "token": "value" — the quoted key defeats the pattern
187
+ // above. Capture ONLY the value so masking preserves the JSON structure.
188
+ pattern: /"(?:user(?:name)?|login|pass(?:word|wd)?|pwd|secret|token|api[_\-]?key|access[_\-]?token|auth[_\-]?token|bearer)"\s*:\s*"([^"]+)"/gi,
189
+ validate: credentialValueValidate,
190
+ captureValue: true,
191
+ },
192
+ {
193
+ type: 'profile-username-password',
194
+ // HTTP auth headers: `Authorization: Bearer <token>` and siblings. This is the
195
+ // gap behind the 28551619 incident — the secret rode through in four Bearer
196
+ // headers unmasked. Capture the opaque credential value only.
197
+ pattern: /(?:Authorization|Proxy-Authorization|X-Auth-Token|X-Api-Key)["']?\s*[:=]\s*["']?(?:(?:Bearer|Basic|Token|OAuth)\s+)?([A-Za-z0-9][A-Za-z0-9._~+\/=-]{7,})/gi,
198
+ validate: credentialValueValidate,
199
+ captureValue: true,
200
+ },
201
+ {
202
+ type: 'profile-username-password',
203
+ // Standalone `Bearer <opaque-token>` not preceded by a header name. Min length
204
+ // 12 keeps ordinary prose words after "Bearer" from matching.
205
+ pattern: /\bBearer\s+([A-Za-z0-9][A-Za-z0-9._~+\/=-]{11,})/g,
206
+ validate: credentialValueValidate,
207
+ captureValue: true,
121
208
  },
122
209
  {
123
210
  type: 'profile-address',
@@ -128,6 +215,7 @@ const DETECTORS = [
128
215
  type: 'profile-pronouns-gender',
129
216
  // Context-anchored pronoun declarations
130
217
  pattern: /(?:pronouns?|goes\s+by)\s*[:=]?\s*((?:he|she|they|ze|xe|ey|fae)(?:\s*\/\s*(?:him|her|them|zir|xem|em|faer))+)/gi,
218
+ captureValue: true,
131
219
  },
132
220
  ];
133
221
  /**
@@ -142,16 +230,67 @@ function detectRegexEntities(text, enabledEntities) {
142
230
  if (!enabledTypes.has(detector.type) && !sensitiveDataEnabled) {
143
231
  continue;
144
232
  }
145
- // Reset lastIndex for global regex
146
- detector.pattern.lastIndex = 0;
233
+ // Reset lastIndex for global regex. Ensure the `d` (hasIndices) flag so we can
234
+ // locate a captured group by its true offset instead of a fragile indexOf.
235
+ const pattern = detector.pattern.flags.includes('d')
236
+ ? detector.pattern
237
+ : new RegExp(detector.pattern.source, detector.pattern.flags + 'd');
238
+ pattern.lastIndex = 0;
147
239
  let match;
148
- while ((match = detector.pattern.exec(text)) !== null) {
240
+ while ((match = pattern.exec(text)) !== null) {
149
241
  const fullMatch = match[0];
150
- const capturedGroup = match[1]; // Some patterns use capture groups
151
- // Use captured group if available (for context-anchored patterns)
152
- const entityText = capturedGroup || fullMatch;
153
- const start = capturedGroup ? match.index + fullMatch.indexOf(capturedGroup) : match.index;
154
- const end = start + entityText.length;
242
+ // Extract a capture group ONLY for value-capturing detectors (captureValue).
243
+ // Structural detectors (phone/SSN/credit-card) have format sub-groups that are
244
+ // NOT the entity extracting one there feeds e.g. an area code to the validator
245
+ // and drops the match. For value-capturing patterns the value can land in any of
246
+ // several alternation groups, so use the first DEFINED group.
247
+ let groupIdx = -1;
248
+ if (detector.captureValue) {
249
+ for (let g = 1; g < match.length; g++) {
250
+ if (match[g] !== undefined) {
251
+ groupIdx = g;
252
+ break;
253
+ }
254
+ }
255
+ }
256
+ const capturedGroup = groupIdx >= 0 ? match[groupIdx] : undefined;
257
+ // Use captured group if available (for context-anchored / value-only patterns).
258
+ // Locate it via match.indices (the `d` flag) rather than fullMatch.indexOf,
259
+ // which mis-locates a value that also occurs inside the key (e.g. token=token123).
260
+ let entityText = capturedGroup ?? fullMatch;
261
+ const indices = match.indices;
262
+ const start = (capturedGroup !== undefined && indices && indices[groupIdx])
263
+ ? indices[groupIdx][0]
264
+ : match.index;
265
+ let end = start + entityText.length;
266
+ // Credential values captured by a greedy `\S+` (or the Bearer char class) can
267
+ // swallow trailing sentence punctuation — e.g. `token: <secret>.` grabs the
268
+ // period, minting a DIFFERENT content-derived token than the same secret seen
269
+ // as `Bearer <secret>'`. Trim trailing punctuation so one secret → one token
270
+ // (and the punctuation stays in the surrounding text).
271
+ if (detector.type === 'profile-username-password' && capturedGroup !== undefined) {
272
+ const trimmed = entityText.replace(/[.,;:!?)\]}>'"]+$/, '');
273
+ if (trimmed.length >= 4 && trimmed.length !== entityText.length) {
274
+ end -= entityText.length - trimmed.length;
275
+ entityText = trimmed;
276
+ }
277
+ }
278
+ // URLs: mask only the ORIGIN (scheme://host[:port]), never the path/query.
279
+ // The placeholder is URL-shaped (see ReplacementMap), so the model can still
280
+ // compose variants (append paths, add query params) and the composed URL
281
+ // remains unmaskable — masking the full URL made composition impossible and
282
+ // pushed models into fabricating placeholder ids.
283
+ if (detector.type === 'profile-url') {
284
+ // Skip loopback/private/template URLs entirely (see isMaskableUrl).
285
+ if (!isMaskableUrl(entityText)) {
286
+ continue;
287
+ }
288
+ const originMatch = entityText.match(/^(?:(?:https?|wss?|ftps?):\/\/)?[^/?#]+/);
289
+ if (originMatch) {
290
+ entityText = originMatch[0];
291
+ end = start + entityText.length;
292
+ }
293
+ }
155
294
  // Run post-detection validation if defined
156
295
  if (detector.validate && !detector.validate(entityText)) {
157
296
  continue;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Entity-category toggles for the pseudonymizationPlugin.
3
+ *
4
+ * api_config.json can enable/disable masking categories with boolean maps
5
+ * ({ "<category>": true|false }) at three layers: global
6
+ * (api_config.pseudonymization.entities), per endpoint
7
+ * (defaultHooks.<endpoint>.pseudonymization.entities), and per model
8
+ * (model_list_changes.<model>.pseudonymization.entities). Later layers win.
9
+ *
10
+ * These toggles configure the plugin's DEFAULT entity set (force-config and
11
+ * triggerword activation) only. They do not activate masking by themselves —
12
+ * the plugin must be wired into the hook chain and an activation source must
13
+ * fire — and an explicit caller-supplied body `masking` config is used as-is.
14
+ */
15
+ import { EntityConfig } from './types';
16
+ /** All category types the plugin understands, for toggle validation. */
17
+ export declare function buildKnownEntityTypes(defaults: EntityConfig[]): Set<string>;
18
+ /**
19
+ * Apply a toggle map to a base entity list. `false` removes the category,
20
+ * `true` adds it if absent; categories not mentioned keep their current state.
21
+ * Unknown category names are ignored with a single WARN — a typo can never
22
+ * crash request handling or silently widen masking. Pure — returns a new array.
23
+ */
24
+ export declare function applyEntityToggles(base: EntityConfig[], toggles: Record<string, boolean> | null | undefined, knownTypes: Set<string>): EntityConfig[];
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ /**
3
+ * Entity-category toggles for the pseudonymizationPlugin.
4
+ *
5
+ * api_config.json can enable/disable masking categories with boolean maps
6
+ * ({ "<category>": true|false }) at three layers: global
7
+ * (api_config.pseudonymization.entities), per endpoint
8
+ * (defaultHooks.<endpoint>.pseudonymization.entities), and per model
9
+ * (model_list_changes.<model>.pseudonymization.entities). Later layers win.
10
+ *
11
+ * These toggles configure the plugin's DEFAULT entity set (force-config and
12
+ * triggerword activation) only. They do not activate masking by themselves —
13
+ * the plugin must be wired into the hook chain and an activation source must
14
+ * fire — and an explicit caller-supplied body `masking` config is used as-is.
15
+ */
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.buildKnownEntityTypes = buildKnownEntityTypes;
18
+ exports.applyEntityToggles = applyEntityToggles;
19
+ const logger_1 = require("@libs/logger");
20
+ /** All category types the plugin understands, for toggle validation. */
21
+ function buildKnownEntityTypes(defaults) {
22
+ return new Set([...defaults.map(e => e.type), 'profile-sensitive-data']);
23
+ }
24
+ // Unknown toggle keys already warned about (one WARN per key per process, not per request).
25
+ const warnedUnknownToggles = new Set();
26
+ /**
27
+ * Apply a toggle map to a base entity list. `false` removes the category,
28
+ * `true` adds it if absent; categories not mentioned keep their current state.
29
+ * Unknown category names are ignored with a single WARN — a typo can never
30
+ * crash request handling or silently widen masking. Pure — returns a new array.
31
+ */
32
+ function applyEntityToggles(base, toggles, knownTypes) {
33
+ if (!toggles || typeof toggles !== 'object')
34
+ return base;
35
+ let result = base.slice();
36
+ for (const [type, enabled] of Object.entries(toggles)) {
37
+ if (typeof enabled !== 'boolean')
38
+ continue;
39
+ if (!knownTypes.has(type)) {
40
+ if (!warnedUnknownToggles.has(type)) {
41
+ warnedUnknownToggles.add(type);
42
+ (0, logger_1.getDefaultLogger)().warn('Pseudonymization', `Ignoring unknown entity toggle '${type}' in api_config.json pseudonymization config (known types: ${Array.from(knownTypes).join(', ')})`);
43
+ }
44
+ continue;
45
+ }
46
+ if (enabled === false) {
47
+ result = result.filter(e => e.type !== type);
48
+ }
49
+ else if (!result.some(e => e.type === type)) {
50
+ result.push({ type });
51
+ }
52
+ }
53
+ return result;
54
+ }
@@ -15,7 +15,8 @@
15
15
  *
16
16
  * Strategies: before + after + stream
17
17
  *
18
- * @see pseudonymization-proxy-prompt.md - Full specification
18
+ * @see ../pseudonymization.md - Usage, activation methods, and category configuration
19
+ * @see ../../../../../docs/security/pseudonymization-security-assessment.md - Security assessment
19
20
  */
20
21
  import { Request, Response } from 'express';
21
22
  interface PluginContext {