@jwilleke/ngdpbase-types 0.0.0 → 5.18.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.
Files changed (302) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +16 -2
  3. package/lib/packages/ngdpbase-types/index.d.ts +32 -0
  4. package/lib/packages/ngdpbase-types/testing.d.ts +43 -0
  5. package/lib/packages/ngdpbase-types/testing.js +53 -0
  6. package/lib/src/cache/CacheDependencies.d.ts +50 -0
  7. package/lib/src/cache/CacheDependencies.js +50 -0
  8. package/lib/src/cache/ICacheAdapter.d.ts +86 -0
  9. package/lib/src/cache/RegionCache.d.ts +171 -0
  10. package/lib/src/context/ActorContext.d.ts +48 -0
  11. package/lib/src/context/ActorContext.js +33 -0
  12. package/lib/src/context/ApiContext.d.ts +194 -0
  13. package/lib/src/context/ApiContext.js +273 -0
  14. package/lib/src/context/BaseContext.d.ts +110 -0
  15. package/lib/src/context/BaseContext.js +143 -0
  16. package/lib/src/context/JobContext.d.ts +153 -0
  17. package/lib/src/context/JobContext.js +144 -0
  18. package/lib/src/context/WikiContext.d.ts +404 -0
  19. package/lib/src/context/bootActions.d.ts +64 -0
  20. package/lib/src/context/bootActions.js +111 -0
  21. package/lib/src/converters/HtmlConverter.d.ts +70 -0
  22. package/lib/src/converters/IContentConverter.d.ts +94 -0
  23. package/lib/src/converters/JSPWikiConverter.d.ts +117 -0
  24. package/lib/src/converters/conversionWarning.d.ts +15 -0
  25. package/lib/src/converters/ncm/bridge.d.ts +22 -0
  26. package/lib/src/converters/ncm/existing.d.ts +23 -0
  27. package/lib/src/converters/ncm/fix/blocks.d.ts +89 -0
  28. package/lib/src/converters/ncm/fix/bulletMarkers.d.ts +21 -0
  29. package/lib/src/converters/ncm/fix/index.d.ts +36 -0
  30. package/lib/src/converters/ncm/fix/jspwikiBullets.d.ts +20 -0
  31. package/lib/src/converters/ncm/fix/jspwikiCodeMarkers.d.ts +38 -0
  32. package/lib/src/converters/ncm/fix/jspwikiHeadings.d.ts +25 -0
  33. package/lib/src/converters/ncm/fix/jspwikiIndent.d.ts +24 -0
  34. package/lib/src/converters/ncm/fix/jspwikiInsert.d.ts +21 -0
  35. package/lib/src/converters/ncm/fix/jspwikiItalic.d.ts +29 -0
  36. package/lib/src/converters/ncm/fix/jspwikiLinkLeftovers.d.ts +27 -0
  37. package/lib/src/converters/ncm/fix/moreInformationFooter.d.ts +25 -0
  38. package/lib/src/converters/ncm/fix/styleClosers.d.ts +25 -0
  39. package/lib/src/converters/ncm/fix/tightenLists.d.ts +30 -0
  40. package/lib/src/converters/ncm/fix/types.d.ts +33 -0
  41. package/lib/src/converters/ncm/footnotes.d.ts +39 -0
  42. package/lib/src/converters/ncm/images.d.ts +49 -0
  43. package/lib/src/converters/ncm/index.d.ts +20 -0
  44. package/lib/src/converters/ncm/links.d.ts +21 -0
  45. package/lib/src/converters/ncm/normalize.d.ts +39 -0
  46. package/lib/src/converters/ncm/placeholder.d.ts +25 -0
  47. package/lib/src/converters/ncm/tables.d.ts +36 -0
  48. package/lib/src/converters/ncm/types.d.ts +42 -0
  49. package/lib/src/http/egressPolicy.d.ts +52 -0
  50. package/lib/src/http/guardedFetch.d.ts +89 -0
  51. package/lib/src/http/guardedFetch.js +113 -0
  52. package/lib/src/http/guardedLookup.d.ts +48 -0
  53. package/lib/src/http/guardedLookup.js +73 -0
  54. package/lib/src/http/ssrf.d.ts +83 -0
  55. package/lib/src/http/ssrf.js +311 -0
  56. package/lib/src/mail/MailProvider.d.ts +36 -0
  57. package/lib/src/mail/NodemailerMailProvider.d.ts +26 -0
  58. package/lib/src/managers/AddonsManager.d.ts +691 -0
  59. package/lib/src/managers/AgentTokenManager.d.ts +269 -0
  60. package/lib/src/managers/AssetManager.d.ts +116 -0
  61. package/lib/src/managers/AttachmentManager.d.ts +1032 -0
  62. package/lib/src/managers/AuthManager.d.ts +1257 -0
  63. package/lib/src/managers/BackgroundJobManager.d.ts +434 -0
  64. package/lib/src/managers/BaseManager.d.ts +391 -0
  65. package/lib/src/managers/BaseManager.js +415 -0
  66. package/lib/src/managers/CacheManager.d.ts +159 -0
  67. package/lib/src/managers/CatalogManager.d.ts +235 -0
  68. package/lib/src/managers/CommentManager.d.ts +75 -0
  69. package/lib/src/managers/ConfigurationManager.d.ts +747 -0
  70. package/lib/src/managers/DatabaseManager.d.ts +89 -0
  71. package/lib/src/managers/DatabaseManager.js +236 -0
  72. package/lib/src/managers/EmailManager.d.ts +47 -0
  73. package/lib/src/managers/ExportManager.d.ts +129 -0
  74. package/lib/src/managers/ExportManager.js +420 -0
  75. package/lib/src/managers/FootnoteManager.d.ts +118 -0
  76. package/lib/src/managers/MediaManager.d.ts +279 -0
  77. package/lib/src/managers/MetricsManager.d.ts +89 -0
  78. package/lib/src/managers/NotificationManager.d.ts +165 -0
  79. package/lib/src/managers/OidcManager.d.ts +210 -0
  80. package/lib/src/managers/OrganizationManager.d.ts +107 -0
  81. package/lib/src/managers/PageManager.d.ts +1645 -0
  82. package/lib/src/managers/PersonManager.d.ts +48 -0
  83. package/lib/src/managers/PluginManager.d.ts +179 -0
  84. package/lib/src/managers/PluginManager.js +393 -0
  85. package/lib/src/managers/PolicyEvaluator.d.ts +213 -0
  86. package/lib/src/managers/RenderingManager.d.ts +358 -0
  87. package/lib/src/managers/RoleManager.d.ts +134 -0
  88. package/lib/src/managers/SearchManager.d.ts +402 -0
  89. package/lib/src/managers/ShareManager.d.ts +251 -0
  90. package/lib/src/managers/TemplateManager.d.ts +176 -0
  91. package/lib/src/managers/ThemeManager.d.ts +56 -0
  92. package/lib/src/managers/UserManager.d.ts +480 -0
  93. package/lib/src/managers/ValidationManager.d.ts +426 -0
  94. package/lib/src/managers/VariableManager.d.ts +145 -0
  95. package/lib/src/middleware/csrf.d.ts +3 -0
  96. package/lib/src/parsers/LinkParser.d.ts +457 -0
  97. package/lib/src/parsers/MarkupParser.d.ts +769 -0
  98. package/lib/src/parsers/context/ParseContext.d.ts +314 -0
  99. package/lib/src/parsers/data/emoji-map.d.ts +18 -0
  100. package/lib/src/parsers/dom/DOMBuilder.d.ts +210 -0
  101. package/lib/src/parsers/dom/DOMParser.d.ts +252 -0
  102. package/lib/src/parsers/dom/Tokenizer.d.ts +358 -0
  103. package/lib/src/parsers/dom/WikiDocument.d.ts +272 -0
  104. package/lib/src/parsers/dom/handlers/DOMLinkHandler.d.ts +260 -0
  105. package/lib/src/parsers/dom/handlers/DOMPluginHandler.d.ts +212 -0
  106. package/lib/src/parsers/dom/handlers/DOMVariableHandler.d.ts +171 -0
  107. package/lib/src/parsers/filters/BaseFilter.d.ts +269 -0
  108. package/lib/src/parsers/filters/FilterChain.d.ts +320 -0
  109. package/lib/src/parsers/handlers/BaseSyntaxHandler.d.ts +331 -0
  110. package/lib/src/parsers/handlers/HandlerRegistry.d.ts +225 -0
  111. package/lib/src/parsers/handlers/JSPWikiPreprocessor.d.ts +89 -0
  112. package/lib/src/parsers/handlers/LinkParserHandler.d.ts +80 -0
  113. package/lib/src/parsers/handlers/PluginSyntaxHandler.d.ts +78 -0
  114. package/lib/src/parsers/handlers/WikiFormHandler.d.ts +149 -0
  115. package/lib/src/parsers/handlers/WikiTagHandler.d.ts +125 -0
  116. package/lib/src/parsers/jspwikiTableRow.d.ts +39 -0
  117. package/lib/src/parsers/styleBlockSyntax.d.ts +14 -0
  118. package/lib/src/plugins/types.d.ts +79 -0
  119. package/lib/src/providers/AgentTokenAuthProvider.d.ts +40 -0
  120. package/lib/src/providers/AuthentikBearerAuthProvider.d.ts +75 -0
  121. package/lib/src/providers/BaseAttachmentProvider.d.ts +197 -0
  122. package/lib/src/providers/BaseAuthProvider.d.ts +220 -0
  123. package/lib/src/providers/BaseCacheProvider.d.ts +140 -0
  124. package/lib/src/providers/BaseCredentialsProvider.d.ts +84 -0
  125. package/lib/src/providers/BaseDatabaseProvider.d.ts +22 -0
  126. package/lib/src/providers/BaseDatabaseProvider.js +12 -0
  127. package/lib/src/providers/BaseLoggingProvider.d.ts +68 -0
  128. package/lib/src/providers/BaseLoggingProvider.js +37 -0
  129. package/lib/src/providers/BaseMediaProvider.d.ts +308 -0
  130. package/lib/src/providers/BasePageProvider.d.ts +337 -0
  131. package/lib/src/providers/BaseProvider.d.ts +98 -0
  132. package/lib/src/providers/BaseProvider.js +74 -0
  133. package/lib/src/providers/BaseSearchProvider.d.ts +362 -0
  134. package/lib/src/providers/BasicAttachmentProvider.d.ts +599 -0
  135. package/lib/src/providers/CloudflareAccessAuthProvider.d.ts +72 -0
  136. package/lib/src/providers/FileCredentialsProvider.d.ts +33 -0
  137. package/lib/src/providers/FileJobStateProvider.d.ts +58 -0
  138. package/lib/src/providers/FileLoggingProvider.d.ts +43 -0
  139. package/lib/src/providers/FileLoggingProvider.js +120 -0
  140. package/lib/src/providers/FileOidcAdapter.d.ts +25 -0
  141. package/lib/src/providers/FileSystemMediaProvider.d.ts +403 -0
  142. package/lib/src/providers/LunrSearchProvider.d.ts +280 -0
  143. package/lib/src/providers/MagicLinkAuthProvider.d.ts +125 -0
  144. package/lib/src/providers/NullCacheProvider.d.ts +72 -0
  145. package/lib/src/providers/OidcAuthProvider.d.ts +180 -0
  146. package/lib/src/providers/OidcBearerAuthProvider.d.ts +30 -0
  147. package/lib/src/providers/PasskeyAuthProvider.d.ts +58 -0
  148. package/lib/src/providers/PasswordAuthProvider.d.ts +20 -0
  149. package/lib/src/providers/SqliteDatabaseProvider.d.ts +55 -0
  150. package/lib/src/providers/SqliteDatabaseProvider.js +90 -0
  151. package/lib/src/providers/SqliteJobStateProvider.d.ts +41 -0
  152. package/lib/src/providers/databaseMigrations.d.ts +8 -0
  153. package/lib/src/providers/databaseMigrations.js +47 -0
  154. package/lib/src/providers/jobState.d.ts +99 -0
  155. package/lib/src/providers/lunrQueryToMiniSearch.d.ts +32 -0
  156. package/lib/src/providers/mediaFilenameDate.d.ts +22 -0
  157. package/lib/src/providers/sqliteLocation.d.ts +17 -0
  158. package/lib/src/providers/sqliteLocation.js +55 -0
  159. package/lib/src/providers/sqliteMigrations.d.ts +48 -0
  160. package/lib/src/providers/sqliteMigrations.js +72 -0
  161. package/lib/src/rendering/githubAlerts.d.ts +22 -0
  162. package/lib/src/rendering/htmlPolicy.d.ts +76 -0
  163. package/lib/src/rendering/markdownConverter.d.ts +73 -0
  164. package/lib/src/security/PolicyDecisionPoint.d.ts +245 -0
  165. package/lib/src/security/PolicyInformationPoint.d.ts +448 -0
  166. package/lib/src/security/pageActions.d.ts +40 -0
  167. package/lib/src/security/permissions.generated.d.ts +78 -0
  168. package/lib/src/types/Asset.d.ts +414 -0
  169. package/lib/src/types/Catalog.d.ts +61 -0
  170. package/lib/src/types/Comment.d.ts +11 -0
  171. package/lib/src/types/Config.d.ts +388 -0
  172. package/lib/src/types/Organization.d.ts +78 -0
  173. package/lib/src/types/OrganizationProvider.d.ts +38 -0
  174. package/lib/src/types/Page.d.ts +220 -0
  175. package/lib/src/types/Person.d.ts +56 -0
  176. package/lib/src/types/PersonProvider.d.ts +25 -0
  177. package/lib/src/types/Policy.d.ts +139 -0
  178. package/lib/src/types/Policy.js +17 -0
  179. package/lib/src/types/Provider.d.ts +810 -0
  180. package/lib/src/types/Role.d.ts +84 -0
  181. package/lib/src/types/RoleProvider.d.ts +35 -0
  182. package/lib/src/types/Schema.d.ts +425 -0
  183. package/lib/src/types/Share.d.ts +172 -0
  184. package/lib/src/types/Share.js +81 -0
  185. package/lib/src/types/User.d.ts +304 -0
  186. package/lib/src/types/Version.d.ts +189 -0
  187. package/lib/src/types/WikiEngine.d.ts +128 -0
  188. package/lib/src/types/express.d.ts +120 -0
  189. package/lib/src/types/express.js +1 -0
  190. package/lib/src/types/guards.d.ts +78 -0
  191. package/lib/src/types/heic-decode.d.ts +16 -0
  192. package/lib/src/types/index.d.ts +19 -0
  193. package/lib/src/types/linkedom.d.ts +77 -0
  194. package/lib/src/types/markdown-it-plugins.d.ts +21 -0
  195. package/lib/src/utils/DeltaStorage.d.ts +118 -0
  196. package/lib/src/utils/LocaleUtils.d.ts +105 -0
  197. package/lib/src/utils/LocaleUtils.js +294 -0
  198. package/lib/src/utils/PageNameMatcher.d.ts +98 -0
  199. package/lib/src/utils/PathPreflight.d.ts +38 -0
  200. package/lib/src/utils/PathPreflight.js +50 -0
  201. package/lib/src/utils/SectionUtils.d.ts +40 -0
  202. package/lib/src/utils/addonConfigLayer.d.ts +144 -0
  203. package/lib/src/utils/addonConfigLayer.js +382 -0
  204. package/lib/src/utils/addonDatabaseRules.d.ts +25 -0
  205. package/lib/src/utils/addonDatabaseRules.js +57 -0
  206. package/lib/src/utils/addonHost.d.ts +51 -0
  207. package/lib/src/utils/addonHost.js +72 -0
  208. package/lib/src/utils/addonInstaller.d.ts +109 -0
  209. package/lib/src/utils/addonLock.d.ts +37 -0
  210. package/lib/src/utils/addonLock.js +48 -0
  211. package/lib/src/utils/addonPageSync.d.ts +45 -0
  212. package/lib/src/utils/addonProvenance.d.ts +48 -0
  213. package/lib/src/utils/addonTarball.d.ts +41 -0
  214. package/lib/src/utils/addonsPathResolver.d.ts +76 -0
  215. package/lib/src/utils/addonsPathResolver.js +129 -0
  216. package/lib/src/utils/assetMetadataTags.d.ts +66 -0
  217. package/lib/src/utils/atomicWrite.d.ts +28 -0
  218. package/lib/src/utils/auditConfigChange.d.ts +57 -0
  219. package/lib/src/utils/auditEventNames.d.ts +50 -0
  220. package/lib/src/utils/auditEventNames.generated.d.ts +163 -0
  221. package/lib/src/utils/auditEventNames.generated.js +168 -0
  222. package/lib/src/utils/auditEventNames.js +49 -0
  223. package/lib/src/utils/auditEvents.d.ts +265 -0
  224. package/lib/src/utils/auditEvents.js +309 -0
  225. package/lib/src/utils/auditRegistry.d.ts +102 -0
  226. package/lib/src/utils/auditRegistry.js +168 -0
  227. package/lib/src/utils/bip39English.d.ts +2 -0
  228. package/lib/src/utils/configEnvKeys.d.ts +68 -0
  229. package/lib/src/utils/configFiles.d.ts +74 -0
  230. package/lib/src/utils/configFiles.js +150 -0
  231. package/lib/src/utils/delegation.d.ts +48 -0
  232. package/lib/src/utils/deriveUsername.d.ts +21 -0
  233. package/lib/src/utils/explainMediaPath.d.ts +123 -0
  234. package/lib/src/utils/formerTitles.d.ts +69 -0
  235. package/lib/src/utils/frontmatterAccess.d.ts +54 -0
  236. package/lib/src/utils/headlessAdminPassword.d.ts +55 -0
  237. package/lib/src/utils/imageTransform.d.ts +52 -0
  238. package/lib/src/utils/instanceEnvSecret.d.ts +51 -0
  239. package/lib/src/utils/jobSlots.d.ts +43 -0
  240. package/lib/src/utils/keywordNormalizer.d.ts +127 -0
  241. package/lib/src/utils/logger.d.ts +70 -0
  242. package/lib/src/utils/logger.js +106 -0
  243. package/lib/src/utils/magicLinkDeviceState.d.ts +95 -0
  244. package/lib/src/utils/managerUtils.d.ts +40 -0
  245. package/lib/src/utils/migrateLegacyPrivateAttachments.d.ts +15 -0
  246. package/lib/src/utils/orgFilename.d.ts +22 -0
  247. package/lib/src/utils/pageFeature.d.ts +27 -0
  248. package/lib/src/utils/pageFrontmatter.d.ts +21 -0
  249. package/lib/src/utils/pageNotes.d.ts +150 -0
  250. package/lib/src/utils/pageTitleRule.d.ts +37 -0
  251. package/lib/src/utils/pageToArticle.d.ts +59 -0
  252. package/lib/src/utils/pageUrl.d.ts +18 -0
  253. package/lib/src/utils/passwordChange.d.ts +12 -0
  254. package/lib/src/utils/passwordHash.d.ts +60 -0
  255. package/lib/src/utils/permissionCatalog.d.ts +52 -0
  256. package/lib/src/utils/pluginFormatters.d.ts +452 -0
  257. package/lib/src/utils/pluginFormatters.js +714 -0
  258. package/lib/src/utils/privateLinkRewrite.d.ts +42 -0
  259. package/lib/src/utils/privateStoreAccess.d.ts +57 -0
  260. package/lib/src/utils/privateStoreAccess.js +65 -0
  261. package/lib/src/utils/privateStoreCatalogs.d.ts +40 -0
  262. package/lib/src/utils/privateStoreCrypto.d.ts +127 -0
  263. package/lib/src/utils/privateStoreDoor.d.ts +181 -0
  264. package/lib/src/utils/privateStoreExport.d.ts +72 -0
  265. package/lib/src/utils/privateStoreFiles.d.ts +120 -0
  266. package/lib/src/utils/privateStoreMeta.d.ts +64 -0
  267. package/lib/src/utils/privateStoreMigrations.d.ts +37 -0
  268. package/lib/src/utils/privateStorePath.d.ts +259 -0
  269. package/lib/src/utils/privateStorePath.js +436 -0
  270. package/lib/src/utils/privateStoreTakeout.d.ts +76 -0
  271. package/lib/src/utils/privateStoreUnlock.d.ts +175 -0
  272. package/lib/src/utils/proxyClaim.d.ts +59 -0
  273. package/lib/src/utils/proxyClaim.js +30 -0
  274. package/lib/src/utils/redactPrivateNames.d.ts +21 -0
  275. package/lib/src/utils/redactPrivateNames.js +89 -0
  276. package/lib/src/utils/redactSecrets.d.ts +58 -0
  277. package/lib/src/utils/redactSecrets.js +171 -0
  278. package/lib/src/utils/renameLinkRewrite.d.ts +112 -0
  279. package/lib/src/utils/roleGrants.d.ts +35 -0
  280. package/lib/src/utils/safeRedirect.d.ts +32 -0
  281. package/lib/src/utils/schedule.d.ts +54 -0
  282. package/lib/src/utils/secretFileMode.d.ts +17 -0
  283. package/lib/src/utils/seededShippedPages.d.ts +45 -0
  284. package/lib/src/utils/sensitiveValues.d.ts +59 -0
  285. package/lib/src/utils/sensitiveValues.js +106 -0
  286. package/lib/src/utils/sessionEnd.d.ts +12 -0
  287. package/lib/src/utils/sessionGeneration.d.ts +24 -0
  288. package/lib/src/utils/sessionIdle.d.ts +41 -0
  289. package/lib/src/utils/sessionLimits.d.ts +68 -0
  290. package/lib/src/utils/shareLockbox.d.ts +64 -0
  291. package/lib/src/utils/sigstoreTrustedRoot.d.ts +33 -0
  292. package/lib/src/utils/sniffMime.d.ts +21 -0
  293. package/lib/src/utils/storeSearchIndex.d.ts +75 -0
  294. package/lib/src/utils/subjectMayDo.d.ts +21 -0
  295. package/lib/src/utils/systemCategoryRules.d.ts +30 -0
  296. package/lib/src/utils/systemCategoryRules.js +41 -0
  297. package/lib/src/utils/userCreateError.d.ts +62 -0
  298. package/lib/src/utils/username.d.ts +53 -0
  299. package/lib/src/utils/vaultUrlRewrite.d.ts +27 -0
  300. package/lib/src/utils/version.d.ts +84 -0
  301. package/lib/src/utils/videoFrame.d.ts +21 -0
  302. package/package.json +54 -3
