documentation-hub 5.7.2

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 (271) hide show
  1. package/.eslintrc.json +43 -0
  2. package/.github/workflows/build.yml +64 -0
  3. package/.github/workflows/ci.yml +39 -0
  4. package/.vscode/extensions.json +3 -0
  5. package/Current.md +97 -0
  6. package/DocHub_Image.png +0 -0
  7. package/README.md +666 -0
  8. package/USER_GUIDE.md +1173 -0
  9. package/Updater.md +311 -0
  10. package/build/256x256.png +0 -0
  11. package/build/512x512.png +0 -0
  12. package/build/app-update.yml +4 -0
  13. package/build/create-icon.js +208 -0
  14. package/build/icon.ico +0 -0
  15. package/build/icon.png +0 -0
  16. package/build/icon_1024x1024.png +0 -0
  17. package/dist/assets/Analytics-BpsG9895.js +1 -0
  18. package/dist/assets/Card-IAZin8kp.js +1 -0
  19. package/dist/assets/CurrentSession-B-rFkHvf.js +12 -0
  20. package/dist/assets/Dashboard-C_5gMb0q.js +1 -0
  21. package/dist/assets/Documents-CqZ25axS.js +1 -0
  22. package/dist/assets/Input-l89xwXBi.js +1 -0
  23. package/dist/assets/Reporting-DqdHJY_a.js +1 -0
  24. package/dist/assets/Search-XNbu5z_3.js +1 -0
  25. package/dist/assets/SessionManager-lH9hZfzH.js +1 -0
  26. package/dist/assets/Sessions-ClZOPYNc.js +1 -0
  27. package/dist/assets/Settings-DUEHGURa.js +11 -0
  28. package/dist/assets/index-8xUe8ptc.js +24 -0
  29. package/dist/assets/index-RYyJqF7O.css +1 -0
  30. package/dist/assets/path-BkOl0AGO.js +1 -0
  31. package/dist/assets/promises-ID_B9S-h.js +1 -0
  32. package/dist/assets/urlHelpers-TvgahX0r.js +1 -0
  33. package/dist/assets/useToast-yRSO1dkm.js +1 -0
  34. package/dist/assets/vendor-charts-RkGK5ROP.js +36 -0
  35. package/dist/assets/vendor-db-l0sNRNKZ.js +1 -0
  36. package/dist/assets/vendor-react-BVZ_anCF.js +4 -0
  37. package/dist/assets/vendor-search-Dw8P0qyA.js +1 -0
  38. package/dist/assets/vendor-ui-BU7NfluV.js +53 -0
  39. package/dist/electron/PowerAutomateApiService-LfW09ZGr.js +147 -0
  40. package/dist/electron/main-CXkNtyv-.js +19789 -0
  41. package/dist/electron/main.js +5 -0
  42. package/dist/electron/preload.js +1 -0
  43. package/dist/icon.png +0 -0
  44. package/dist/index.html +27 -0
  45. package/docs/CODEBASE_ANALYSIS_REPORT.md +309 -0
  46. package/docs/DEBUG_LOGGING_GUIDE.md +244 -0
  47. package/docs/README.md +115 -0
  48. package/docs/TOC_WIRING_GUIDE.md +344 -0
  49. package/docs/analysis/Bullet_Symbol_Bug_Analysis.md +136 -0
  50. package/docs/analysis/DOCXMLATER_ANALYSIS_SUMMARY.txt +169 -0
  51. package/docs/analysis/Document_Processing_Issues_Analysis.md +704 -0
  52. package/docs/analysis/FIELD_PRESERVATION_ANALYSIS.md +1200 -0
  53. package/docs/analysis/INDENTATION_PRESERVE_ANALYSIS.md +181 -0
  54. package/docs/analysis/INDENTATION_PRESERVE_IMPLEMENTATION.md +207 -0
  55. package/docs/analysis/List_Implementation.md +206 -0
  56. package/docs/analysis/List_Implementation_Accuracy_Report.md +366 -0
  57. package/docs/analysis/PROCESSING_OPTIONS_UI_UPDATES.md +220 -0
  58. package/docs/analysis/RefactorStyles.md +852 -0
  59. package/docs/analysis/STYLE_PARAMETER_ENHANCEMENT.md +143 -0
  60. package/docs/analysis/docxmlater-comparison-todo-2025-11-13.md +636 -0
  61. package/docs/analysis/docxmlater-implementation-analysis-2025-11-13.md +340 -0
  62. package/docs/analysis/docxmlater-template_ui-integration-analysis.md +263 -0
  63. package/docs/analysis/github-issues-to-create.md +237 -0
  64. package/docs/api/API_README.md +538 -0
  65. package/docs/api/API_REFERENCE.md +751 -0
  66. package/docs/api/TYPE_DEFINITIONS.md +869 -0
  67. package/docs/architecture/FONT_EMBEDDING_GUIDE.md +318 -0
  68. package/docs/architecture/docxmlater-functions-and-structure.md +726 -0
  69. package/docs/docxmlater-readme.md +1341 -0
  70. package/docs/fixes/EXECUTION_LOG_TEST_BASE.md +573 -0
  71. package/docs/fixes/HYPERLINK_TEXT_SANITIZATION.md +253 -0
  72. package/docs/fixes/README.md +37 -0
  73. package/docs/github-issues/issue-1-body.md +125 -0
  74. package/docs/github-issues/issue-10-body.md +850 -0
  75. package/docs/github-issues/issue-2-body.md +200 -0
  76. package/docs/github-issues/issue-3-body.md +270 -0
  77. package/docs/github-issues/issue-4-body.md +169 -0
  78. package/docs/github-issues/issue-5-body.md +173 -0
  79. package/docs/github-issues/issue-6-body.md +158 -0
  80. package/docs/github-issues/issue-7-body.md +171 -0
  81. package/docs/github-issues/issue-8-body.md +407 -0
  82. package/docs/github-issues/issue-9-body.md +515 -0
  83. package/docs/github-issues/issue-tracker.md +274 -0
  84. package/docs/github-issues/predictive-analysis-2025-10-18.md +2131 -0
  85. package/docs/implementation/List_Framework_Refactor_Plan.md +336 -0
  86. package/docs/implementation/PRIMARY_TEXT_COLOR_FEATURE.md +217 -0
  87. package/docs/implementation/RELEASE_PLAN_v2.1.0.md +362 -0
  88. package/docs/implementation/RefactorStyles.md +588 -0
  89. package/docs/implementation/implement-plan.md +489 -0
  90. package/docs/implementation/missing-helpers-implementation.md +391 -0
  91. package/docs/implementation/refactor-plan.md +520 -0
  92. package/docs/implementation/session-implementation-complete.md +233 -0
  93. package/docs/implementation/session-management-plan.md +250 -0
  94. package/docs/setup-checklist.md +77 -0
  95. package/docs/versions/changelog.md +345 -0
  96. package/electron/customUpdater.ts +656 -0
  97. package/electron/main.ts +2441 -0
  98. package/electron/memoryConfig.ts +187 -0
  99. package/electron/preload.ts +394 -0
  100. package/electron/proxyConfig.ts +340 -0
  101. package/electron/services/BackupService.ts +452 -0
  102. package/electron/services/DictionaryService.ts +402 -0
  103. package/electron/services/LocalDictionaryLookupService.ts +147 -0
  104. package/electron/services/PowerAutomateApiService.ts +231 -0
  105. package/electron/services/SharePointSyncService.ts +474 -0
  106. package/electron/windowsCertStore.ts +427 -0
  107. package/electron/zscalerConfig.ts +381 -0
  108. package/eslint.config.js +92 -0
  109. package/jest.config.js +52 -0
  110. package/package.json +214 -0
  111. package/postcss.config.mjs +6 -0
  112. package/public/icon.png +0 -0
  113. package/publish-release.ps1 +5 -0
  114. package/renovate.json +30 -0
  115. package/src/App.tsx +216 -0
  116. package/src/__mocks__/p-limit.js +12 -0
  117. package/src/__mocks__/styleMock.js +1 -0
  118. package/src/components/common/BugReportButton.tsx +44 -0
  119. package/src/components/common/BugReportDialog.tsx +193 -0
  120. package/src/components/common/Button.tsx +153 -0
  121. package/src/components/common/Card.tsx +86 -0
  122. package/src/components/common/ColorPickerDialog.tsx +177 -0
  123. package/src/components/common/ConfirmDialog.tsx +96 -0
  124. package/src/components/common/DebugConsole.tsx +275 -0
  125. package/src/components/common/EmptyState.tsx +183 -0
  126. package/src/components/common/ErrorBoundary.tsx +98 -0
  127. package/src/components/common/ErrorDetailsDialog.tsx +153 -0
  128. package/src/components/common/ErrorFallback.tsx +218 -0
  129. package/src/components/common/Input.tsx +109 -0
  130. package/src/components/common/Skeleton.tsx +184 -0
  131. package/src/components/common/SplashScreen.tsx +81 -0
  132. package/src/components/common/Toast.tsx +155 -0
  133. package/src/components/common/Tooltip.tsx +79 -0
  134. package/src/components/common/UpdateNotification.tsx +320 -0
  135. package/src/components/comparison/ComparisonWindow.tsx +374 -0
  136. package/src/components/comparison/SideBySideDiff.tsx +486 -0
  137. package/src/components/comparison/index.ts +8 -0
  138. package/src/components/document/DocumentUploader.tsx +288 -0
  139. package/src/components/document/HyperlinkPreview.tsx +430 -0
  140. package/src/components/document/HyperlinkService.md +1484 -0
  141. package/src/components/document/Hyperlink_Technical_Documentation.md +496 -0
  142. package/src/components/document/InlineChangesView.tsx +707 -0
  143. package/src/components/document/ProcessingProgress.tsx +303 -0
  144. package/src/components/document/ProcessingResults.tsx +256 -0
  145. package/src/components/document/TrackedChangesDetail.tsx +530 -0
  146. package/src/components/document/TrackedChangesPanel.tsx +546 -0
  147. package/src/components/document/VirtualDocumentList.tsx +240 -0
  148. package/src/components/editor/DocumentEditor.tsx +723 -0
  149. package/src/components/editor/DocumentEditorModal.tsx +640 -0
  150. package/src/components/editor/EditorQuickActions.tsx +502 -0
  151. package/src/components/editor/EditorToolbar.tsx +312 -0
  152. package/src/components/editor/TableEditor.tsx +926 -0
  153. package/src/components/editor/index.ts +18 -0
  154. package/src/components/layout/Header.tsx +190 -0
  155. package/src/components/layout/Sidebar.tsx +313 -0
  156. package/src/components/layout/TitleBar.tsx +190 -0
  157. package/src/components/navigation/CommandPalette.tsx +233 -0
  158. package/src/components/navigation/KeyboardShortcutsModal.tsx +173 -0
  159. package/src/components/sessions/ChangeItem.tsx +408 -0
  160. package/src/components/sessions/ChangeViewer.tsx +1155 -0
  161. package/src/components/sessions/DocumentComparisonModal.tsx +314 -0
  162. package/src/components/sessions/ProcessingOptions.tsx +297 -0
  163. package/src/components/sessions/ReplacementsTab.tsx +438 -0
  164. package/src/components/sessions/RevisionHandlingOptions.tsx +87 -0
  165. package/src/components/sessions/SessionManager.tsx +188 -0
  166. package/src/components/sessions/StylesEditor.tsx +1335 -0
  167. package/src/components/sessions/TabContainer.tsx +151 -0
  168. package/src/components/sessions/VirtualSessionList.tsx +157 -0
  169. package/src/components/sessions/sessionToProcessorManager.tsx +420 -0
  170. package/src/components/settings/CertificateManager.tsx +410 -0
  171. package/src/components/settings/SegmentedControl.tsx +88 -0
  172. package/src/components/settings/SettingRow.tsx +52 -0
  173. package/src/contexts/GlobalStatsContext.tsx +396 -0
  174. package/src/contexts/SessionContext.tsx +2129 -0
  175. package/src/contexts/ThemeContext.tsx +428 -0
  176. package/src/contexts/UserSettingsContext.tsx +290 -0
  177. package/src/contexts/__tests__/GlobalStatsContext.test.tsx +390 -0
  178. package/src/global.d.ts +273 -0
  179. package/src/hooks/useDocumentQueue.tsx +210 -0
  180. package/src/hooks/useToast.tsx +55 -0
  181. package/src/main.tsx +10 -0
  182. package/src/pages/Analytics.tsx +386 -0
  183. package/src/pages/CurrentSession.tsx +1174 -0
  184. package/src/pages/Dashboard.tsx +319 -0
  185. package/src/pages/Documents.tsx +317 -0
  186. package/src/pages/Projects.tsx +250 -0
  187. package/src/pages/Reporting.tsx +386 -0
  188. package/src/pages/Search.tsx +349 -0
  189. package/src/pages/Sessions.tsx +285 -0
  190. package/src/pages/Settings.tsx +2662 -0
  191. package/src/services/HyperlinkService.ts +1085 -0
  192. package/src/services/document/DocXMLaterProcessor.ts +617 -0
  193. package/src/services/document/DocumentProcessingComparison.ts +856 -0
  194. package/src/services/document/DocumentSnapshotService.ts +575 -0
  195. package/src/services/document/WordDocumentProcessor.ts +10509 -0
  196. package/src/services/document/__tests__/DocXMLaterProcessor.hyperlinks.test.md +311 -0
  197. package/src/services/document/__tests__/WordDocumentProcessor.integration.test.ts +515 -0
  198. package/src/services/document/__tests__/WordDocumentProcessor.test.ts +812 -0
  199. package/src/services/document/blanklines/BlankLineManager.ts +658 -0
  200. package/src/services/document/blanklines/__tests__/paragraphChecks.test.ts +281 -0
  201. package/src/services/document/blanklines/helpers/blankLineInsertion.ts +87 -0
  202. package/src/services/document/blanklines/helpers/blankLineSnapshot.ts +251 -0
  203. package/src/services/document/blanklines/helpers/clearCustom.ts +121 -0
  204. package/src/services/document/blanklines/helpers/contextChecks.ts +117 -0
  205. package/src/services/document/blanklines/helpers/imageChecks.ts +51 -0
  206. package/src/services/document/blanklines/helpers/paragraphChecks.ts +236 -0
  207. package/src/services/document/blanklines/helpers/removeBlanksBetweenListItems.ts +91 -0
  208. package/src/services/document/blanklines/helpers/removeTrailingBlanks.ts +35 -0
  209. package/src/services/document/blanklines/helpers/tableGuards.ts +21 -0
  210. package/src/services/document/blanklines/index.ts +67 -0
  211. package/src/services/document/blanklines/rules/additionRules.ts +337 -0
  212. package/src/services/document/blanklines/rules/indentationRules.ts +317 -0
  213. package/src/services/document/blanklines/rules/removalRules.ts +362 -0
  214. package/src/services/document/blanklines/rules/ruleTypes.ts +92 -0
  215. package/src/services/document/blanklines/types.ts +29 -0
  216. package/src/services/document/helpers/ImageBorderCropper.ts +377 -0
  217. package/src/services/document/helpers/__tests__/whitespace.test.ts +272 -0
  218. package/src/services/document/helpers/whitespace.ts +117 -0
  219. package/src/services/document/list/ListNormalizer.ts +947 -0
  220. package/src/services/document/list/index.ts +45 -0
  221. package/src/services/document/list/list-detection.ts +275 -0
  222. package/src/services/document/list/list-types.ts +162 -0
  223. package/src/services/document/processors/HyperlinkProcessor.ts +370 -0
  224. package/src/services/document/processors/ListProcessor.ts +257 -0
  225. package/src/services/document/processors/StructureProcessor.ts +176 -0
  226. package/src/services/document/processors/StyleProcessor.ts +389 -0
  227. package/src/services/document/processors/TableProcessor.ts +2238 -0
  228. package/src/services/document/processors/__tests__/HyperlinkProcessor.test.ts +314 -0
  229. package/src/services/document/processors/__tests__/ListProcessor.test.ts +291 -0
  230. package/src/services/document/processors/__tests__/StructureProcessor.test.ts +257 -0
  231. package/src/services/document/processors/__tests__/TableProcessor.hlp-tips-bullets.test.ts +459 -0
  232. package/src/services/document/processors/__tests__/TableProcessor.test.ts +1604 -0
  233. package/src/services/document/processors/index.ts +28 -0
  234. package/src/services/document/types/docx-processing.ts +310 -0
  235. package/src/services/editor/EditorActionHandlers.ts +901 -0
  236. package/src/services/editor/index.ts +13 -0
  237. package/src/setupTests.ts +47 -0
  238. package/src/styles/global.css +782 -0
  239. package/src/types/backup.ts +132 -0
  240. package/src/types/dictionary.ts +125 -0
  241. package/src/types/document-processing.ts +331 -0
  242. package/src/types/docxmlater-augments.d.ts +142 -0
  243. package/src/types/editor.ts +280 -0
  244. package/src/types/electron.ts +340 -0
  245. package/src/types/globalStats.ts +155 -0
  246. package/src/types/hyperlink.ts +471 -0
  247. package/src/types/operations.ts +354 -0
  248. package/src/types/session.ts +427 -0
  249. package/src/types/settings.ts +112 -0
  250. package/src/utils/MemoryMonitor.ts +248 -0
  251. package/src/utils/cn.ts +6 -0
  252. package/src/utils/colorConvert.ts +306 -0
  253. package/src/utils/diffUtils.ts +347 -0
  254. package/src/utils/documentUtils.ts +202 -0
  255. package/src/utils/electronGuard.ts +62 -0
  256. package/src/utils/indexedDB.ts +915 -0
  257. package/src/utils/logger.ts +717 -0
  258. package/src/utils/pathSecurity.ts +232 -0
  259. package/src/utils/pathValidator.ts +236 -0
  260. package/src/utils/processingTimeEstimator.ts +153 -0
  261. package/src/utils/safeJsonParse.ts +62 -0
  262. package/src/utils/textSanitizer.ts +162 -0
  263. package/src/utils/urlHelpers.ts +304 -0
  264. package/src/utils/urlPatterns.ts +198 -0
  265. package/src/utils/urlSanitizer.ts +152 -0
  266. package/src/vite-env.d.ts +11 -0
  267. package/tsconfig.electron.json +19 -0
  268. package/tsconfig.json +36 -0
  269. package/tsconfig.node.json +12 -0
  270. package/typedoc.json +45 -0
  271. package/vite.config.ts +152 -0
