@etcsec-com/etc-collector 1.4.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 (617) hide show
  1. package/.env.example +60 -0
  2. package/.env.test.example +33 -0
  3. package/.github/workflows/ci.yml +83 -0
  4. package/.github/workflows/release.yml +246 -0
  5. package/.prettierrc.json +10 -0
  6. package/CHANGELOG.md +15 -0
  7. package/Dockerfile +57 -0
  8. package/LICENSE +190 -0
  9. package/README.md +194 -0
  10. package/dist/api/controllers/audit.controller.d.ts +21 -0
  11. package/dist/api/controllers/audit.controller.d.ts.map +1 -0
  12. package/dist/api/controllers/audit.controller.js +179 -0
  13. package/dist/api/controllers/audit.controller.js.map +1 -0
  14. package/dist/api/controllers/auth.controller.d.ts +16 -0
  15. package/dist/api/controllers/auth.controller.d.ts.map +1 -0
  16. package/dist/api/controllers/auth.controller.js +146 -0
  17. package/dist/api/controllers/auth.controller.js.map +1 -0
  18. package/dist/api/controllers/export.controller.d.ts +27 -0
  19. package/dist/api/controllers/export.controller.d.ts.map +1 -0
  20. package/dist/api/controllers/export.controller.js +80 -0
  21. package/dist/api/controllers/export.controller.js.map +1 -0
  22. package/dist/api/controllers/health.controller.d.ts +5 -0
  23. package/dist/api/controllers/health.controller.d.ts.map +1 -0
  24. package/dist/api/controllers/health.controller.js +16 -0
  25. package/dist/api/controllers/health.controller.js.map +1 -0
  26. package/dist/api/controllers/jobs.controller.d.ts +13 -0
  27. package/dist/api/controllers/jobs.controller.d.ts.map +1 -0
  28. package/dist/api/controllers/jobs.controller.js +125 -0
  29. package/dist/api/controllers/jobs.controller.js.map +1 -0
  30. package/dist/api/controllers/providers.controller.d.ts +15 -0
  31. package/dist/api/controllers/providers.controller.d.ts.map +1 -0
  32. package/dist/api/controllers/providers.controller.js +112 -0
  33. package/dist/api/controllers/providers.controller.js.map +1 -0
  34. package/dist/api/dto/AuditRequest.dto.d.ts +6 -0
  35. package/dist/api/dto/AuditRequest.dto.d.ts.map +1 -0
  36. package/dist/api/dto/AuditRequest.dto.js +3 -0
  37. package/dist/api/dto/AuditRequest.dto.js.map +1 -0
  38. package/dist/api/dto/AuditResponse.dto.d.ts +17 -0
  39. package/dist/api/dto/AuditResponse.dto.d.ts.map +1 -0
  40. package/dist/api/dto/AuditResponse.dto.js +3 -0
  41. package/dist/api/dto/AuditResponse.dto.js.map +1 -0
  42. package/dist/api/dto/TokenRequest.dto.d.ts +6 -0
  43. package/dist/api/dto/TokenRequest.dto.d.ts.map +1 -0
  44. package/dist/api/dto/TokenRequest.dto.js +3 -0
  45. package/dist/api/dto/TokenRequest.dto.js.map +1 -0
  46. package/dist/api/dto/TokenResponse.dto.d.ts +12 -0
  47. package/dist/api/dto/TokenResponse.dto.d.ts.map +1 -0
  48. package/dist/api/dto/TokenResponse.dto.js +3 -0
  49. package/dist/api/dto/TokenResponse.dto.js.map +1 -0
  50. package/dist/api/middlewares/authenticate.d.ts +12 -0
  51. package/dist/api/middlewares/authenticate.d.ts.map +1 -0
  52. package/dist/api/middlewares/authenticate.js +141 -0
  53. package/dist/api/middlewares/authenticate.js.map +1 -0
  54. package/dist/api/middlewares/errorHandler.d.ts +3 -0
  55. package/dist/api/middlewares/errorHandler.d.ts.map +1 -0
  56. package/dist/api/middlewares/errorHandler.js +30 -0
  57. package/dist/api/middlewares/errorHandler.js.map +1 -0
  58. package/dist/api/middlewares/rateLimit.d.ts +3 -0
  59. package/dist/api/middlewares/rateLimit.d.ts.map +1 -0
  60. package/dist/api/middlewares/rateLimit.js +34 -0
  61. package/dist/api/middlewares/rateLimit.js.map +1 -0
  62. package/dist/api/middlewares/validate.d.ts +4 -0
  63. package/dist/api/middlewares/validate.d.ts.map +1 -0
  64. package/dist/api/middlewares/validate.js +31 -0
  65. package/dist/api/middlewares/validate.js.map +1 -0
  66. package/dist/api/routes/audit.routes.d.ts +5 -0
  67. package/dist/api/routes/audit.routes.d.ts.map +1 -0
  68. package/dist/api/routes/audit.routes.js +24 -0
  69. package/dist/api/routes/audit.routes.js.map +1 -0
  70. package/dist/api/routes/auth.routes.d.ts +6 -0
  71. package/dist/api/routes/auth.routes.d.ts.map +1 -0
  72. package/dist/api/routes/auth.routes.js +22 -0
  73. package/dist/api/routes/auth.routes.js.map +1 -0
  74. package/dist/api/routes/export.routes.d.ts +5 -0
  75. package/dist/api/routes/export.routes.d.ts.map +1 -0
  76. package/dist/api/routes/export.routes.js +16 -0
  77. package/dist/api/routes/export.routes.js.map +1 -0
  78. package/dist/api/routes/health.routes.d.ts +4 -0
  79. package/dist/api/routes/health.routes.d.ts.map +1 -0
  80. package/dist/api/routes/health.routes.js +11 -0
  81. package/dist/api/routes/health.routes.js.map +1 -0
  82. package/dist/api/routes/index.d.ts +10 -0
  83. package/dist/api/routes/index.d.ts.map +1 -0
  84. package/dist/api/routes/index.js +20 -0
  85. package/dist/api/routes/index.js.map +1 -0
  86. package/dist/api/routes/providers.routes.d.ts +5 -0
  87. package/dist/api/routes/providers.routes.d.ts.map +1 -0
  88. package/dist/api/routes/providers.routes.js +13 -0
  89. package/dist/api/routes/providers.routes.js.map +1 -0
  90. package/dist/api/validators/audit.schemas.d.ts +60 -0
  91. package/dist/api/validators/audit.schemas.d.ts.map +1 -0
  92. package/dist/api/validators/audit.schemas.js +55 -0
  93. package/dist/api/validators/audit.schemas.js.map +1 -0
  94. package/dist/api/validators/auth.schemas.d.ts +17 -0
  95. package/dist/api/validators/auth.schemas.d.ts.map +1 -0
  96. package/dist/api/validators/auth.schemas.js +21 -0
  97. package/dist/api/validators/auth.schemas.js.map +1 -0
  98. package/dist/app.d.ts +3 -0
  99. package/dist/app.d.ts.map +1 -0
  100. package/dist/app.js +62 -0
  101. package/dist/app.js.map +1 -0
  102. package/dist/config/config.schema.d.ts +65 -0
  103. package/dist/config/config.schema.d.ts.map +1 -0
  104. package/dist/config/config.schema.js +95 -0
  105. package/dist/config/config.schema.js.map +1 -0
  106. package/dist/config/index.d.ts +4 -0
  107. package/dist/config/index.d.ts.map +1 -0
  108. package/dist/config/index.js +75 -0
  109. package/dist/config/index.js.map +1 -0
  110. package/dist/container.d.ts +47 -0
  111. package/dist/container.d.ts.map +1 -0
  112. package/dist/container.js +137 -0
  113. package/dist/container.js.map +1 -0
  114. package/dist/data/database.d.ts +13 -0
  115. package/dist/data/database.d.ts.map +1 -0
  116. package/dist/data/database.js +68 -0
  117. package/dist/data/database.js.map +1 -0
  118. package/dist/data/jobs/token-cleanup.job.d.ts +23 -0
  119. package/dist/data/jobs/token-cleanup.job.d.ts.map +1 -0
  120. package/dist/data/jobs/token-cleanup.job.js +96 -0
  121. package/dist/data/jobs/token-cleanup.job.js.map +1 -0
  122. package/dist/data/migrations/migration.runner.d.ts +13 -0
  123. package/dist/data/migrations/migration.runner.d.ts.map +1 -0
  124. package/dist/data/migrations/migration.runner.js +136 -0
  125. package/dist/data/migrations/migration.runner.js.map +1 -0
  126. package/dist/data/models/Token.model.d.ts +30 -0
  127. package/dist/data/models/Token.model.d.ts.map +1 -0
  128. package/dist/data/models/Token.model.js +3 -0
  129. package/dist/data/models/Token.model.js.map +1 -0
  130. package/dist/data/repositories/token.repository.d.ts +16 -0
  131. package/dist/data/repositories/token.repository.d.ts.map +1 -0
  132. package/dist/data/repositories/token.repository.js +97 -0
  133. package/dist/data/repositories/token.repository.js.map +1 -0
  134. package/dist/providers/azure/auth.provider.d.ts +5 -0
  135. package/dist/providers/azure/auth.provider.d.ts.map +1 -0
  136. package/dist/providers/azure/auth.provider.js +13 -0
  137. package/dist/providers/azure/auth.provider.js.map +1 -0
  138. package/dist/providers/azure/azure-errors.d.ts +40 -0
  139. package/dist/providers/azure/azure-errors.d.ts.map +1 -0
  140. package/dist/providers/azure/azure-errors.js +121 -0
  141. package/dist/providers/azure/azure-errors.js.map +1 -0
  142. package/dist/providers/azure/azure-retry.d.ts +41 -0
  143. package/dist/providers/azure/azure-retry.d.ts.map +1 -0
  144. package/dist/providers/azure/azure-retry.js +85 -0
  145. package/dist/providers/azure/azure-retry.js.map +1 -0
  146. package/dist/providers/azure/graph-client.d.ts +26 -0
  147. package/dist/providers/azure/graph-client.d.ts.map +1 -0
  148. package/dist/providers/azure/graph-client.js +146 -0
  149. package/dist/providers/azure/graph-client.js.map +1 -0
  150. package/dist/providers/azure/graph.provider.d.ts +23 -0
  151. package/dist/providers/azure/graph.provider.d.ts.map +1 -0
  152. package/dist/providers/azure/graph.provider.js +161 -0
  153. package/dist/providers/azure/graph.provider.js.map +1 -0
  154. package/dist/providers/azure/queries/app.queries.d.ts +6 -0
  155. package/dist/providers/azure/queries/app.queries.d.ts.map +1 -0
  156. package/dist/providers/azure/queries/app.queries.js +9 -0
  157. package/dist/providers/azure/queries/app.queries.js.map +1 -0
  158. package/dist/providers/azure/queries/policy.queries.d.ts +6 -0
  159. package/dist/providers/azure/queries/policy.queries.d.ts.map +1 -0
  160. package/dist/providers/azure/queries/policy.queries.js +9 -0
  161. package/dist/providers/azure/queries/policy.queries.js.map +1 -0
  162. package/dist/providers/azure/queries/user.queries.d.ts +7 -0
  163. package/dist/providers/azure/queries/user.queries.d.ts.map +1 -0
  164. package/dist/providers/azure/queries/user.queries.js +10 -0
  165. package/dist/providers/azure/queries/user.queries.js.map +1 -0
  166. package/dist/providers/interfaces/IGraphProvider.d.ts +31 -0
  167. package/dist/providers/interfaces/IGraphProvider.d.ts.map +1 -0
  168. package/dist/providers/interfaces/IGraphProvider.js +3 -0
  169. package/dist/providers/interfaces/IGraphProvider.js.map +1 -0
  170. package/dist/providers/interfaces/ILDAPProvider.d.ts +37 -0
  171. package/dist/providers/interfaces/ILDAPProvider.d.ts.map +1 -0
  172. package/dist/providers/interfaces/ILDAPProvider.js +3 -0
  173. package/dist/providers/interfaces/ILDAPProvider.js.map +1 -0
  174. package/dist/providers/ldap/acl-parser.d.ts +8 -0
  175. package/dist/providers/ldap/acl-parser.d.ts.map +1 -0
  176. package/dist/providers/ldap/acl-parser.js +157 -0
  177. package/dist/providers/ldap/acl-parser.js.map +1 -0
  178. package/dist/providers/ldap/ad-mappers.d.ts +8 -0
  179. package/dist/providers/ldap/ad-mappers.d.ts.map +1 -0
  180. package/dist/providers/ldap/ad-mappers.js +162 -0
  181. package/dist/providers/ldap/ad-mappers.js.map +1 -0
  182. package/dist/providers/ldap/ldap-client.d.ts +33 -0
  183. package/dist/providers/ldap/ldap-client.d.ts.map +1 -0
  184. package/dist/providers/ldap/ldap-client.js +195 -0
  185. package/dist/providers/ldap/ldap-client.js.map +1 -0
  186. package/dist/providers/ldap/ldap-errors.d.ts +48 -0
  187. package/dist/providers/ldap/ldap-errors.d.ts.map +1 -0
  188. package/dist/providers/ldap/ldap-errors.js +120 -0
  189. package/dist/providers/ldap/ldap-errors.js.map +1 -0
  190. package/dist/providers/ldap/ldap-retry.d.ts +14 -0
  191. package/dist/providers/ldap/ldap-retry.d.ts.map +1 -0
  192. package/dist/providers/ldap/ldap-retry.js +102 -0
  193. package/dist/providers/ldap/ldap-retry.js.map +1 -0
  194. package/dist/providers/ldap/ldap-sanitizer.d.ts +12 -0
  195. package/dist/providers/ldap/ldap-sanitizer.d.ts.map +1 -0
  196. package/dist/providers/ldap/ldap-sanitizer.js +104 -0
  197. package/dist/providers/ldap/ldap-sanitizer.js.map +1 -0
  198. package/dist/providers/ldap/ldap.provider.d.ts +21 -0
  199. package/dist/providers/ldap/ldap.provider.d.ts.map +1 -0
  200. package/dist/providers/ldap/ldap.provider.js +165 -0
  201. package/dist/providers/ldap/ldap.provider.js.map +1 -0
  202. package/dist/providers/ldap/queries/computer.queries.d.ts +6 -0
  203. package/dist/providers/ldap/queries/computer.queries.d.ts.map +1 -0
  204. package/dist/providers/ldap/queries/computer.queries.js +9 -0
  205. package/dist/providers/ldap/queries/computer.queries.js.map +1 -0
  206. package/dist/providers/ldap/queries/group.queries.d.ts +6 -0
  207. package/dist/providers/ldap/queries/group.queries.d.ts.map +1 -0
  208. package/dist/providers/ldap/queries/group.queries.js +9 -0
  209. package/dist/providers/ldap/queries/group.queries.js.map +1 -0
  210. package/dist/providers/ldap/queries/user.queries.d.ts +7 -0
  211. package/dist/providers/ldap/queries/user.queries.d.ts.map +1 -0
  212. package/dist/providers/ldap/queries/user.queries.js +10 -0
  213. package/dist/providers/ldap/queries/user.queries.js.map +1 -0
  214. package/dist/providers/smb/smb.provider.d.ts +68 -0
  215. package/dist/providers/smb/smb.provider.d.ts.map +1 -0
  216. package/dist/providers/smb/smb.provider.js +382 -0
  217. package/dist/providers/smb/smb.provider.js.map +1 -0
  218. package/dist/server.d.ts +2 -0
  219. package/dist/server.d.ts.map +1 -0
  220. package/dist/server.js +44 -0
  221. package/dist/server.js.map +1 -0
  222. package/dist/services/audit/ad-audit.service.d.ts +70 -0
  223. package/dist/services/audit/ad-audit.service.d.ts.map +1 -0
  224. package/dist/services/audit/ad-audit.service.js +1019 -0
  225. package/dist/services/audit/ad-audit.service.js.map +1 -0
  226. package/dist/services/audit/attack-graph.service.d.ts +62 -0
  227. package/dist/services/audit/attack-graph.service.d.ts.map +1 -0
  228. package/dist/services/audit/attack-graph.service.js +702 -0
  229. package/dist/services/audit/attack-graph.service.js.map +1 -0
  230. package/dist/services/audit/audit.service.d.ts +4 -0
  231. package/dist/services/audit/audit.service.d.ts.map +1 -0
  232. package/dist/services/audit/audit.service.js +10 -0
  233. package/dist/services/audit/audit.service.js.map +1 -0
  234. package/dist/services/audit/azure-audit.service.d.ts +37 -0
  235. package/dist/services/audit/azure-audit.service.d.ts.map +1 -0
  236. package/dist/services/audit/azure-audit.service.js +153 -0
  237. package/dist/services/audit/azure-audit.service.js.map +1 -0
  238. package/dist/services/audit/detectors/ad/accounts.detector.d.ts +37 -0
  239. package/dist/services/audit/detectors/ad/accounts.detector.d.ts.map +1 -0
  240. package/dist/services/audit/detectors/ad/accounts.detector.js +881 -0
  241. package/dist/services/audit/detectors/ad/accounts.detector.js.map +1 -0
  242. package/dist/services/audit/detectors/ad/adcs.detector.d.ts +21 -0
  243. package/dist/services/audit/detectors/ad/adcs.detector.d.ts.map +1 -0
  244. package/dist/services/audit/detectors/ad/adcs.detector.js +227 -0
  245. package/dist/services/audit/detectors/ad/adcs.detector.js.map +1 -0
  246. package/dist/services/audit/detectors/ad/advanced.detector.d.ts +63 -0
  247. package/dist/services/audit/detectors/ad/advanced.detector.d.ts.map +1 -0
  248. package/dist/services/audit/detectors/ad/advanced.detector.js +867 -0
  249. package/dist/services/audit/detectors/ad/advanced.detector.js.map +1 -0
  250. package/dist/services/audit/detectors/ad/attack-paths.detector.d.ts +16 -0
  251. package/dist/services/audit/detectors/ad/attack-paths.detector.d.ts.map +1 -0
  252. package/dist/services/audit/detectors/ad/attack-paths.detector.js +369 -0
  253. package/dist/services/audit/detectors/ad/attack-paths.detector.js.map +1 -0
  254. package/dist/services/audit/detectors/ad/compliance.detector.d.ts +28 -0
  255. package/dist/services/audit/detectors/ad/compliance.detector.d.ts.map +1 -0
  256. package/dist/services/audit/detectors/ad/compliance.detector.js +896 -0
  257. package/dist/services/audit/detectors/ad/compliance.detector.js.map +1 -0
  258. package/dist/services/audit/detectors/ad/computers.detector.d.ts +30 -0
  259. package/dist/services/audit/detectors/ad/computers.detector.d.ts.map +1 -0
  260. package/dist/services/audit/detectors/ad/computers.detector.js +799 -0
  261. package/dist/services/audit/detectors/ad/computers.detector.js.map +1 -0
  262. package/dist/services/audit/detectors/ad/gpo.detector.d.ts +17 -0
  263. package/dist/services/audit/detectors/ad/gpo.detector.d.ts.map +1 -0
  264. package/dist/services/audit/detectors/ad/gpo.detector.js +257 -0
  265. package/dist/services/audit/detectors/ad/gpo.detector.js.map +1 -0
  266. package/dist/services/audit/detectors/ad/groups.detector.d.ts +19 -0
  267. package/dist/services/audit/detectors/ad/groups.detector.d.ts.map +1 -0
  268. package/dist/services/audit/detectors/ad/groups.detector.js +488 -0
  269. package/dist/services/audit/detectors/ad/groups.detector.js.map +1 -0
  270. package/dist/services/audit/detectors/ad/index.d.ts +15 -0
  271. package/dist/services/audit/detectors/ad/index.d.ts.map +1 -0
  272. package/dist/services/audit/detectors/ad/index.js +51 -0
  273. package/dist/services/audit/detectors/ad/index.js.map +1 -0
  274. package/dist/services/audit/detectors/ad/kerberos.detector.d.ts +17 -0
  275. package/dist/services/audit/detectors/ad/kerberos.detector.d.ts.map +1 -0
  276. package/dist/services/audit/detectors/ad/kerberos.detector.js +293 -0
  277. package/dist/services/audit/detectors/ad/kerberos.detector.js.map +1 -0
  278. package/dist/services/audit/detectors/ad/monitoring.detector.d.ts +23 -0
  279. package/dist/services/audit/detectors/ad/monitoring.detector.d.ts.map +1 -0
  280. package/dist/services/audit/detectors/ad/monitoring.detector.js +328 -0
  281. package/dist/services/audit/detectors/ad/monitoring.detector.js.map +1 -0
  282. package/dist/services/audit/detectors/ad/network.detector.d.ts +39 -0
  283. package/dist/services/audit/detectors/ad/network.detector.d.ts.map +1 -0
  284. package/dist/services/audit/detectors/ad/network.detector.js +257 -0
  285. package/dist/services/audit/detectors/ad/network.detector.js.map +1 -0
  286. package/dist/services/audit/detectors/ad/password.detector.d.ts +14 -0
  287. package/dist/services/audit/detectors/ad/password.detector.d.ts.map +1 -0
  288. package/dist/services/audit/detectors/ad/password.detector.js +235 -0
  289. package/dist/services/audit/detectors/ad/password.detector.js.map +1 -0
  290. package/dist/services/audit/detectors/ad/permissions.detector.d.ts +20 -0
  291. package/dist/services/audit/detectors/ad/permissions.detector.d.ts.map +1 -0
  292. package/dist/services/audit/detectors/ad/permissions.detector.js +392 -0
  293. package/dist/services/audit/detectors/ad/permissions.detector.js.map +1 -0
  294. package/dist/services/audit/detectors/ad/trusts.detector.d.ts +11 -0
  295. package/dist/services/audit/detectors/ad/trusts.detector.d.ts.map +1 -0
  296. package/dist/services/audit/detectors/ad/trusts.detector.js +186 -0
  297. package/dist/services/audit/detectors/ad/trusts.detector.js.map +1 -0
  298. package/dist/services/audit/detectors/azure/app-security.detector.d.ts +11 -0
  299. package/dist/services/audit/detectors/azure/app-security.detector.d.ts.map +1 -0
  300. package/dist/services/audit/detectors/azure/app-security.detector.js +184 -0
  301. package/dist/services/audit/detectors/azure/app-security.detector.js.map +1 -0
  302. package/dist/services/audit/detectors/azure/conditional-access.detector.d.ts +10 -0
  303. package/dist/services/audit/detectors/azure/conditional-access.detector.d.ts.map +1 -0
  304. package/dist/services/audit/detectors/azure/conditional-access.detector.js +130 -0
  305. package/dist/services/audit/detectors/azure/conditional-access.detector.js.map +1 -0
  306. package/dist/services/audit/detectors/azure/privilege-security.detector.d.ts +8 -0
  307. package/dist/services/audit/detectors/azure/privilege-security.detector.d.ts.map +1 -0
  308. package/dist/services/audit/detectors/azure/privilege-security.detector.js +113 -0
  309. package/dist/services/audit/detectors/azure/privilege-security.detector.js.map +1 -0
  310. package/dist/services/audit/detectors/azure/user-security.detector.d.ts +14 -0
  311. package/dist/services/audit/detectors/azure/user-security.detector.d.ts.map +1 -0
  312. package/dist/services/audit/detectors/azure/user-security.detector.js +198 -0
  313. package/dist/services/audit/detectors/azure/user-security.detector.js.map +1 -0
  314. package/dist/services/audit/detectors/index.d.ts +2 -0
  315. package/dist/services/audit/detectors/index.d.ts.map +1 -0
  316. package/dist/services/audit/detectors/index.js +38 -0
  317. package/dist/services/audit/detectors/index.js.map +1 -0
  318. package/dist/services/audit/response-formatter.d.ts +176 -0
  319. package/dist/services/audit/response-formatter.d.ts.map +1 -0
  320. package/dist/services/audit/response-formatter.js +240 -0
  321. package/dist/services/audit/response-formatter.js.map +1 -0
  322. package/dist/services/audit/scoring.service.d.ts +15 -0
  323. package/dist/services/audit/scoring.service.d.ts.map +1 -0
  324. package/dist/services/audit/scoring.service.js +139 -0
  325. package/dist/services/audit/scoring.service.js.map +1 -0
  326. package/dist/services/auth/crypto.service.d.ts +19 -0
  327. package/dist/services/auth/crypto.service.d.ts.map +1 -0
  328. package/dist/services/auth/crypto.service.js +135 -0
  329. package/dist/services/auth/crypto.service.js.map +1 -0
  330. package/dist/services/auth/errors.d.ts +19 -0
  331. package/dist/services/auth/errors.d.ts.map +1 -0
  332. package/dist/services/auth/errors.js +46 -0
  333. package/dist/services/auth/errors.js.map +1 -0
  334. package/dist/services/auth/token.service.d.ts +41 -0
  335. package/dist/services/auth/token.service.d.ts.map +1 -0
  336. package/dist/services/auth/token.service.js +208 -0
  337. package/dist/services/auth/token.service.js.map +1 -0
  338. package/dist/services/config/config.service.d.ts +6 -0
  339. package/dist/services/config/config.service.d.ts.map +1 -0
  340. package/dist/services/config/config.service.js +64 -0
  341. package/dist/services/config/config.service.js.map +1 -0
  342. package/dist/services/export/export.service.d.ts +28 -0
  343. package/dist/services/export/export.service.d.ts.map +1 -0
  344. package/dist/services/export/export.service.js +28 -0
  345. package/dist/services/export/export.service.js.map +1 -0
  346. package/dist/services/export/formatters/csv.formatter.d.ts +8 -0
  347. package/dist/services/export/formatters/csv.formatter.d.ts.map +1 -0
  348. package/dist/services/export/formatters/csv.formatter.js +46 -0
  349. package/dist/services/export/formatters/csv.formatter.js.map +1 -0
  350. package/dist/services/export/formatters/json.formatter.d.ts +40 -0
  351. package/dist/services/export/formatters/json.formatter.d.ts.map +1 -0
  352. package/dist/services/export/formatters/json.formatter.js +58 -0
  353. package/dist/services/export/formatters/json.formatter.js.map +1 -0
  354. package/dist/services/jobs/azure-job-runner.d.ts +38 -0
  355. package/dist/services/jobs/azure-job-runner.d.ts.map +1 -0
  356. package/dist/services/jobs/azure-job-runner.js +199 -0
  357. package/dist/services/jobs/azure-job-runner.js.map +1 -0
  358. package/dist/services/jobs/index.d.ts +4 -0
  359. package/dist/services/jobs/index.d.ts.map +1 -0
  360. package/dist/services/jobs/index.js +20 -0
  361. package/dist/services/jobs/index.js.map +1 -0
  362. package/dist/services/jobs/job-runner.d.ts +64 -0
  363. package/dist/services/jobs/job-runner.d.ts.map +1 -0
  364. package/dist/services/jobs/job-runner.js +952 -0
  365. package/dist/services/jobs/job-runner.js.map +1 -0
  366. package/dist/services/jobs/job-store.d.ts +27 -0
  367. package/dist/services/jobs/job-store.d.ts.map +1 -0
  368. package/dist/services/jobs/job-store.js +261 -0
  369. package/dist/services/jobs/job-store.js.map +1 -0
  370. package/dist/services/jobs/job.types.d.ts +67 -0
  371. package/dist/services/jobs/job.types.d.ts.map +1 -0
  372. package/dist/services/jobs/job.types.js +36 -0
  373. package/dist/services/jobs/job.types.js.map +1 -0
  374. package/dist/types/ad.types.d.ts +74 -0
  375. package/dist/types/ad.types.d.ts.map +1 -0
  376. package/dist/types/ad.types.js +3 -0
  377. package/dist/types/ad.types.js.map +1 -0
  378. package/dist/types/adcs.types.d.ts +58 -0
  379. package/dist/types/adcs.types.d.ts.map +1 -0
  380. package/dist/types/adcs.types.js +38 -0
  381. package/dist/types/adcs.types.js.map +1 -0
  382. package/dist/types/attack-graph.types.d.ts +135 -0
  383. package/dist/types/attack-graph.types.d.ts.map +1 -0
  384. package/dist/types/attack-graph.types.js +58 -0
  385. package/dist/types/attack-graph.types.js.map +1 -0
  386. package/dist/types/audit.types.d.ts +34 -0
  387. package/dist/types/audit.types.d.ts.map +1 -0
  388. package/dist/types/audit.types.js +3 -0
  389. package/dist/types/audit.types.js.map +1 -0
  390. package/dist/types/azure.types.d.ts +61 -0
  391. package/dist/types/azure.types.d.ts.map +1 -0
  392. package/dist/types/azure.types.js +3 -0
  393. package/dist/types/azure.types.js.map +1 -0
  394. package/dist/types/config.types.d.ts +63 -0
  395. package/dist/types/config.types.d.ts.map +1 -0
  396. package/dist/types/config.types.js +3 -0
  397. package/dist/types/config.types.js.map +1 -0
  398. package/dist/types/error.types.d.ts +33 -0
  399. package/dist/types/error.types.d.ts.map +1 -0
  400. package/dist/types/error.types.js +70 -0
  401. package/dist/types/error.types.js.map +1 -0
  402. package/dist/types/finding.types.d.ts +133 -0
  403. package/dist/types/finding.types.d.ts.map +1 -0
  404. package/dist/types/finding.types.js +3 -0
  405. package/dist/types/finding.types.js.map +1 -0
  406. package/dist/types/gpo.types.d.ts +39 -0
  407. package/dist/types/gpo.types.d.ts.map +1 -0
  408. package/dist/types/gpo.types.js +15 -0
  409. package/dist/types/gpo.types.js.map +1 -0
  410. package/dist/types/token.types.d.ts +26 -0
  411. package/dist/types/token.types.d.ts.map +1 -0
  412. package/dist/types/token.types.js +3 -0
  413. package/dist/types/token.types.js.map +1 -0
  414. package/dist/types/trust.types.d.ts +45 -0
  415. package/dist/types/trust.types.d.ts.map +1 -0
  416. package/dist/types/trust.types.js +71 -0
  417. package/dist/types/trust.types.js.map +1 -0
  418. package/dist/utils/entity-converter.d.ts +17 -0
  419. package/dist/utils/entity-converter.d.ts.map +1 -0
  420. package/dist/utils/entity-converter.js +285 -0
  421. package/dist/utils/entity-converter.js.map +1 -0
  422. package/dist/utils/graph.util.d.ts +66 -0
  423. package/dist/utils/graph.util.d.ts.map +1 -0
  424. package/dist/utils/graph.util.js +382 -0
  425. package/dist/utils/graph.util.js.map +1 -0
  426. package/dist/utils/logger.d.ts +7 -0
  427. package/dist/utils/logger.d.ts.map +1 -0
  428. package/dist/utils/logger.js +86 -0
  429. package/dist/utils/logger.js.map +1 -0
  430. package/dist/utils/type-name-normalizer.d.ts +5 -0
  431. package/dist/utils/type-name-normalizer.d.ts.map +1 -0
  432. package/dist/utils/type-name-normalizer.js +218 -0
  433. package/dist/utils/type-name-normalizer.js.map +1 -0
  434. package/docker-compose.yml +26 -0
  435. package/docs/api/README.md +178 -0
  436. package/docs/api/openapi.yaml +1524 -0
  437. package/eslint.config.js +54 -0
  438. package/jest.config.js +38 -0
  439. package/package.json +97 -0
  440. package/scripts/fetch-ad-cert.sh +142 -0
  441. package/src/.gitkeep +0 -0
  442. package/src/api/.gitkeep +0 -0
  443. package/src/api/controllers/.gitkeep +0 -0
  444. package/src/api/controllers/audit.controller.ts +313 -0
  445. package/src/api/controllers/auth.controller.ts +258 -0
  446. package/src/api/controllers/export.controller.ts +153 -0
  447. package/src/api/controllers/health.controller.ts +16 -0
  448. package/src/api/controllers/jobs.controller.ts +187 -0
  449. package/src/api/controllers/providers.controller.ts +165 -0
  450. package/src/api/dto/.gitkeep +0 -0
  451. package/src/api/dto/AuditRequest.dto.ts +8 -0
  452. package/src/api/dto/AuditResponse.dto.ts +19 -0
  453. package/src/api/dto/TokenRequest.dto.ts +8 -0
  454. package/src/api/dto/TokenResponse.dto.ts +14 -0
  455. package/src/api/middlewares/.gitkeep +0 -0
  456. package/src/api/middlewares/authenticate.ts +203 -0
  457. package/src/api/middlewares/errorHandler.ts +54 -0
  458. package/src/api/middlewares/rateLimit.ts +35 -0
  459. package/src/api/middlewares/validate.ts +32 -0
  460. package/src/api/routes/.gitkeep +0 -0
  461. package/src/api/routes/audit.routes.ts +77 -0
  462. package/src/api/routes/auth.routes.ts +71 -0
  463. package/src/api/routes/export.routes.ts +34 -0
  464. package/src/api/routes/health.routes.ts +14 -0
  465. package/src/api/routes/index.ts +40 -0
  466. package/src/api/routes/providers.routes.ts +24 -0
  467. package/src/api/validators/.gitkeep +0 -0
  468. package/src/api/validators/audit.schemas.ts +59 -0
  469. package/src/api/validators/auth.schemas.ts +59 -0
  470. package/src/app.ts +87 -0
  471. package/src/config/.gitkeep +0 -0
  472. package/src/config/config.schema.ts +108 -0
  473. package/src/config/index.ts +82 -0
  474. package/src/container.ts +221 -0
  475. package/src/data/.gitkeep +0 -0
  476. package/src/data/database.ts +78 -0
  477. package/src/data/jobs/token-cleanup.job.ts +166 -0
  478. package/src/data/migrations/.gitkeep +0 -0
  479. package/src/data/migrations/001_initial_schema.sql +47 -0
  480. package/src/data/migrations/migration.runner.ts +125 -0
  481. package/src/data/models/.gitkeep +0 -0
  482. package/src/data/models/Token.model.ts +35 -0
  483. package/src/data/repositories/.gitkeep +0 -0
  484. package/src/data/repositories/token.repository.ts +160 -0
  485. package/src/providers/.gitkeep +0 -0
  486. package/src/providers/azure/.gitkeep +0 -0
  487. package/src/providers/azure/auth.provider.ts +14 -0
  488. package/src/providers/azure/azure-errors.ts +189 -0
  489. package/src/providers/azure/azure-retry.ts +168 -0
  490. package/src/providers/azure/graph-client.ts +315 -0
  491. package/src/providers/azure/graph.provider.ts +294 -0
  492. package/src/providers/azure/queries/app.queries.ts +9 -0
  493. package/src/providers/azure/queries/policy.queries.ts +9 -0
  494. package/src/providers/azure/queries/user.queries.ts +10 -0
  495. package/src/providers/interfaces/.gitkeep +0 -0
  496. package/src/providers/interfaces/IGraphProvider.ts +117 -0
  497. package/src/providers/interfaces/ILDAPProvider.ts +142 -0
  498. package/src/providers/ldap/.gitkeep +0 -0
  499. package/src/providers/ldap/acl-parser.ts +231 -0
  500. package/src/providers/ldap/ad-mappers.ts +280 -0
  501. package/src/providers/ldap/ldap-client.ts +259 -0
  502. package/src/providers/ldap/ldap-errors.ts +188 -0
  503. package/src/providers/ldap/ldap-retry.ts +267 -0
  504. package/src/providers/ldap/ldap-sanitizer.ts +273 -0
  505. package/src/providers/ldap/ldap.provider.ts +293 -0
  506. package/src/providers/ldap/queries/computer.queries.ts +9 -0
  507. package/src/providers/ldap/queries/group.queries.ts +9 -0
  508. package/src/providers/ldap/queries/user.queries.ts +10 -0
  509. package/src/providers/smb/smb.provider.ts +653 -0
  510. package/src/server.ts +60 -0
  511. package/src/services/.gitkeep +0 -0
  512. package/src/services/audit/.gitkeep +0 -0
  513. package/src/services/audit/ad-audit.service.ts +1481 -0
  514. package/src/services/audit/attack-graph.service.ts +1104 -0
  515. package/src/services/audit/audit.service.ts +12 -0
  516. package/src/services/audit/azure-audit.service.ts +286 -0
  517. package/src/services/audit/detectors/ad/accounts.detector.ts +1232 -0
  518. package/src/services/audit/detectors/ad/adcs.detector.ts +449 -0
  519. package/src/services/audit/detectors/ad/advanced.detector.ts +1270 -0
  520. package/src/services/audit/detectors/ad/attack-paths.detector.ts +600 -0
  521. package/src/services/audit/detectors/ad/compliance.detector.ts +1421 -0
  522. package/src/services/audit/detectors/ad/computers.detector.ts +1188 -0
  523. package/src/services/audit/detectors/ad/gpo.detector.ts +485 -0
  524. package/src/services/audit/detectors/ad/groups.detector.ts +685 -0
  525. package/src/services/audit/detectors/ad/index.ts +84 -0
  526. package/src/services/audit/detectors/ad/kerberos.detector.ts +424 -0
  527. package/src/services/audit/detectors/ad/monitoring.detector.ts +501 -0
  528. package/src/services/audit/detectors/ad/network.detector.ts +538 -0
  529. package/src/services/audit/detectors/ad/password.detector.ts +324 -0
  530. package/src/services/audit/detectors/ad/permissions.detector.ts +637 -0
  531. package/src/services/audit/detectors/ad/trusts.detector.ts +315 -0
  532. package/src/services/audit/detectors/azure/app-security.detector.ts +246 -0
  533. package/src/services/audit/detectors/azure/conditional-access.detector.ts +186 -0
  534. package/src/services/audit/detectors/azure/privilege-security.detector.ts +176 -0
  535. package/src/services/audit/detectors/azure/user-security.detector.ts +280 -0
  536. package/src/services/audit/detectors/index.ts +18 -0
  537. package/src/services/audit/response-formatter.ts +604 -0
  538. package/src/services/audit/scoring.service.ts +234 -0
  539. package/src/services/auth/.gitkeep +0 -0
  540. package/src/services/auth/crypto.service.ts +230 -0
  541. package/src/services/auth/errors.ts +47 -0
  542. package/src/services/auth/token.service.ts +420 -0
  543. package/src/services/config/.gitkeep +0 -0
  544. package/src/services/config/config.service.ts +75 -0
  545. package/src/services/export/.gitkeep +0 -0
  546. package/src/services/export/export.service.ts +99 -0
  547. package/src/services/export/formatters/csv.formatter.ts +124 -0
  548. package/src/services/export/formatters/json.formatter.ts +160 -0
  549. package/src/services/jobs/azure-job-runner.ts +312 -0
  550. package/src/services/jobs/index.ts +9 -0
  551. package/src/services/jobs/job-runner.ts +1280 -0
  552. package/src/services/jobs/job-store.ts +384 -0
  553. package/src/services/jobs/job.types.ts +182 -0
  554. package/src/types/.gitkeep +0 -0
  555. package/src/types/ad.types.ts +91 -0
  556. package/src/types/adcs.types.ts +107 -0
  557. package/src/types/attack-graph.types.ts +260 -0
  558. package/src/types/audit.types.ts +42 -0
  559. package/src/types/azure.types.ts +68 -0
  560. package/src/types/config.types.ts +79 -0
  561. package/src/types/error.types.ts +69 -0
  562. package/src/types/finding.types.ts +284 -0
  563. package/src/types/gpo.types.ts +72 -0
  564. package/src/types/smb2.d.ts +73 -0
  565. package/src/types/token.types.ts +32 -0
  566. package/src/types/trust.types.ts +140 -0
  567. package/src/utils/.gitkeep +0 -0
  568. package/src/utils/entity-converter.ts +453 -0
  569. package/src/utils/graph.util.ts +609 -0
  570. package/src/utils/logger.ts +111 -0
  571. package/src/utils/type-name-normalizer.ts +302 -0
  572. package/tests/.gitkeep +0 -0
  573. package/tests/e2e/.gitkeep +0 -0
  574. package/tests/fixtures/.gitkeep +0 -0
  575. package/tests/integration/.gitkeep +0 -0
  576. package/tests/integration/README.md +156 -0
  577. package/tests/integration/ad-audit.integration.test.ts +216 -0
  578. package/tests/integration/api/.gitkeep +0 -0
  579. package/tests/integration/api/endpoints.integration.test.ts +431 -0
  580. package/tests/integration/auth/jwt-authentication.integration.test.ts +358 -0
  581. package/tests/integration/providers/.gitkeep +0 -0
  582. package/tests/integration/providers/azure-basic.integration.test.ts +167 -0
  583. package/tests/integration/providers/ldap-basic.integration.test.ts +152 -0
  584. package/tests/integration/providers/ldap-connectivity.test.ts +44 -0
  585. package/tests/integration/providers/ldap-provider.integration.test.ts +347 -0
  586. package/tests/mocks/.gitkeep +0 -0
  587. package/tests/setup.ts +16 -0
  588. package/tests/unit/.gitkeep +0 -0
  589. package/tests/unit/api/middlewares/authenticate.test.ts +446 -0
  590. package/tests/unit/providers/.gitkeep +0 -0
  591. package/tests/unit/providers/azure/azure-errors.test.ts +193 -0
  592. package/tests/unit/providers/azure/azure-retry.test.ts +254 -0
  593. package/tests/unit/providers/azure/graph-provider.test.ts +313 -0
  594. package/tests/unit/providers/ldap/ad-mappers.test.ts +392 -0
  595. package/tests/unit/providers/ldap/ldap-provider.test.ts +376 -0
  596. package/tests/unit/providers/ldap/ldap-retry.test.ts +377 -0
  597. package/tests/unit/providers/ldap/ldap-sanitizer.test.ts +301 -0
  598. package/tests/unit/sample.test.ts +19 -0
  599. package/tests/unit/services/.gitkeep +0 -0
  600. package/tests/unit/services/audit/detectors/ad/accounts.detector.test.ts +393 -0
  601. package/tests/unit/services/audit/detectors/ad/advanced.detector.test.ts +380 -0
  602. package/tests/unit/services/audit/detectors/ad/computers.detector.test.ts +440 -0
  603. package/tests/unit/services/audit/detectors/ad/groups.detector.test.ts +276 -0
  604. package/tests/unit/services/audit/detectors/ad/kerberos.detector.test.ts +215 -0
  605. package/tests/unit/services/audit/detectors/ad/password.detector.test.ts +226 -0
  606. package/tests/unit/services/audit/detectors/ad/permissions.detector.test.ts +244 -0
  607. package/tests/unit/services/audit/detectors/azure/app-security.detector.test.ts +349 -0
  608. package/tests/unit/services/audit/detectors/azure/conditional-access.detector.test.ts +374 -0
  609. package/tests/unit/services/audit/detectors/azure/privilege-security.detector.test.ts +374 -0
  610. package/tests/unit/services/audit/detectors/azure/user-security.detector.test.ts +297 -0
  611. package/tests/unit/services/auth/crypto.service.test.ts +296 -0
  612. package/tests/unit/services/auth/token.service.test.ts +579 -0
  613. package/tests/unit/services/export/export.service.test.ts +241 -0
  614. package/tests/unit/services/export/formatters/csv.formatter.test.ts +270 -0
  615. package/tests/unit/services/export/formatters/json.formatter.test.ts +258 -0
  616. package/tests/unit/utils/.gitkeep +0 -0
  617. package/tsconfig.json +50 -0