@@ -0,0 +1,48 @@
1
+ /**
2
+ * The guarded resolver (#1133) — the actual SSRF control.
3
+ *
4
+ * `validateUrl` in ./ssrf.ts inspects a URL string and is a courtesy: it gives
5
+ * an operator a clear message instead of a connection error. It cannot be the
6
+ * boundary, because a name that resolves to a public address when validated
7
+ * can resolve to an internal one moments later when connected, and because a
8
+ * redirect sends the request somewhere the original URL never named.
9
+ *
10
+ * This runs at connect time, after resolution, for every connection an agent
11
+ * makes — which is every redirect hop as well.
12
+ *
13
+ * Adapted from `src/http/ssrf.ts` in jwilleke/yourphr, sole-authored by the
14
+ * copyright holder and contributed here under this repository's Apache-2.0
15
+ * licence. The callback-shape handling and the `all: true` reasoning carry
16
+ * over; taking an `EgressPolicy` rather than a boolean, and the injected
17
+ * resolver, are new.
18
+ */
19
+ import { type LookupAddress } from 'node:dns';
20
+ import { type EgressPolicy } from './ssrf.js';
21
+ /** Marks a refusal as the guard's own decision rather than a network failure. */
22
+ export declare const ESSRF_BLOCKED = "ESSRFBLOCKED";
23
+ /**
24
+ * The resolution step, injectable.
25
+ *
26
+ * Production passes `node:dns`'s `lookup`. Tests pass a stub, which is how the
27
+ * plumbing is exercised without DNS or a socket — and deliberately the ONLY
28
+ * seam: tier 1 in ./ssrf.ts is absolute, so no policy can open loopback to
29
+ * reach a fixture server, and a production flag that could is precisely the
30
+ * switch that gets left on. The seam is a parameter a caller must supply, not
31
+ * a mode the shipped module can be put into.
32
+ */
33
+ export type Resolver = (hostname: string, options: {
34
+ all: true;
35
+ }, callback: (err: NodeJS.ErrnoException | null, addresses?: LookupAddress[]) => void) => void;
36
+ /**
37
+ * A `dns.lookup` that refuses to hand back an address the policy does not permit.
38
+ *
39
+ * Pass as the `lookup` option to an `http.Agent` / `https.Agent`; Node then
40
+ * calls it for every connection that agent opens.
41
+ *
42
+ * Resolution is always requested with `all: true` regardless of what the
43
+ * caller asked for, and EVERY returned address is judged. A name with several
44
+ * A records must not pass on the strength of one public answer while another
45
+ * points inward — that is round-robin SSRF, and checking only the first record
46
+ * misses it entirely. The caller's requested shape is restored afterwards.
47
+ */
48
+ export declare function guardedLookup(policy: EgressPolicy, resolver?: Resolver): (hostname: string, options: unknown, callback?: unknown) => void;
@@ -0,0 +1,73 @@
1
+ /**
2
+ * The guarded resolver (#1133) — the actual SSRF control.
3
+ *
4
+ * `validateUrl` in ./ssrf.ts inspects a URL string and is a courtesy: it gives
5
+ * an operator a clear message instead of a connection error. It cannot be the
6
+ * boundary, because a name that resolves to a public address when validated
7
+ * can resolve to an internal one moments later when connected, and because a
8
+ * redirect sends the request somewhere the original URL never named.
9
+ *
10
+ * This runs at connect time, after resolution, for every connection an agent
11
+ * makes — which is every redirect hop as well.
12
+ *
13
+ * Adapted from `src/http/ssrf.ts` in jwilleke/yourphr, sole-authored by the
14
+ * copyright holder and contributed here under this repository's Apache-2.0
15
+ * licence. The callback-shape handling and the `all: true` reasoning carry
16
+ * over; taking an `EgressPolicy` rather than a boolean, and the injected
17
+ * resolver, are new.
18
+ */
19
+ import { lookup as dnsLookup } from 'node:dns';
20
+ import { isAddressAllowed, isBlockedHostname, REFUSAL } from './ssrf.js';
21
+ /** Marks a refusal as the guard's own decision rather than a network failure. */
22
+ export const ESSRF_BLOCKED = 'ESSRFBLOCKED';
23
+ function refuse(message) {
24
+ return Object.assign(new Error(message), { code: ESSRF_BLOCKED });
25
+ }
26
+ /**
27
+ * A `dns.lookup` that refuses to hand back an address the policy does not permit.
28
+ *
29
+ * Pass as the `lookup` option to an `http.Agent` / `https.Agent`; Node then
30
+ * calls it for every connection that agent opens.
31
+ *
32
+ * Resolution is always requested with `all: true` regardless of what the
33
+ * caller asked for, and EVERY returned address is judged. A name with several
34
+ * A records must not pass on the strength of one public answer while another
35
+ * points inward — that is round-robin SSRF, and checking only the first record
36
+ * misses it entirely. The caller's requested shape is restored afterwards.
37
+ */
38
+ export function guardedLookup(policy, resolver = dnsLookup) {
39
+ return function guarded(hostname, options, callback) {
40
+ // Node calls lookup(hostname, options, cb) or lookup(hostname, cb).
41
+ const cb = (typeof options === 'function' ? options : callback);
42
+ const opts = (typeof options === 'function' ? {} : options ?? {});
43
+ // Names that are internal by definition never reach the resolver.
44
+ if (isBlockedHostname(hostname)) {
45
+ cb(refuse(`${REFUSAL}: ${hostname}`));
46
+ return;
47
+ }
48
+ resolver(hostname, { ...opts, all: true }, (err, addresses) => {
49
+ if (err) {
50
+ cb(err);
51
+ return;
52
+ }
53
+ const resolved = addresses ?? [];
54
+ const blocked = resolved.find((a) => !isAddressAllowed(a.address, policy));
55
+ if (blocked) {
56
+ cb(refuse(`${REFUSAL}: ${hostname} resolved to ${blocked.address}`));
57
+ return;
58
+ }
59
+ if (opts['all'] === true) {
60
+ cb(null, resolved);
61
+ return;
62
+ }
63
+ const first = resolved[0];
64
+ if (!first) {
65
+ // An empty answer must be an error, not `undefined` handed onward as
66
+ // though it were an address.
67
+ cb(Object.assign(new Error(`no addresses for ${hostname}`), { code: 'ENOTFOUND' }));
68
+ return;
69
+ }
70
+ cb(null, first.address, first.family);
71
+ });
72
+ };
73
+ }
@@ -0,0 +1,83 @@
1
+ /** Message every refusal carries, so callers and tests recognise the guard's own decision. */
2
+ export declare const REFUSAL = "refusing to connect to an internal address";
3
+ /** The operator-settable half of the boundary. Both lists hold IPv4 and IPv6 CIDRs. */
4
+ export interface EgressPolicy {
5
+ /** Ranges refused in addition to the defaults. */
6
+ deniedRanges: string[];
7
+ /** Ranges permitted despite the defaults — e.g. a home instance opening its own LAN. */
8
+ allowedRanges: string[];
9
+ }
10
+ /**
11
+ * The host as an address string, with the decorations URL parsing leaves on.
12
+ *
13
+ * `new URL('http://[::1]/').hostname` is the string `"[::1]"`, and
14
+ * `net.isIP('[::1]')` is 0 — so a literal-address check that skips the
15
+ * brackets concludes "not an address" and lets every IPv6 literal through.
16
+ * That was measured reaching a live local service.
17
+ */
18
+ export declare function normalizeHost(hostname: string): string;
19
+ /**
20
+ * Names that never leave the machine, refused before any resolution.
21
+ *
22
+ * The suffixes are the ones mDNS, container runtimes and cloud providers use
23
+ * for names that are internal by definition.
24
+ */
25
+ export declare function isBlockedHostname(host: string): boolean;
26
+ /**
27
+ * Tier 1 only: is this an address no configuration may permit?
28
+ *
29
+ * Separate from `isAddressAllowed` so the mechanism can be asserted on its own
30
+ * — a profile or an allow-list that could reach these would be a control that
31
+ * can be switched off, which is not a control.
32
+ */
33
+ export declare function isMechanismDenied(address: string): boolean;
34
+ /**
35
+ * The full decision for one address.
36
+ *
37
+ * Order, and the reasoning behind it:
38
+ *
39
+ * 1. Unparseable -> refuse. Fail closed.
40
+ * 2. Tier 1 -> refuse, whatever the policy says.
41
+ * 3. Explicit policy, longest prefix wins; a tie goes to deny. Explicit
42
+ * entries outrank the built-in defaults at equal length, mirroring the
43
+ * profile rule that an explicit key always beats a preset (#1137).
44
+ * 4. Built-in policy defaults -> refuse.
45
+ * 5. Otherwise public -> allow.
46
+ *
47
+ * Longest-prefix is the ROUTING convention, chosen deliberately over the
48
+ * access-control conventions (iptables, Cisco ACLs and AWS NACLs all resolve
49
+ * by rule order). It is used here because the values are routes: specificity
50
+ * is intrinsic to a CIDR and needs no operator-maintained ordering, which in a
51
+ * JSON config would drift silently.
52
+ */
53
+ export declare function isAddressAllowed(address: string, policy: EgressPolicy): boolean;
54
+ /**
55
+ * The early, friendly check. NOT the boundary — see the file header.
56
+ *
57
+ * A host that is not a literal address passes here and is judged at connect
58
+ * time instead, which is the only place a name's resolution can be seen.
59
+ */
60
+ export declare function validateUrl(raw: string, policy: EgressPolicy): {
61
+ ok: true;
62
+ url: URL;
63
+ } | {
64
+ ok: false;
65
+ reason: string;
66
+ };
67
+ /**
68
+ * Reconcile the two configured lists at BOOT, so a contradiction is a startup
69
+ * message rather than one dropped image months later — which would surface as
70
+ * a `fetch` warning indistinguishable from the remote host being down.
71
+ *
72
+ * An overlap between the lists is the normal case and not a conflict: the
73
+ * allow-list exists to narrow the deny-list. Only these are contradictions.
74
+ */
75
+ export interface ReconciledPolicy {
76
+ /** Every contradiction, human-readable. */
77
+ conflicts: string[];
78
+ /** Ranges that do not parse as CIDR. No safe silent resolution — see #1152. */
79
+ malformed: string[];
80
+ /** Ranges present verbatim in both lists. A prefix-length tie; the deny wins. */
81
+ duplicates: string[];
82
+ }
83
+ export declare function reconcilePolicy(deniedRanges: readonly string[], allowedRanges: readonly string[]): ReconciledPolicy;
@@ -0,0 +1,311 @@
1
+ /**
2
+ * The outbound address boundary (#1133).
3
+ *
4
+ * Three places in core called `fetch()` on a URL the server did not choose,
5
+ * with no host validation and `redirect: 'follow'`. The most reachable was
6
+ * gated on the page's own edit ACL, so anyone who could edit a page could make
7
+ * the server issue an HTTP request from inside the network.
8
+ *
9
+ * Adapted from `src/http/ssrf.ts` in jwilleke/yourphr — authored solely by the
10
+ * copyright holder and contributed here under this repository's Apache-2.0
11
+ * licence. Its numeric-host and bracket handling are carried over; the
12
+ * mechanism/policy tiering, the CIDR policy lists and the IPv6 transition
13
+ * ranges are new.
14
+ *
15
+ * THE REAL BOUNDARY IS AT CONNECT TIME, AFTER DNS. Nothing here is the control
16
+ * on its own — `validateUrl` is a courtesy that produces a friendly error
17
+ * early, and the guarded lookup that runs for every connection (including each
18
+ * redirect hop) is what actually holds. A URL check cannot be the boundary,
19
+ * because a name resolving to a public address at validation time can resolve
20
+ * to an internal one moments later at connect time.
21
+ *
22
+ * These are pure functions on purpose: the rules have to be checkable without
23
+ * a socket, an engine or a filesystem.
24
+ */
25
+ import ipaddr from 'ipaddr.js';
26
+ /** Message every refusal carries, so callers and tests recognise the guard's own decision. */
27
+ export const REFUSAL = 'refusing to connect to an internal address';
28
+ /**
29
+ * Tier 1 — the mechanism. Never overridable, by an allow-list or a profile.
30
+ *
31
+ * These are the addresses an editor cannot otherwise reach. On a LAN instance
32
+ * the editor is already on the LAN, so the server reaching 192.168.x grants
33
+ * them nothing new — but `127.0.0.1` on the server is a capability they do not
34
+ * have, and that is the actual privilege boundary.
35
+ *
36
+ * Teredo is here rather than in the policy tier because its embedded IPv4 is
37
+ * obfuscated and so cannot be decoded and judged the way NAT64 and 6to4 are.
38
+ * The range is a legacy transition mechanism with no innocent use here.
39
+ */
40
+ const MECHANISM_DENIED = [
41
+ '0.0.0.0/8', // "this network", includes the unspecified address
42
+ '127.0.0.0/8', // loopback
43
+ '169.254.0.0/16', // link-local, includes the 169.254.169.254 metadata address
44
+ '224.0.0.0/4', // multicast
45
+ '240.0.0.0/4', // reserved
46
+ '::/128', // unspecified
47
+ '::1/128', // loopback
48
+ 'fe80::/10', // link-local
49
+ 'ff00::/8', // multicast
50
+ '2001::/32' // Teredo — undecodable, denied wholesale
51
+ ];
52
+ /**
53
+ * Tier 2 — policy. Denied by default, and an allow-list entry may override it.
54
+ *
55
+ * This is the tier a home deployment typically opens to reach its own NAS or
56
+ * another host on the same segment — what the `baseline` recommendation
57
+ * describes, and which the operator opens themselves by setting the allow-list.
58
+ * Nothing here reads a posture name.
59
+ */
60
+ const DEFAULT_POLICY_DENIED = [
61
+ '10.0.0.0/8', // RFC1918
62
+ '172.16.0.0/12', // RFC1918
63
+ '192.168.0.0/16', // RFC1918
64
+ '100.64.0.0/10', // RFC6598 carrier-grade NAT
65
+ 'fc00::/7' // unique-local, includes fd00:ec2::254
66
+ ];
67
+ const MECHANISM_CIDRS = MECHANISM_DENIED.map((c) => ipaddr.parseCIDR(c));
68
+ const DEFAULT_POLICY_CIDRS = DEFAULT_POLICY_DENIED.map((c) => ipaddr.parseCIDR(c));
69
+ /**
70
+ * The host as an address string, with the decorations URL parsing leaves on.
71
+ *
72
+ * `new URL('http://[::1]/').hostname` is the string `"[::1]"`, and
73
+ * `net.isIP('[::1]')` is 0 — so a literal-address check that skips the
74
+ * brackets concludes "not an address" and lets every IPv6 literal through.
75
+ * That was measured reaching a live local service.
76
+ */
77
+ export function normalizeHost(hostname) {
78
+ let host = hostname.trim().toLowerCase();
79
+ if (host.startsWith('[') && host.endsWith(']')) {
80
+ host = host.slice(1, -1);
81
+ }
82
+ const zone = host.indexOf('%');
83
+ return zone === -1 ? host : host.slice(0, zone);
84
+ }
85
+ /**
86
+ * Names that never leave the machine, refused before any resolution.
87
+ *
88
+ * The suffixes are the ones mDNS, container runtimes and cloud providers use
89
+ * for names that are internal by definition.
90
+ */
91
+ export function isBlockedHostname(host) {
92
+ const h = host.trim().toLowerCase().replace(/\.$/, '');
93
+ if (h === 'localhost')
94
+ return true;
95
+ return ['.localhost', '.local', '.internal'].some((suffix) => h.endsWith(suffix));
96
+ }
97
+ /** Build an IPv4 address from four octets, or null if any is out of range. */
98
+ function ipv4From(octets) {
99
+ return octets.every((n) => Number.isInteger(n) && n >= 0 && n <= 255)
100
+ ? ipaddr.fromByteArray(octets)
101
+ : null;
102
+ }
103
+ /**
104
+ * Parse an address, unwrapping the IPv6 forms that carry an IPv4 destination.
105
+ *
106
+ * Returns null for anything unparseable, which callers treat as a refusal: an
107
+ * address we cannot parse is not one we can vouch for, and the alternative is
108
+ * connecting blind.
109
+ *
110
+ * `ipaddr.js` 2 decodes the short and numeric forms this comment used to say it
111
+ * rejected — `127.1`, `0x7f.1`, `2130706433` all now parse, with the same octal
112
+ * and hex reading the system resolver uses. The verdict on each is unchanged,
113
+ * because what refuses them is the loopback range, not a parse failure; the
114
+ * difference is that the guard now judges the address the resolver would reach
115
+ * instead of refusing a string it could not read. That closes the differential
116
+ * rather than covering it, so the null path here is the narrower fallback it
117
+ * was always meant to be.
118
+ */
119
+ function canonicalise(address) {
120
+ let parsed;
121
+ try {
122
+ parsed = ipaddr.parse(normalizeHost(address));
123
+ }
124
+ catch {
125
+ return null;
126
+ }
127
+ if (parsed.kind() !== 'ipv6')
128
+ return parsed;
129
+ const v6 = parsed;
130
+ // ::ffff:0:0/96 — the documented bypass when a guard reads only the text.
131
+ if (v6.isIPv4MappedAddress())
132
+ return v6.toIPv4Address();
133
+ const p = v6.parts;
134
+ // 64:ff9b::/96 — NAT64. The last 32 bits are the IPv4 destination, so on a
135
+ // NAT64 deployment 64:ff9b::a9fe:a9fe reaches 169.254.169.254.
136
+ if (p[0] === 0x0064 && p[1] === 0xff9b && p[2] === 0 && p[3] === 0 && p[4] === 0 && p[5] === 0) {
137
+ const v4 = ipv4From([p[6] >> 8, p[6] & 0xff, p[7] >> 8, p[7] & 0xff]);
138
+ if (v4)
139
+ return v4;
140
+ }
141
+ // 2002::/16 — 6to4. The IPv4 address sits in the second and third groups.
142
+ if (p[0] === 0x2002) {
143
+ const v4 = ipv4From([p[1] >> 8, p[1] & 0xff, p[2] >> 8, p[2] & 0xff]);
144
+ if (v4)
145
+ return v4;
146
+ }
147
+ return v6;
148
+ }
149
+ /**
150
+ * Does `addr` fall inside `cidr`?
151
+ *
152
+ * `ipaddr.parse` returns `IPv4 | IPv6`, and the two `match` signatures do not
153
+ * unify — so the families are narrowed apart rather than cast across. A
154
+ * cross-family comparison is false, never an error: an IPv4 address is not in
155
+ * an IPv6 range, and mapped forms were already unwrapped by `canonicalise`.
156
+ */
157
+ function inRange(addr, cidr) {
158
+ if (addr.kind() !== cidr[0].kind())
159
+ return false;
160
+ return addr.kind() === 'ipv4'
161
+ ? addr.match(cidr)
162
+ : addr.match(cidr);
163
+ }
164
+ /** Longest prefix among `cidrs` that contains `addr`, or null when none does. */
165
+ function longestMatch(addr, cidrs) {
166
+ let best = null;
167
+ for (const cidr of cidrs) {
168
+ if (inRange(addr, cidr) && (best === null || cidr[1] > best)) {
169
+ best = cidr[1];
170
+ }
171
+ }
172
+ return best;
173
+ }
174
+ /** Parse a CIDR list, silently dropping malformed entries — `reconcilePolicy` reports those. */
175
+ function parseList(ranges) {
176
+ const out = [];
177
+ for (const range of ranges) {
178
+ try {
179
+ out.push(ipaddr.parseCIDR(range));
180
+ }
181
+ catch {
182
+ // Reported at boot by reconcilePolicy; ignoring it here would be the
183
+ // only place a malformed range could widen access.
184
+ }
185
+ }
186
+ return out;
187
+ }
188
+ /**
189
+ * Tier 1 only: is this an address no configuration may permit?
190
+ *
191
+ * Separate from `isAddressAllowed` so the mechanism can be asserted on its own
192
+ * — a profile or an allow-list that could reach these would be a control that
193
+ * can be switched off, which is not a control.
194
+ */
195
+ export function isMechanismDenied(address) {
196
+ const addr = canonicalise(address);
197
+ if (!addr)
198
+ return true;
199
+ return longestMatch(addr, MECHANISM_CIDRS) !== null;
200
+ }
201
+ /**
202
+ * The full decision for one address.
203
+ *
204
+ * Order, and the reasoning behind it:
205
+ *
206
+ * 1. Unparseable -> refuse. Fail closed.
207
+ * 2. Tier 1 -> refuse, whatever the policy says.
208
+ * 3. Explicit policy, longest prefix wins; a tie goes to deny. Explicit
209
+ * entries outrank the built-in defaults at equal length, mirroring the
210
+ * profile rule that an explicit key always beats a preset (#1137).
211
+ * 4. Built-in policy defaults -> refuse.
212
+ * 5. Otherwise public -> allow.
213
+ *
214
+ * Longest-prefix is the ROUTING convention, chosen deliberately over the
215
+ * access-control conventions (iptables, Cisco ACLs and AWS NACLs all resolve
216
+ * by rule order). It is used here because the values are routes: specificity
217
+ * is intrinsic to a CIDR and needs no operator-maintained ordering, which in a
218
+ * JSON config would drift silently.
219
+ */
220
+ export function isAddressAllowed(address, policy) {
221
+ const addr = canonicalise(address);
222
+ if (!addr)
223
+ return false;
224
+ if (longestMatch(addr, MECHANISM_CIDRS) !== null)
225
+ return false;
226
+ const deniedPrefix = longestMatch(addr, parseList(policy.deniedRanges));
227
+ const allowedPrefix = longestMatch(addr, parseList(policy.allowedRanges));
228
+ if (deniedPrefix !== null || allowedPrefix !== null) {
229
+ if (allowedPrefix === null)
230
+ return false;
231
+ if (deniedPrefix === null)
232
+ return true;
233
+ return allowedPrefix > deniedPrefix;
234
+ }
235
+ return longestMatch(addr, DEFAULT_POLICY_CIDRS) === null;
236
+ }
237
+ /**
238
+ * The early, friendly check. NOT the boundary — see the file header.
239
+ *
240
+ * A host that is not a literal address passes here and is judged at connect
241
+ * time instead, which is the only place a name's resolution can be seen.
242
+ */
243
+ export function validateUrl(raw, policy) {
244
+ let url;
245
+ try {
246
+ url = new URL(raw);
247
+ }
248
+ catch {
249
+ return { ok: false, reason: 'not a valid URL' };
250
+ }
251
+ // An exact allow-list, not `startsWith('http')` — which accepts `httpfoo:`
252
+ // and happens to be safe only because fetch rejects unknown schemes.
253
+ if (url.protocol !== 'http:' && url.protocol !== 'https:') {
254
+ return { ok: false, reason: `unsupported scheme ${url.protocol}` };
255
+ }
256
+ const host = normalizeHost(url.hostname);
257
+ if (isBlockedHostname(host)) {
258
+ return { ok: false, reason: `${REFUSAL}: ${host}` };
259
+ }
260
+ if (canonicalise(host) && !isAddressAllowed(host, policy)) {
261
+ return { ok: false, reason: `${REFUSAL}: ${host}` };
262
+ }
263
+ return { ok: true, url };
264
+ }
265
+ export function reconcilePolicy(deniedRanges, allowedRanges) {
266
+ const conflicts = [];
267
+ // #1144: the three flagged cases are not equivalent, and a caller that can
268
+ // only see message strings cannot tell them apart. `malformed` has no safe
269
+ // silent resolution — dropping a malformed DENY fails open — while the other
270
+ // two resolve by convention (D8).
271
+ const malformed = [];
272
+ const duplicates = [];
273
+ for (const [label, ranges] of [['denied-ranges', deniedRanges], ['allowed-ranges', allowedRanges]]) {
274
+ for (const range of ranges) {
275
+ try {
276
+ ipaddr.parseCIDR(range);
277
+ }
278
+ catch {
279
+ conflicts.push(`${label}: '${range}' is not a valid CIDR range`);
280
+ malformed.push(range);
281
+ }
282
+ }
283
+ }
284
+ for (const range of allowedRanges) {
285
+ let cidr;
286
+ try {
287
+ cidr = ipaddr.parseCIDR(range);
288
+ }
289
+ catch {
290
+ continue; // already reported above
291
+ }
292
+ const hits = MECHANISM_CIDRS.filter((m) => cidrsIntersect(cidr, m));
293
+ if (hits.length > 0) {
294
+ conflicts.push(`allowed-ranges: '${range}' covers an address range that is never permitted ` +
295
+ `(${hits.map(([a, p]) => `${a.toString()}/${p}`).join(', ')}). ` +
296
+ 'Loopback, link-local and multicast are the mechanism, not policy.');
297
+ }
298
+ }
299
+ const deniedSet = new Set(deniedRanges);
300
+ for (const range of allowedRanges) {
301
+ if (deniedSet.has(range)) {
302
+ conflicts.push(`'${range}' appears verbatim in both denied-ranges and allowed-ranges — the deny wins`);
303
+ duplicates.push(range);
304
+ }
305
+ }
306
+ return { conflicts, malformed, duplicates };
307
+ }
308
+ /** Do two CIDRs overlap at all? True when either network address falls inside the other. */
309
+ function cidrsIntersect(a, b) {
310
+ return inRange(a[0], b) || inRange(b[0], a);
311
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * MailProvider — pluggable email transport interface.
3
+ *
4
+ * Implementations:
5
+ * ConsoleMailProvider — prints to server log (development / no-SMTP installs)
6
+ * NodemailerMailProvider — sends via SMTP using nodemailer
7
+ *
8
+ * The active transport is selected by the
9
+ * `ngdpbase.mail.provider` config key ('console' | 'smtp').
10
+ */
11
+ /**
12
+ * An outbound email message.
13
+ */
14
+ export interface MailMessage {
15
+ /** Recipient address */
16
+ to: string;
17
+ /** Email subject line */
18
+ subject: string;
19
+ /** Plain-text body */
20
+ text: string;
21
+ /** Optional HTML body */
22
+ html?: string;
23
+ }
24
+ /**
25
+ * Common interface for all mail transports.
26
+ */
27
+ export interface MailProvider {
28
+ send(message: MailMessage): Promise<void>;
29
+ }
30
+ /**
31
+ * Development transport — writes the email to the server log instead of
32
+ * sending it. Useful for local installs where no SMTP server is available.
33
+ */
34
+ export declare class ConsoleMailProvider implements MailProvider {
35
+ send(message: MailMessage): Promise<void>;
36
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * NodemailerMailProvider — SMTP mail transport using nodemailer.
3
+ *
4
+ * Configure via app-custom-config.json:
5
+ * ngdpbase.mail.provider.smtp.host
6
+ * ngdpbase.mail.provider.smtp.port
7
+ * ngdpbase.mail.provider.smtp.secure
8
+ * ngdpbase.mail.provider.smtp.user
9
+ * ngdpbase.mail.provider.smtp.pass
10
+ * ngdpbase.mail.provider.smtp.from
11
+ */
12
+ import type { MailMessage, MailProvider } from './MailProvider.js';
13
+ export interface SmtpConfig {
14
+ host: string;
15
+ port: number;
16
+ secure: boolean;
17
+ user?: string;
18
+ pass?: string;
19
+ from: string;
20
+ }
21
+ export declare class NodemailerMailProvider implements MailProvider {
22
+ private config;
23
+ private transporter;
24
+ constructor(config: SmtpConfig);
25
+ send(message: MailMessage): Promise<void>;
26
+ }