@@ -0,0 +1,427 @@
1
+ import { exec } from 'child_process';
2
+ import { promisify } from 'util';
3
+ import * as fs from 'fs';
4
+ import * as path from 'path';
5
+ import { app } from 'electron';
6
+ import * as crypto from 'crypto';
7
+ import { logger } from '../src/utils/logger';
8
+
9
+ const execAsync = promisify(exec);
10
+ const log = logger.namespace('WindowsCertStore');
11
+
12
+ /**
13
+ * Windows Certificate Store Integration
14
+ * Provides methods to interact with Windows certificate store for automatic
15
+ * Zscaler and corporate certificate discovery
16
+ */
17
+ export class WindowsCertStore {
18
+ private certificateCache: Map<string, string> = new Map();
19
+ private readonly tempCertDir: string;
20
+
21
+ constructor() {
22
+ this.tempCertDir = path.join(app.getPath('userData'), 'certs');
23
+ this.ensureCertDirectory();
24
+ }
25
+
26
+ /**
27
+ * Ensure certificate directory exists
28
+ */
29
+ private ensureCertDirectory(): void {
30
+ if (!fs.existsSync(this.tempCertDir)) {
31
+ fs.mkdirSync(this.tempCertDir, { recursive: true });
32
+ log.info('[ZscalerConfig] Created certificate directory:', this.tempCertDir);
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Search for Zscaler certificate in Windows certificate store
38
+ */
39
+ public async findZscalerCertificate(): Promise<string | null> {
40
+ if (process.platform !== 'win32') {
41
+ log.info('[ZscalerConfig] Not on Windows, skipping certificate store search');
42
+ return null;
43
+ }
44
+
45
+ log.info('[ZscalerConfig] Searching for Zscaler certificate in Windows store...');
46
+
47
+ try {
48
+ // PowerShell script to find and export Zscaler certificate
49
+ const psScript = `
50
+ $ErrorActionPreference = 'Stop'
51
+
52
+ # Search in multiple certificate stores
53
+ $stores = @('Root', 'CA', 'AuthRoot', 'Trust', 'My')
54
+ $zscalerCert = $null
55
+
56
+ foreach ($storeName in $stores) {
57
+ try {
58
+ $store = New-Object System.Security.Cryptography.X509Certificates.X509Store($storeName, 'LocalMachine')
59
+ $store.Open('ReadOnly')
60
+
61
+ # Look for certificates with Zscaler in the subject or issuer
62
+ $certs = $store.Certificates | Where-Object {
63
+ $_.Subject -like '*Zscaler*' -or
64
+ $_.Issuer -like '*Zscaler*' -or
65
+ $_.FriendlyName -like '*Zscaler*'
66
+ }
67
+
68
+ if ($certs) {
69
+ $zscalerCert = $certs[0]
70
+ Write-Host "FOUND:$storeName"
71
+ Write-Host "SUBJECT:$($zscalerCert.Subject)"
72
+ Write-Host "ISSUER:$($zscalerCert.Issuer)"
73
+ Write-Host "THUMBPRINT:$($zscalerCert.Thumbprint)"
74
+ Write-Host "NOTAFTER:$($zscalerCert.NotAfter)"
75
+
76
+ # Export certificate to Base64
77
+ $base64 = [System.Convert]::ToBase64String($zscalerCert.RawData)
78
+ Write-Host "-----BEGIN CERTIFICATE-----"
79
+ # Output in 64-character lines
80
+ for ($i = 0; $i -lt $base64.Length; $i += 64) {
81
+ $length = [Math]::Min(64, $base64.Length - $i)
82
+ Write-Host $base64.Substring($i, $length)
83
+ }
84
+ Write-Host "-----END CERTIFICATE-----"
85
+
86
+ $store.Close()
87
+ exit 0
88
+ }
89
+
90
+ $store.Close()
91
+ } catch {
92
+ # Continue to next store
93
+ }
94
+ }
95
+
96
+ # Also check CurrentUser stores
97
+ foreach ($storeName in $stores) {
98
+ try {
99
+ $store = New-Object System.Security.Cryptography.X509Certificates.X509Store($storeName, 'CurrentUser')
100
+ $store.Open('ReadOnly')
101
+
102
+ $certs = $store.Certificates | Where-Object {
103
+ $_.Subject -like '*Zscaler*' -or
104
+ $_.Issuer -like '*Zscaler*' -or
105
+ $_.FriendlyName -like '*Zscaler*'
106
+ }
107
+
108
+ if ($certs) {
109
+ $zscalerCert = $certs[0]
110
+ Write-Host "FOUND:$storeName (CurrentUser)"
111
+ Write-Host "SUBJECT:$($zscalerCert.Subject)"
112
+ Write-Host "ISSUER:$($zscalerCert.Issuer)"
113
+ Write-Host "THUMBPRINT:$($zscalerCert.Thumbprint)"
114
+ Write-Host "NOTAFTER:$($zscalerCert.NotAfter)"
115
+
116
+ # Export certificate to Base64
117
+ $base64 = [System.Convert]::ToBase64String($zscalerCert.RawData)
118
+ Write-Host "-----BEGIN CERTIFICATE-----"
119
+ for ($i = 0; $i -lt $base64.Length; $i += 64) {
120
+ $length = [Math]::Min(64, $base64.Length - $i)
121
+ Write-Host $base64.Substring($i, $length)
122
+ }
123
+ Write-Host "-----END CERTIFICATE-----"
124
+
125
+ $store.Close()
126
+ exit 0
127
+ }
128
+
129
+ $store.Close()
130
+ } catch {
131
+ # Continue
132
+ }
133
+ }
134
+
135
+ Write-Host "NOTFOUND:No Zscaler certificate found in Windows stores"
136
+ exit 1
137
+ `.replace(/\n/g, ' ');
138
+
139
+ const { stdout } = await execAsync(
140
+ `powershell -NoProfile -NonInteractive -Command "${psScript}"`,
141
+ { maxBuffer: 10 * 1024 * 1024 } // 10MB buffer for certificate data
142
+ );
143
+
144
+ // Parse the output
145
+ if (stdout.includes('-----BEGIN CERTIFICATE-----')) {
146
+ log.info('[ZscalerConfig] Found Zscaler certificate in Windows store');
147
+
148
+ // Extract certificate info
149
+ const lines = stdout.split('\n');
150
+ let certInfo: any = {};
151
+ let certPEM = '';
152
+ let inCert = false;
153
+
154
+ for (const line of lines) {
155
+ if (line.startsWith('FOUND:')) {
156
+ certInfo.store = line.substring(6).trim();
157
+ } else if (line.startsWith('SUBJECT:')) {
158
+ certInfo.subject = line.substring(8).trim();
159
+ } else if (line.startsWith('ISSUER:')) {
160
+ certInfo.issuer = line.substring(7).trim();
161
+ } else if (line.startsWith('THUMBPRINT:')) {
162
+ certInfo.thumbprint = line.substring(11).trim();
163
+ } else if (line.startsWith('NOTAFTER:')) {
164
+ certInfo.notAfter = line.substring(9).trim();
165
+ } else if (line.includes('-----BEGIN CERTIFICATE-----')) {
166
+ inCert = true;
167
+ certPEM += line + '\n';
168
+ } else if (line.includes('-----END CERTIFICATE-----')) {
169
+ certPEM += line + '\n';
170
+ inCert = false;
171
+ } else if (inCert) {
172
+ certPEM += line.trim() + '\n';
173
+ }
174
+ }
175
+
176
+ log.info('[ZscalerConfig] Certificate info:', certInfo);
177
+
178
+ // Save certificate to file (PEM files are text files, use UTF-8)
179
+ const certPath = path.join(this.tempCertDir, 'zscaler-root.pem');
180
+ fs.writeFileSync(certPath, certPEM, 'utf-8');
181
+ log.info('[ZscalerConfig] Saved certificate to:', certPath);
182
+
183
+ // Cache the certificate
184
+ this.certificateCache.set('zscaler', certPath);
185
+
186
+ return certPath;
187
+ } else {
188
+ log.info('[ZscalerConfig] No Zscaler certificate found in Windows store');
189
+ return null;
190
+ }
191
+ } catch (error) {
192
+ log.error('[ZscalerConfig] Error searching for certificate:', error);
193
+ return null;
194
+ }
195
+ }
196
+
197
+ /**
198
+ * Export all trusted root certificates from Windows store
199
+ */
200
+ public async exportTrustedRootCertificates(): Promise<string | null> {
201
+ if (process.platform !== 'win32') {
202
+ return null;
203
+ }
204
+
205
+ log.info('[ZscalerConfig] Exporting trusted root certificates...');
206
+
207
+ try {
208
+ const psScript = `
209
+ $ErrorActionPreference = 'Stop'
210
+
211
+ # Get all certificates from Root store
212
+ $store = New-Object System.Security.Cryptography.X509Certificates.X509Store('Root', 'LocalMachine')
213
+ $store.Open('ReadOnly')
214
+
215
+ $certCount = 0
216
+
217
+ foreach ($cert in $store.Certificates) {
218
+ # Only export certificates that are currently valid
219
+ if ($cert.NotAfter -gt (Get-Date) -and $cert.NotBefore -lt (Get-Date)) {
220
+ $base64 = [System.Convert]::ToBase64String($cert.RawData)
221
+ Write-Host "-----BEGIN CERTIFICATE-----"
222
+ for ($i = 0; $i -lt $base64.Length; $i += 64) {
223
+ $length = [Math]::Min(64, $base64.Length - $i)
224
+ Write-Host $base64.Substring($i, $length)
225
+ }
226
+ Write-Host "-----END CERTIFICATE-----"
227
+ $certCount++
228
+ }
229
+ }
230
+
231
+ $store.Close()
232
+ Write-Host "EXPORTED:$certCount certificates"
233
+ `.replace(/\n/g, ' ');
234
+
235
+ const { stdout } = await execAsync(
236
+ `powershell -NoProfile -NonInteractive -Command "${psScript}"`,
237
+ { maxBuffer: 50 * 1024 * 1024 } // 50MB buffer for all certificates
238
+ );
239
+
240
+ if (stdout.includes('-----BEGIN CERTIFICATE-----')) {
241
+ // Save bundle to file
242
+ const bundlePath = path.join(this.tempCertDir, 'windows-ca-bundle.pem');
243
+
244
+ // Extract just the certificates
245
+ const certLines = stdout.split('\n');
246
+ let bundle = '';
247
+ let inCert = false;
248
+
249
+ for (const line of certLines) {
250
+ if (line.includes('-----BEGIN CERTIFICATE-----')) {
251
+ inCert = true;
252
+ bundle += line + '\n';
253
+ } else if (line.includes('-----END CERTIFICATE-----')) {
254
+ bundle += line + '\n';
255
+ inCert = false;
256
+ } else if (inCert) {
257
+ bundle += line.trim() + '\n';
258
+ }
259
+ }
260
+
261
+ fs.writeFileSync(bundlePath, bundle);
262
+ log.info('[ZscalerConfig] Exported Windows CA bundle to:', bundlePath);
263
+
264
+ return bundlePath;
265
+ }
266
+
267
+ return null;
268
+ } catch (error) {
269
+ log.error('[ZscalerConfig] Error exporting certificates:', error);
270
+ return null;
271
+ }
272
+ }
273
+
274
+ /**
275
+ * Find certificate by thumbprint
276
+ */
277
+ public async findCertificateByThumbprint(thumbprint: string): Promise<string | null> {
278
+ if (process.platform !== 'win32') {
279
+ return null;
280
+ }
281
+
282
+ // Check cache first
283
+ if (this.certificateCache.has(thumbprint)) {
284
+ return this.certificateCache.get(thumbprint)!;
285
+ }
286
+
287
+ try {
288
+ const psScript = `
289
+ $cert = Get-ChildItem -Path Cert:\\LocalMachine\\Root,Cert:\\LocalMachine\\CA,Cert:\\CurrentUser\\Root,Cert:\\CurrentUser\\CA -Recurse |
290
+ Where-Object { $_.Thumbprint -eq '${thumbprint}' } |
291
+ Select-Object -First 1
292
+
293
+ if ($cert) {
294
+ $base64 = [System.Convert]::ToBase64String($cert.RawData)
295
+ Write-Host "-----BEGIN CERTIFICATE-----"
296
+ for ($i = 0; $i -lt $base64.Length; $i += 64) {
297
+ $length = [Math]::Min(64, $base64.Length - $i)
298
+ Write-Host $base64.Substring($i, $length)
299
+ }
300
+ Write-Host "-----END CERTIFICATE-----"
301
+ }
302
+ `.replace(/\n/g, ' ');
303
+
304
+ const { stdout } = await execAsync(
305
+ `powershell -NoProfile -NonInteractive -Command "${psScript}"`
306
+ );
307
+
308
+ if (stdout.includes('-----BEGIN CERTIFICATE-----')) {
309
+ const certPath = path.join(this.tempCertDir, `cert-${thumbprint}.pem`);
310
+ fs.writeFileSync(certPath, stdout.trim());
311
+ this.certificateCache.set(thumbprint, certPath);
312
+ return certPath;
313
+ }
314
+
315
+ return null;
316
+ } catch (error) {
317
+ log.error('[ZscalerConfig] Error finding certificate by thumbprint:', error);
318
+ return null;
319
+ }
320
+ }
321
+
322
+ /**
323
+ * Check if running with elevated privileges (might be needed for some cert operations)
324
+ */
325
+ public async checkElevation(): Promise<boolean> {
326
+ if (process.platform !== 'win32') {
327
+ return false;
328
+ }
329
+
330
+ try {
331
+ const { stdout } = await execAsync(
332
+ 'powershell -Command "[Security.Principal.WindowsIdentity]::GetCurrent().Groups -contains \\"S-1-5-32-544\\""'
333
+ );
334
+ return stdout.trim().toLowerCase() === 'true';
335
+ } catch {
336
+ return false;
337
+ }
338
+ }
339
+
340
+ /**
341
+ * Get certificate info from a PEM file
342
+ */
343
+ public getCertificateInfo(pemPath: string): any {
344
+ try {
345
+ const pemContent = fs.readFileSync(pemPath, 'utf8');
346
+ const lines = pemContent.split('\n');
347
+
348
+ // Basic parsing - in production you'd use a proper X.509 parser
349
+ const info = {
350
+ path: pemPath,
351
+ exists: true,
352
+ size: fs.statSync(pemPath).size,
353
+ hash: crypto.createHash('sha256').update(pemContent).digest('hex')
354
+ };
355
+
356
+ return info;
357
+ } catch (error) {
358
+ return null;
359
+ }
360
+ }
361
+
362
+ /**
363
+ * Create a combined certificate bundle
364
+ */
365
+ public async createCombinedBundle(additionalCerts: string[] = []): Promise<string | null> {
366
+ try {
367
+ const bundlePath = path.join(this.tempCertDir, 'combined-ca-bundle.pem');
368
+ let combinedBundle = '';
369
+
370
+ // Add Windows trusted roots
371
+ const windowsBundle = await this.exportTrustedRootCertificates();
372
+ if (windowsBundle && fs.existsSync(windowsBundle)) {
373
+ combinedBundle += fs.readFileSync(windowsBundle, 'utf8') + '\n';
374
+ }
375
+
376
+ // Add Zscaler certificate
377
+ const zscalerCert = await this.findZscalerCertificate();
378
+ if (zscalerCert && fs.existsSync(zscalerCert)) {
379
+ combinedBundle += fs.readFileSync(zscalerCert, 'utf8') + '\n';
380
+ }
381
+
382
+ // Add any additional certificates
383
+ for (const certPath of additionalCerts) {
384
+ if (fs.existsSync(certPath)) {
385
+ combinedBundle += fs.readFileSync(certPath, 'utf8') + '\n';
386
+ }
387
+ }
388
+
389
+ if (combinedBundle) {
390
+ fs.writeFileSync(bundlePath, combinedBundle);
391
+ log.info('[ZscalerConfig] Created combined certificate bundle:', bundlePath);
392
+ return bundlePath;
393
+ }
394
+
395
+ return null;
396
+ } catch (error) {
397
+ log.error('[ZscalerConfig] Error creating combined bundle:', error);
398
+ return null;
399
+ }
400
+ }
401
+
402
+ /**
403
+ * Clean up old certificate files
404
+ */
405
+ public cleanupOldCertificates(daysOld: number = 7): void {
406
+ try {
407
+ const now = Date.now();
408
+ const files = fs.readdirSync(this.tempCertDir);
409
+
410
+ for (const file of files) {
411
+ const filePath = path.join(this.tempCertDir, file);
412
+ const stats = fs.statSync(filePath);
413
+ const age = (now - stats.mtime.getTime()) / (1000 * 60 * 60 * 24);
414
+
415
+ if (age > daysOld) {
416
+ fs.unlinkSync(filePath);
417
+ log.info(` Cleaned up old certificate: ${file}`);
418
+ }
419
+ }
420
+ } catch (error) {
421
+ log.error('[ZscalerConfig] Error cleaning up certificates:', error);
422
+ }
423
+ }
424
+ }
425
+
426
+ // Export singleton instance
427
+ export const windowsCertStore = new WindowsCertStore();