@@ -0,0 +1,1280 @@
1
+ /**
2
+ * Job Runner
3
+ *
4
+ * Executes AD audits asynchronously with step-by-step progress tracking.
5
+ * Each step reports progress to JobStore for polling clients.
6
+ */
7
+
8
+ import { LDAPProvider } from '../../providers/ldap/ldap.provider';
9
+ import { ADUser, ADGroup, ADComputer, ADDomain, AclEntry } from '../../types/ad.types';
10
+ import { LDAPControl } from '../../providers/interfaces/ILDAPProvider';
11
+ import { parseSecurityDescriptor, resetParseStats } from '../../providers/ldap/acl-parser';
12
+ import { Finding } from '../../types/finding.types';
13
+ import { calculateSecurityScore, SecurityScore } from '../audit/scoring.service';
14
+ import { DomainConfig } from '../audit/response-formatter';
15
+ import { logger } from '../../utils/logger';
16
+ import { JobStore } from './job-store';
17
+ import { AuditStepName, Job, JobType } from './job.types';
18
+ import { SMBProvider, formatKerberosPolicy, getDefaultKerberosPolicy, SMBConfig, FormattedKerberosPolicy } from '../../providers/smb/smb.provider';
19
+ import { SMBConfig as AppSMBConfig, LDAPConfig } from '../../types/config.types';
20
+
21
+ // Import all AD detectors
22
+ import {
23
+ detectPasswordVulnerabilities,
24
+ detectKerberosVulnerabilities,
25
+ detectAccountsVulnerabilities,
26
+ detectGroupsVulnerabilities,
27
+ detectComputersVulnerabilities,
28
+ detectAdvancedVulnerabilities,
29
+ detectPermissionsVulnerabilities,
30
+ detectAdcsVulnerabilities,
31
+ detectGpoVulnerabilities,
32
+ detectTrustVulnerabilities,
33
+ detectAttackPathVulnerabilities,
34
+ detectMonitoringVulnerabilities,
35
+ } from '../audit/detectors/ad';
36
+
37
+ // Import new types for ADCS, GPO, Trusts
38
+ import { ADCSCertificateTemplate, ADCSCertificateAuthority } from '../../types/adcs.types';
39
+ import { ADGPO, GPOLink } from '../../types/gpo.types';
40
+ import { ADTrustExtended, parseTrustAttributes, parseTrustDirection, parseTrustType } from '../../types/trust.types';
41
+ import { computeAttackGraph } from '../audit/attack-graph.service';
42
+ import { AttackGraphExport } from '../../types/attack-graph.types';
43
+
44
+ /**
45
+ * Create LDAP_SERVER_SD_FLAGS_OID control for reading Security Descriptors
46
+ */
47
+ function createSDFlagsControl(): LDAPControl {
48
+ const LDAP_SERVER_SD_FLAGS_OID = '1.2.840.113556.1.4.801';
49
+ const SD_FLAGS = 0x00000007;
50
+ const buffer = Buffer.from([0x02, 0x01, SD_FLAGS]);
51
+ return {
52
+ oid: LDAP_SERVER_SD_FLAGS_OID,
53
+ critical: false,
54
+ value: buffer,
55
+ };
56
+ }
57
+
58
+ /**
59
+ * Convert Windows FILETIME to JavaScript Date
60
+ */
61
+ function convertFiletimeToDate(filetime: any): Date | undefined {
62
+ if (!filetime) return undefined;
63
+
64
+ let filetimeNum: number;
65
+ if (typeof filetime === 'string') {
66
+ filetimeNum = parseInt(filetime, 10);
67
+ } else if (typeof filetime === 'number') {
68
+ filetimeNum = filetime;
69
+ } else {
70
+ return undefined;
71
+ }
72
+
73
+ if (filetimeNum === 0 || filetimeNum >= 9223372036854775807) {
74
+ return undefined;
75
+ }
76
+
77
+ const milliseconds = filetimeNum / 10000;
78
+ const epochOffset = 11644473600000;
79
+ const unixTimestamp = milliseconds - epochOffset;
80
+
81
+ if (unixTimestamp < 0 || unixTimestamp > Date.now() + 100 * 365 * 24 * 60 * 60 * 1000) {
82
+ return undefined;
83
+ }
84
+
85
+ return new Date(unixTimestamp);
86
+ }
87
+
88
+ /**
89
+ * Audit options for job runner
90
+ */
91
+ export interface JobRunnerOptions {
92
+ includeDetails?: boolean;
93
+ maxUsers?: number;
94
+ maxGroups?: number;
95
+ maxComputers?: number;
96
+ }
97
+
98
+ /**
99
+ * Audit result with all findings
100
+ */
101
+ export interface AuditResult {
102
+ score: SecurityScore;
103
+ findings: Finding[];
104
+ stats: {
105
+ totalUsers: number;
106
+ enabledUsers: number;
107
+ disabledUsers: number;
108
+ totalGroups: number;
109
+ totalComputers: number;
110
+ totalOUs: number;
111
+ totalFindings: number;
112
+ executionTimeMs: number;
113
+ };
114
+ timestamp: Date;
115
+ domainConfig?: DomainConfig;
116
+ attackGraph?: AttackGraphExport;
117
+ }
118
+
119
+ /**
120
+ * Job Runner - Executes AD audits with progress tracking
121
+ */
122
+ export class JobRunner {
123
+ private jobStore: JobStore;
124
+ private ldapProvider: LDAPProvider;
125
+ private smbConfig?: { smb: AppSMBConfig; ldap: LDAPConfig };
126
+
127
+ constructor(ldapProvider: LDAPProvider, smbConfig?: { smb: AppSMBConfig; ldap: LDAPConfig }) {
128
+ this.jobStore = JobStore.getInstance();
129
+ this.ldapProvider = ldapProvider;
130
+ this.smbConfig = smbConfig;
131
+ }
132
+
133
+ /**
134
+ * Start an async AD audit job
135
+ * Returns immediately with job ID, audit runs in background
136
+ */
137
+ startAudit(options: JobRunnerOptions = {}): Job {
138
+ const job = this.jobStore.createJob({
139
+ type: 'ad-audit' as JobType,
140
+ options: options as Record<string, unknown>,
141
+ });
142
+
143
+ // Start audit in background (don't await)
144
+ this.runAuditAsync(job.job_id, options).catch((error) => {
145
+ logger.error('Async audit failed', { job_id: job.job_id, error });
146
+ });
147
+
148
+ return job;
149
+ }
150
+
151
+ /**
152
+ * Run the audit asynchronously with step progress updates
153
+ */
154
+ private async runAuditAsync(jobId: string, options: JobRunnerOptions): Promise<void> {
155
+ const startTime = Date.now();
156
+ const { includeDetails = false, maxUsers, maxGroups, maxComputers } = options;
157
+
158
+ try {
159
+ // Mark job as running
160
+ this.jobStore.startJob(jobId);
161
+
162
+ // ===== STEP 1: CONNECTING =====
163
+ this.jobStore.startStep(jobId, 'CONNECTING', 'Connecting to LDAP server');
164
+ const connectionTest = await this.ldapProvider.testConnection();
165
+ if (!connectionTest.success) {
166
+ throw new Error(`LDAP connection failed: ${connectionTest.message}`);
167
+ }
168
+ this.jobStore.completeStep(jobId, 'CONNECTING');
169
+
170
+ // ===== STEP 2: FETCHING_USERS =====
171
+ this.jobStore.startStep(jobId, 'FETCHING_USERS', 'Fetching users from Active Directory');
172
+ const users = await this.fetchUsers(maxUsers);
173
+ this.jobStore.completeStep(jobId, 'FETCHING_USERS', { count: users.length });
174
+
175
+ // ===== STEP 3: FETCHING_GROUPS =====
176
+ this.jobStore.startStep(jobId, 'FETCHING_GROUPS', 'Fetching groups from Active Directory');
177
+ const groups = await this.fetchGroups(maxGroups);
178
+ this.jobStore.completeStep(jobId, 'FETCHING_GROUPS', { count: groups.length });
179
+
180
+ // ===== STEP 4: FETCHING_COMPUTERS =====
181
+ this.jobStore.startStep(jobId, 'FETCHING_COMPUTERS', 'Fetching computers from Active Directory');
182
+ const computers = await this.fetchComputers(maxComputers);
183
+ this.jobStore.completeStep(jobId, 'FETCHING_COMPUTERS', { count: computers.length });
184
+
185
+ // ===== STEP 5: FETCHING_DOMAIN =====
186
+ this.jobStore.startStep(jobId, 'FETCHING_DOMAIN', 'Fetching domain information and OUs');
187
+ const [domain, ouCount] = await Promise.all([
188
+ this.fetchDomain(),
189
+ this.fetchOUCount(),
190
+ ]);
191
+ this.jobStore.completeStep(jobId, 'FETCHING_DOMAIN', { count: ouCount });
192
+
193
+ // ===== STEP 6: FETCHING_ACLS =====
194
+ this.jobStore.startStep(jobId, 'FETCHING_ACLS', 'Fetching security descriptors (ACLs)');
195
+ const aclEntries = await this.fetchAcls(users, groups, computers, jobId);
196
+ this.jobStore.completeStep(jobId, 'FETCHING_ACLS', { count: aclEntries.length });
197
+
198
+ // ===== STEP 6.5: FETCHING ADCS/GPO/TRUSTS =====
199
+ // Fetch certificate templates, CAs, GPOs, and extended trusts in parallel
200
+ const [certTemplates, certAuthorities, gpoData, trustsExtended] = await Promise.all([
201
+ this.fetchCertificateTemplates(),
202
+ this.fetchCertificateAuthorities(),
203
+ this.fetchGPOsWithAcls(),
204
+ this.fetchTrustsExtended(),
205
+ ]);
206
+
207
+ // Data for advanced detectors
208
+ const templates: any[] = certTemplates;
209
+ const cas: any[] = certAuthorities;
210
+ const fsps: any[] = [];
211
+
212
+ // All findings
213
+ const findings: Finding[] = [];
214
+
215
+ // ===== STEP 7: DETECTING_PASSWORDS =====
216
+ this.jobStore.startStep(jobId, 'DETECTING_PASSWORDS', 'Analyzing password vulnerabilities (7 checks)');
217
+ const passwordFindings = detectPasswordVulnerabilities(users, includeDetails);
218
+ findings.push(...passwordFindings);
219
+ this.jobStore.completeStep(jobId, 'DETECTING_PASSWORDS', { findings: passwordFindings.length });
220
+
221
+ // ===== STEP 8: DETECTING_KERBEROS =====
222
+ this.jobStore.startStep(jobId, 'DETECTING_KERBEROS', 'Analyzing Kerberos vulnerabilities (8 checks)');
223
+ const kerberosFindings = detectKerberosVulnerabilities(users, includeDetails);
224
+ findings.push(...kerberosFindings);
225
+ this.jobStore.completeStep(jobId, 'DETECTING_KERBEROS', { findings: kerberosFindings.length });
226
+
227
+ // ===== STEP 9: DETECTING_ACCOUNTS =====
228
+ this.jobStore.startStep(jobId, 'DETECTING_ACCOUNTS', 'Analyzing account vulnerabilities (15 checks)');
229
+ const accountsFindings = detectAccountsVulnerabilities(users, includeDetails);
230
+ findings.push(...accountsFindings);
231
+ this.jobStore.completeStep(jobId, 'DETECTING_ACCOUNTS', { findings: accountsFindings.length });
232
+
233
+ // ===== STEP 10: DETECTING_GROUPS =====
234
+ this.jobStore.startStep(jobId, 'DETECTING_GROUPS', 'Analyzing group vulnerabilities (7 checks)');
235
+ const groupsFindings = detectGroupsVulnerabilities(users, groups, includeDetails);
236
+ findings.push(...groupsFindings);
237
+ this.jobStore.completeStep(jobId, 'DETECTING_GROUPS', { findings: groupsFindings.length });
238
+
239
+ // ===== STEP 11: DETECTING_COMPUTERS =====
240
+ this.jobStore.startStep(jobId, 'DETECTING_COMPUTERS', 'Analyzing computer vulnerabilities (17 checks)');
241
+ const computersFindings = detectComputersVulnerabilities(computers, includeDetails);
242
+ findings.push(...computersFindings);
243
+ this.jobStore.completeStep(jobId, 'DETECTING_COMPUTERS', { findings: computersFindings.length });
244
+
245
+ // ===== STEP 12: DETECTING_ADVANCED =====
246
+ this.jobStore.startStep(jobId, 'DETECTING_ADVANCED', 'Analyzing advanced vulnerabilities (22 checks)');
247
+ const advancedFindings = detectAdvancedVulnerabilities(
248
+ users,
249
+ computers,
250
+ domain,
251
+ templates,
252
+ cas,
253
+ fsps,
254
+ includeDetails
255
+ );
256
+ findings.push(...advancedFindings);
257
+ this.jobStore.completeStep(jobId, 'DETECTING_ADVANCED', { findings: advancedFindings.length });
258
+
259
+ // ===== STEP 13: DETECTING_PERMISSIONS =====
260
+ this.jobStore.startStep(jobId, 'DETECTING_PERMISSIONS', 'Analyzing permission vulnerabilities (9 checks)');
261
+ const permissionsFindings = detectPermissionsVulnerabilities(aclEntries, includeDetails);
262
+ findings.push(...permissionsFindings);
263
+ this.jobStore.completeStep(jobId, 'DETECTING_PERMISSIONS', { findings: permissionsFindings.length });
264
+
265
+ // ===== STEP 13.5: DETECTING_ADCS =====
266
+ this.jobStore.startStep(jobId, 'DETECTING_ADCS' as AuditStepName, 'Analyzing ADCS vulnerabilities (ESC1-ESC8)');
267
+ const adcsFindings = detectAdcsVulnerabilities(certTemplates, certAuthorities, includeDetails);
268
+ findings.push(...adcsFindings);
269
+ this.jobStore.completeStep(jobId, 'DETECTING_ADCS' as AuditStepName, { findings: adcsFindings.length });
270
+
271
+ // ===== STEP 13.6: DETECTING_GPO =====
272
+ this.jobStore.startStep(jobId, 'DETECTING_GPO' as AuditStepName, 'Analyzing GPO security (5 checks)');
273
+ const gpoFindings = detectGpoVulnerabilities(
274
+ gpoData.gpos,
275
+ gpoData.links,
276
+ domain ? { minPasswordLength: domain['minPwdLength'] as number | undefined } : null,
277
+ includeDetails
278
+ );
279
+ findings.push(...gpoFindings);
280
+ this.jobStore.completeStep(jobId, 'DETECTING_GPO' as AuditStepName, { findings: gpoFindings.length });
281
+
282
+ // ===== STEP 13.7: DETECTING_TRUSTS =====
283
+ this.jobStore.startStep(jobId, 'DETECTING_TRUSTS' as AuditStepName, 'Analyzing trust relationships (4 checks)');
284
+ const trustsFindings = detectTrustVulnerabilities(trustsExtended, includeDetails);
285
+ findings.push(...trustsFindings);
286
+ this.jobStore.completeStep(jobId, 'DETECTING_TRUSTS' as AuditStepName, { findings: trustsFindings.length });
287
+
288
+ // ===== STEP 13.8: DETECTING_ATTACK_PATHS =====
289
+ this.jobStore.startStep(jobId, 'DETECTING_ATTACK_PATHS' as AuditStepName, 'Analyzing attack paths (11 checks)');
290
+ const attackPathFindings = detectAttackPathVulnerabilities(
291
+ users,
292
+ groups,
293
+ computers,
294
+ aclEntries,
295
+ gpoData.gpos,
296
+ trustsExtended,
297
+ certTemplates,
298
+ includeDetails
299
+ );
300
+ findings.push(...attackPathFindings);
301
+ this.jobStore.completeStep(jobId, 'DETECTING_ATTACK_PATHS' as AuditStepName, { findings: attackPathFindings.length });
302
+
303
+ // ===== STEP 13.9: DETECTING_MONITORING =====
304
+ this.jobStore.startStep(jobId, 'DETECTING_MONITORING' as AuditStepName, 'Analyzing monitoring configuration (6 checks)');
305
+ const monitoringFindings = detectMonitoringVulnerabilities(users, groups, domain, includeDetails);
306
+ findings.push(...monitoringFindings);
307
+ this.jobStore.completeStep(jobId, 'DETECTING_MONITORING' as AuditStepName, { findings: monitoringFindings.length });
308
+
309
+ // ===== STEP 14: CALCULATING_SCORE =====
310
+ this.jobStore.startStep(jobId, 'CALCULATING_SCORE', 'Calculating security score');
311
+ const score = calculateSecurityScore(findings, users.length);
312
+ this.jobStore.completeStep(jobId, 'CALCULATING_SCORE');
313
+
314
+ // ===== STEP 15: FETCHING_CONFIG =====
315
+ this.jobStore.startStep(jobId, 'FETCHING_CONFIG', 'Fetching domain configuration');
316
+ const domainConfig = await this.fetchDomainConfig(domain);
317
+ this.jobStore.completeStep(jobId, 'FETCHING_CONFIG');
318
+
319
+ // ===== STEP 15.5: COMPUTING_ATTACK_GRAPH =====
320
+ this.jobStore.startStep(jobId, 'COMPUTING_ATTACK_GRAPH' as AuditStepName, 'Computing attack paths graph');
321
+ const baseDN = this.ldapProvider.getBaseDN();
322
+ const attackGraph = computeAttackGraph(
323
+ users,
324
+ groups,
325
+ computers,
326
+ aclEntries,
327
+ certTemplates,
328
+ gpoData.gpos,
329
+ {
330
+ name: domainConfig?.domainInfo?.domainName || this.extractDomainNameFromDN(baseDN),
331
+ sid: undefined,
332
+ },
333
+ 500
334
+ );
335
+ this.jobStore.completeStep(jobId, 'COMPUTING_ATTACK_GRAPH' as AuditStepName, {
336
+ count: attackGraph.paths.length,
337
+ });
338
+
339
+ // ===== STEP 16: FORMATTING =====
340
+ this.jobStore.startStep(jobId, 'FORMATTING', 'Formatting audit response');
341
+ const executionTimeMs = Date.now() - startTime;
342
+
343
+ // Calculate enabled/disabled user counts (UAC flag 0x2 = ACCOUNTDISABLE)
344
+ const disabledUsers = users.filter((u) => ((u.userAccountControl ?? 0) & 0x2) !== 0).length;
345
+ const enabledUsers = users.length - disabledUsers;
346
+
347
+ const result: AuditResult = {
348
+ score,
349
+ findings,
350
+ stats: {
351
+ totalUsers: users.length,
352
+ enabledUsers,
353
+ disabledUsers,
354
+ totalGroups: groups.length,
355
+ totalComputers: computers.length,
356
+ totalOUs: ouCount,
357
+ totalFindings: findings.length,
358
+ executionTimeMs,
359
+ },
360
+ timestamp: new Date(),
361
+ domainConfig,
362
+ attackGraph,
363
+ };
364
+ this.jobStore.completeStep(jobId, 'FORMATTING');
365
+
366
+ // Complete job with result
367
+ this.jobStore.completeJob(jobId, result);
368
+ } catch (error) {
369
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
370
+ const currentStep = this.jobStore.getJob(jobId)?.current_step || 'CONNECTING';
371
+
372
+ this.jobStore.failStep(jobId, currentStep as AuditStepName, errorMessage);
373
+ this.jobStore.failJob(jobId, {
374
+ code: 'AUDIT_FAILED',
375
+ message: errorMessage,
376
+ step: currentStep as AuditStepName,
377
+ });
378
+ }
379
+ }
380
+
381
+ /**
382
+ * Fetch users from AD
383
+ */
384
+ private async fetchUsers(maxUsers?: number): Promise<ADUser[]> {
385
+ const baseDN = this.ldapProvider.getBaseDN();
386
+ const filter = '(&(objectClass=user)(objectCategory=person))';
387
+ const attributes = [
388
+ // Identity
389
+ 'dn',
390
+ 'sAMAccountName',
391
+ 'userPrincipalName',
392
+ 'displayName',
393
+ 'mail',
394
+ // Organization
395
+ 'title',
396
+ 'department',
397
+ 'company',
398
+ 'manager',
399
+ 'physicalDeliveryOfficeName',
400
+ 'description',
401
+ 'employeeID',
402
+ 'telephoneNumber',
403
+ // Dates
404
+ 'whenCreated',
405
+ 'whenChanged',
406
+ 'lastLogon',
407
+ 'pwdLastSet',
408
+ 'passwordLastSet',
409
+ 'accountExpires',
410
+ // Security
411
+ 'badPwdCount',
412
+ 'lockoutTime',
413
+ 'adminCount',
414
+ 'memberOf',
415
+ 'userAccountControl',
416
+ // Technical (for detection)
417
+ 'servicePrincipalName',
418
+ 'msDS-SupportedEncryptionTypes',
419
+ 'sIDHistory',
420
+ 'msDS-KeyCredentialLink',
421
+ 'msDS-AllowedToActOnBehalfOfOtherIdentity',
422
+ 'msDS-AllowedToDelegateTo',
423
+ ];
424
+
425
+ const results = await this.ldapProvider.search<any>(baseDN, {
426
+ filter,
427
+ attributes,
428
+ scope: 'sub',
429
+ sizeLimit: maxUsers,
430
+ paged: true,
431
+ });
432
+
433
+ return results.map((entry: any) => ({
434
+ ...entry,
435
+ dn: entry.dn,
436
+ sAMAccountName: entry.sAMAccountName as string,
437
+ userPrincipalName: entry.userPrincipalName as string,
438
+ displayName: entry.displayName as string,
439
+ enabled: !((entry.userAccountControl as number) & 0x2),
440
+ passwordLastSet: convertFiletimeToDate(entry.passwordLastSet),
441
+ lastLogon: convertFiletimeToDate(entry.lastLogon),
442
+ accountExpires: convertFiletimeToDate(entry.accountExpires),
443
+ adminCount: entry.adminCount as number,
444
+ memberOf: !entry.memberOf ? [] : Array.isArray(entry.memberOf) ? entry.memberOf : [entry.memberOf],
445
+ servicePrincipalName: !entry.servicePrincipalName
446
+ ? []
447
+ : Array.isArray(entry.servicePrincipalName)
448
+ ? entry.servicePrincipalName
449
+ : [entry.servicePrincipalName],
450
+ userAccountControl: entry.userAccountControl as number,
451
+ }));
452
+ }
453
+
454
+ /**
455
+ * Fetch groups from AD
456
+ */
457
+ private async fetchGroups(maxGroups?: number): Promise<ADGroup[]> {
458
+ const baseDN = this.ldapProvider.getBaseDN();
459
+ const filter = '(objectClass=group)';
460
+ const attributes = ['dn', 'sAMAccountName', 'displayName', 'groupType', 'memberOf', 'member'];
461
+
462
+ const results = await this.ldapProvider.search<any>(baseDN, {
463
+ filter,
464
+ attributes,
465
+ scope: 'sub',
466
+ sizeLimit: maxGroups,
467
+ paged: true,
468
+ });
469
+
470
+ return results.map((entry: any) => ({
471
+ ...entry,
472
+ dn: entry.dn,
473
+ sAMAccountName: entry.sAMAccountName as string,
474
+ displayName: entry.displayName as string,
475
+ groupType: entry.groupType as number,
476
+ memberOf: !entry.memberOf ? [] : Array.isArray(entry.memberOf) ? entry.memberOf : [entry.memberOf],
477
+ member: !entry.member ? [] : Array.isArray(entry.member) ? entry.member : [entry.member],
478
+ }));
479
+ }
480
+
481
+ /**
482
+ * Fetch computers from AD
483
+ */
484
+ private async fetchComputers(maxComputers?: number): Promise<ADComputer[]> {
485
+ const baseDN = this.ldapProvider.getBaseDN();
486
+ const filter = '(objectClass=computer)';
487
+ const attributes = [
488
+ 'dn',
489
+ 'sAMAccountName',
490
+ 'dNSHostName',
491
+ 'operatingSystem',
492
+ 'operatingSystemVersion',
493
+ 'lastLogon',
494
+ 'userAccountControl',
495
+ 'pwdLastSet',
496
+ 'servicePrincipalName',
497
+ 'ms-Mcs-AdmPwd',
498
+ 'msLAPS-Password',
499
+ 'msDS-AllowedToDelegateTo',
500
+ 'msDS-AllowedToActOnBehalfOfOtherIdentity',
501
+ 'msDS-SupportedEncryptionTypes',
502
+ 'memberOf',
503
+ 'description',
504
+ 'whenChanged',
505
+ 'adminCount',
506
+ ];
507
+
508
+ const results = await this.ldapProvider.search<any>(baseDN, {
509
+ filter,
510
+ attributes,
511
+ scope: 'sub',
512
+ sizeLimit: maxComputers,
513
+ paged: true,
514
+ });
515
+
516
+ return results.map((entry: any) => ({
517
+ ...entry,
518
+ dn: entry.dn,
519
+ sAMAccountName: entry.sAMAccountName as string,
520
+ dNSHostName: entry.dNSHostName as string,
521
+ operatingSystem: entry.operatingSystem as string,
522
+ operatingSystemVersion: entry.operatingSystemVersion as string,
523
+ lastLogon: convertFiletimeToDate(entry.lastLogon),
524
+ pwdLastSet: convertFiletimeToDate(entry.pwdLastSet),
525
+ enabled: !((entry.userAccountControl as number) & 0x2),
526
+ memberOf: !entry.memberOf ? [] : Array.isArray(entry.memberOf) ? entry.memberOf : [entry.memberOf],
527
+ servicePrincipalName: !entry.servicePrincipalName
528
+ ? []
529
+ : Array.isArray(entry.servicePrincipalName)
530
+ ? entry.servicePrincipalName
531
+ : [entry.servicePrincipalName],
532
+ }));
533
+ }
534
+
535
+ /**
536
+ * Fetch domain configuration
537
+ */
538
+ private async fetchDomain(): Promise<ADDomain | null> {
539
+ try {
540
+ const baseDN = this.ldapProvider.getBaseDN();
541
+ const filter = '(objectClass=domain)';
542
+ const attributes = [
543
+ 'dn',
544
+ 'name',
545
+ 'msDS-Behavior-Version', // Domain functional level
546
+ 'minPwdLength',
547
+ 'maxPwdAge',
548
+ 'pwdHistoryLength',
549
+ 'ms-DS-MachineAccountQuota',
550
+ ];
551
+
552
+ const results = await this.ldapProvider.search<any>(baseDN, {
553
+ filter,
554
+ attributes,
555
+ scope: 'base',
556
+ });
557
+
558
+ if (results.length === 0) return null;
559
+
560
+ const entry: any = results[0];
561
+ const domainFunctionalLevel = entry['msDS-Behavior-Version'] !== undefined
562
+ ? parseInt(entry['msDS-Behavior-Version'], 10)
563
+ : undefined;
564
+
565
+ // Fetch forest functional level from Configuration partition
566
+ let forestFunctionalLevel: number | undefined;
567
+ try {
568
+ const configDN = `CN=Partitions,CN=Configuration,${baseDN}`;
569
+ const forestResults = await this.ldapProvider.search<any>(configDN, {
570
+ filter: '(objectClass=crossRefContainer)',
571
+ attributes: ['msDS-Behavior-Version'],
572
+ scope: 'base',
573
+ });
574
+ if (forestResults.length > 0 && forestResults[0]['msDS-Behavior-Version'] !== undefined) {
575
+ forestFunctionalLevel = parseInt(forestResults[0]['msDS-Behavior-Version'], 10);
576
+ }
577
+ } catch (e) {
578
+ // Forest level fetch failed, continue without it
579
+ }
580
+
581
+ return {
582
+ dn: entry.dn,
583
+ name: entry.name as string,
584
+ domainFunctionalLevel,
585
+ forestFunctionalLevel,
586
+ ...entry,
587
+ };
588
+ } catch (error) {
589
+ logger.error('Failed to fetch domain:', error);
590
+ return null;
591
+ }
592
+ }
593
+
594
+ /**
595
+ * Fetch OU count from AD
596
+ */
597
+ private async fetchOUCount(): Promise<number> {
598
+ try {
599
+ const baseDN = this.ldapProvider.getBaseDN();
600
+ const filter = '(objectClass=organizationalUnit)';
601
+
602
+ const results = await this.ldapProvider.search<any>(baseDN, {
603
+ filter,
604
+ attributes: ['dn'],
605
+ scope: 'sub',
606
+ paged: true,
607
+ });
608
+
609
+ return results.length;
610
+ } catch (error) {
611
+ logger.warn('Failed to fetch OU count', { error });
612
+ return 0;
613
+ }
614
+ }
615
+
616
+ /**
617
+ * Fetch ACLs from sensitive AD objects with progress updates
618
+ */
619
+ private async fetchAcls(
620
+ users: ADUser[],
621
+ groups: ADGroup[],
622
+ computers: ADComputer[],
623
+ jobId: string
624
+ ): Promise<AclEntry[]> {
625
+ const allAclEntries: AclEntry[] = [];
626
+ const baseDN = this.ldapProvider.getBaseDN();
627
+
628
+ try {
629
+ resetParseStats();
630
+
631
+ const totalObjects = users.length + groups.length + computers.length + 3; // +3 for system objects
632
+ let processed = 0;
633
+
634
+ // 1. Fetch ACLs for all users
635
+ const userDns = users.map((u) => u.dn);
636
+ const userAcls = await this.fetchAclsForObjects(userDns, (count) => {
637
+ processed += count;
638
+ this.jobStore.updateStepProgress(jobId, 'FETCHING_ACLS', {
639
+ progress: Math.round((processed / totalObjects) * 100),
640
+ description: `Fetching ACLs: ${processed}/${totalObjects} objects`,
641
+ });
642
+ });
643
+ for (const entry of userAcls) {
644
+ allAclEntries.push(entry);
645
+ }
646
+
647
+ // 2. Fetch ACLs for all groups
648
+ const groupDns = groups.map((g) => g.dn);
649
+ const groupAcls = await this.fetchAclsForObjects(groupDns, (count) => {
650
+ processed += count;
651
+ this.jobStore.updateStepProgress(jobId, 'FETCHING_ACLS', {
652
+ progress: Math.round((processed / totalObjects) * 100),
653
+ description: `Fetching ACLs: ${processed}/${totalObjects} objects`,
654
+ });
655
+ });
656
+ for (const entry of groupAcls) {
657
+ allAclEntries.push(entry);
658
+ }
659
+
660
+ // 3. Fetch ACLs for all computers
661
+ const computerDns = computers.map((c) => c.dn);
662
+ const computerAcls = await this.fetchAclsForObjects(computerDns, (count) => {
663
+ processed += count;
664
+ this.jobStore.updateStepProgress(jobId, 'FETCHING_ACLS', {
665
+ progress: Math.round((processed / totalObjects) * 100),
666
+ description: `Fetching ACLs: ${processed}/${totalObjects} objects`,
667
+ });
668
+ });
669
+ for (const entry of computerAcls) {
670
+ allAclEntries.push(entry);
671
+ }
672
+
673
+ // 4. Fetch ACLs for critical system objects
674
+ const systemObjects = [baseDN, `CN=AdminSDHolder,CN=System,${baseDN}`, `CN=Policies,CN=System,${baseDN}`];
675
+ const systemAcls = await this.fetchAclsForObjects(systemObjects, (count) => {
676
+ processed += count;
677
+ this.jobStore.updateStepProgress(jobId, 'FETCHING_ACLS', {
678
+ progress: Math.round((processed / totalObjects) * 100),
679
+ description: `Fetching ACLs: ${processed}/${totalObjects} objects`,
680
+ });
681
+ });
682
+ for (const entry of systemAcls) {
683
+ allAclEntries.push(entry);
684
+ }
685
+
686
+ return allAclEntries;
687
+ } catch (error) {
688
+ logger.warn('Failed to fetch ACL entries - insufficient permissions or unsupported configuration');
689
+ return [];
690
+ }
691
+ }
692
+
693
+ /**
694
+ * Fetch ACLs for a list of object DNs with progress callback
695
+ */
696
+ private async fetchAclsForObjects(
697
+ objectDns: string[],
698
+ onProgress?: (processedCount: number) => void
699
+ ): Promise<AclEntry[]> {
700
+ const allAclEntries: AclEntry[] = [];
701
+
702
+ const BATCH_SIZE = 100;
703
+ for (let i = 0; i < objectDns.length; i += BATCH_SIZE) {
704
+ const batch = objectDns.slice(i, i + BATCH_SIZE);
705
+
706
+ const batchPromises = batch.map(async (dn) => {
707
+ try {
708
+ const results = await this.ldapProvider.search<any>(dn, {
709
+ filter: '(objectClass=*)',
710
+ attributes: ['nTSecurityDescriptor'],
711
+ scope: 'base',
712
+ controls: [createSDFlagsControl()],
713
+ });
714
+
715
+ if (results.length > 0 && results[0].nTSecurityDescriptor) {
716
+ const secDescriptor = results[0].nTSecurityDescriptor;
717
+ const buffer = Buffer.isBuffer(secDescriptor)
718
+ ? secDescriptor
719
+ : Buffer.from(secDescriptor, 'binary');
720
+ const entries = parseSecurityDescriptor(buffer, dn);
721
+ return entries;
722
+ }
723
+ } catch (error) {
724
+ return [];
725
+ }
726
+ return [];
727
+ });
728
+
729
+ const batchResults = await Promise.all(batchPromises);
730
+ batchResults.forEach((entries) => allAclEntries.push(...entries));
731
+
732
+ // Report progress
733
+ if (onProgress) {
734
+ onProgress(batch.length);
735
+ }
736
+ }
737
+
738
+ return allAclEntries;
739
+ }
740
+
741
+ /**
742
+ * Fetch domain configuration
743
+ */
744
+ private async fetchDomainConfig(domain: ADDomain | null): Promise<DomainConfig> {
745
+ const baseDN = this.ldapProvider.getBaseDN();
746
+
747
+ const config: DomainConfig = {
748
+ passwordPolicy: {
749
+ minPasswordLength: 0,
750
+ passwordHistoryLength: 0,
751
+ maxPasswordAge: 'Not configured',
752
+ minPasswordAge: 'Not configured',
753
+ lockoutThreshold: 0,
754
+ lockoutDuration: 'Not configured',
755
+ lockoutObservationWindow: 'Not configured',
756
+ complexity: false,
757
+ },
758
+ kerberosPolicy: getDefaultKerberosPolicy(),
759
+ domainInfo: {
760
+ forestName: '',
761
+ domainName: '',
762
+ domainMode: 'Unknown',
763
+ forestMode: 'Unknown',
764
+ domainControllers: [],
765
+ fsmoRoles: {},
766
+ },
767
+ trusts: [],
768
+ gpoSummary: {
769
+ totalGPOs: 0,
770
+ linkedGPOs: 0,
771
+ },
772
+ };
773
+
774
+ try {
775
+ // Fetch password policy
776
+ const policyResults = await this.ldapProvider.search<any>(baseDN, {
777
+ filter: '(objectClass=domain)',
778
+ attributes: [
779
+ 'minPwdLength',
780
+ 'pwdHistoryLength',
781
+ 'maxPwdAge',
782
+ 'minPwdAge',
783
+ 'lockoutThreshold',
784
+ 'lockoutDuration',
785
+ 'lockOutObservationWindow',
786
+ 'pwdProperties',
787
+ 'name',
788
+ ],
789
+ scope: 'base',
790
+ });
791
+
792
+ if (policyResults.length > 0) {
793
+ const policy = policyResults[0];
794
+ config.passwordPolicy.minPasswordLength = parseInt(policy.minPwdLength || '0', 10);
795
+ config.passwordPolicy.passwordHistoryLength = parseInt(policy.pwdHistoryLength || '0', 10);
796
+ config.passwordPolicy.maxPasswordAge = this.formatFiletimeDuration(policy.maxPwdAge);
797
+ config.passwordPolicy.minPasswordAge = this.formatFiletimeDuration(policy.minPwdAge);
798
+ config.passwordPolicy.lockoutThreshold = parseInt(policy.lockoutThreshold || '0', 10);
799
+ config.passwordPolicy.lockoutDuration = this.formatFiletimeDuration(policy.lockoutDuration);
800
+ config.passwordPolicy.lockoutObservationWindow = this.formatFiletimeDuration(
801
+ policy.lockOutObservationWindow
802
+ );
803
+ config.passwordPolicy.complexity = (parseInt(policy.pwdProperties || '0', 10) & 1) === 1;
804
+ config.domainInfo.domainName = policy.name || '';
805
+ }
806
+
807
+ if (domain) {
808
+ config.domainInfo.domainMode = this.getDomainModeName(domain.domainFunctionalLevel);
809
+ config.domainInfo.forestMode = this.getDomainModeName(domain.forestFunctionalLevel);
810
+ config.domainInfo.forestName = this.extractDomainNameFromDN(baseDN);
811
+ config.domainInfo.domainName = this.extractDomainNameFromDN(baseDN);
812
+ }
813
+
814
+ // Fetch domain controllers
815
+ const dcResults = await this.ldapProvider.search<any>(baseDN, {
816
+ filter: '(&(objectClass=computer)(userAccountControl:1.2.840.113556.1.4.803:=8192))',
817
+ attributes: ['dNSHostName', 'name'],
818
+ scope: 'sub',
819
+ });
820
+
821
+ config.domainInfo.domainControllers = dcResults
822
+ .map((dc: any) => dc.dNSHostName || dc.name)
823
+ .filter((name: string) => name);
824
+
825
+ // Fetch FSMO roles
826
+ await this.fetchFSMORoles(config, baseDN);
827
+
828
+ // Fetch trusts
829
+ await this.fetchTrusts(config, baseDN);
830
+
831
+ // Fetch GPO count
832
+ await this.fetchGPOCount(config, baseDN);
833
+
834
+ // Fetch Kerberos policy via SMB (or use defaults)
835
+ const dcHostname = config.domainInfo.domainControllers[0];
836
+ if (dcHostname) {
837
+ const domainDnsName = this.extractDomainNameFromDN(baseDN);
838
+ config.kerberosPolicy = await this.fetchKerberosPolicyViaSMB(domainDnsName, dcHostname);
839
+ } else {
840
+ // No DC found, use defaults
841
+ config.kerberosPolicy = getDefaultKerberosPolicy();
842
+ }
843
+ } catch (error) {
844
+ logger.warn('Failed to fetch some domain configuration', { error });
845
+ }
846
+
847
+ return config;
848
+ }
849
+
850
+ private formatFiletimeDuration(filetime: any): string {
851
+ if (!filetime) return 'Not configured';
852
+ const value = typeof filetime === 'string' ? parseInt(filetime, 10) : filetime;
853
+ if (value === 0 || isNaN(value)) return 'Not configured';
854
+
855
+ const minutes = Math.abs(value) / (10000000 * 60);
856
+
857
+ if (minutes < 60) {
858
+ return `${Math.round(minutes)} min`;
859
+ } else if (minutes < 1440) {
860
+ const hours = Math.round(minutes / 60);
861
+ return `${hours} hour${hours > 1 ? 's' : ''}`;
862
+ } else {
863
+ const days = Math.round(minutes / 1440);
864
+ return `${days} day${days > 1 ? 's' : ''}`;
865
+ }
866
+ }
867
+
868
+ private getDomainModeName(level: number | undefined): string {
869
+ if (level === undefined) return 'Unknown';
870
+
871
+ const levels: Record<number, string> = {
872
+ 0: 'Windows2000Domain',
873
+ 1: 'Windows2003InterimDomain',
874
+ 2: 'Windows2003Domain',
875
+ 3: 'Windows2008Domain',
876
+ 4: 'Windows2008R2Domain',
877
+ 5: 'Windows2012Domain',
878
+ 6: 'Windows2012R2Domain',
879
+ 7: 'Windows2016Domain',
880
+ };
881
+
882
+ return levels[level] || `Unknown (${level})`;
883
+ }
884
+
885
+ private extractDomainNameFromDN(dn: string): string {
886
+ const parts = dn.match(/DC=([^,]+)/gi);
887
+ if (!parts) return dn;
888
+ return parts.map((p) => p.replace(/DC=/i, '')).join('.');
889
+ }
890
+
891
+ private async fetchFSMORoles(config: DomainConfig, baseDN: string): Promise<void> {
892
+ try {
893
+ const domainRoles = await this.ldapProvider.search<any>(baseDN, {
894
+ filter: '(objectClass=domain)',
895
+ attributes: ['fSMORoleOwner'],
896
+ scope: 'base',
897
+ });
898
+
899
+ if (domainRoles.length > 0 && domainRoles[0].fSMORoleOwner) {
900
+ config.domainInfo.fsmoRoles.pdcEmulator = this.extractServerFromDN(domainRoles[0].fSMORoleOwner);
901
+ }
902
+
903
+ const ridResults = await this.ldapProvider.search<any>(`CN=RID Manager$,CN=System,${baseDN}`, {
904
+ filter: '(objectClass=*)',
905
+ attributes: ['fSMORoleOwner'],
906
+ scope: 'base',
907
+ });
908
+
909
+ if (ridResults.length > 0 && ridResults[0].fSMORoleOwner) {
910
+ config.domainInfo.fsmoRoles.ridMaster = this.extractServerFromDN(ridResults[0].fSMORoleOwner);
911
+ }
912
+
913
+ const infraResults = await this.ldapProvider.search<any>(`CN=Infrastructure,${baseDN}`, {
914
+ filter: '(objectClass=*)',
915
+ attributes: ['fSMORoleOwner'],
916
+ scope: 'base',
917
+ });
918
+
919
+ if (infraResults.length > 0 && infraResults[0].fSMORoleOwner) {
920
+ config.domainInfo.fsmoRoles.infrastructureMaster = this.extractServerFromDN(
921
+ infraResults[0].fSMORoleOwner
922
+ );
923
+ }
924
+ } catch (error) {
925
+ logger.debug('Could not fetch all FSMO roles', { error });
926
+ }
927
+ }
928
+
929
+ private extractServerFromDN(dn: string): string {
930
+ const match = dn.match(/CN=NTDS Settings,CN=([^,]+)/i);
931
+ return match && match[1] ? match[1] : dn;
932
+ }
933
+
934
+ private async fetchTrusts(config: DomainConfig, baseDN: string): Promise<void> {
935
+ try {
936
+ const trustResults = await this.ldapProvider.search<any>(`CN=System,${baseDN}`, {
937
+ filter: '(objectClass=trustedDomain)',
938
+ attributes: ['name', 'trustDirection', 'trustType', 'trustAttributes'],
939
+ scope: 'one',
940
+ });
941
+
942
+ config.trusts = trustResults.map((trust: any) => {
943
+ const direction = parseInt(trust.trustDirection || '0', 10);
944
+ const trustType = parseInt(trust.trustType || '0', 10);
945
+ const attributes = parseInt(trust.trustAttributes || '0', 10);
946
+
947
+ return {
948
+ name: trust.name || 'Unknown',
949
+ direction: this.getTrustDirection(direction),
950
+ type: this.getTrustType(trustType),
951
+ transitive: (attributes & 1) === 1,
952
+ };
953
+ });
954
+ } catch (error) {
955
+ logger.debug('Could not fetch trust relationships', { error });
956
+ }
957
+ }
958
+
959
+ private getTrustDirection(direction: number): 'inbound' | 'outbound' | 'bidirectional' {
960
+ switch (direction) {
961
+ case 1:
962
+ return 'inbound';
963
+ case 2:
964
+ return 'outbound';
965
+ case 3:
966
+ return 'bidirectional';
967
+ default:
968
+ return 'inbound';
969
+ }
970
+ }
971
+
972
+ private getTrustType(type: number): 'forest' | 'external' | 'realm' | 'shortcut' {
973
+ switch (type) {
974
+ case 1:
975
+ return 'external';
976
+ case 2:
977
+ return 'external';
978
+ case 3:
979
+ return 'realm';
980
+ case 4:
981
+ return 'external';
982
+ default:
983
+ return 'external';
984
+ }
985
+ }
986
+
987
+ private async fetchGPOCount(config: DomainConfig, baseDN: string): Promise<void> {
988
+ try {
989
+ const gpoResults = await this.ldapProvider.search<any>(`CN=Policies,CN=System,${baseDN}`, {
990
+ filter: '(objectClass=groupPolicyContainer)',
991
+ attributes: ['cn', 'gPCFileSysPath'],
992
+ scope: 'one',
993
+ });
994
+
995
+ config.gpoSummary.totalGPOs = gpoResults.length;
996
+
997
+ const linkedResults = await this.ldapProvider.search<any>(baseDN, {
998
+ filter: '(gPLink=*)',
999
+ attributes: ['gPLink'],
1000
+ scope: 'sub',
1001
+ sizeLimit: 1000,
1002
+ });
1003
+
1004
+ const linkedGPOs = new Set<string>();
1005
+ linkedResults.forEach((obj: any) => {
1006
+ const gpLink = obj.gPLink;
1007
+ if (gpLink) {
1008
+ const matches = gpLink.match(/cn=\{[^}]+\}/gi);
1009
+ if (matches) {
1010
+ matches.forEach((m: string) => linkedGPOs.add(m.toLowerCase()));
1011
+ }
1012
+ }
1013
+ });
1014
+
1015
+ config.gpoSummary.linkedGPOs = linkedGPOs.size;
1016
+ } catch (error) {
1017
+ logger.debug('Could not fetch GPO count', { error });
1018
+ }
1019
+ }
1020
+
1021
+ /**
1022
+ * Fetch Kerberos policy from SYSVOL via SMB
1023
+ * Returns formatted policy with isDefault flag
1024
+ * Returns default Windows values if file not found or SMB disabled
1025
+ */
1026
+ private async fetchKerberosPolicyViaSMB(domainDnsName: string, dcHostname: string): Promise<FormattedKerberosPolicy> {
1027
+ // Check if SMB is enabled and we have config
1028
+ if (!this.smbConfig || !this.smbConfig.smb.enabled) {
1029
+ logger.debug('SMB is disabled, returning default Kerberos policy values');
1030
+ return getDefaultKerberosPolicy();
1031
+ }
1032
+
1033
+ const { smb, ldap } = this.smbConfig!;
1034
+
1035
+ // Use SMB credentials or fall back to LDAP credentials
1036
+ const username = smb.username || ldap.bindDN.split(',')[0]?.replace(/^CN=/i, '') || '';
1037
+ const password = smb.password || ldap.bindPassword;
1038
+
1039
+ const baseDNMatch = ldap.baseDN.match(/DC=([^,]+)/i);
1040
+ const smbProviderConfig: SMBConfig = {
1041
+ host: dcHostname,
1042
+ share: 'SYSVOL',
1043
+ domain: baseDNMatch?.[1] || '',
1044
+ username,
1045
+ password,
1046
+ timeout: smb.timeout,
1047
+ };
1048
+
1049
+ const smbProvider = new SMBProvider(smbProviderConfig);
1050
+
1051
+ try {
1052
+ await smbProvider.connect();
1053
+ const kerberosPolicy = await smbProvider.readKerberosPolicy(domainDnsName);
1054
+
1055
+ if (kerberosPolicy) {
1056
+ logger.debug('Successfully fetched Kerberos policy from SYSVOL', { domainDnsName });
1057
+ return formatKerberosPolicy(kerberosPolicy, false);
1058
+ }
1059
+
1060
+ // File not found - return Windows defaults
1061
+ logger.debug('GptTmpl.inf not found, using Windows default Kerberos policy values');
1062
+ return getDefaultKerberosPolicy();
1063
+ } catch (error) {
1064
+ logger.warn('Failed to fetch Kerberos policy via SMB, using defaults', { error, dcHostname });
1065
+ return getDefaultKerberosPolicy();
1066
+ } finally {
1067
+ await smbProvider.disconnect();
1068
+ }
1069
+ }
1070
+
1071
+ /**
1072
+ * Fetch ADCS Certificate Templates from Configuration partition
1073
+ */
1074
+ private async fetchCertificateTemplates(): Promise<ADCSCertificateTemplate[]> {
1075
+ try {
1076
+ const baseDN = this.ldapProvider.getBaseDN();
1077
+ const templatesDN = `CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,${baseDN}`;
1078
+
1079
+ const results = await this.ldapProvider.search<any>(templatesDN, {
1080
+ filter: '(objectClass=pKICertificateTemplate)',
1081
+ attributes: [
1082
+ 'dn',
1083
+ 'cn',
1084
+ 'name',
1085
+ 'displayName',
1086
+ 'msPKI-Certificate-Name-Flag',
1087
+ 'msPKI-Enrollment-Flag',
1088
+ 'pKIExtendedKeyUsage',
1089
+ 'nTSecurityDescriptor',
1090
+ ],
1091
+ scope: 'one',
1092
+ controls: [createSDFlagsControl()],
1093
+ });
1094
+
1095
+ return results.map((entry: any) => ({
1096
+ dn: entry.dn,
1097
+ cn: entry.cn || entry.name,
1098
+ name: entry.name || entry.cn,
1099
+ displayName: entry.displayName,
1100
+ 'msPKI-Certificate-Name-Flag': entry['msPKI-Certificate-Name-Flag']
1101
+ ? parseInt(entry['msPKI-Certificate-Name-Flag'], 10)
1102
+ : 0,
1103
+ 'msPKI-Enrollment-Flag': entry['msPKI-Enrollment-Flag']
1104
+ ? parseInt(entry['msPKI-Enrollment-Flag'], 10)
1105
+ : 0,
1106
+ pKIExtendedKeyUsage: !entry.pKIExtendedKeyUsage
1107
+ ? []
1108
+ : Array.isArray(entry.pKIExtendedKeyUsage)
1109
+ ? entry.pKIExtendedKeyUsage
1110
+ : [entry.pKIExtendedKeyUsage],
1111
+ nTSecurityDescriptor: entry.nTSecurityDescriptor,
1112
+ }));
1113
+ } catch (error) {
1114
+ logger.debug('Could not fetch certificate templates (ADCS may not be configured)', { error });
1115
+ return [];
1116
+ }
1117
+ }
1118
+
1119
+ /**
1120
+ * Fetch ADCS Certificate Authorities from Configuration partition
1121
+ */
1122
+ private async fetchCertificateAuthorities(): Promise<ADCSCertificateAuthority[]> {
1123
+ try {
1124
+ const baseDN = this.ldapProvider.getBaseDN();
1125
+ const enrollmentDN = `CN=Enrollment Services,CN=Public Key Services,CN=Services,CN=Configuration,${baseDN}`;
1126
+
1127
+ const results = await this.ldapProvider.search<any>(enrollmentDN, {
1128
+ filter: '(objectClass=pKIEnrollmentService)',
1129
+ attributes: [
1130
+ 'dn',
1131
+ 'cn',
1132
+ 'name',
1133
+ 'dNSHostName',
1134
+ 'certificateTemplates',
1135
+ 'nTSecurityDescriptor',
1136
+ ],
1137
+ scope: 'one',
1138
+ controls: [createSDFlagsControl()],
1139
+ });
1140
+
1141
+ return results.map((entry: any) => ({
1142
+ dn: entry.dn,
1143
+ cn: entry.cn || entry.name,
1144
+ name: entry.name || entry.cn,
1145
+ dNSHostName: entry.dNSHostName,
1146
+ certificateTemplates: !entry.certificateTemplates
1147
+ ? []
1148
+ : Array.isArray(entry.certificateTemplates)
1149
+ ? entry.certificateTemplates
1150
+ : [entry.certificateTemplates],
1151
+ nTSecurityDescriptor: entry.nTSecurityDescriptor,
1152
+ }));
1153
+ } catch (error) {
1154
+ logger.debug('Could not fetch certificate authorities (ADCS may not be configured)', { error });
1155
+ return [];
1156
+ }
1157
+ }
1158
+
1159
+ /**
1160
+ * Fetch GPOs with ACLs and their links
1161
+ */
1162
+ private async fetchGPOsWithAcls(): Promise<{ gpos: ADGPO[]; links: GPOLink[] }> {
1163
+ const baseDN = this.ldapProvider.getBaseDN();
1164
+ const gpos: ADGPO[] = [];
1165
+ const links: GPOLink[] = [];
1166
+
1167
+ try {
1168
+ // 1. Fetch all GPOs
1169
+ const gpoResults = await this.ldapProvider.search<any>(`CN=Policies,CN=System,${baseDN}`, {
1170
+ filter: '(objectClass=groupPolicyContainer)',
1171
+ attributes: [
1172
+ 'dn',
1173
+ 'cn',
1174
+ 'displayName',
1175
+ 'gPCFileSysPath',
1176
+ 'flags',
1177
+ 'gPCMachineExtensionNames',
1178
+ 'nTSecurityDescriptor',
1179
+ ],
1180
+ scope: 'one',
1181
+ controls: [createSDFlagsControl()],
1182
+ });
1183
+
1184
+ for (const entry of gpoResults) {
1185
+ const flags = entry.flags ? parseInt(entry.flags, 10) : 0;
1186
+ gpos.push({
1187
+ dn: entry.dn,
1188
+ cn: entry.cn,
1189
+ displayName: entry.displayName,
1190
+ gPCFileSysPath: entry.gPCFileSysPath,
1191
+ flags,
1192
+ gPCMachineExtensionNames: entry.gPCMachineExtensionNames,
1193
+ nTSecurityDescriptor: entry.nTSecurityDescriptor,
1194
+ });
1195
+ }
1196
+
1197
+ // 2. Fetch GPO links from OUs, domain, and sites
1198
+ const linkResults = await this.ldapProvider.search<any>(baseDN, {
1199
+ filter: '(gPLink=*)',
1200
+ attributes: ['dn', 'gPLink'],
1201
+ scope: 'sub',
1202
+ sizeLimit: 1000,
1203
+ });
1204
+
1205
+ for (const entry of linkResults) {
1206
+ const gpLink = entry.gPLink;
1207
+ if (!gpLink) continue;
1208
+
1209
+ // Parse gPLink format: [LDAP://cn={GUID},...;options][...]
1210
+ const linkMatches = gpLink.matchAll(/\[LDAP:\/\/([^\]]+);(\d+)\]/gi);
1211
+ for (const match of linkMatches) {
1212
+ const gpoDn = match[1];
1213
+ const options = parseInt(match[2], 10);
1214
+
1215
+ // Extract GUID from DN
1216
+ const guidMatch = gpoDn.match(/cn=(\{[^}]+\})/i);
1217
+ if (guidMatch) {
1218
+ links.push({
1219
+ gpoGuid: guidMatch[1],
1220
+ linkedTo: entry.dn,
1221
+ enforced: (options & 2) !== 0, // GPO_FLAG_ENFORCED
1222
+ disabled: (options & 1) !== 0, // GPO_FLAG_DISABLED
1223
+ });
1224
+ }
1225
+ }
1226
+ }
1227
+ } catch (error) {
1228
+ logger.debug('Could not fetch GPOs with ACLs', { error });
1229
+ }
1230
+
1231
+ return { gpos, links };
1232
+ }
1233
+
1234
+ /**
1235
+ * Fetch extended trust information with security attributes
1236
+ */
1237
+ private async fetchTrustsExtended(): Promise<ADTrustExtended[]> {
1238
+ try {
1239
+ const baseDN = this.ldapProvider.getBaseDN();
1240
+
1241
+ const results = await this.ldapProvider.search<any>(`CN=System,${baseDN}`, {
1242
+ filter: '(objectClass=trustedDomain)',
1243
+ attributes: [
1244
+ 'dn',
1245
+ 'name',
1246
+ 'trustDirection',
1247
+ 'trustType',
1248
+ 'trustAttributes',
1249
+ 'flatName',
1250
+ 'securityIdentifier',
1251
+ ],
1252
+ scope: 'one',
1253
+ });
1254
+
1255
+ return results.map((entry: any) => {
1256
+ const trustDirection = parseInt(entry.trustDirection || '0', 10);
1257
+ const trustType = parseInt(entry.trustType || '0', 10);
1258
+ const trustAttributes = parseInt(entry.trustAttributes || '0', 10);
1259
+
1260
+ // Parse and enrich trust data
1261
+ const parsed = parseTrustAttributes(trustAttributes);
1262
+
1263
+ return {
1264
+ dn: entry.dn,
1265
+ name: entry.name,
1266
+ flatName: entry.flatName,
1267
+ trustDirection,
1268
+ trustType,
1269
+ trustAttributes,
1270
+ direction: parseTrustDirection(trustDirection),
1271
+ type: parseTrustType(trustType, trustAttributes),
1272
+ ...parsed, // sidFilteringEnabled, selectiveAuthEnabled, isTransitive
1273
+ };
1274
+ });
1275
+ } catch (error) {
1276
+ logger.debug('Could not fetch extended trust information', { error });
1277
+ return [];
1278
+ }
1279
+ }
1280
+ }